Skip to content

Register interface

In assembly, registers are small storage units located inside the CPU that are used to temporarily store data, addresses, or control information. The main function of registers is to provide fast data access and operations when the CPU executes instructions. Registers play a very important role in assembly because they directly participate in the execution of instructions and data processing, providing underlying control over computer hardware.

LyScript provides standardized APIs for accessing and modifying register values. These API interfaces encapsulate operations to access registers, allowing users to directly use these functions to read and modify register values.

Universal Register

Universal registers are a type of register with a wide range of applications and functions, playing an important role in computer architecture. They are typically used to store data, addresses, and temporary results, as well as perform arithmetic and logical operations. By using general-purpose registers, programs can more effectively manage data and perform various computational tasks.

The standard API interface can be used to read general registers. Taking reading general registers eax, ebx, ecx, and edx as an example, this function is passed without parameters and will be output to the user as a decimal unsigned integer after calling. We can convert it to hexadecimal format using hex(), as shown in the following example;

python
>>> eax = dbg.get_eax()
>>> ebx = dbg.get_ebx()
>>> ecx = dbg.get_ecx()
>>> edx = dbg.get_edx()
>>>
>>> print("eax={} ebx={} ecx={} edx={}".format(eax,ebx,ecx,edx))
eax=0 ebx=5242880 ecx=706543616 edx=0
>>>
>>> print("ebx={} ecx={}".format(hex(ebx),hex(ecx)))
ebx=0x500000 ecx=0x2a1d0000

Secondly, general-purpose registers support length splitting. For example, an ebx register can usually be split into three types: bx, bh, and bl. If it is 64 bit, it can also be split into four types: ebx, bx, bh, and bl. Taking 32-bit as an example, we can usually directly call the output, as shown in the following case;

python
>>> ebx = dbg.get_ebx()
>>> bx = dbg.get_bx()
>>> bh = dbg.get_bh()
>>> bl = dbg.get_bl()
>>>
>>> print("ebx={} bx={} bh={} bl={}".format(hex(ebx),hex(bx),hex(bh),hex(bl)))
ebx=0x500000 bx=0x0 bh=0x0 bl=0x0

The standard interface also provides a get_register function, which reads parameters from the corresponding register by passing in the name of a register. If the function is executed successfully, it returns a decimal unsigned integer. If failed, return false.

python
>>> eax = dbg.get_register("eax")
>>> eip = dbg.get_register("eip")
>>>
>>> print("eax = {} | eip = {}".format(hex(eax),hex(eip)))
eax = 0x0 | eip = 0x77e0f127
>>>
>>> cip = dbg.get_register("CIP")
>>> cip
2011230503
>>> dr1 = dbg.get_register("dr1")
>>> dr1
0

The available register ranges for this method include the following:

  • DR0, DR1, DR2, DR3, DR6, DR7
  • EAX, AX, AH, AL
  • EBX, BX, BH, BL
  • ECX, CX, CH, CL
  • EDX, DX, DH, DL
  • EDI, DI
  • ESI, SI
  • EBP, BP
  • ESP, SP
  • EIP, RAX, RBX, RCX, RDX, RSI, SIL, RDI, DIL, RBP, BPL, RSP, SPL, RIP
  • CIP, CSP, CAX, CBX, CCX, CDX, CDI, CSI, CBP, CFLAGS

For 64 bit processes, a register set has been added as shown below.

  • R8, R8D, R8W, R8B
  • R9, R9D, R9W, R9B
  • R10, R10D, R10W, R10B
  • R11, R11D, R11W, R11B
  • R12, R12D, R12W, R12B
  • R13, R13D, R13W, R13B
  • R14, R14D, R14W, R14B
  • R15, R15D, R15W, R15B

For setting general registers, set_ can be used, or set_register can be used. For setting the set method, a decimal integer parameter needs to be passed in, while for the latter, two parameters are required. Parameter 1 uses a string, and parameter 2 uses a decimal unsigned integer. If the function is executed successfully, it returns a decimal unsigned integer, and if it fails, it returns false.

python
>>> eax = dbg.get_register("eax")
>>> eax
0
>>> dbg.set_eax(0x1000)
True
>>> eax = dbg.get_register("eax")
>>> eax
4096
>>> hex(eax)
'0x1000'
>>>
>>> dbg.set_register("eax",0x2000)
True
>>> eax = dbg.get_register("eax")
>>> hex(eax)
'0x2000'

Flag Register

Flag register is a special type of register in computer architecture that stores state information and flag bits generated during processor operations. Flag registers are typically used to store various condition codes that record the result or state of the most recent arithmetic or logical operation. Programs can use the status information of flag registers to make conditional judgments, control program flow, and implement various algorithms and logical operations. Flag registers are an important component of computer architecture, which have a significant impact on the execution of instructions and the correctness of programs.

Reading flag registers can be achieved using standard API interfaces, similar to general-purpose registers. Reading flags can be achieved using the get_ series function or the get_flag_register function. The former does not require passing in parameters, while the latter requires passing in a flag register string, as shown in the following example;

python
>>> zf = dbg.get_zf()
>>> of = dbg.get_of()
>>> pf = dbg.get_pf()
>>>
>>> print("zf = {} of ={} pf = {}".format(zf,of,pf))
zf = True of =False pf = True
>>>
>>> zf = dbg.get_flag_register("zf")
>>> of = dbg.get_flag_register("of")
>>> pf = dbg.get_flag_register("pf")
>>> print("zf = {} of ={} pf = {}".format(zf,of,pf))
zf = True of =False pf = True

The available register ranges for this method include the following:

  • ZF, OF, CF, PF, SF, TF, AF, DF, IF

Consistent with general-purpose registers, the setting of flag registers can be achieved using the set_ series function, or directly using the set_flag_register function. This series of functions only pass in 1 or 0 as parameters, which represent true or false. Both functions return Boolean values, as shown in the following example;

python
>>> dbg.set_zf(1)
True
>>> dbg.set_zf(0)
True
>>> dbg.set_flag_register("pf",1)
True
>>> dbg.set_flag_register("tf",0)
True
>>>
>>> dbg.get_zf()
False
>>> dbg.get_pf()
True
>>> dbg.get_tf()
False