Appearance
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