ESP定律原理:在我们开始讨论ESP定律之前,先讲解一下一些简单的汇编知识。
    ①.call

    这个命令是访问子程序的一个汇编基本指令,从底层原理来看我们可以这样来理解:
    以以下汇编程序为例:
    1.向堆栈中压入下一行程序的地址;
    2.JMP到call的子程序地址处。
    例如:00401029.E8 DA240A00 call 004A3508
    0040102E.5A pop edx
    在执行了00401029以后,程序会将0040102E压入堆栈,然后JMP到004A3508地址处!
    ②.RETN
    与call对应的就是RETN了。对于RETN我们可以这样来理解:
    1.将当前的ESP中指向的地址出栈;
    2.JMP到这个地址。
    这个就完成了一次调用子程序的过程。在这里关键的地方是:如果我们要返回父程序,则当我们在堆栈中进行堆栈的操作的时候,一定要保证在RETN这条指令之前,ESP指向的是我们压入栈中的地址。这也就是著名的“堆栈平衡”原理!
    ③.狭义ESP定律
    ESP定律的原理就是“堆栈平衡”原理。
    让我们来到程序的入口处看看吧!
    1.这个是加了ASPACK壳的入口时各个寄存器的值!
    EAX 00000000
    ECX 0012FFB0
    EDX 7FFE0304 //堆栈值

    EBX 7FFDF000 //堆栈值

    ESP 0012FFC4
    EBP 0012FFF0
    ESI 77F57D70 ntdll.77F57D70
    EDI 77F944A8 ntdll.77F944A8
    EIP 0040D000 ASPACK.
    2.这个是ASPACK壳JMP到OEP后的寄存器的值!
    EAX 004010CC ASPACK.004010CC
    ECX 0012FFB0
    EDX 7FFE0304 //堆栈值

    EBX 7FFDF000 //堆栈值

    ESP 0012FFC4
    EBP 0012FFF0
    ESI 77F57D70 ntdll.77F57D70
    EDI 77F944A8 ntdll.77F944A8
    EIP 004010CC ASPACK.004010CC
    是不是除了EIP不同以外,eax保存当前OEP值,其他都一模一样啊!
    造成这样的结果原因如下:
    0040D000 A> 60 pushad //注意这里ESP=0012FFC4

    0040D001 E8 00000000 callASPACK.0040D006 //ESP=0012FFA4

    PUSHAD就是把所有寄存器压栈!我们再到壳的最后看看:
    0040D558 61 popad //ESP=0012FFA4

    0040D559 75 08 jnz short ASPACK.0040D563 //注意这里ESP=0012FFC4

    也就是说当我们对ESP的0012FFA4下硬件访问断点之后。当程序要通过堆栈访问这些值,从而恢复原来寄存器的值,准备跳向苦苦寻觅的OEP的时候,OD帮助我们中断下来。
    小结:我们可以把壳假设为一个子程序,当壳把代码解压前和解压后,他必须要做的是遵循堆栈平衡的原理。
    因为大家对ESP理解各有异同,但是,大同小异!一般理解可以为:
    1、在命令行下断hresp-4(此时的ESP就是OD载入后当前显示的值)
    2、hr ESP(关键标志下一行代码所指示的ESP值(单步通过))**

    总结:

    **①.ESP定律的原理是堆栈平衡

    ②.ESP定律的适用范围:几乎全部的压缩壳,部分加密壳。只要是在JMP到OEP后,ESP=0012FFC4的壳,理论上我们都可以使用。但是在何时下断点避开校验,何时下断OD才能断下来,这还需要多多总结和多多积累。
    如果我们知道壳在什么地方对code段解压完毕我们就可以使用内存断点,找到OEP。如果不知道,那么我们就依靠2次内存断点去找,如果还不行就用多次内存断点。总之明白了原理在多次的内存断点其实都一样。从这个过程中我们了解的是壳在对区段解码的顺序!
    基于ESP原理,我们可以通过F8步进,注意右面寄存器FPU的显示,当有且只有ESP和EIP为红色时,我们可以用ESP定律了。