4.6 x64dbg 内存扫描与查壳实现
LyScript 插件中默认提供了多种内存特征扫描函数,每一种扫描函数用法各不相同,在使用扫描函数时应首先搞清楚不同函数之间的差异,本章内容将分别详细介绍每一种内存扫描函数是如何灵活运用,并实现一种内存查壳脚本,可快速定位目标程序加了什么壳以及寻找被加壳程序的入口点。
计算机中的壳定义
加壳通常指对可执行文件或者动态链接库等二进制文件进行加密或压缩等处理,以使得这些文件难以被反汇编或破解。通常情况下,加壳会增加二进制文件的大小,并在程序运行时增加一定的开销。加壳技术通常被用于保护软件的知识产权和防止软件被盗版。通过加壳,软件开发者可以使得软件更难被破解和复制,从而保护自己的商业利益。
计算机中查壳的原理
软件查壳的实现原理可以分为静态分析和动态分析两种方式。静态分析是指在不运行被加壳程序的情况下,通过对程序的二进制代码进行解析,识别出程序是否被加壳,以及加壳的种类和方法。动态分析是指通过运行被加壳程序,并观察程序运行时的行为,识别程序是否被加壳,以及加壳的种类和方法。
静态分析的实现原理通常包括以下几个步骤:
- 提取被分析程序的二进制代码;
- 识别程序的文件格式,并解析文件头等元数据信息;
- 根据文件格式,从程序代码中提取节区、导入表、导出表等重要信息;
- 分析程序的代码段,并检查代码中是否存在加壳相关的特征,如代码的执行流程、加密算法等;
- 根据分析结果,判断程序是否被加壳,以及加壳的种类和方法。
静态分析的优点是分析速度快,不需要运行程序,可以有效地识别出程序中的加壳。但是它也有一些缺点,例如无法识别动态加载的加壳、易受代码混淆和反调试等技术的影响。
动态分析的实现原理通常包括以下几个步骤:
- 启动被分析程序,并在程序运行期间捕捉程序的行为;
- 跟踪程序的执行流程,并分析程序的内存、寄存器、堆栈等状态信息;
- 检查程序的内存中是否存在加壳相关的特征,如解密函数、加壳程序等;
- 根据分析结果,判断程序是否被加壳,以及加壳的种类和方法。
动态分析的优点是可以识别动态加载的加壳,并且可以有效地避免代码混淆和反调试等技术的影响。但是它也有一些缺点,例如需要运行被分析程序,因此可能会影响程序的性能和稳定性,并且可能会被加壳程序的反调试技术所绕过。
本例中将采用scan_memory_all()函数对特定内存进行动态扫描,该函数用来扫描当前进程内EIP所指向位置处整个内存段中符合条件的特征,如果找到了则返回一个列表,如果没有找到则返回False,该函数与scan_memory_one()函数原理是一致的,唯一的不同是all以列表形式返回所有匹配到的行,one则只返回匹配到的第一条记录,这两个函数都支持??模糊匹配。
如果载入一个程序,默认停留在系统领空,则调用该函数你所能得到的特征记录只能是系统领空当前dll内的特征集。

例如在程序被载入后,其EIP指针默认会停留在ntdll.dll模块上,需要注意的是,函数scan_memory_one用于扫描并返回第一段符合条件的内存,函数scan_memory_all则用于扫描并输出当前所有符合条件的内存地址;
>>> from LyScript32 import MyDebug
>>>
>>> dbg = MyDebug()
>>> conn = dbg.connect()
>>>
>>> ref_one = dbg.scan_memory_one("55 8b ec")
>>> ref_one
1995793090
>>>
>>> ref_all = dbg.scan_memory_all("55 8b ec")
>>> ref_all
[1995793090, 1995793298, 1995793455, 1995793799, 1995794214]
而有时,我们需要指定扫描某个模块,例如扫描进程内的msvcr120.dll模块里面的特征,则此时读者可调用scan_memory_any()函数,该函数无需切换到模块入口处即可实现扫描特定模块内的特征,不过该函数只能返回找到的第一条记录,且需要传入扫描起始位置以及扫描长度,不过得到这些参数并不难。
from LyScript32 import MyDebug
if __name__ == "__main__":
dbg = MyDebug()
conn = dbg.connect()
# 得到进程模块
local_module = dbg.get_all_module()[0]
# 得到模块参数
module_base = local_module.get("base")
module_size = local_module.get("size")
print("基地址: {} 长度: {} 结束地址: {}".format(hex(module_base),hex(module_size),hex(module_base+module_size)))
# 扫描内存
ref = dbg.scan_memory_any(module_base,module_size,"55 8b ec ??")
if ref != False:
print("找到内存: {}".format(hex(ref)))
dbg.close()
运行如上所示的代码片段,则默认会扫描进程内第一个模块也就是主程序模块内的,特征55 8b ec ??指令集,当找到后会输出如下图所示的提示信息;

如上代码中的内存扫描方法如果能被读者理解,那么查壳这个功能就变得很简单了,市面上的查壳软件PEID等基本都是采用特征码定位的方式,所以我们想要实现查壳以及检测编译器特征可以采用特征码扫描法,只需要将上述代码更改一下,即可实现查壳功能。
from LyScript32 import MyDebug
# 查壳功能
def Scan(dbg, string):
# 得到进程模块
local_module = dbg.get_all_module()[0]
# 得到模块参数
module_base = local_module.get("base")
module_size = local_module.get("size")
# print("基地址: {} 长度: {} 结束地址: {}".format(hex(module_base),hex(module_size),hex(module_base+module_size)))
# 扫描内存
ref = dbg.scan_memory_any(module_base,module_size,string)
if ref != False:
return True
return False
if __name__ == "__main__":
dbg = MyDebug()
conn = dbg.connect()
# 存储特征码
signs = [
{"key": "Microsoft Visual C++ 2013", "value": "e8 ?? ?? ?? ?? e9 ?? ?? ?? ?? 55 8b ec"},
{"key": "UPX 3.96w", "value": "60 be ?? ?? ?? ?? 8d be 00 90 ff ff 57"}
]
for index in signs:
check = Scan(dbg, index.get("value"))
if check == True:
print("编译特征: {}".format(index.get("key")))
dbg.close()
当运行上述代码片段,则将会寻找特定指令集,如果找到则说明采用的是特定的加壳程序或特定编译器编译生成的,输出效果图如下所示;

通过运用scan_memory_one()函数,读者何以很容易的实现对特定内存区域的寻找,以32为代码为例,通过get_register函数获取到当前EIP的内存地址,并通过read_memory_word读入前四个字节的数据,当前四个字节的数据为0xBE60此时说明是UPX壳的开头,通过scan_memory_one("83 EC ?? E9")匹配特征码,当找到后顺势在此处set_breakpoint(patternAddr)下一个断点,程序通过不断地运行,最终即可定位到程序真正的入口地址;
from LyScript32 import MyDebug
if __name__ == "__main__":
# 初始化
dbg = MyDebug()
# 连接到调试器
connect_flag = dbg.connect()
print("连接状态: {}".format(connect_flag))
# 检测套接字是否还在
ref = dbg.is_connect()
print("是否在连接: ", ref)
is_64 = False
# 判断是否时64位数
if is_64 == False:
currentIP = dbg.get_register("eip")
if dbg.read_memory_word(currentIP) != int(0xBE60):
print("[-] 可能不是UPX")
dbg.close()
patternAddr = dbg.scan_memory_one("83 EC ?? E9")
print("匹配到的地址: {}".format(hex(patternAddr)))
dbg.set_breakpoint(patternAddr)
dbg.set_debug("Run")
dbg.set_debug("Wait")
dbg.delete_breakpoint(patternAddr)
dbg.set_debug("StepOver")
dbg.set_debug("StepOver")
print("[+] 程序OEP = 0x{:x}".format(dbg.get_register("eip")))
else:
currentIP = dbg.get_register("rip")
if dbg.read_memory_dword(currentIP) != int(0x55575653):
print("[-] 可能不是UPX")
dbg.close()
patternAddr = dbg.scan_memory_one("48 83 EC ?? E9")
print("匹配到的地址: {}".format(hex(patternAddr)))
dbg.set_breakpoint(patternAddr)
dbg.set_debug("Run")
dbg.set_debug("Wait")
dbg.delete_breakpoint(patternAddr)
dbg.set_debug("StepOver")
dbg.set_debug("StepOver")
print("[+] 程序OEP = 0x{:x}".format(dbg.get_register("eip")))
dbg.close()
运行上述代码片段,则会看到如下图所示的输出效果,并以自动跳转到了程序的真正入口位置0x40153e,此时用户只需要脱壳转存即可;

4.6 x64dbg 内存扫描与查壳实现的更多相关文章
- LyScript 内存扫描与查壳实现
LyScript 中提供了多种内存特征扫描函数,每一种扫描函数用法各不相同,在使用扫描函数时应首先搞清楚他们之间的差异,如下将分别详细介绍每一种内存扫描函数是如何灵活运用的,最后将实现一个简易版内存查 ...
- .net 查壳工具
请问大神.NET查壳工具都有哪些? 已知的有DotNet Id 除了这个还有别的吗?脱MAXTOCODE发现是双壳.脱掉第一层还有一层,DotNet Id检测没壳了,但是反编译还是加密状态. 用 ...
- 标记下 'net 查壳/脱壳/加壳' 工具
net查壳工具 DotNet Id v1.0.0.3 该net程序集被“MaxToCode”加壳了(也不能完全相信). 加壳工具就是列表中列出的了. de4Net.exe脱壳工具版本de4dot-v3 ...
- C/C++ 导入表与IAT内存修正
本章教程中,使用的工具是上次制作的PE结构解析器,如果还不会使用请先看前一篇文章中对该工具的介绍,本章节内容主要复习导入表结构的基础知识点,并通过前面编写的一些小案例,实现对内存的转储与导入表的脱壳修 ...
- 病毒木马查杀实战第016篇:U盘病毒之逆向分析
比对脱壳前后的程序 我们这次所要研究的是经过上次的脱壳操作之后,所获取的无壳病毒样本.其实我们这里可以先进行一下对比,看看有壳与无壳的反汇编代码的区别.首先用IDA Pro载入原始病毒样本: 图1 可 ...
- Web漏洞扫描工具(批量脱壳、反序列化、CMS)
一.什么是Web漏洞扫描工具 即是指“扫描Web应用以查找安全漏洞(如跨站脚本,SQL注入,命令执行,目录遍历和不安全服务器配置)的自动化工具”,其中许多可能是由不安全或不正确的编码和设计.另一方面, ...
- [原创]K8_C段旁注工具6.0 新增SMB漏洞扫描
工具: K8_C段旁注工具6.0_0510[K.8]编译: 自己查壳组织: K8搞基大队[K8team]作者: K8拉登哥哥博客: http://qqhack8.blog.163.com发布: 201 ...
- 手动脱WinUpack 壳实战
作者:Fly2015 吾爱破解培训第一课选修作业第6个练习演示样例程序.不得不反复那句话,没见过这样的壳,该壳是压缩壳的一种,相对于压缩壳,加密壳的难度要大一些.特别是IAT表的修复问题上. 首先分别 ...
- 手动脱NsPacK壳实战
作者:Fly2015 这里脱壳的程序是吾爱破解培训的作业2,相较于作业1略微要强一点,可是仅仅要掌握了脱壳的ESP定律,脱这个Nspack壳并不难.只是还是蛮有意思的. 1.使用查壳软件对加壳的程序进 ...
- 手脱ASProtect v1.23 RC1(有Stolen Code)之以壳解壳
1.载入PEID ASProtect v1.23 RC1 2.载入OD,不勾选内存访问异常,其他异常全部勾选 > 01C06D00 push SoWorker.006DC001 ; //入口点 ...
随机推荐
- C# 通过一个控制台打开另一个控制台
现有个需求是通过一个主程序获取配置的线程数和进程数打开连一个控制台程序,将线程数和系统编码作为参数传给控制台程序. 下面附上Demo. 1 private static void Main(strin ...
- JS 一些基本正则校验
记录下JS一些基本正则校验,以备后需. 1 //手机号码校验 2 function formCheckMobilePhone(data) { 3 var pattern = /^[1-9]{1}\d{ ...
- Java设计模式 —— 组合模式
11 组合模式 11.1 组合模式概述 Composite Pattern: 组合多个对象形成树形结构以表示具有部分-整体关系的层次结构.组合模式使得客户端可以统一处理单个对象和组合对象. 组合模式关 ...
- python和js实现AES加解密
小白学习中...... AES算法 AES全称为高级加密标准,是Advanced Encryption Standard的首字母简写.详细了解,可以找专门的资料进行学习. 场景 开发一个web网站过程 ...
- Carla 自动驾驶仿真平台的安装与配置指南
简介 Carla 是一款基于 Python 编写和 UE(虚幻引擎)的开源仿真器,用于模拟自动驾驶车辆在不同场景下的行为和决策.它提供了高度可定制和可扩展的驾驶环境,包括城市.高速公路和农村道路等.C ...
- 图计算引擎分析--GridGraph
作者:京东科技 李永萍 GridGraph:Large-Scale Graph Processing on a Single Machine Using 2-Level Hierarchical Pa ...
- Nginx 面试题总结大全
转载请注明出处: 1 介绍下nginx特点与常用模块 2 nginx特点详细 3 反向代理和正向代理 4 负载均衡策略有哪些 5 Nginx如何实现动静分离? 6 Nginx 常用命令有哪些? 7 ...
- Protobuf: 高效数据传输的秘密武器
当涉及到网络通信和数据存储时,数据序列化一直都是一个重要的话题:特别是现在很多公司都在推行微服务,数据序列化更是重中之重,通常会选择使用 JSON 作为数据交换格式,且 JSON 已经成为业界的主流. ...
- 2023-02-18:ffmpeg是c编写的音视频编解码库,请问用go语言如何调用?例子是03输出版本号。
2023-02-18:ffmpeg是c编写的音视频编解码库,请问用go语言如何调用?例子是03输出版本号. 答案2023-02-18: 用 github.com/moonfdd/ffmpeg-go 这 ...
- ET介绍——组件式设计(优化版的ECS)
组件式设计 在代码复用和组织数据方面,面向对象可能是大家第一反应.面向对象三大特性继承,封装,多态,在一定程度上能解决不少代码复用,数据复用的问题.不过面向对象不是万能的,它也有极大的缺陷: 1. 数 ...