飘云阁

 找回密码
 加入我们

QQ登录

只需一步,快速开始

查看: 3929|回复: 7

Lenus的两篇经典文章

[复制链接]

该用户从未签到

发表于 2005-12-15 02:14:09 | 显示全部楼层 |阅读模式
第一篇:寻找真正的入口(OEP)--广义ESP定律
1.前言

在论坛上看到很多朋友,不知道什么是ESP定律,ESP的适用范围是什么,ESP定律的原理是什么,如何使用ESP定律?看到了我在“”调查结果发现,大家对ESP定律很感兴趣,当然因为实在是太好用了,现在我就来告诉大家什么是ESP定律,它的原理是什么!

BTW:在看完了手动脱壳入门十八篇了以后,再看这篇文章也许会对你更有帮助!

在下面地址下载:

http://www.jetdown.com/down/down.asp?id=37350&no=1

2.准备知识

在我们开始讨论ESP定律之前,我先给你讲解一下一些简单的汇编知识。
1.call
这个命令是访问子程序的一个汇编基本指令。也许你说,这个我早就知道了!别急请继续看完。
call真正的意义是什么呢?我们可以这样来理解:1.向堆栈中压入下一行程序的地址;2.JMP到call的子程序地址处。例如:

00401029  . E8 DA240A00  call 004A3508
0040102E  . 5A       pop edx
在执行了00401029以后,程序会将0040102E压入堆栈,然后JMP到004A3508地址处!
  
2.RET
与call对应的就是RET了。对于RET我们可以这样来理解:1.将当前的ESP中指向的地址出栈;2.JMP到这个地址。

这个就完成了一次调用子程序的过程。在这里关键的地方是:如果我们要返回父程序,则当我们在堆栈中进行堆栈的操作的时候,一定要保证在RET这条指令之前,ESP指向的是我们压入栈中的地址。这也就是著名的“堆栈平衡”原理!

3.狭义ESP定律

ESP定律的原理就是“堆栈平衡”原理。

让我们来到程序的入口处看看吧!

1.这个是加了UPX壳的入口时各个寄存器的值!
EAX 00000000
ECX 0012FFB0
EDX 7FFE0304
EBX 7FFDF000
ESP 0012FFC4
EBP 0012FFF0
ESI 77F51778 ntdll.77F51778
EDI 77F517E6 ntdll.77F517E6
EIP 0040EC90 note-upx.<ModuleEntryPoint>
C 0 ES 0023 32bit 0(FFFFFFFF)
P 1 CS 001B 32bit 0(FFFFFFFF)
A 0 SS 0023 32bit 0(FFFFFFFF)
Z 0 DS 0023 32bit 0(FFFFFFFF)
S 1 FS 0038 32bit 7FFDE000(FFF)
T 0 GS 0000 NULL
D 0
O 0 LastErr ERROR_MOD_NOT_FOUND (0000007E)

2.这个是UPX壳JMP到OEP后的寄存器的值!
EAX 00000000
ECX 0012FFB0
EDX 7FFE0304
EBX 7FFDF000
ESP 0012FFC4
EBP 0012FFF0
ESI 77F51778 ntdll.77F51778
EDI 77F517E6 ntdll.77F517E6
EIP 004010CC note-upx.004010CC
C 0 ES 0023 32bit 0(FFFFFFFF)
P 1 CS 001B 32bit 0(FFFFFFFF)
A 0 SS 0023 32bit 0(FFFFFFFF)
Z 1 DS 0023 32bit 0(FFFFFFFF)
S 0 FS 0038 32bit 7FFDE000(FFF)
T 0 GS 0000 NULL
D 0
O 0 LastErr ERROR_MOD_NOT_FOUND (0000007E)

呵呵~是不是除了EIP不同以外,其他都一模一样啊!
PYG19周年生日快乐!

该用户从未签到

 楼主| 发表于 2005-12-15 02:15:54 | 显示全部楼层
为什么会这样呢?
我们来看看UPX的壳的第一行:

0040EC90 n> 60        pushad   //****注意这里*****
0040EC91   BE 15B04000   mov esi,note-upx.0040B015

PUSHAD就是把所有寄存器压栈!我们在到壳的最后看看:

0040EE0F   61        popad   //****注意这里*****
0040EE10  - E9 B722FFFF   jmp note-upx.004010CC  //JMP到OEP

POP就是将所有寄存器出栈!


而当我们PUSHAD的时候,ESP将寄存器压入了0012FFC0--0012FFA4的堆栈中!如下:

0012FFA4  77F517E6 返回到 ntdll.77F517E6 来自 ntdll.77F78C4E      //EDI
0012FFA8  77F51778 返回到 ntdll.77F51778 来自 ntdll.77F517B5     //ESI
0012FFAC  0012FFF0                          //EBP
0012FFB0  0012FFC4                          //ESP
0012FFB4  7FFDF000                         //EBX
0012FFB8  7FFE0304                         //EDX
0012FFBC  0012FFB0                        //ECX
0012FFC0  00000000                        //EAX

所以这个时候,在教程上面就告诉我们对ESP的0012FFA4下硬件访问断点。也就是说当程序要访问这些堆栈,从而恢复原来寄存器的值,准备跳向苦苦寻觅的OEP的时候,OD帮助我们中断下来。

于是我们停在0040EE10这一行!

总结:我们可以把壳假设为一个子程序,当壳把代码解压前和解压后,他必须要做的是遵循堆栈平衡的原理,让ESP执行到OEP的时候,使ESP=0012FFC4。

4.广义ESP定律

很多人看完了教程就会问:ESP定律是不是就是0012FFA4,ESP定律的适用范围是不是只能是压缩壳!

我的回答是:NO!

看完了上面你就知道你如果用0012FFA8也是可以的,ESP定律不仅用于压缩壳他也可以用于加密壳!!!

首先,告诉你一条经验也是事实---当PE文件运行开始的时候,也就是进入壳的第一行代码的时候。寄存器的值总是上面的那些值,不信你自己去试试!而当到达OEP后,绝大多的程序都第一句都是压栈!(除了BC编写的程序,BC一般是在下面几句压栈)

现在,根据上面的ESP原理,我们知道多数壳在运行到OEP的时候ESP=0012FFC4。这就是说程序的第一句是对0012FFC0进行写入操作!

最后我们得到了广义的ESP定律,对只要在0012FFC0下,硬件写入断点,我们就能停在OEP的第二句处!!

下面我们来举个例子,就脱壳进阶第一篇吧!

载入OD后,来到这里:

0040D042 N> B8 00D04000   mov eax,Notepad.0040D000 //停在这里
0040D047   68 4C584000   push Notepad.0040584C
0040D04C   64:FF35 00000000 push dword ptr fs:[0]  //第一次硬件中断,F9
0040D053   64:8925 00000000 mov dword ptr fs:[0],esp
0040D05A   66:9C      pushfw
0040D05C   60        pushad
0040D05D   50        push eax

直接对0012FFC0下硬件写入断点,F9运行。(注意硬件中断)

在0040D04C第一次硬件中断,F9继续!

0040D135   A4        movs byte ptr es:[edi],byte ptr ds:[esi] //访问异常,不管他 shift+F9继续
0040D136   33C9       xor ecx,ecx
0040D138   83FB 00     cmp ebx,0
0040D13B  ^ 7E A4      jle short Notepad.0040D0E1

第二次硬件中断。

004058B5    64       db 64                 //断在这里
004058B6    89       db 89
004058B7    1D       db 1D
004058B8    00       db 00
004058B9    00       db 00

这里也不是,F9继续!

004010CC  /. 55       push ebp
004010CD  |. 8BEC      mov ebp,esp //断在这里,哈哈,到了!(如果发现有花指令,用ctrl+A分析一下就能显示出来)
004010CF  |. 83EC 44    sub esp,44
004010D2  |. 56       push esi

  快吧!还不过瘾,在来一个例子。

  脱壳进阶第二篇

  如果按上面的方法断不下来,程序直接运行了!没什么,我们在用另一种方法!

  载入后停在这里,用插件把OD隐藏!

0040DBD6 N>^\E9 25E4FFFF   jmp Note_tEl.0040C000         //停在这里
0040DBDB   0000       add byte ptr ds:[eax],al
0040DBDD   0038       add byte ptr ds:[eax],bh
0040DBDF   A4        movs byte ptr es:[edi],byte ptr ds:[esi]
0040DBE0   54        push esp

  F9运行,然后用SHIFT+F9跳过异常来到这里:

0040D817  ^\73 DC      jnb short Note_tEl.0040D7F5    //到这里
0040D819   CD20 64678F06  vxdcall 68F6764
0040D81F   0000       add byte ptr ds:[eax],al
0040D821   58        pop eax

  在这里对0012FFC0下硬件写入断点!(命令行里键入HW 12FFC0)SHIFT+F9跳过异常,就来到OEP的第二行处:(用CTRL+A分析一下)

004010CC  /. 55       push ebp
004010CD  |. 8BEC      mov ebp,esp            //断在这里
004010CF  |. 83EC 44    sub esp,44
004010D2  |. 56       push esi
004010D3  |. FF15 E4634000 call dword ptr ds:[4063E4]
004010D9  |. 8BF0      mov esi,eax
004010DB  |. 8A00      mov al,byte ptr ds:[eax]
004010DD  |. 3C 22     cmp al,22

  就这样我们轻松搞定了两个加密壳的找OEP问题!

5.总结

现在我们可以轻松的回答一些问题了。

1.ESP定律的原理是什么?

堆栈平衡原理。

2.ESP定律的适用范围是什么?

几乎全部的压缩壳,部分加密壳。只要是在JMP到OEP后,ESP=0012FFC4的壳,理论上我们都可以使用。但是在何时下断点避开校验,何时下断OD才能断下来,这还需要多多总结和多多积累。欢迎你将你的经验和我们分享。

3.是不是只能下断12FFA4的访问断点?

当然不是,那只是ESP定律的一个体现,我们运用的是ESP定律的原理,而不应该是他的具体数值,不能说12FFA4,或者12FFC0就是ESP定律,他们只是ESP定律的一个应用罢了!

4.对于STOLEN CODE我们怎么办?

哈哈,这正是寻找STOLEN CODE最好的办法!当我们断下时,正好断在了壳处理STOLEN CODE的地方,在F8一会就到OEP了!

6.后话


以上的方法原理都是我自己总结,自己的经验,如果有什么不对的地方,有什么没解释清楚的地方。还请海涵!但是如果觉得我很厉害,那就大可不必,因为ESP定律也是别人教我的,不是我第一个提出来的!我只是个比你们早飞一点的菜鸟罢了^-^

看了上面的文字希望能对你在寻找OEP的时候有帮助,但是别忘了一句话:菜鸟认为找OEP很难,高手认为修复才是最难! 好了,下一篇应该写IAT的修复原理了!让我们共同努力吧!

最后如果转载注明作者并保持文章的完整, 谢谢你看完
PYG19周年生日快乐!

该用户从未签到

 楼主| 发表于 2005-12-15 02:16:41 | 显示全部楼层
第二篇:
知其所以然----以壳解壳

----------------------------------------------------------------
1.前言
在论坛里面,发现有很多朋友对以壳解壳的原理和步骤不太理解,所以我觉得有必要和大家研究一下以壳解壳的原理。便有了这篇文章,在这里我会尽量尝试解决一些大家都想知道的问题。
  1.什么叫以壳解壳?
  2.为什么要以壳解壳或者说什么情况下我们要使用以壳解壳?
  3.如何以壳解壳?
这篇文章的目的是解决上面三个问题,以及希望能起到抛砖引玉的效果。
----------------------------------------------------------------
2.以壳解壳的原理
I.什么是stolen code。
=====================
关于stolen bytes
=====================
稍微说明一下:
每一种编译工具例如 : VC++ , Delphi , Borland , etc..
在OEP有一个唯一的/相同的PE头
其中的一些是这样的:

Push EBP
MOV Ebp,Esp
Add ESP , -010
Mov EAX, SOME_VALUE

(共11bytes)
或者:

Push EBP
MOV Ebp,Esp     ;*
Add ESP , -010    ;**
Push EBX       ;***
Push ESi
Push EDi
Mov EAX, SOME_VALUE ;****

(共14 bytes)
上面应该是一个老外LaBBa的话吧。问题是我们菜鸟如何理解呢?

1.对于*的部分
原程序的OEP,通常是一开始以 Push EBP 和MOV Ebp,Esp这两句开始的,不用我多说大家也知道这两句的意思是以EBP代替ESP,作为访问堆栈的指针。
为什么要这样呢?为什么几乎每个程序都是的开头能?
因为如果我们写过C等函数的时候就应该清楚,程序的开始是以一个主函数main()为开始的,而函数在访问的过程中最重要的事情就是要确保堆栈的平衡,而在win32的环境下保持平衡的办法是这样的:
1.让EBP保存ESP的值;
2.在结束的时候调用
mov esp,ebp
pop ebp
retn
或者是
leave
retn
两个形式是一个意思。
这样做的好处是不用考虑ESP等于多少,PUSH了多少次,要POP多少次了,因为我们知道EBP里面放的是开始时候的ESP值。

2.对于**的部分
Add ESP , -010这种代码的意思是在堆栈区域开辟一块区域保存和使用局部变量,用EBP-XX来调用。

3.对于***的部分

在下来就是保存寄存器的初始值。

4.对于****的部分

初始化寄存器。

小节:我们知道了这就是程序在编译后程序OEP的一般形式,而某些壳在处理OEP的代码时,把这些固定的代码nop掉,然后把他们放到壳代码的空间里面(而且还常伴随着花指令)!使原程序的开始从壳空间开始,然后再JMP回程序空间。如果我们脱掉壳了以后,这一部分就会遗失,也就达到了反脱壳的目的。这就是stolen code技术,或者更确切的说是stolen OEP code技术。

II.replace code

这是一种将原程序代码抽出,放到壳代码中执行的技术。应该就是一般说的SDK(Software Development Kit)技术。(其实我也不是很懂^^)

怎么实现的不清楚,但是他是如何体现出来的呢?

当你脱完壳了以后,运行发现提示“XXXXXX处不易读取”十有八九就是因为采用了SDK技术。因为当我们脱壳后这部分代码都没有了,我们怎么能读取呢?

总之我把replace code看成是又一种形式的stolen code,他偷掉的是程序中的的code。

III.以壳解壳的提出

上面的两种反脱壳的技术是横在我们面前的拦路虎,我们如何解决他呢?

办法有2个:

1.跟踪并且分析代码然后自己补充完整。

2.用以壳解壳的办法。

对于一些stolen OEP code我们好象还可以接受(还不太多嘛),但是如果SDK把成段成段的抽调代码,那我估计你能把那些代码补全了还不如自己去去把这个程序写出来^-^。而我们知道这些被偷掉的代码全在壳里面,只不过他不属于程序的空间,所以我们不能把他dump下来。那么我们想:如果我们把他们全都dump下来以后,那不就不用自己去补了。在这种想法的促使下,以壳解壳的方法诞生了。

接下来的问题便是:我们怎么dump下来呢?

但是在这个问题的前面应该是dump什么部分?按照前面所说的,如果我们在dump下程序了以后运行发现程序在读某一块内存区域,但是那一块区域却什么都没有,这时你便应该知道要dump什么区域了吧!

那么如何dump呢?

使用LordPE的 dump region...(区域dump)功能就可以了,选种自己想要的区域就可以直接dump下来了!



在接下来是该如何装入原来的文件呢?

先给大概的出步骤:(具体的请看这个帖子:http://www.popbase.net/bbs/dispbbs.asp?BoardID=5&ID=40
1.     使用PE Editor的Load section from disk...(从硬盘载入区段头)功能增加一个区域,放入壳代码;

2.修改那个新区段的RVA;
3.重建PE文件(在重建选项一般保留“验正PE”就可以了)

对于1没什么好说的,我们来解释一下为什么要修改这个RVA。

  代码在文件中有他们自己的位置,我们称为文件地址。而当他映射到内存的时候并不是像文件中的地址那样排列。他是按照RVA(相对虚拟地址)的地址+基址 来确定自己在内存中的位置的。所以我们要将原来壳代码所在的位置减掉基址添入就可以了。编辑使用edit section head....的功能。最后因为实际的PE文件和PE头里面的信息有写不一样(你都添加了一个区段当然不一样了),那么需要修复PE头,所以只选用验正PE,不建议在这个时候优化程序,因为我们一般的步骤是先添加区段壳代码,然后修复IAT,这个也是是要添加一个区段的哦!


3.总结
   现在我们来回答开始提出的三个问题:

1.什么是以壳解壳?
以壳解壳是针对主程序的部分代码被偷,进而提出来的一种解决办法。采用添加原壳代码的的手段,达到使程序运行时仍能像未脱壳以前那样正确的访问到壳代码空间。可以说是一种“懒”办法,但是对于某些程序被偷得太多的时候却是一种无奈之举!

2.什么时候要以壳解壳?
当你发现被偷掉的代码很多,自己不能手动补充完整的时候。

3.如何以壳解壳?

上面给出了一个以壳解壳的实例。下面也是一个(明天再帖^^)

4.以壳解壳有什么不足?

因为以壳解壳采用了壳中的代码,那么如果在壳中代码中有部分是校验,或者是一些检验脱壳的代码,那么我们无异于是引狼入室。而且保留了壳代码,不仅增加了文件大小而且留下了原程序本来就没有的东西,总让人心理不是很爽。所以我的向来主张是以壳解壳的办法是最后的救命稻草,不要太过于依赖。

5.什么时候不能使用以壳解壳的办法?

当壳代码的RVA小于基址的时候!上面的方法将不在适用。

例如:壳代码的RVA=300000 而程序的基址=400000。

那时我们怎么修改这个RVA呢?我还没有找到合适的办法,期待那位能告诉我 ^-^。

问题在这个帖子里面解决了:

http://www.popbase.net/bbs/dispb ... p;skin=0&page=1

----------------------------------------------------------------
4.后话
因为论坛里面有些兄弟要求我写一篇以壳解壳的文章,于是就胡乱的瞎说一通,其实我也知道我对以壳解壳的原理有些地方还是很不明白,但是我认为如果让我一个人来找到解决之道不过把这个方法基本思想给大家尽可能的解释清楚,让大家一起寻找解决之道。希望这篇文章不会误导你。如有什么不足之处希望不吝赐教,如果转载请保持文章的完整。最后谢谢你能看完!
PYG19周年生日快乐!

该用户从未签到

发表于 2008-3-2 10:25:39 | 显示全部楼层
嗯,确实是好文章,我顶起。
PYG19周年生日快乐!

该用户从未签到

发表于 2008-3-3 11:23:08 | 显示全部楼层
学习一下。好文章,谢谢分享。
PYG19周年生日快乐!

该用户从未签到

发表于 2008-3-27 08:15:21 | 显示全部楼层
正适合我们这些新手.谢谢了
PYG19周年生日快乐!
  • TA的每日心情
    开心
    3 天前
  • 签到天数: 1992 天

    [LV.Master]伴坛终老

    发表于 2008-3-31 10:49:17 | 显示全部楼层
    好文章,继续
    /:good
    PYG19周年生日快乐!

    该用户从未签到

    发表于 2008-9-24 17:25:41 | 显示全部楼层
    学习一下。好文章,谢谢分享。/:018 /:018
    PYG19周年生日快乐!
    您需要登录后才可以回帖 登录 | 加入我们

    本版积分规则

    快速回复 返回顶部 返回列表