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_codeelse: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 ShellCodeif __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_codeelse: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 ShellCodedef Patch_PEB(dbg):PEB = dbg.get_peb_address(dbg.get_process_id())if PEB == 0:return 0# 写出0 Patching PEB.IsDebuggeddbg.write_memory_byte(PEB + 0x2,GetOpCode(dbg,["db 0"])[0])print("补丁地址: {}".format(hex(PEB+0x2)))# 写出0 Patching PEB.ProcessHeap.Flagtemp = dbg.read_memory_dword(PEB + 0x18)temp += 0x10dbg.write_memory_dword(temp, GetOpCode(dbg,["db 0"])[0])print(("补丁地址: {}".format(hex(temp))))# 写出0 atching PEB.NtGlobalFlagdbg.write_memory_dword(PEB+0x68, 0)print(("补丁地址: {}".format(hex(PEB+0x68))))# 循环替换 Patch PEB_LDR_DATA 0xFEEEFEEE fill bytes about 3000 of themaddr = dbg.read_memory_dword(PEB + 0x0c)while addr != 0:addr += 1try: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:breakif __name__ == "__main__":dbg = MyDebug()connect = dbg.connect()Patch_PEB(dbg)dbg.close()

Patch_IsDebuggerPresent

该函数用于检测自身是否处于调试状态,其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_codeelse: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 ShellCodedef 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 = 0for index in range(0,len(ShellCode)):flag = dbg.write_memory_byte(ispresent + index,ShellCode[index])if flag:flag = 1else:flag = 0return flagif __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_codeelse: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 ShellCodedef 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 = 0for index in range(0,len(ShellCode)):flag = dbg.write_memory_byte(ispresent + index,ShellCode[index])if flag:flag = 1else:flag = 0return flagif __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_codeelse: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 ShellCodedef 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 = 0for index in range(0,len(ShellCode)):flag = dbg.write_memory_byte(ispresent + index,ShellCode[index])if flag:flag = 1else:flag = 0return flagif __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(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_codeelse: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 = 0dasm = dbg.get_disasm_code(address,index)for index in dasm:count = dbg.assemble_code_size(index.get("opcode"))ref_size += countreturn ref_sizedef 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 = 0for index in range(0,len(ShellCode)):flag = dbg.write_memory_byte(create_address + index,ShellCode[index])if flag:flag = 1else: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 = 1else:flag = 0return flagif __name__ == "__main__":dbg = MyDebug()connect = dbg.connect()ref = Patch_ZwQueryInformationProcess(dbg)print("补丁状态: {}".format(ref))dbg.close()

这段代码运行后,首先会申请内存,然后将特定的一段机器码写出到此内存中。

内存写出以后,再将函数头部替换为跳转,这样一来当函数被调用,也就自动转向了。

Patch_FindWindow

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_codeelse: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 ShellCodedef 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 = 0for 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 = 1else:flag = 0for 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 = 1else:flag = 0return flagif __name__ == "__main__":dbg = MyDebug()connect = dbg.connect()ref = Patch_FindWindow(dbg)print("补丁状态: {}".format(ref))dbg.close()

补丁应用会分别替换四个函数。

Patch_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_codeelse: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 = 0dasm = dbg.get_disasm_code(address,index)for index in dasm:count = dbg.assemble_code_size(index.get("opcode"))ref_size += countreturn ref_sizedef 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 = 0call_next_addr = 0for 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 = 0for index in range(0,len(ShellCode)):flag = dbg.write_memory_byte(create_address + index,ShellCode[index])if flag:flag = 1else: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 = 1else:flag = 0return flagif __name__ == "__main__":dbg = MyDebug()connect = dbg.connect()ref = Patch_EnumWindows(dbg)dbg.close()

输出效果如下:

LyScript 实现Hook隐藏调试器相关推荐

  1. eac 反调试_自己动手制作一个过保护调试器

    一.起因 本人是新手第一次接触驱动开发的小白,事情是这样的,一个星期前突发奇想想做一个调试器保护程序用于调试游戏,既然要调试驱动保护的程序,自然也要深入驱动底层.做调试器必须要hook api去隐藏调 ...

  2. 反调试(设置主线程为隐藏调试破坏调试通道调试器的检测)

    调试本质就是下一个int3(异常),有了异常,调试器会接收到异常,就会给处理的机会,处理前先找调试器,处理完再找调试器,找两次调试器.利用驱动层的函数能够把线程设置调试下隐藏,本意是进程不调试,但带来 ...

  3. 关于滴水的VT调试器

    关于滴水的VT调试器 by 海风月影 论坛上今天吵的比较火热,主要是关于滴水的VT调试器,很多人不了解这个东西,我对Intel的VT技术略有了解,所以我来简单的介绍一下. 第一,什么是VT技术 VT是 ...

  4. 处理veh调试器检测_越狱检测抖音逻辑???

    对于应用安全甲方一般会在这三个方面做防御.按逻辑分类的话应该应该分为这几类, 但如果从实现原理的话, 应该分为两类, 用API实现的 和 不用API实现的(这说的不用 API 实现, 不是指换成 in ...

  5. [虚拟机保护] [原创]关于滴水的VT调试器

    关于滴水的VT调试器 by 海风月影 论坛上今天吵的比较火热,主要是关于滴水的VT调试器,很多人不了解这个东西,我对Intel的VT技术略有了解,所以我来简单的介绍一下. 第一,什么是VT技术 VT是 ...

  6. 跨平台PHP调试器设计及使用方法——高阶封装

    在<跨平台PHP调试器设计及使用方法--协议解析>一文中介绍了如何将pydbgp返回的数据转换成我们需要的数据.我们使用该问中的接口已经可以构建一个简单的调试器.但是由于pydbgp存在的 ...

  7. 跨平台PHP调试器设计及使用方法——立项

    作为一个闲不住且希望一直能挑战自己的人,我总是在琢磨能做点什么.自从今年初开始接触PHP,我也总想能在这个领域内产生点贡献.那能做点什么呢?我经常看到很多phper说自己设计了一个什么框架,或者说自己 ...

  8. 开源项目-基于Intel VT技术的Linux内核调试器

    本开源项目将硬件虚拟化技术应用在内核调试器上,使内核调试器成为VMM,将操作系统置于虚拟机中运行,即操作系统成为GuestOS,以这样的一种形式进行调试,最主要的好处就是调试器对操作系统完全透明.如下 ...

  9. 如何在Google Chrome浏览器中启动JavaScript调试器?

    使用Google Chrome浏览器时,我想调试一些JavaScript代码. 我怎样才能做到这一点? #1楼 在Chrome浏览器中按F12功能键以启动JavaScript调试器,然后单击" ...

最新文章

  1. 苹果签名分发系统需要什么配置的服务器呢,苹果/IOS超级签名分发系统
  2. bootstrap 树形表格渲染慢_bootstrap-table-treegrid数据量较大时渲染太久了
  3. 50个不上你网站的理由
  4. spring mvc返回页面显示空白_Spring 框架基础(06):Mvc架构模式简介,执行流程详解...
  5. Linux(CentOS)下设置nginx开机自动启动和chkconfig管理
  6. QT-Linux开发环境的搭建
  7. mysql binlog空间维护
  8. jq 通过标签名称获取标签_如何快速通过今日头条原创标签的审核?
  9. JAVA之旅(五)——this,static,关键字,main函数,封装工具类,生成javadoc说明书,静态代码块...
  10. 管理Shader——Shader概览
  11. matlab2c使用c++实现matlab函数系列教程-randn函数
  12. typora 公式_Typora 编辑器的Vue主题类介绍
  13. 在Windows使用mingw32编译opencv
  14. GRE词汇会难记到什么程度
  15. 1999-2019中国互联网发展二十年趋势
  16. web前端期末大作业 html+css+javascript网页设计实例 企业网站制作——星利源商贸物流有限公司(毕业设计)
  17. 康宇的OJ愚人手账1
  18. pycharm安装、首次使用及汉化
  19. soul框架简单介绍与设计模式分析
  20. 基于JAVA读书网络社区设计计算机毕业设计源码+系统+lw文档+部署

热门文章

  1. 如果人生非要比作一场戏,我愿开头凄凉,结尾是圆满的。
  2. 判断 jdk8是否为jce限制版本
  3. Web一些主要的安全防护措施
  4. 葛洲坝集团电力有限责任公司召开第七次董事会
  5. http://www.runoob.com
  6. 输入一个年份,月份,输出该月份的日历。
  7. linux图片添加滤镜,在 Ubuntu 中给你的照片加上 Instagram 风格的滤镜程序
  8. AVPlayer 基础用法
  9. 半小时掌握进制转换!
  10. 【嵌入式Linux开发学习】基于TFTP的通用代码烧写方式