飘云阁

 找回密码
 加入我们

QQ登录

只需一步,快速开始

楼主: 飘云

ESP定律详解

  [复制链接]
  • TA的每日心情
    开心
    2018-5-6 16:27
  • 签到天数: 7 天

    [LV.3]偶尔看看II

    发表于 2006-5-21 02:59:21 | 显示全部楼层
    顶上去!我们第二课学习引导资料用.

    在网上溜达了一下,看到Lenus兄弟再次写了一篇...转过来大家学习下.
    http://bbs.77169.org/read.php?tid=134715

    +++++++++++++++++++++++++++++++++

    寻找真正的入口(OEP)--内存断点

    Author:Lenus

    1.前言
    发现论坛中很多兄弟在询问:什么是二次内存断点,三次内存断点。还有很多人对内存断点的原理不是很明白。其实只要懂得壳是如何解压代码的,那么就完全可以按自己的喜欢来下断。

    本文要解决的问题是:
    1.什么是内存断点?
    2.如何在寻找OEP时使用内存断点。
    3.内存断点的局限性。

    2.内存断点寻找OEP的原理
    i.首先,在OD中内存断点和普通断点(F2下断)是有本质区别的。
    内存断点等效与命令bpm,他的中断要用到DR0-DR7的调试寄存器,也就是说OD通过这些DR0-DR7的调试寄存器来判断是否断下
    普通断点(F2下断)等效于bpx,他是在所执行的的代码的当前地址的一个字节修改为CC(int3)。当程序运行到int3的时候就会产生一个异常,而这个异常将交给OD处理,把这个异常的regEIP-1以后就正好停在了需要的中断的地方(这个根据系统不同会不一样),同时OD在把上面的int3修改回原来的代码。

    内存断点分为:内存访问断点,内存写入断点。
    我们知道,在程序运行的时候会有3种基本的状态产生:读取,写入,执行。



    CODE:


    004AE242   A1 00104000   mov eax,dword ptr ds:[004AE24C]       //004AE24C处的内存读取
    004AE247   A3 00104000   mov dword ptr ds:[004AE24C],eax     //004AE24C处的内存写入
    004AE24C   83C0 01     add eax,1               //004AE24C处的内存执行


    [Copy to clipboard]


    那么我们应该如何中断在上面的几行呢?
    1.当我们对004AE24C下内存访问断点的时候,可以中断在004AE242也可以中断在004AE247。
    2.当我们对004AE24C下内存写入断点的时候,只能中断在004AE247。
    3.当我们对004AE24C下内存访问断点的时候,能中断在004AE24C。

    到这里你可能不明白了,为什么内存访问断点能中断在004AE247这一句对004AE24C的写入,而且还能中断在004AE24C的执行呢?

    其实很简单,我们只要仔细体会一下“内存访问”这四个字的含义遍可以知道,当我们对004AE24C进行读取的时候需要“访问”他吧,当我对004AE24C进行写入的时候也需要“访问”他吧!!当然我们要执行内存地址004AE24C的代码的时候也是还是要“访问”他的!

    所以我们不难得出下面的结论:
    1.内存写入中断的地方,一定是也可以用内存访问中断。
    2.内存执行的地方,也可以用内存访问中断。
    如果这时你认为,那么内存写入岂不是没用了。呵呵~那我要告诉你当然不是,如果你想快速的准确的定位到004AE247这一行的时候,那么他就大有作用了!

    总结一下:内存断点不修改改原代码,不会像普通断点那样因为修改代码被程序校验而导致中断失败;对于区段的访问只是区域大了一点,其原理和上面分析的三行代码是一样的。

    ii.如何使用内存断点来寻找OEP呢?
    要回答这个问题首先要回答这一个问题:壳是如何解压代码的?

    正如我们知道的,壳如果要把原来加密或压缩的代码运行起来就必须要解压和解密原来的代码。而这一个过程我们难道不能将他看做是对代码段(code段)的写入吗?好了,解压完毕了。我们要从壳代码的区段JMP到原来的代码段的时候,难道不正是对代码段(code段)的执行吗?

    理清了上面的关系就好办了,那么如果载入OD后,我们直接对code段下内存访问断点的时候,一定会中断在壳对code段的写入的代码的上面,就像上面的004AE247的这一行。而如果当他把code段的代码全部解压解密完毕了以后,JMP到OEP的时候,我们是不是还可以停在OEP的代码上面呢?而且每按下F9都会中断,因为这时code段在执行中哦!

    相信很多人到这里已经明白了,为什么在教程中到达了某一个时候,某一行的时候。牛人们就叫我们对code段下内存访问断点了吧。

    而如果你还要继续问我为什么一定要到那个地方才可以下断呢?我难道不可以一开始就下断吗?

    正入我上面所说的,如果你在前面下断很可能壳对code段还没解压完毕呢,这时如果你不停的按F9,你将会看到OD的下方不断的在提示你,“对401000写入中断” “对401002写入中断”“对401004写入中断”.......如果你不介意按F9到他把正个code段写完的话,我除了同情你的“F9”以外,没什么其他的意见!

    那么我们就没有别更快一点的办法了吗?
    有的!那就是我们呼之欲出的两次内存断点办法。
    怎么理解两次内存断点呢?

    让我来做一个假设吧,假设我是一个壳的作者。一个EXE文件的有code段,data段,rsrc段.....依次排列在你的内存空间中,那么我会怎么解码呢?呵呵~我比较笨一点,我会先将code段解码,然后再将data段解压,接着是rsrc段......那么聪明的你不难发现,只要你在data断或者rsrc段下内存访问断点,那么中断的时候code段就已经解压完毕了。这时我们再对code段下内存反问断点,不就可以到达OEP了吗?

    这里注意上面虽然下了两次内存访问断点,但是本质是不一样的,目的也是不一样的。

    1.对data段下内存访问断点而中断是因为内存写入中断,目的是断在对对data段的解压时,这时壳要对data段写数据,但是code段已经解压 完毕。
    2.对code段下内存访问断点而中断是因为内存执行中断,目的当然就是寻找OEP了。

    总结一下:如果我们知道壳在什么地方对code段解压完毕我们就可以使用内存断点,找到OEP。如果不知道,那么我们就依*2次内存断点去找,如果还不行就用多次内存断点。总之明白了原理在多次的内存断点其实都一样。从这个过程中我们了解的是壳在对区段解码的顺序!

    iii.实战

    说了这么多,我想大家都越越欲试了吧。
    好吧,来弄一个猛壳怎么样:

    练习文件下面下载
    这个壳是一个hying的旧版,我们用他来实验一下我们内存断点法。

    OD载入以后来到这里



    CODE:


    0040D000 u> 56         push esi       //这里
    0040D001   52         push edx
    0040D002   51         push ecx
    0040D003   53         push ebx
    0040D004   55         push ebp
    0040D005   E8 15010000     call unpackme.0040D11F


    [Copy to clipboard]


    根据跟过一次的经验我们将先设置,除int3异常以外忽略其他异常,SHIFT+F9



    CODE:


    003725B9   90         nop         //到这里
    003725BA   8BCD         mov ecx,ebp


    [Copy to clipboard]


    然后再设置除“除零”异常外,忽略其他异常。SHIFT+F9



    CODE:


    00372660   F7F3         div ebx       //到这里
    00372662   90         nop


    [Copy to clipboard]


    下面是很多的单步异常,太麻烦我们不管他,现在开始用内存断点的方法。

    对code段下内存访问断点,希望他已经解压完毕。F9



    CODE:


    0040D19D   A4         movs byte ptr es:[edi],byte ptr ds:[esi]     //还没解完呢
    0040D19E   B3 02       mov bl,2


    [Copy to clipboard]


    对data段下内存“写入”断点,试试看他是不是要写data段。



    CODE:


    00372712   F3:A4       rep movs byte ptr es:[edi],byte ptr ds:[esi]   //断到这里
    00372714   5E         pop esi


    [Copy to clipboard]


    下面再对code段下内存访问断点。F9



    CODE:


    00372855   8907         mov dword ptr ds:[edi],eax           ; SHELL32.DragFinish //这里是对IAT加密的地方了!!!
    00372857   5A         pop edx
    00372858   0FB642 FF       movzx eax,byte ptr ds:[edx-1]
    0037285C   03D0         add edx,eax
    0037285E   42         inc edx
    0037285F   83C7 04       add edi,4
    00372862   59         pop ecx
    00372863   ^ E2 A9       loopd short 0037280E
    00372865   ^ E9 63FFFFFF     jmp 003727CD
    0037286A   8BB5 93060000     mov esi,dword ptr ss:[ebp+693]           //到这里下断F2


    [Copy to clipboard]

    现在如果再对data下访问断点已经是没用了。这时应该格外的小心。

    我们现在就想既然这一段是对code解码的,那么我们就绕过他吧!

    到0037286A下断F2,然后清除内存断点!!!!

    F9以后停在这里,继续对code下内存访问断点。

    看看左下角还在解码,哎~真是麻烦!



    CODE:


    003728E1   /EB 1D       jmp short 00372900
    003728E3   |25 FFFFFF7F     and eax,7FFFFFFF
    003728E8   |0385 83060000     add eax,dword ptr ss:[ebp+683]
    003728EE   |2B85 8F060000     sub eax,dword ptr ss:[ebp+68F]
    003728F4   |8BDE         mov ebx,esi
    003728F6   |2BD8         sub ebx,eax
    003728F8   |8958 FC       mov dword ptr ds:[eax-4],ebx           //停在这里
    003728FB   |83C7 08       add edi,8
    003728FE   ^|EB DB       jmp short 003728DB
    00372900   \64:FF35 30000000   push dword ptr fs:[30]           //清除内存断点以后到这里下断,F9


    [Copy to clipboard]

    又是一段解码的代码,再次使用上面的办法手动跳出去。
    现在继续对code段下内存访问断点!!F9以后到达这里。



    CODE:


    004010CC   FFD7         call edi                   ; unpackme.004010CE   //OEP哦
    004010CE   58         pop eax
    004010CF   83EC 44       sub esp,44
    004010D2   56         push esi
    004010D3   90         nop
    004010D4   E8 B518F7FF     call 0037298E
    004010D9   8BF0         mov esi,eax


    [Copy to clipboard]


    呵呵~虽然不是我们熟悉的OEP,但是地址是没错了,况且根据我们的步骤,我可以很肯定的说这是code段的第一次“执行”中断!

    所以这就是OEP了。

    总结一下:当我们在寻找OEP的时候,要多次对code下断“赌”一“赌”他解压完毕,如果不是就对别的段试试~如果程序跑飞了,那就没办法了,重来呗~其实说起来要赌的是:当data段,idata段,rsrc段摆在你的面前,你会好好“珍惜”那个段,不过还好上天还会给我们从来一次的机会(ctrl+F2 ^_^),那么我们会对那个不会跑飞的段说3个字----“先断你”如果非要在上面加一个次数,我希望是“一次内存断点就好了”

    vi.下面来讨论一下内存断点的局限性问题。
    是不是什么壳都可以用内存中断啊?
    不是每个都可以的,一些像UPX和ASPACK就不行。
    为什么?
    呵呵~follew me!
    情况1.
    我们来看看UPX的壳

    首先,他的壳代码在UPX1段。

    这里是他要跳到OEP的地方



    CODE:


    0040ED4F   /77 11       ja short NOTEPAD_.0040ED62      
    0040ED51   |01C3       add ebx,eax
    0040ED53   |8B03       mov eax,dword ptr ds:[ebx]
    0040ED55   |86C4       xchg ah,al
    0040ED57   |C1C0 10     rol eax,10             //在解码
    0040ED5A   |86C4       xchg ah,al
    0040ED5C   |01F0       add eax,esi
    0040ED5E   |8903       mov dword ptr ds:[ebx],eax
    0040ED60   ^|EB E2       jmp short NOTEPAD_.0040ED44
    0040ED62   \24 0F       and al,0F
    0040ED64   C1E0 10     shl eax,10
    0040ED67   66:8B07     mov ax,word ptr ds:[edi]
    0040ED6A   83C7 02     add edi,2
    0040ED6D   ^ EB E2       jmp short NOTEPAD_.0040ED51   //回跳解码
    0040ED6F   61       popad
    0040ED70   - E9 5723FFFF   jmp NOTEPAD_.004010CC       //跳到OEP


    [Copy to clipboard]


    我们看到他在对code段解压完毕的时候马上就JMP到OEP去了,那么我们根本就来不及使用内存断点的办法。

    你可能说,我可以在

    0040ED6F   61       popad //这一句下段然后使用啊

    呵呵~~当然可以,不过你把花在下内存断点的时间,多按下几次F8不更好?!

    也就是说当一个壳如果他在JMP 到OEP前的一行代码仍在都在对code段解压,那么我们就不能再使用这种办法了!

    或者说我们没必要使用内存断点更贴切一点!

    情况2.:
    对于一些在OEP处有stolen code的代码
    我们来看看一个OEP



    CODE:


    0049E2F4 u> 55       push ebp               //OEP
    0049E2F5   8BEC       mov ebp,esp
    0049E2F7   83C4 F4     add esp,-0C
    0049E2FA   B8 BCE04900   mov eax,unpack.0049E0BC
    0049E2FF   E8 048CF6FF   call unpack.00406F08         //这里调用子程序
    0049E304   A1 B8FE4900   mov eax,dword ptr ds:[49FEB8]
    0049E309   50       push eax
    0049E30A   6A 00       push 0
    0049E30C   68 1F000F00   push 0F001F
    0049E311   E8 E68EF6FF   call <jmp.&kernel32.OpenFileMappingA> //API
    0049E316   A3 60194A00   mov dword ptr ds:[4A1960],eax
    0049E31B   833D 60194A00 00 cmp dword ptr ds:[4A1960],0


    [Copy to clipboard]
    这个软件在被PESPIN加壳了以后这些全被偷掉了!

    也就是说,壳在模拟OEP代码的时候必然会执行



    QUOTE:


    0049E2FF   E8 048CF6FF   call unpack.00406F08 //这一步


    而这个地方是call向code段的。如果我们使用内存访问断点,那么就停在这个子程序的地方



    CODE:


    00406F08   50       push eax                   //会停在这里
    00406F09   6A 00       push 0
    00406F0B   E8 F8FEFFFF   call <jmp.&kernel32.GetModuleHandleA>
    00406F10   BA 04F14900   mov edx,unpack.0049F104
    00406F15   52       push edx


    [Copy to clipboard]


    这里既不是处理stolen code的地方,也不是FOEP的地方。这就会对我们的判断产生误导。

    当然你可以alt+F9返回到壳处理stolen的地方,然后用内存断点,或者按几下F8到达FOEP处,但试问如果你拿到一个未知的壳的时候又怎么知道应该这么处理呢?

    还有其他一些情况留给大家总结吧!
    在下的砖已抛出,各位的玉不久矣。

    3.总结:
      好了说了很多,大家应该对内存断点的办法有了全面的了解,如果了解了内存断点的原理就不难明白他的使用方法,不难明白为什么有写壳不能使用内存断点的办法,其实任何的一种办法都需要经验的积累。相信如果大家在回答开篇的3个问题,已经不难了。
      下面给出一些使用内存断点寻找OEP的实例,大家可以结合原理再好好的体会一下。
      1.手动脱壳进阶第八篇Skvp1.32
      2.http://popbase.gamewan.com/bbs/dispbbs.asp?BoardID=5&ID=1485

    4.思考题
    使用多次内存断点的办法要谨慎对待data,rsrc区域的内存访问断点,即使要下也要注意尽量用写入断点。为什么?
    PYG19周年生日快乐!

    该用户从未签到

    发表于 2007-6-2 09:47:26 | 显示全部楼层
    我试了一下果然不错,效果对付一般的东西比较有用
    PYG19周年生日快乐!

    该用户从未签到

    发表于 2007-6-2 11:32:24 | 显示全部楼层
    好东西,下载回去研究一下。/:06
    PYG19周年生日快乐!
  • TA的每日心情
    开心
    4 天前
  • 签到天数: 113 天

    [LV.6]常住居民II

    发表于 2007-6-2 17:34:45 | 显示全部楼层
    厉害,收藏,再学习中
    PYG19周年生日快乐!

    该用户从未签到

    发表于 2007-6-2 21:58:15 | 显示全部楼层
    好多啊
    PYG19周年生日快乐!

    该用户从未签到

    发表于 2007-6-4 18:31:41 | 显示全部楼层
    提示: 作者被禁止或删除 内容自动屏蔽
    PYG19周年生日快乐!

    该用户从未签到

    发表于 2007-6-5 19:34:34 | 显示全部楼层
    ESP定律 我用的最多 太好用了  可是还真不知道原理 谢谢指点
    PYG19周年生日快乐!
  • TA的每日心情
    无聊
    2019-2-7 06:42
  • 签到天数: 5 天

    [LV.2]偶尔看看I

    发表于 2007-6-8 06:41:26 | 显示全部楼层
    这是壳的原理吗~ESP定律法
    PYG19周年生日快乐!
  • TA的每日心情
    开心
    2017-4-28 00:30
  • 签到天数: 2 天

    [LV.1]初来乍到

    发表于 2007-9-3 11:45:12 | 显示全部楼层
    很实用的东西,新手学习
    PYG19周年生日快乐!
  • TA的每日心情
    开心
    2016-11-20 00:46
  • 签到天数: 1 天

    [LV.1]初来乍到

    发表于 2007-9-3 14:48:17 | 显示全部楼层
    支持一下,回去慢慢理解
    PYG19周年生日快乐!
    您需要登录后才可以回帖 登录 | 加入我们

    本版积分规则

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