Appearance
Patching and bypassing protection cases
Bypassing anti debugging functions refers to bypassing the integrated anti debugging techniques in a program, making it impossible for the debugger to correctly detect or block debugging operations on the program. Anti debugging techniques are typically used by software developers or security engineers to prevent malicious analysis, cracking, or reverse engineering. Attackers may attempt to bypass these anti debugging techniques in order to analyze the internal logic of the program, obtain sensitive information, or perform malicious operations. In the following case, it is explained how to use plugins to patch specific functions, and users can develop similar functions according to their own needs.
Bypassing IsDebuggerPresent
IsDebuggerPresent is a Windows API function used to detect whether the current process is being debugged by a debugger (such as a debugger program or a process to which the debugger is attached). It can help programs detect whether they are in a debugging environment, and thus carry out corresponding anti debugging or defense operations.
When the debugger is attached to the target process, the IsDebuggerPresent function will return a non-zero value (True), indicating that the current process is being debugged. If there is no debugger attached to the process, return a value of zero (FALSE).
There are multiple ways to bypass the simple debugging protection measures of IsDebuggerPresent, including using PEB structures, modifying function code, and using other debugging techniques. These methods aim to deceive or evade debugger detection, making it difficult for protected programs to accurately detect the presence of the debugger.
Modify PEB structure
Debuggers typically modify the flags in the Process Environment Block (PEB) to indicate that the process is being debugged. A common bypass method is to directly modify the debugger flag in PEB, making it unable to correctly detect debugging status. For example, the BeingDebugged flag in PEB can be set to 0 through inline assembly or direct memory writing to bypass the detection of the IsDebuggerPresent function.
python
from x32dbg import Debugger
if __name__ == "__main__":
dbg = Debugger(address="127.0.0.1", port=6589)
if dbg.connect() == True:
peb = dbg.get_peb_address(dbg.get_process_id())
print("Parsing the PEB process environment block = {:08}".format(hex(peb)))
# Read debugging flags
debug_flag = dbg.get_memory_byte(peb + 2)
print("debug = {}".format(debug_flag))
# Write debugging status as 0
nop = dbg.set_memory_byte(peb+2,0)
print("Set anti debugging status = {}".format(nop))
dbg.close_connect()
After the program runs successfully, it will output the following content:
python
Parsing the PEB process environment block = 0x9f7000
debug = False
Set anti debugging status = True
Modify function code
You can bypass debugging and detection by modifying the code of the function. For example, inserting some assembly instructions at the beginning of a function to skip the debugger's detection section and return the preset value directly, making it impossible for the debugger to correctly detect the debugging state.
python
from x32dbg import Debugger
# Obtain machine code through assembly
def GetHexFromAssembly(dbg_ptr, Assembly):
ShellCode = []
for index in Assembly:
# Convert assembly strings to machine code
retn = dbg_ptr.assemble_code_hex(index)
ShellCode.append(retn.get("Hex"))
return ShellCode
# Convert string machine code to byte list
def StrToArray(string_list):
decimal_list = []
for hex_str in string_list:
# Convert hexadecimal strings to integers
hex_int = int(hex_str, 16)
# Convert integers to a list containing each hexadecimal number
hex_digits = []
while hex_int > 0:
# Get the lowest 8 digits
hex_digits.append(hex_int & 0xFF)
# Shift 8 bits to the right
hex_int >>= 8
# Reverse the list to ensure order
hex_digits.reverse()
# Add each hexadecimal number to the result list
decimal_list.extend(hex_digits)
return decimal_list
if __name__ == "__main__":
dbg = Debugger(address="127.0.0.1", port=6589)
if dbg.connect() == True:
# Obtain the memory address of the IsDebuggerPresent function
is_debugger = dbg.get_module_proc_addr("kernel32.dll","IsDebuggerPresent")
if(is_debugger <= 0):
print("Failed to obtain module function address")
else:
print("Module Base = {}".format(hex(is_debugger)))
# Generate corresponding assembly code
ShellCode = GetHexFromAssembly(dbg,
["DB 0x64",
"mov eax,dword ptr ds:[18]",
"sub eax,eax",
"ret"]
)
# Convert a string to a byte array
byte_array = StrToArray(ShellCode)
for index in range(0,len(byte_array)):
retn = dbg.set_memory_byte(is_debugger + index, byte_array[index])
print("[*] Write out memory = {:08} | Write data = {}".format(hex(is_debugger+index),byte_array[index]))
dbg.close_connect()
After the program runs successfully, it will output the following content:
python
Module Base = 0x75182d30
[*] Write out memory = 0x75182d30 | Write data = 100
[*] Write out memory = 0x75182d31 | Write data = 161
[*] Write out memory = 0x75182d32 | Write data = 18
[*] Write out memory = 0x75182d33 | Write data = 0
[*] Write out memory = 0x75182d34 | Write data = 0
[*] Write out memory = 0x75182d35 | Write data = 0
[*] Write out memory = 0x75182d36 | Write data = 41
[*] Write out memory = 0x75182d37 | Write data = 192
[*] Write out memory = 0x75182d38 | Write data = 195
Bypassing ZwQueryInformationProcess
ZwQueryInformationProcess is a Windows system call used to query information about a specified process. It is usually used to obtain various properties and status information about a process, such as its memory usage, thread list, process priority, etc.
The query operation that bypasses system calls such as ZwQueryInformationProcess is usually done to prevent malicious programs or cracking tools from obtaining critical process information, or to evade certain security software or anti debugging techniques. Although the access permissions for Windows system calls are protected by the operating system, there are some technical means to try to bypass this protection.
The behavior of the ZwQueryInformationProcess function can be tampered with through the Hook system call table or by directly modifying the system call table. In this way, malicious programs can replace ZwQueryInformationProcess with a function that returns fake or invalid data, thereby deceiving the caller.
python
from x32dbg import Debugger
# Obtain machine code through assembly
def GetHexFromAssembly(dbg_ptr, Assembly):
pass
# Convert string machine code to byte list
def StrToArray(string_list):
pass
if __name__ == "__main__":
dbg = Debugger(address="127.0.0.1", port=6589)
if dbg.connect() == True:
# Obtain the ZwQueryInformationProcess base address
base_address = dbg.get_module_proc_addr("ntdll.dll","ZwQueryInformationProcess")
print("ZwQueryInformationProcess = {:08}".format(hex(base_address)))
# Create a heap space of 1024 in size
alloc_address = dbg.create_alloc(0,1024)
print("Alloc Address = {:08}".format(hex(alloc_address)))
# Output the ShellCode machine code that needs to be replaced
ShellCode = GetHexFromAssembly(dbg,
[
"cmp dword [esp + 8],7",
"DB 0x74",
"DB 0x06",
f"push {hex(base_address)}",
"ret",
"mov eax,dword [esp +0x0c]",
"push 0",
"pop dword [eax]",
"xor eax,eax",
"ret 14"
])
# Convert ShellCode string to inner bytearray
shellcode_list = StrToArray(ShellCode)
# Write ShellCode into your own allocated heap
for index in range(0,len(shellcode_list)):
dbg.set_memory_byte(alloc_address + index, shellcode_list[index])
# Fill in the jump position at the beginning of ZwQueryInformationProcess
JmpShellCode = GetHexFromAssembly(dbg,
[
f"push {hex(alloc_address)}",
"ret"
])
# Write the jump instruction to the first address of the ZwQueryInformationProcess function
jmp_shellcode_list = StrToArray(JmpShellCode)
for index in range(0,len(jmp_shellcode_list)):
dbg.set_memory_byte(base_address+index, jmp_shellcode_list[index])
print("[*] ZwQueryInformationProcess The address has been replaced")
dbg.close_connect()
After the program runs successfully, it will output the following content:
python
ZwQueryInformationProcess = 0x776f1800
Alloc Address = 0x29c0000
[*] ZwQueryInformationProcess The address has been replaced