您现在的位置是:网站首页> 编程资料编程资料

LyScript实现Hook隐藏调试器的方法详解_python_

2023-05-25 301人已围观

简介 LyScript实现Hook隐藏调试器的方法详解_python_

LyScript 插件集成的内置API函数可灵活的实现绕过各类反调试保护机制,前段时间发布的那一篇文章并没有详细讲解各类反调试机制的绕过措施,本次将补充这方面的知识点,运用LyScript实现绕过大多数通用调试机制,实现隐藏调试器的目的。

我们以此实现Patches如下函数:

  • IsDebuggerPresent
  • ZwQueryInformationProcess
  • CheckRemoteDebuggerPresent
  • PEB.IsDebugged
  • PEB.ProcessHeap.Flag
  • PEB.NtGlobalFlag
  • PEB.Ldr 0xFEEEFEEE filling
  • GetTickCount
  • ZwQuerySystemInformation
  • FindWindowA
  • FindWindowW
  • FindWindowExA
  • FindWindowExW
  • EnumWindows

首先第一步我们需要自己封装实现一个反汇编转机器码的函数,其作用是当用户传入汇编列表时,自动将其转为机器码并输出为列表格式。

from LyScript32 import MyDebug # 传入汇编代码,得到对应机器码 def get_opcode_from_assemble(dbg_ptr,asm): byte_code = bytearray() addr = dbg_ptr.create_alloc(1024) if addr != 0: asm_size = dbg_ptr.assemble_code_size(asm) # print("汇编代码占用字节: {}".format(asm_size)) write = dbg_ptr.assemble_write_memory(addr,asm) if write == True: for index in range(0,asm_size): read = dbg_ptr.read_memory_byte(addr + index) # print("{:02x} ".format(read),end="") byte_code.append(read) dbg_ptr.delete_alloc(addr) return byte_code else: return bytearray(0) # 传入汇编机器码得到机器码列表 def GetOpCode(dbg, Code): ShellCode = [] for index in Code: ref = get_opcode_from_assemble(dbg,index) for opcode in ref: ShellCode.append(opcode) return ShellCode if __name__ == "__main__": dbg = MyDebug() connect = dbg.connect() ShellCode = GetOpCode(dbg, ["DB 0x64","mov eax,dword ptr ds:[18]","sub eax,eax","ret"]) print(ShellCode) dbg.close() 

输出效果如下:

Patch_PEB

PEB结构存在许多反调试变量,首先我们需要先将这些变量填充为空。

# ---------------------------------------------- # By: LyShark # Email: me@lyshark.com # Project: https://github.com/lyshark/LyScript # ---------------------------------------------- from LyScript32 import MyDebug # 传入汇编代码,得到对应机器码 def get_opcode_from_assemble(dbg_ptr,asm): byte_code = bytearray() addr = dbg_ptr.create_alloc(1024) if addr != 0: asm_size = dbg_ptr.assemble_code_size(asm) # print("汇编代码占用字节: {}".format(asm_size)) write = dbg_ptr.assemble_write_memory(addr,asm) if write == True: for index in range(0,asm_size): read = dbg_ptr.read_memory_byte(addr + index) # print("{:02x} ".format(read),end="") byte_code.append(read) dbg_ptr.delete_alloc(addr) return byte_code else: return bytearray(0) # 传入汇编机器码得到机器码列表 def GetOpCode(dbg, Code): ShellCode = [] for index in Code: ref = get_opcode_from_assemble(dbg,index) for opcode in ref: ShellCode.append(opcode) return ShellCode def Patch_PEB(dbg): PEB = dbg.get_peb_address(dbg.get_process_id()) if PEB == 0: return 0 # 写出0 Patching PEB.IsDebugged dbg.write_memory_byte(PEB + 0x2,GetOpCode(dbg,["db 0"])[0]) print("补丁地址: {}".format(hex(PEB+0x2))) # 写出0 Patching PEB.ProcessHeap.Flag temp = dbg.read_memory_dword(PEB + 0x18) temp += 0x10 dbg.write_memory_dword(temp, GetOpCode(dbg,["db 0"])[0]) print(("补丁地址: {}".format(hex(temp)))) # 写出0 atching PEB.NtGlobalFlag dbg.write_memory_dword(PEB+0x68, 0) print(("补丁地址: {}".format(hex(PEB+0x68)))) # 循环替换 Patch PEB_LDR_DATA 0xFEEEFEEE fill bytes about 3000 of them addr = dbg.read_memory_dword(PEB + 0x0c) while addr != 0: addr += 1 try: b = dbg.read_memory_dword(addr) c = dbg.read_memory_dword(addr + 4) # 仅修补填充运行 print(b) if (b == 0xFEEEFEEE) and (c == 0xFEEEFEEE): dbg.write_memory_dword(addr,0) dbg.write_memory_dword(addr + 4, 0) print("patch") except Exception: break if __name__ == "__main__": dbg = MyDebug() connect = dbg.connect() Patch_PEB(dbg) dbg.close() 

Patch_IsDebuggerPresent

该函数用于检测自身是否处于调试状态,其C系列代码如下所示,绕过此种方式很简单,只需要在函数头部写出ret指令即可。

#include  #include  int _tmain(int argc, _TCHAR* argv[]) { BOOL ref = IsDebuggerPresent(); printf("是否被调试: %d \n", ref); getchar(); return 0; } 

注意:此Api检查PEB中的值,因此如果修补PEB,则无需修补Api,这段绕过代码如下。

from LyScript32 import MyDebug # 传入汇编代码,得到对应机器码 def get_opcode_from_assemble(dbg_ptr,asm): byte_code = bytearray() addr = dbg_ptr.create_alloc(1024) if addr != 0: asm_size = dbg_ptr.assemble_code_size(asm) # print("汇编代码占用字节: {}".format(asm_size)) write = dbg_ptr.assemble_write_memory(addr,asm) if write == True: for index in range(0,asm_size): read = dbg_ptr.read_memory_byte(addr + index) # print("{:02x} ".format(read),end="") byte_code.append(read) dbg_ptr.delete_alloc(addr) return byte_code else: return bytearray(0) # 传入汇编机器码得到机器码列表 def GetOpCode(dbg, Code): ShellCode = [] for index in Code: ref = get_opcode_from_assemble(dbg,index) for opcode in ref: ShellCode.append(opcode) return ShellCode def Patch_IsDebuggerPresent(dbg): # 得到模块句柄 ispresent = dbg.get_module_from_function("kernel32.dll","IsDebuggerPresent") print(hex(ispresent)) if(ispresent <= 0): print("无法得到模块基地址,请以管理员方式运行调试器.") return 0 # 将反调试语句转为机器码 ShellCode = GetOpCode(dbg, ["DB 0x64", "mov eax,dword ptr ds:[18]", "sub eax,eax", "ret"]) print(ShellCode) flag = 0 for index in range(0,len(ShellCode)): flag = dbg.write_memory_byte(ispresent + index,ShellCode[index]) if flag: flag = 1 else: flag = 0 return flag if __name__ == "__main__": dbg = MyDebug() connect = dbg.connect() ref = Patch_IsDebuggerPresent(dbg) print("补丁状态: {}".format(ref)) dbg.close() 

当程序运行后会向IsDebuggerPresent函数写出返回,从而实现绕过调试的目的。

Patch_CheckRemoteDebuggerPresent

此Api调用ZwQueryInformationProcess因此通常不需要对两者进行修补。

from LyScript32 import MyDebug # 传入汇编代码,得到对应机器码 def get_opcode_from_assemble(dbg_ptr,asm): byte_code = bytearray() addr = dbg_ptr.create_alloc(1024) if addr != 0: asm_size = dbg_ptr.assemble_code_size(asm) # print("汇编代码占用字节: {}".format(asm_size)) write = dbg_ptr.assemble_write_memory(addr,asm) if write == True: for index in range(0,asm_size): read = dbg_ptr.read_memory_byte(addr + index) # print("{:02x} ".format(read),end="") byte_code.append(read) dbg_ptr.delete_alloc(addr) return byte_code else: return bytearray(0) # 传入汇编机器码得到机器码列表 def GetOpCode(dbg, Code): ShellCode = [] for index in Code: ref = get_opcode_from_assemble(dbg,index) for opcode in ref: ShellCode.append(opcode) return ShellCode def Patch_CheckRemoteDebuggerPresent(dbg): # 得到模块句柄 ispresent = dbg.get_module_from_function("kernel32.dll","CheckRemoteDebuggerPresent") print(hex(ispresent)) # 将反调试语句转为机器码 ShellCode = GetOpCode(dbg, [ "mov edi,edi", "push ebp", "mov ebp,esp", "mov eax,[ebp+0xc]", "push 0", "pop dword ptr ds:[eax]", "xor eax,eax", "pop ebp", "ret 8" ] ) print(ShellCode) flag = 0 for index in range(0,len(ShellCode)): flag = dbg.write_memory_byte(ispresent + index,ShellCode[index]) if flag: flag = 1 else: flag = 0 return flag if __name__ == "__main__": dbg = MyDebug() connect = dbg.connect() ref = Patch_CheckRemoteDebuggerPresent(dbg) print("写出状态: {}".format(ref)) dbg.close() 

写出效果如下:

Patch_GetTickCount

GetTickCount返回(retrieve)从操作系统启动所经过(elapsed)的毫秒数,常用于定时计数,绕过方式只需初始化即可。

from LyScript32 import MyDebug # 传入汇编代码,得到对应机器码 def get_opcode_from_assemble(dbg_ptr,asm): byte_code = bytearray() addr = dbg_ptr.create_alloc(1024) if addr != 0: asm_size = dbg_ptr.assemble_code_size(asm) # print("汇编代码占用字节: {}".format(asm_size)) write = dbg_ptr.assemble_write_memory(addr,asm) if write == True: for index in range(0,asm_size): read = dbg_ptr.read_memory_byte(addr + index) # print("{:02x} ".format(read),end="") byte_code.append(read) dbg_ptr.delete_alloc(addr) return byte_code else: return bytearray(0) # 传入汇编机器码得到机器码列表 def GetOpCode(dbg, Code): ShellCode = [] for index in Code: ref = get_opcode_from_assemble(dbg,index) for opcode in ref: ShellCode.append(opcode) return ShellCode def Patch_GetTickCount(dbg): # 得到模块句柄 ispresent = dbg.get_module_from_function("kernel32.dll","GetTickCount") print(hex(ispresent)) # 将反调试语句转为机器码 ShellCode = GetOpCode(dbg, [ "mov edx,0x7ffe0000", "sub eax,eax", "add eax,0xB0B1560D", "ret" ] ) print(ShellCode) flag = 0 for index in range(0,len(ShellCode)): flag = dbg.write_memory_byte(ispresent + index,ShellCode[index]) if flag: flag = 1 else: flag = 0 return flag if __name__ == "__main__": dbg = MyDebug() connect = dbg.connect() ref = Patch_GetTickCount(dbg) print("写出状态: {}".format(ref)) dbg.close() 

写出效果如下:

Patch_ZwQueryInformationProcess

此函数打补丁需要跳转两次,原因是因为函数开头部分无法填充更多指令,需要我们自己来申请空间,并实现跳转。

# ---------------------------------------------- # By: LyShark # Email: me@lyshark.com # Project: https://github.com/lyshark/LyScript # ---------------------------------------------- from LyScript32 import MyDebug # 传入汇编代码,得到对应机器码 def get_opcode_from_assemble(dbg_ptr,asm): byte_code = bytearray() addr = dbg_ptr.create_alloc(1024) if addr != 0: asm_size = dbg_ptr.assemble_code_size(as
                
                

-六神源码网