您好,登录后才能下订单哦!
本篇内容主要讲解“LyScript怎么实现Hook隐藏调试器”,感兴趣的朋友不妨来看看。本文介绍的方法操作简单快捷,实用性强。下面就让小编来带大家学习“LyScript怎么实现Hook隐藏调试器”吧!
我们以此实现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()
输出效果如下:
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()
该函数用于检测自身是否处于调试状态,其C系列代码如下所示,绕过此种方式很简单,只需要在函数头部写出ret指令即可。
#include <Windows.h> #include <stdio.h> 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
函数写出返回,从而实现绕过调试的目的。
此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()
写出效果如下:
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()
写出效果如下:
此函数打补丁需要跳转两次,原因是因为函数开头部分无法填充更多指令,需要我们自己来申请空间,并实现跳转。
# ---------------------------------------------- # 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 # 获取指定位置前index条指令的长度 def GetOpCodeSize(dbg,address,index): ref_size = 0 dasm = dbg.get_disasm_code(address,index) for index in dasm: count = dbg.assemble_code_size(index.get("opcode")) ref_size += count return ref_size def Patch_ZwQueryInformationProcess(dbg): # 得到模块句柄 ispresent = dbg.get_module_from_function("ntdll.dll","ZwQueryInformationProcess") print(hex(ispresent)) create_address = dbg.create_alloc(1024) print("分配空间: {}".format(hex(create_address))) # 将反调试语句转为机器码 ShellCode = GetOpCode(dbg, [ "cmp dword [esp + 8],7", "DB 0x74", "DB 0x06", f"push {hex(ispresent)}", "ret", "mov eax,dword [esp +0x0c]", "push 0", "pop dword [eax]", "xor eax,eax", "ret 14" ] ) print(ShellCode) # 把shellcode写出到自己分配的堆中 flag = 0 for index in range(0,len(ShellCode)): flag = dbg.write_memory_byte(create_address + index,ShellCode[index]) if flag: flag = 1 else: flag = 0 # 填充跳转位置 jmp_shellcode = GetOpCode(dbg, [ f"push {hex(create_address)}", "ret" ] ) for index in range(0,len(jmp_shellcode)): flag = dbg.write_memory_byte(ispresent + index,jmp_shellcode[index]) if flag: flag = 1 else: flag = 0 return flag if __name__ == "__main__": dbg = MyDebug() connect = dbg.connect() ref = Patch_ZwQueryInformationProcess(dbg) print("补丁状态: {}".format(ref)) dbg.close()
这段代码运行后,首先会申请内存,然后将特定的一段机器码写出到此内存中。
内存写出以后,再将函数头部替换为跳转,这样一来当函数被调用,也就自动转向了。
FindWindow函数功能是取窗体句柄,有AW与Ex系列,使用同上方法替代即可。
# ---------------------------------------------- # By: LyShark # Email: me@lyshark.com # Project: https://github.com/lyshark/LyScript # ---------------------------------------------- from LyScript32 import MyDebug import ctypes # 传入汇编代码,得到对应机器码 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_FindWindow(dbg): # 得到模块句柄 FindWindowA = dbg.get_module_from_function("user32.dll","FindWindowA") FindWindowW = dbg.get_module_from_function("user32.dll","FindWindowW") FindWindowExA = dbg.get_module_from_function("user32.dll","FindWindowExA") FindWindowExW = dbg.get_module_from_function("user32.dll","FindWindowExW") print("A = {} w = {} exA = {} exW = {}".format(hex(FindWindowA),hex(FindWindowW),hex(FindWindowExA),hex(FindWindowExW))) # 将反调试语句转为机器码 ShellCode = GetOpCode(dbg, [ "xor eax,eax", "ret 0x8", ] ) ShellCodeEx = GetOpCode(dbg, [ "xor eax,eax", "ret 0x10", ] ) # 写出 flag = 0 for index in range(0,len(ShellCode)): flag = dbg.write_memory_byte(FindWindowA + index,ShellCode[index]) flag = dbg.write_memory_byte(FindWindowW + index,ShellCode[index]) if flag: flag = 1 else: flag = 0 for index in range(0,len(ShellCodeEx)): flag = dbg.write_memory_byte(FindWindowExA + index,ShellCodeEx[index]) flag = dbg.write_memory_byte(FindWindowExW + index,ShellCodeEx[index]) if flag: flag = 1 else: flag = 0 return flag if __name__ == "__main__": dbg = MyDebug() connect = dbg.connect() ref = Patch_FindWindow(dbg) print("补丁状态: {}".format(ref)) dbg.close()
补丁应用会分别替换四个函数。
枚举窗体的补丁与上方代码一致,此处就不再分析了。
如下案例,实现了在枚举窗体过程中实现弹窗,并不影响窗体的枚举。
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 # 获取指定位置前index条指令的长度 def GetOpCodeSize(dbg,address,index): ref_size = 0 dasm = dbg.get_disasm_code(address,index) for index in dasm: count = dbg.assemble_code_size(index.get("opcode")) ref_size += count return ref_size def Patch_EnumWindows(dbg): # 得到模块句柄 address = dbg.get_module_from_function("user32.dll","EnumWindows") print(hex(address)) msg_box = dbg.get_module_from_function("user32.dll","MessageBoxA") print(hex(msg_box)) create_address = dbg.create_alloc(1024) print("分配空间: {}".format(hex(create_address))) # 找call地址,找到后取出他的内存地址 dasm_list = dbg.get_disasm_code(address,20) call_addr = 0 call_next_addr = 0 for index in range(0,len(dasm_list)): # 如果找到了call,取出call地址以及下一条地址 if dasm_list[index].get("opcode").split(" ")[0] == "call": call_addr = dasm_list[index].get("addr") call_next_addr = dasm_list[index+1].get("addr") print("call = {} call_next = {}".format(hex(call_addr),hex(call_next_addr))) # 将反调试语句转为机器码 ShellCode = GetOpCode(dbg, [ "push 0", "push 0", "push 0", "push 0", f"call {hex(msg_box)}", "mov eax,1", "pop ebp", "ret 10", f"call {hex(call_addr)}", "pop ebp", "ret 8" ] ) print(ShellCode) # 把shellcode写出到自己分配的堆中 flag = 0 for index in range(0,len(ShellCode)): flag = dbg.write_memory_byte(create_address + index,ShellCode[index]) if flag: flag = 1 else: flag = 0 # 填充跳转位置 jmp_shellcode = GetOpCode(dbg, [ f"push {hex(create_address)}", "ret" ] ) for index in range(0,len(jmp_shellcode)): flag = dbg.write_memory_byte(call_addr + index,jmp_shellcode[index]) if flag: flag = 1 else: flag = 0 return flag if __name__ == "__main__": dbg = MyDebug() connect = dbg.connect() ref = Patch_EnumWindows(dbg) dbg.close()
输出效果如下:
到此,相信大家对“LyScript怎么实现Hook隐藏调试器”有了更深的了解,不妨来实际操作一番吧!这里是亿速云网站,更多相关内容可以进入相关频道进行查询,关注我们,继续学习!
免责声明:本站发布的内容(图片、视频和文字)以原创、转载和分享为主,文章观点不代表本网站立场,如果涉及侵权请联系站长邮箱:is@yisu.com进行举报,并提供相关证据,一经查实,将立刻删除涉嫌侵权内容。