PEiD查壳工具进阶:从“Nothing found”到自定义特征库识别未知壳
2026/9/9 3:15:26 网站建设 项目流程

简介:PEiD侦测未知壳加强版是一款面向逆向工程与安全分析人员的查壳工具包,聚焦识别可执行文件所加的UPX、ASPack、MEW等已知壳,并通过扩展签名库、行为分析插件增强对未知壳的侦测能力。资源共45个文件,体积仅2.48MB,核心包含PEiD.exe主程序、36个dll插件(如FileInfo、kanal、ImpREC、UNUPX等)以及txt/ini配置文件与签名数据,另有bpl运行时库和少量c/h源码文件,方便使用者了解插件接口并二次开发。目前已有233人学习下载。包内插件覆盖文件信息扫描、OEP查找、资源查看、CRC校验、壳脱壳辅助等方向,适合搭建便携式查壳实验环境;初级用户可用该加强版直接扫描程序获取壳类型,中高级安全从业者则可结合源码与插件机制研究未知壳特征提取与绕过思路,作为日常逆向分析工具箱中的有效补充。 第一次用PEiD的人,十有八九会在扫到一个文件、看到“Nothing found”之后直接关掉窗口,然后在报告里写下“未加壳”。我早年做样本分析时也干过这事,后来被一个加了自定义壳的测试程序狠狠教育了一顿——那次教训之后我才明白,PEiD这类查壳工具的真正价值,不在于它“查到”了什么,而在于它“查不到”的时候,你能不能把那个“Nothing found”当成一条新的侦查线索。这篇文章要讲的,就是怎么把一个官方停更多年的PEiD,打造成一台能持续识别未知壳的“加强版”查壳机:从它背后的特征码识别原理,到手工扩充特征库的具体方法,再到拿到一个未知壳样本时的完整分析链路。无论你是刚入门逆向的小白,还是做恶意软件分析的老手,这套思路都能直接用。

1. 一个停更十几年的老工具,为什么还没被淘汰

1.1 它解决的是“看穿外壳”这一步

先聊一个基础问题:壳到底是个什么东西。Windows可执行文件加壳后,程序原本的入口点被替换成一段“外壳代码”,原始代码以压缩或加密的形式躺在文件里不直接可见。程序运行时,壳的代码先拿到控制权,在内存里把原始代码还原出来,最后再跳转到真正的程序入口点,也就是常说的OEP。对外部分析者来说,不把壳这层皮看穿,静态分析看到的全是壳的代码,几乎拿不到原始逻辑。

PEiD干的活,就是在你开始正式分析之前,快速判断“这个文件到底有没有壳,如果有,是什么壳”。这个判断直接影响后续所有决策:壳是UPX还是Themida,处理方法完全不一样。前者可能一条upx -d就能脱掉,后者可能要花几天时间处理反调试、虚拟化和代码混淆。所以查壳虽然只是分析流程里的第一步,却决定了后面几步走多快、走多远。

1.2 新工具辈出,它却一直留在工具箱里

现在做个逆向或者病毒分析,大家的工作流里通常不止一个查壳工具。DIE(Detect It Easy)和Exeinfo PE都是后起之秀,特征库更新快、识别能力强,DIE甚至能识别PE、ELF、Mach-O、APK等多种格式。按道理说,PEiD这种官方版本停在0.95、十几年没有更新的老古董应该被淘汰了,但实际很多人工具箱里依然留着它。

原因很现实。PEiD是单文件绿色工具,双击就能用,不需要装运行库,不依赖网络,在隔离沙箱和Windows XP老分析虚拟机里跑得飞快。很多老病毒分析环境恰恰就是XP虚拟机,新工具在这里反而不如PEiD顺手。再加上它的特征库文件userdb.txt是开放结构,社区和个人随时可以往里加特征,这意味着“版本旧”不等于“能力旧”。别人整合好的所谓“加强版”,本质也只是预先塞了更多特征码进去而已;真正会玩的人,都是自己往里喂特征的。顺带说一句,后来移动App加固流行,也有不少人问PEiD能不能查APK的壳——不能,PEiD只管Windows的PE格式,APK加固得靠DIE或者JEB这类工具。不过它背后那套“特征匹配”的思路,放到DEX壳上依然成立。

2. “未知壳”卡在哪一步:PEiD的查壳机制拆解

2.1 壳在PE文件里留下的三类指纹

想理解PEiD为什么查不出未知壳,先得知道它查的是什么。壳不是完美隐身衣,它必须在PE文件里留下痕迹,而且是藏不住的那种。最典型的指纹有三类。

第一类是入口点(EP)附近的机器码。壳的入口代码通常是“解压循环”或“解密循环”,这种循环的指令组合在正常编译器生成的代码里非常少见。举一个经典例子:UPX加壳后的程序,入口处经常是一连串pushadmov esilea edipush edi之类的组合,后面跟着rep movsb这类搬运指令。编译器不会生成这种开头,但壳会。

第二类是区段名字。老一代壳的区段名特征极其明显:UPX会生成UPX0UPX1,ASPack会有.aspack.adata,NSPack会有NSP0NSP1,Themida会有.themida,VMProtect会有.vmp0.vmp1。这些名字虽然不是官方规范强制要求的,但绝大多数壳开发者懒得改,等于免费送了个招牌。新一代壳学聪明了,会起一些伪装名或者随机名,这招就没那么灵了。

第三类是区段权限。普通PE文件里,代码段一般“可读+可执行”,数据段“可读+可写”,很少出现一个区段同时具备可读、可写、可执行三种权限。但壳要动态解密和改写代码,它的区段往往就是RWX权限俱全。拿CFF Explorer或者PeStudio看一眼区段表,这种异常权限组合本身就是强烈的加壳信号。

2.2 特征码匹配的底层逻辑

PEiD的识别机制,说白了就是模式匹配。它解析PE文件头,拿到入口点RVA,换算成文件里的实际偏移,然后把入口点附近的一段机器码抠出来,去和userdb.txt里的每一条签名做比对。比对上了,就显示壳的名字;都比不上,就显示Nothing found。

签名的写法很直白,就是一段十六进制字节序列,用??表示任意字节。比如PEiD老数据库里UPX的经典签名长这样:

[UPX 0.89.6 - 1.02 / 1.05 - 2.90 -> Markus & Laszlo] signature = 60 BE ?? ?? ?? ?? 8D BE ?? ?? ?? ?? 57 89 E5 50 54 53 56 52 8B ?? 8F ?? ?? ?? ?? 83 EC 28 ep_only = true

这里的ep_only = true表示只在入口点处匹配这条签名。因为UPX的解压循环入口非常稳定,锁定EP就够用了。如果这个壳的入口特征会变,或者壳代码会把真正的解密逻辑放在别处,就需要把ep_only设为false,在全文件范围内搜索。代价是扫描速度变慢、误报概率也变高。

2.3 老特征库为什么查不出“未知壳”

原因其实就一句话:特征码匹配是典型的“已知威胁检测”,对没见过的东西天然失灵。PEiD 0.95自带特征库覆盖的主要是2000到2010年代流行的壳,后来出现的VMProtect 3.x、Themida 3.x、Enigma等新一代壳,入口逻辑完全变了,老库里根本没有对应签名。

更麻烦的是,壳作者也在不断进化。往入口处插入花指令、随机化入口点、修改区段名、把特征字符串加密,这些手段都能让老特征库失效。以前靠“看到UPX0区段就报UPX”的日子早过去了。所以遇到一个PEiD报Nothing found的文件,不代表它没壳,只代表它的壳特征不在你的签名库里。这也是“加强版”这个需求最核心的痛点——不是工具不行,是特征库没喂饱。

3. 亲手打造“加强版”:给PEiD扩充特征库的完整流程

3.1 读懂userdb.txt的签名语法

PEiD的特征库文件就是同目录下的userdb.txt,纯文本格式,用记事本就能编辑。修改之前强烈建议先备份一份,免得不小心写坏了一整行导致PEiD读取异常。

签名的基本结构就是一个方括号的名字,加上一行signature,再根据需要加ep_only等选项:

[MyTestProtector 1.0] signature = 60 E8 ?? ?? ?? ?? 5D 81 ED ?? ?? ?? ?? 8D 95 ?? ?? ?? ?? ep_only = true

几个字段的注意点。方括号里的名字就是识别成功后界面上显示的内容,建议写成“壳名+版本号”的格式,方便后续区分同系列不同版本。signature是十六进制字节序列,每个字节之间要有空格,这个格式一个字都不能错。??是通配符,但不要在一段特征里连续堆太多通配符,否则误报率会直线上升。ep_only = true意味着只在EP处匹配,适合那些入口特征足够稳定的壳;如果换成false,PEiD就会在整个文件里搜索这段特征,速度慢且容易误报,能不用就不用。

3.2 从“Nothing found”到新签名的五个步骤

我自己的习惯流程是这样的,照着走基本不会出错。

第一步,先把待分析的样本拖进PEiD,普通模式扫一遍,再切深度模式和硬核模式各扫一遍,确认是真的Nothing found。硬核模式会忽略ep_only限制,强制全文件匹配,有时候能捞到一些漏网之鱼。如果三种模式都查不到,才进入下一步。

第二步,用x64dbg打开样本,在入口点断下来,单步走几步看代码行为。如果看到类似“从一个内存区域搬运数据到另一个区域”“动态获取API地址”的指令序列,基本可以确定是壳在自解密。这一步是判断“有壳”最直接的手段,比依赖任何工具都可靠。

第三步,静态提取特征。在调试器里选中EP附近的20到40个字节,复制机器码;或者用IDA直接把EP地址处的字节扣出来。挑特征的时候有个小技巧:专挑那些正常编译器不可能生成的指令组合,比如连续压栈后接rep movsb,或者成片的xor、add花指令。这些片段信息量大,作为签名最合适。

第四步,照着3.1的格式,在userdb.txt末尾新起一个签名块,保存文件,重新打开PEiD,拖入样本验证。如果还是Nothing found,说明特征没写对或者壳在运行后才展开真实入口,回去调整 ep_only 设置,或者换一段特征字节。

第五步,也是最容易忽略的一步——复测。拿同系列其他版本的三五个样本跑一遍,确认新签名能稳定识别,同时又不会把普通编译器生成的文件误报成壳。复测通过之后,这条签名才算真正可用。

3.3 辅助工具生成特征,但别指望全自动

每次手工从调试器复制机器码再粘到文本编辑器里,确实有点费手。OllyDbg和x64dbg圈子里有一些SigMaker类的插件,可以选中一段代码后自动生成特征串,省去手动整理的麻烦。

但我的建议是:可以拿来当辅助,别全指望它。这类插件生成的签名往往非常长,包含了大量非关键字节,直接丢进userdb.txt只会让误报率暴涨。正确做法是把自动生成的签名当作草稿,再手工裁剪,去掉那些通用指令(像pushmov这类高频指令),只保留壳特异的组合,最后控制在8到20个字节之间。特征太短容易撞车,太长则失去普适性,这个度需要在复测里慢慢调。

4. 实战:一个查不出来的样本,是怎么被一步步定性的

4.1 先读PEiD基础信息,别急着关窗口

有一回我拿到一个加了自定义壳的测试程序,PEiD三个模式全扫一遍,都是Nothing found。很多人到这里就直接放弃了,但PEiD主界面中间那块信息列表其实还有一堆东西没看:入口点、文件偏移、区段名、子系统、链接器版本、映像大小。

那个样本的入口点显示在0x1A2D3C,对于一个普通的32位程序来说,这个位置非常奇怪——正常编译器生成的程序入口通常紧挨着启动代码节,而不是飘在一个很深的偏移上。再看链接器版本显示0.00,这也是个危险信号。编译器生成的PE头里,链接器版本怎么也不可能填零,只能是壳作者改过头或者根本没管这个字段。到这一步,虽然不知道是什么壳,但“这个文件有异常”的结论已经能下了。

4.2 三路侦察:入口点、区段表、资源段

光有怀疑还不够,我习惯再打开一个PE分析工具(CFF Explorer或者PeStudio都行)做交叉确认。重点看三处。

第一看区段表。那个样本一共三个区段,名字分别是.sys1.sys2.sys3,正常的Visual C++程序几乎不会起这种名字。更关键的是三个区段的权限全是可读、可写、可执行,这种RWX组合在正常程序里极少出现,但对壳来说却是标配。

第二看入口点所在的区段。如果入口点落在最后一个区段,而前面还有一个很大的空白区段,往往是壳预留的解压目标区域。

第三看资源段。加壳程序为了压缩体积,资源往往已经被处理过,表现为资源表很小甚至为空,但文件整体体积却不小。这几个信号叠在一起,就已经足够支撑“这是一个带壳程序”的结论了。注意,PEiD查不出具体壳名,不等于这个文件没壳;把“查不出”直接等同于“没壳”,是新手最容易踩的坑。

4.3 动态调试确认,把特征写成签名

工具层面能确认“有壳”,但要确定到底是什么壳,最终得靠动态调试。我在x64dbg里重新加载样本,停在系统断点后一路F8单步,很快就看到一个明显的解压循环:壳在往内存里搬运数据,中间还穿插着LoadLibraryGetProcAddress这类API调用,用于动态解析导入函数。这种“边解压边修复导入表”的行为,是绝大多数壳的共性。

到这里壳的行为已经定性了,剩下的就是把它的特征记住。我复制了EP处前30个字节的机器码,挑了一段包含花指令和解压循环特征识别的片段,写进userdb.txt,再重启PEiD一拖,稳定识别出这个自定义壳的名字。整个过程十分钟出头,之后这个家族的新样本,PEiD一眼就能认出来——这就是“加强版”的实际价值。

5. 识别之后的下一步:从“查出壳”到“定位OEP”

5.1 为什么OEP是脱壳的核心目标

查壳从来不是终点,它只是分析链条的第一环。识别出壳的种类后,真正的工作是找到原始入口点OEP,把程序还原成可以静态分析的状态。

壳的运行逻辑通常是一条固定路线:先是壳入口初始化,然后在内存里解压或解密原始代码,接着修复导入表,最后跳转到OEP。对分析者来说,OEP就是那个“壳的工作结束、原始程序开始”的分界点。找到了OEP,就能dump出一个相对干净的内存镜像,再用PEiD扫一遍确认没有壳信息,这时候整个文件才真正进入正常的逆向流程。定位OEP的方法很多,ESP定律和内存断点是两种最容易上手的路径。

5.2 两类壳的常用处理路径

不同壳的研制路径差别很大,我习惯把它们粗分成两类。

一类是压缩壳,UPX、ASPack、NSPack、MPRESS都算。这类壳的目的核心是压缩,不做加密、不做反调试,所以处理起来相对温和。UPX甚至可以直接用upx -d命令脱壳,省时省力。ASPack和NSPack这类,在调试器里用ESP定律几秒钟就能定位OEP。ESP定律的原理其实不复杂:壳入口经常会先pushad把所有寄存器压栈,压栈后栈顶刚好是进入壳之前的一个状态点;对这个栈顶地址下硬件访问断点,当壳即将跳回OEP时会访问这个地址,调试器就能把断点停在跳转指令附近,顺着跳转往前一步就是OEP。这个方法对压缩壳极其好用。

另一类是保护壳,Themida、VMProtect、Enigma这些属于此列。它们带反调试、代码虚拟化、完整性校验,没有一键脱壳的捷径。面对这类壳,与其死磕完整还原,不如调整分析策略:绕过反调试,识别出哪部分逻辑被虚拟化了,哪部分没有,只分析可读的部分,必要时再配合内存转储做局部还原。这类分析耗时以天计确实正常。

需要强调一句,以上所有方法请只用于分析你拥有或者已获授权分析的软件,不要把它当作绕许可的思路。

5.3 脱壳后的验证:让PEiD再扫一次

很多人脱完壳dump完就以为大功告成,结果程序跑不起来,又要回头排查。我的习惯是dump完成并修复导入表之后,把生成的dump文件再丢进PEiD扫一次。这是一个非常直观的验证手段。

如果扫描结果显示编译器信息,比如“Microsoft Visual C++ 6.0”之类的字符串,说明壳层已经剥掉,OEP位置对了,文件已经还原成接近原始编译产物的状态。如果还是显示之前的壳名,说明OEP没找准,dump出来的东西里还带着壳的尾巴。这个“脱壳前后PEiD结果对比”的方法,比盯着代码纠结半天要省事得多。

6. 别全信PEiD:误报、干扰与多引擎交叉验证

6.1 误报是怎么来的

做了这么多年分析,我早就学会了不百分之百相信单一查壳工具。PEiD会误报,而且误报的坑还很隐蔽。

最常见的一种情况是特征过宽。如果一条签名里通配符太多,或者只包含了一段非常通用的指令序列,它就可能同时匹配到多个完全不同的软件。比如某些花指令库被多个壳共用,A壳的特征可能会命中B壳的文件。

第二种情况是刻意伪装。壳作者会用工具在入口处插入一段正常编译器的入口代码,或者塞一段其他壳的特征片段,让查壳工具误判成别的产品。这就好比故意穿别人的外套,目的就是误导监控。

第三种情况是嵌套加载。一个文件里可能嵌入了另一个可执行体,PEiD扫描时匹配到了嵌入体的特征,而忽略了外层真正的壳。这种情况在捆绑类型的样本里特别常见。PEiD查到一个结果只能说明文件里有东西能“对上特征”,至于这个东西是不是决定性保护层,还需要人工判断。

6.2 一套实用的多引擎验证工作流

我自己的查壳工作流,目前是三道工具并行。第一道用PEiD做快速过滤,第二道用DIE交叉验证,第三道用Exeinfo PE看版本细节。DIE的特征库活跃更新,覆盖面广,支持PE、ELF、Mach-O甚至APK;Exeinfo PE对壳的版本判断比较细。三个工具的结论一致时,判断基本可信;结论互相矛盾的时候,以动态调试里看到的实际行为为准——壳到底在做什么,调试器不会撒谎。

还有一个让工具越用越强的习惯:每当DIE能识别而PEiD不能,我就把DIE给出的壳名和版本记下来,去定位对应壳的EP特征,补充进userdb.txt;反过来如果PEiD能识别而DIE漏了,也值得思考是不是DIE特征库也有盲区。查壳工具从来不是越多越好,而是你越会喂它,它就越懂你手里的样本。

最后说点个人的习惯。我的userdb.txt里现在有大几十条自己补的签名,很多就是在分析那些Nothing found样本时顺手加进去的。PEiD官方确实停止更新了,但这恰恰是它最妙的地方——架子搭好了,谁来喂特征,谁就拥有一个定制版加强工具。下次再看到Nothing found,别急着关窗口,那其实是它在提醒你:这里藏着一个还没被收录的未知壳,等着你去把它变成已知。

本文还有配套的精品资源,点击获取

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询