Skip to content

Debugging interface

Software debugging is a technical process aimed at diagnosing and resolving errors, defects, and abnormal behaviors in computer software. It is a crucial step in the software development process, helping developers understand the execution process of the program, identify problems, and ultimately fix errors.

Software debugging plays a crucial role in computer security, binary security, and vulnerability mining, helping to discover and fix security vulnerabilities, defects, and potential attack surfaces in software.

The LyScript plugin offers a variety of debugging interfaces, which are crucial for automated testing. It's essential to master the use of these interfaces. This guide will cover aspects such as debugging initialization, executing actions, and setting breakpoints.

Initialize debugging

loading a process is the foundation of debugging software. In plugins, the debug_init function is provided by default, which passes in a file path and automatically loads executable files into the debugger. Correspondingly, the debug_close function does not require any parameters and will close the debugged process upon call. In addition, the interface also provides a debug_detach function, which can be used to run software and detach it from the control of the debugger. These three functions return true on success, otherwise they return false.

python
>>> debug_list = ["d://Win32Project.exe","e://Win32Console.exe"]
>>> debug_list
['d://Win32Project.exe', 'e://Win32Console.exe']
>>>
>>> dbg.debug_init(debug_list[0])
True
>>> dbg.debug_close()
True
>>> dbg.debug_detach()
True

In addition, the interface also offers a debugging state detection function for debuggers. By calling debug_isdebugger, one can ascertain whether the debugging process is in a debugging state. Conversely, a corresponding call to debug_isrunning can be used to determine whether the debugging process is running.

python
>>> dbg.debug_isdebugger()
True
>>> dbg.debug_isrunning()
False

Debugging actions

debug_

Debugging actions are functions used to directly affect the debugger's actions. Taking single step into and single step over as examples, the debug_stepout function can be used when executing a single step over. The function of a step is to be executed directly when encountering a call to skip it, while the debug_stepin function will enter the call and continue to execute.

python
>>> dbg.debug_stepin()
True
>>> dbg.debug_stepout()
True
>>> dbg.debug_pause()
True
>>> dbg.debug_run()
True

debug_set

Furthermore, to facilitate debugging, the plugin also offers the debug_set function, which takes in an action string. The current actions available are Wait, Run, Pause, Stop, Stepin, Stepout, and Stepover. An example of its usage is presented below.

python
>>> dbg.debug_set("stepout")
True
>>> dbg.debug_set("stepin")
True
>>> dbg.debug_set("pause")
True

debug_setcount

Sometimes we need to execute repetitive single-step instructions. This can be achieved using the debug_setcount function, which takes two parameters: parameter 1 passes an action string, and parameter 2 passes the action to be executed. The function returns true on successful execution and false on failure.

python
>>> dbg.debug_setcount("stepin",30)
True
>>> dbg.debug_setcount("stepout",50)
True

As there is no delay in this function, it will be executed immediately. If you need to introduce delay, you can package an interface yourself according to the above process. The following code is consistent with the above functionality.

python
>>> import time
>>>
>>> for index in range(1,30):
...     dbg.debug_set("stepin")
...     time.time(0.4)
...
True

Breakpoint setting

Software breakpoints and hardware breakpoints are commonly used tools in debugging. Software breakpoints are achieved by inserting special instructions into program code, pausing execution when the program reaches the breakpoint position, making it easier to observe program status and variable values. In contrast, hardware breakpoints utilize the internal debugging function of the processor to trigger interrupt operations when the processor monitors program addresses, without the need to modify program code. They are suitable for scenarios such as embedded systems and are usually faster.

breakpoint

To set a software breakpoint, simply call the set_breakpoint function. To remove it, use the delete_breakpoint function. Both functions take a decimal integer as an argument.

python
>>> eip = dbg.get_eip()
>>> hex(eip)
'0x1915bb'
>>>
>>> dbg.set_breakpoint(eip)
True
>>> dbg.set_breakpoint(0x001915F5)
True
>>> dbg.set_breakpoint(1644021)
True
>>> dbg.delete_breakpoint(eip)
True
>>> dbg.delete_breakpoint(1644021)
True

If you need to check if a specific breakpoint has been hit, you can use the check_breakpoint function, which passes in a decimal memory address and returns true when the breakpoint is hit, otherwise it returns false. In addition, the is_breakpoint_disabled function can be used to detect whether a breakpoint is in a disabled state. If it is disabled, it returns true, otherwise it returns false.

python
>>> eip = dbg.get_eip()
>>> hex(eip)
'0x1915bb'
>>>
>>> dbg.check_breakpoint(eip)
True
>>>
>>> dbg.is_breakpoint_disabled(0x001915E9)
True
>>> dbg.is_breakpoint_disabled(0x001915E9)
False

In addition, the type information of the breakpoint can be obtained by calling the get_breakpoint_type function. Typically, a regular memory breakpoint returns 1, while a hardware breakpoint returns 2.

python
>>> dbg.get_breakpoint_type(0x001915DA)
1
>>> dbg.get_breakpoint_type(0x001915F5)
2

hardware_breakpoint

Consistent with software breakpoints, hardware breakpoints can be set by calling the set_hardware_breakpoint function, which passes in two parameters. Parameter 1 specifies a memory address, while parameter 2 specifies an integer (HardwareAccess=0, HardwareWrite=1, HardwareExecute=2). When setting, different breakpoints need to be selected based on the type. Additionally, readers should note that hardware breakpoints are very limited and can only be set up to four.

python
>>> HardwareAccess = 0
>>> HardwareWrite = 1
>>> HardwareExecute = 2
>>>
>>> dr0 = dbg.get_dr0()
>>> hex(dr0)
'0x0'
>>>
>>> eip = dbg.get_eip()
>>> hex(eip)
'0x1915bb'
>>>
>>> dbg.set_hardware_breakpoint(eip,HardwareExecute)
True
>>>
>>> dr0 = dbg.get_dr0()
>>> hex(dr0)
'0x1915bb'

When deleting a hardware breakpoint, it can be achieved through the delete_hardware_breakpoint function, which only requires passing in the memory address of the breakpoint to be deleted. If the function is successful, it returns true. Otherwise, it returns false.

python
>>> dbg.delete_hardware_breakpoint(0x1915bb)
True
>>> dr0 = dbg.get_dr0()
>>> hex(dr0)
'0x0'

get_breakpoint

Finally, when we need to obtain all breakpoint information, we can implement it by calling the get_breakpoint function, which returns a list and embeds a dictionary. Due to the excessive output information of the function, only a few commonly used structures are listed here for explanation.

The BpxType flag represents the type of breakpoint, which can be obtained by calling Address when reading the breakpoint address. To verify whether the breakpoint is enabled, it can be obtained through Enable, and the process name corresponding to the breakpoint can be obtained through Mod. Finally, HitCount represents the number of times the breakpoint has been hit.

python
>>> breakpoint = dbg.get_breakpoint()
>>>
>>> breakpoint[0].get("BpxType")
1
>>> hex(breakpoint[0].get("Address"))
'0x191603'
>>> breakpoint[0].get("Enabled")
1
>>> breakpoint[0].get("Active")
1
>>> breakpoint[0].get("Mod")
'win32project.exe'
>>> breakpoint[0].get("HitCount")
0

Readers can sequentially traverse all breakpoint memory addresses and module information through a loop, and obtain different parameters as needed, as shown below;

python
>>> breakpoint = dbg.get_breakpoint()
>>>
>>> for index in range(0,7):
...     address = breakpoint[index].get("Address")
...     type = breakpoint[index].get("BpxType")
...     name = breakpoint[index].get("Mod")
...     print("Address = {} BpxType = {} Name = {}".format(hex(address),type,name))
...
Address = 0x00191603 BpxType = 1 Name = win32project.exe
Address = 0x0019167f BpxType = 1 Name = win32project.exe
Address = 0x00191700 BpxType = 1 Name = win32project.exe
Address = 0x00191730 BpxType = 1 Name = win32project.exe
Address = 0x5ec61011 BpxType = 1 Name = msvcr120.dll
Address = 0x759c101d BpxType = 1 Name = ucrtbase.dll
Address = 0x759c1077 BpxType = 1 Name = ucrtbase.dll