姓名:
电话:
QQ:
学历:

PEID的欺骗方法

发布时间:2007-11-15 17:55   内容发布:武汉科锐软件安全教育机构
    在我们以下的讨论中,你需要去准备一些东西,首先去下载一个开发环境识别工具,比如 PEID ,或者是FI也可以。
然后你需要下载一个反汇编工具,SoftICE\OllDbg\W32dsm,只要其中一个就可以了,我就以W32dsm为例吧。
然后你需要一个VC++6.0来编译《躲弹高手》,我们还会用到VC的其他功能。 
现在开始。
一个执行程序在编译后,不同的编译器的处理都不同,而且他们都会留下自己特有的信息。
总 的来说,区别较大的地方在程序的入口,现在我们来看看一个典型MFC工程编译后的执行入口是个什么模样,我们以茂叶的程序为例,我们先用VC编译这个游 戏,注意,不要用静态编译方式,因为超过100K论坛是不允许上传的,所以我用的是共享方式编译的,如果你能理解今天的讨论话题,你可以自己用静态编译方式去做一个。
然后用W32dsm打开yzm3.exe,在208行有我们感兴趣的信息:
+++++++++++++++++++ ASSEMBLY CODE LISTING ++++++++++++++++++
//********************** Start of Code in Object .text **************
Program Entry Point = 00401F1C (yzm3.exe File Offset:00004F1C)
这里告诉我们程序入口在00401F1C地址处,我们去看看:
//******************** Program Entry Point ********
:00401F1C 55                      push ebp
:00401F1D 8BEC                    mov ebp, esp
:00401F1F 6AFF                    push FFFFFFFF
:00401F21 6840374000              push 00403740
看,这就是VC6.0的入口代码。
这个就是 PEID 这类的程序分析的重要依据。
现在我们开始来欺骗它。
先用16进制编辑器打开yzm3.exe,我喜欢用VC6,尽管我知道很多工具的16进制编辑功能比VC强大。(大家不要骂我老土,而应该去容忍我,而且在后面的讨论中,大家要把容忍我作为自己的习惯。^o^)
用VC打开yzm3.exe,注意打开方式是二进制。
然后我们找个大片全0的地方,这样地方是程序的多余空间,最好找个地址容易记住的。就2200处吧,然后我们在这里敲入机器代码 E9 17 FD FF FF。
 
然后 0 00   00 00 00 00   20 00 00 60
 
这是什么意思呢?大家都知道,一个程序在内存中的结构按作用分有四个段区,分别是数据区、代码区、堆区和栈区,数据区用户存放全局变量,代码区用来存放程序的代码,堆区用来动态内存分配(就是new出来的东西啦),栈区用来存放局部变量。
而这些信息都会预先存放在可执行的文件里,也是按照分区分片的管理方式,把各类信息分门别类地存放起来。
但是,系统并不能预先知道你的数据在程序运行的过程中会会起到什么作用,所以他不能按照数据区、代码区、堆区和栈区这样的区分方式。
对于系统来说,你的数据只有:可读、可写、可执行、可共享访问...等属性,系统不会过问你的数据用来做什么。
所以,我们的代码的属性默认就是可读可执行的,而全局变量数据的属性默认就是可读可写的。
在程序被装载的时候,系统会先按照这些属性把内存开出来,然后把你的数据填写到相应的地方去。
这个分区有个学名,叫做“节区”(Section)。
所有不同属性的节区信息都会被统一管理,这些信息都存放在节表(Sections Table)里。
而我们刚才看到的那些数据就是在节表里的一条记录,这些记录都像数组一样的连续存储的。
其中,2E 74 65 78 74 00 00 00 是节名称,名叫".text",这个节里面存放的就是程序的代码段,节名不超过8字节。
接下来的 E2 11 00 00 是有效数据的长度,
接下来的 00 10 00 00 是节区在内存里的偏移地址,
接下来的 00 20 00 00 是这个节区在文件中的size,
接下来的 00 10 00 00 是节区在执行文件中的偏移地址,
接下来的三个 00 00 00 00 是在obj文件里使用的,还有记录行号供调试使用,
最后的 20 00 00 60 就是节的属性了,这里的属性表示可读、可执行,是代码段啦。
根据有效数据的长度和节区在执行文件中的偏移地址,我们可以计算出有效的代码在执行文件里的范围,起始地址在1000处,末尾地址在 起始地址 + 有效数据的长度 = 21E2。
现在我们的问题就浮出水面了,我伪造的入口在2200处,也写了代码,但是在节表的记录的信息代码范围不超过21E2,虽然操作系统不处理这个溢出问题,但是PEID发现了这个矛盾,从而就报告出了 Not a valid PE file。
那么我们可以开始解决问题了,我决定,把有效数据的长度修改成节区的size。也就是说,整个节区里的数据都是有效的,任何个人和团体都不得以任何形式挪用我们的空间。
定位到文件地址1E8处,将 E2 11 00 00 修改为 00 20 00 00,保存。这样整个节区里的数据都是有效的了。
 
运行看看,OK,一切正常。用PEID看看,他报告“tElock V0.99-1.0”。呵呵,终于搞错了吧。
 
用FI看看,他倒圆滑,报告说这是个 Win GUI 程序.... 
 
接下来我们的目标升级了,我要把这个程序伪造成为Delphi,和所有的识别软件开个玩笑。
手敲酸了,休息,我把伪造成为 Borland Delphi 3.0 的 yzm3.exe 程序先放出来,有兴趣的朋友可以先去研究研究。
预知后事如何,请听下回分解。
伪造成为 Borland Delphi 3.0 的 yzm3.exe
我们更改程序入口,修正节区长度后,PEID和FI两个开发环境识别软件纷纷落马,PEiD说是tElock V0.99-1.0(壳),而Fi说这是个 Win GUI 程序, 这个....说了和没说一样。
    现在我们有目的有方向地去欺骗识别软件,我们的目标是让PEiD等软件将茂叶同志的VC程序识别成 Borland Delphi 3.0。
    现在的识别软件去判断一个应用程序的开发环境主要依据3个地方:
1, 代码入口
2, 可执行文件(PE)结构中的链接器版本
    BYTE    MajorLinkerVersion
    BYTE    MinorLinkerVersion

3, 特征码, 对于Delphi, 把特征码定位在CODE节里, 这里是Delphi的支持库代码。不同的Delphi版本,对应的特征码不同。

我们先来看看典型的 Delphi 程序入口的样子,以某个游戏程序为例。^_^
打开反汇编工具,定位到程序程序入口:
00518F68       55                 push ebp
00518F69       8BEC               mov ebp,esp
00518F6B       83C4 F0            add esp,-10
00518F6E       53                 push ebx
00518F6F       B8 D0895100        mov eax,005189D0
比较一下VC 6.0的入口,以茂叶的游戏为例:
00401F1C       55                 push ebp
00401F1D       8BEC               mov ebp,esp
00401F1F       6A FF              push -1
00401F21       68 40374000        push 403740
00401F26       68 80204000        push 402080
大家看出差别来了吧?
OK, 现在动手。16进制打开 yzm3.exe,定位在 2200 处,然后我们在这里敲入机器代码 55 8BEC 83C4 00 E9 14FDFFFF。
接着在文件地址110处,把 1C 1F 修改为 00 22。
然后修改节区的size,定位到文件地址1E8处,将 E2 11 00 00 修改为 00 20 00 00,保存。
运行一下看看,OK。
PEiD检查,报告 Nothing found *,^_^, 他终于承认不知道了。
FI看看,报告 Borland Delphi,我们先把 FI 搞定了。
为什么PEiD没有报告 Borland Delphi 呢?因为他还去检查了链接器版本。
我们看看文件偏移102处,Delphi对应的值应该是02 19, 而 VC 6.0 是 06 00,这个地方修改为 02 19。
最后是特征码,在通过入口和链接器版本的检查后,PEiD 会在疑似 Delphi 的程序里,找到 CODE 节,这个节是用来存放 Delphi 支持库代码的,起始于 Delphi 程序的文件地址400H处。
不同的开发平台版本,特征码是不同的。我们的目标是 3.0, PEiD里保存的3.0特征码是:
50 6A 00 E8 00 00 FF FF BA 00 00 00 00 52 89 05 00 00 00 00 89 42 04 E8 00 00 00 00 5A 58 E8 00 00 00 00 C3 55 8B EC 33 C0
好了,现在万事具备,只欠东风了。
我们继续欺骗可爱的PEiD,定位到 yzm3.exe 的文件地址 400 处,按顺序输入上面提供的特征码,保存。
PEiD检查,报告 Borland Delphi 3.0。^_^,真是天真无邪啊。
既然可以如此伪造,是不是就没有办法去知道别人的开发语言呢?
不是的,起码我就可以肯定 CONTRA.exe 游戏的作者是用Delphi开发的。
我就算该作者把以上我们讨论到的所有地方都做了手工伪装,但是有个特征是他万万不可能伪造的。
且看以下代码:
00518F86    8B0D 98D55100    mov ecx,dword ptr ds:[51D598]
00518F8C    8B03             mov eax,dword ptr ds:[ebx]
00518F8E    8B15 98804E00    mov edx,dword ptr ds:[4E8098]
00518F94    E8 977BF5FF      call CONTRA.00470B30
注意看,这里是一个典型的Delphi特点的CALL调用,特点在那里?这个CALL调用的参数是通过寄存器传递过去的。
熟悉VC的朋友可能有反对意见了,VC虽然默认是 stdcall 方式调用,使用栈顶来传递参数,但是也可以声明为 __fastcall 方式,这样不就是用寄存器传递参数了?
非也非也,听我慢慢道来。先看一个VC的 __fastcall 方式的函数调用。
声明如下:
int __fastcall test_fastcall(char para1, char para2, char para3, char para4);
调用的反汇编代码如下:
00401156    6A 6A                push 6A
00401158    6A 69                push 69
0040115A    B2 68                mov dl,68
0040115C    B1 67                mov cl,67
0040115E    E8 C0FEFFFF          call TestCall.00401023
有意思吧?__fastcall 确实是通过寄存器方式传递参数的,不过只能有两个寄存器参与, ecxedx ,其余的还是用栈了,VC的编译器是一定不会用 eax 来传递参数的。
我也想到 Delphi 可以嵌入汇编,那也就是说 call 调用也是可以伪造的。
所以呢,我就特地去关心了一下 CODE 节里的 call 调用方式,刚才我已经讨论过了,这个节是用来存放 Delphi 支持库代码的。
再怎么有耐心去一个个嵌入汇编去伪造 call 调用,难道还能把支持库的代码也全修改过来? 支持库的机器代码有 1.12M 啊。
还有很多蛛丝马迹,不能一一胜数。
就现阶段来说,我们人脑识别起来还是比软件强一些的。
这次先讨论到这里,下次我来谈谈研究 PEiD 识别机制的方法和过程。


Copyright©2007-2015 武汉市科锐软件技术有限公司.
公司地址:武汉市东湖新技术开发区关南园一路当代光谷梦工场5号楼十层
鄂ICP备17007538号-1