LyScript實現(xiàn)Hook隱藏調(diào)試器的方法詳解
LyScript 插件集成的內(nèi)置API函數(shù)可靈活的實現(xiàn)繞過各類反調(diào)試保護(hù)機(jī)制,前段時間發(fā)布的那一篇文章并沒有詳細(xì)講解各類反調(diào)試機(jī)制的繞過措施,本次將補(bǔ)充這方面的知識點,運(yùn)用LyScript實現(xiàn)繞過大多數(shù)通用調(diào)試機(jī)制,實現(xiàn)隱藏調(diào)試器的目的。
我們以此實現(xiàn)Patches如下函數(shù):
- IsDebuggerPresent
- ZwQueryInformationProcess
- CheckRemoteDebuggerPresent
- PEB.IsDebugged
- PEB.ProcessHeap.Flag
- PEB.NtGlobalFlag
- PEB.Ldr 0xFEEEFEEE filling
- GetTickCount
- ZwQuerySystemInformation
- FindWindowA
- FindWindowW
- FindWindowExA
- FindWindowExW
- EnumWindows
首先第一步我們需要自己封裝實現(xiàn)一個反匯編轉(zhuǎn)機(jī)器碼的函數(shù),其作用是當(dāng)用戶傳入?yún)R編列表時,自動將其轉(zhuǎn)為機(jī)器碼并輸出為列表格式。
from LyScript32 import MyDebug # 傳入?yún)R編代碼,得到對應(yīng)機(jī)器碼 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("匯編代碼占用字節(jié): {}".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) # 傳入?yún)R編機(jī)器碼得到機(jī)器碼列表 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結(jié)構(gòu)存在許多反調(diào)試變量,首先我們需要先將這些變量填充為空。
# ---------------------------------------------- # By: LyShark # Email: me@lyshark.com # Project: https://github.com/lyshark/LyScript # ---------------------------------------------- from LyScript32 import MyDebug # 傳入?yún)R編代碼,得到對應(yīng)機(jī)器碼 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("匯編代碼占用字節(jié): {}".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) # 傳入?yún)R編機(jī)器碼得到機(jī)器碼列表 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("補(bǔ)丁地址: {}".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(("補(bǔ)丁地址: {}".format(hex(temp)))) # 寫出0 atching PEB.NtGlobalFlag dbg.write_memory_dword(PEB+0x68, 0) print(("補(bǔ)丁地址: {}".format(hex(PEB+0x68)))) # 循環(huán)替換 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) # 僅修補(bǔ)填充運(yùn)行 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
該函數(shù)用于檢測自身是否處于調(diào)試狀態(tài),其C系列代碼如下所示,繞過此種方式很簡單,只需要在函數(shù)頭部寫出ret指令即可。
#include <Windows.h> #include <stdio.h> int _tmain(int argc, _TCHAR* argv[]) { BOOL ref = IsDebuggerPresent(); printf("是否被調(diào)試: %d \n", ref); getchar(); return 0; }
注意:此Api檢查PEB中的值,因此如果修補(bǔ)PEB,則無需修補(bǔ)Api,這段繞過代碼如下。
from LyScript32 import MyDebug # 傳入?yún)R編代碼,得到對應(yīng)機(jī)器碼 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("匯編代碼占用字節(jié): {}".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) # 傳入?yún)R編機(jī)器碼得到機(jī)器碼列表 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("無法得到模塊基地址,請以管理員方式運(yùn)行調(diào)試器.") return 0 # 將反調(diào)試語句轉(zhuǎn)為機(jī)器碼 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("補(bǔ)丁狀態(tài): {}".format(ref)) dbg.close()
當(dāng)程序運(yùn)行后會向IsDebuggerPresent
函數(shù)寫出返回,從而實現(xiàn)繞過調(diào)試的目的。
Patch_CheckRemoteDebuggerPresent
此Api調(diào)用ZwQueryInformationProcess
因此通常不需要對兩者進(jìn)行修補(bǔ)。
from LyScript32 import MyDebug # 傳入?yún)R編代碼,得到對應(yīng)機(jī)器碼 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("匯編代碼占用字節(jié): {}".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) # 傳入?yún)R編機(jī)器碼得到機(jī)器碼列表 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)) # 將反調(diào)試語句轉(zhuǎn)為機(jī)器碼 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("寫出狀態(tài): {}".format(ref)) dbg.close()
寫出效果如下:
Patch_GetTickCount
GetTickCount返回(retrieve)從操作系統(tǒng)啟動所經(jīng)過(elapsed)的毫秒數(shù),常用于定時計數(shù),繞過方式只需初始化即可。
from LyScript32 import MyDebug # 傳入?yún)R編代碼,得到對應(yīng)機(jī)器碼 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("匯編代碼占用字節(jié): {}".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) # 傳入?yún)R編機(jī)器碼得到機(jī)器碼列表 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)) # 將反調(diào)試語句轉(zhuǎn)為機(jī)器碼 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("寫出狀態(tài): {}".format(ref)) dbg.close()
寫出效果如下:
Patch_ZwQueryInformationProcess
此函數(shù)打補(bǔ)丁需要跳轉(zhuǎn)兩次,原因是因為函數(shù)開頭部分無法填充更多指令,需要我們自己來申請空間,并實現(xiàn)跳轉(zhuǎn)。
# ---------------------------------------------- # By: LyShark # Email: me@lyshark.com # Project: https://github.com/lyshark/LyScript # ---------------------------------------------- from LyScript32 import MyDebug # 傳入?yún)R編代碼,得到對應(yīng)機(jī)器碼 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("匯編代碼占用字節(jié): {}".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) # 傳入?yún)R編機(jī)器碼得到機(jī)器碼列表 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))) # 將反調(diào)試語句轉(zhuǎn)為機(jī)器碼 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 # 填充跳轉(zhuǎn)位置 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("補(bǔ)丁狀態(tài): {}".format(ref)) dbg.close()
這段代碼運(yùn)行后,首先會申請內(nèi)存,然后將特定的一段機(jī)器碼寫出到此內(nèi)存中。
內(nèi)存寫出以后,再將函數(shù)頭部替換為跳轉(zhuǎn),這樣一來當(dāng)函數(shù)被調(diào)用,也就自動轉(zhuǎn)向了。
Patch_FindWindow
FindWindow函數(shù)功能是取窗體句柄,有AW與Ex系列,使用同上方法替代即可。
# ---------------------------------------------- # By: LyShark # Email: me@lyshark.com # Project: https://github.com/lyshark/LyScript # ---------------------------------------------- from LyScript32 import MyDebug import ctypes # 傳入?yún)R編代碼,得到對應(yīng)機(jī)器碼 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("匯編代碼占用字節(jié): {}".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) # 傳入?yún)R編機(jī)器碼得到機(jī)器碼列表 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))) # 將反調(diào)試語句轉(zhuǎn)為機(jī)器碼 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("補(bǔ)丁狀態(tài): {}".format(ref)) dbg.close()
補(bǔ)丁應(yīng)用會分別替換四個函數(shù)。
Patch_EnumWindows
枚舉窗體的補(bǔ)丁與上方代碼一致,此處就不再分析了。
如下案例,實現(xiàn)了在枚舉窗體過程中實現(xiàn)彈窗,并不影響窗體的枚舉。
from LyScript32 import MyDebug # 傳入?yún)R編代碼,得到對應(yīng)機(jī)器碼 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("匯編代碼占用字節(jié): {}".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) # 傳入?yún)R編機(jī)器碼得到機(jī)器碼列表 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地址,找到后取出他的內(nèi)存地址 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))) # 將反調(diào)試語句轉(zhuǎn)為機(jī)器碼 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 # 填充跳轉(zhuǎn)位置 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實現(xiàn)Hook隱藏調(diào)試器的方法詳解的詳細(xì)內(nèi)容,更多關(guān)于LyScript Hook隱藏調(diào)試器的資料請關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
基于python神經(jīng)卷積網(wǎng)絡(luò)的人臉識別
這篇文章主要為大家詳細(xì)介紹了基于python神經(jīng)卷積網(wǎng)絡(luò)的人臉識別,具有一定的參考價值,感興趣的小伙伴們可以參考一下2018-05-05python庫geopandas讀取寫入空間數(shù)據(jù)及繪圖實例探索
這篇文章主要為大家介紹了python庫geopandas讀取寫入空間數(shù)據(jù)及繪圖實例探索,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪<BR>2024-02-02運(yùn)籌學(xué)-Python實現(xiàn)圖論與最短距離
需要求解任意兩個節(jié)點之間的最短距離,使用?Floyd?算法,只要求解單源最短路徑問題,有負(fù)權(quán)邊時使用?Bellman-Ford?算法,沒有負(fù)權(quán)邊時使用?Dijkstra?算法,本節(jié)我們只討論Dijkstra?算法,需要的朋友可以參考一下2022-01-01python 字符串轉(zhuǎn)列表 list 出現(xiàn)\ufeff的解決方法
下面小編就為大家?guī)硪黄猵ython 字符串轉(zhuǎn)列表 list 出現(xiàn)\ufeff的解決方法。小編覺得挺不錯的,現(xiàn)在就分享給大家,也給大家做個參考。一起跟隨小編過來看看吧2017-06-06Python3實現(xiàn)帶附件的定時發(fā)送郵件功能
這篇文章主要為大家詳細(xì)介紹了Python3實現(xiàn)帶附件的定時發(fā)送郵件功能,文中示例代碼介紹的非常詳細(xì),具有一定的參考價值,感興趣的小伙伴們可以參考一下2018-02-02Django學(xué)習(xí)筆記之為Model添加Action
這篇文章主要介紹了Django給admin添加Action,小編覺得挺不錯的,現(xiàn)在分享給大家,也給大家做個參考。一起跟隨小編過來看看吧2019-04-04python實現(xiàn)對一個完整url進(jìn)行分割的方法
這篇文章主要介紹了python實現(xiàn)對一個完整url進(jìn)行分割的方法,涉及Python操作URL的相關(guān)技巧,非常具有實用價值,需要的朋友可以參考下2015-04-04