堆栈平衡.jpg

    堆栈平衡
    指令解析 push register:将寄存器内存储的值压入栈中;
    pop register:将栈顶地址存储的值传递给寄存器;
    开辟栈帧 函数内使用的局部变量都是临时存储的,如果每次调用都要往内存中存数据并且不去删除,就会造成很大的浪费
    解决方法就是给他一块临时的空间,用完之后就覆盖掉,这就是开辟堆栈
    标志:从 push ebp 开始,到下一个push ebp 结束
    堆栈平衡 作用 函数的调用,要时刻的保持堆栈平衡。不平衡就会造成地址错位,从而程序代码就回不到调用函数的位置了。
    平衡方式
    1. cdecl 调用约定:栈外(函数外)平衡,平衡方式:add 8。
    2. fastcall,thiscall,stdcall 调用约定:是栈内(函数内)平衡,平衡方式:ret 8 (相当与将retn 和 add 8 合并成一条指令)。
    3. delphi call: 前三个参数使用 1.eax,2.edx,3.ecx 传递,后面使用栈传递
    堆栈平衡中
    寄存器
    EBP——在程序的运行过程中有着特定的作用,保存数据的基址,根据这个特性,EBP一般不会被更改。
    但是在汇编中,函数需要用到的数据都会在运行前入栈,函数可以通过栈顶找到这些数据,所以在函数运行时,EBP就暂时失去了意义,所以我们就用它来临时保存开辟的栈底。
    EIP——即返回地址,在堆栈中的位置,ebp+4 。ret命令 执行,返回有调用函数指令指向的下一条指令。备注:如果调用约定是_cdecl,应为平衡堆栈 add x
    ESP——在调用函数前后,ESP的值应该不变的。
    脱壳中的ESP平衡
    - pushad 的作用是把通用寄存器压栈。寄存器的入栈顺序依次是:EAX,ECX,EDX,EBX,ESP(初始值),EBP,ESI,EDI.
    - 在ESP寄存器保存的的初始值(即开辟堆栈的栈底)下硬件断点,只有当堆栈快执行完毕,popad时才会再次访问堆栈的栈底,则此时触发硬件断点。
    汇编调用CALL 地址的计算 image.png
    E8 Call Offset(调用普通函数)(Offset 是一段偏移)
    FF 15 Call Dword ptr [ 地址 ] (调用IAT)([地址]内 IAT表中函数的地址)
    E9 JMP Offset 跳到一个地址(可手动计算出)
    FF25 JMP Dword ptr [ 地址 ] (跳到IAT地址,不可手动算出)

    一个函数的解析

    1. //主函数
    2. int main()
    3. {
    4. int x= AFunc(5, 6);
    5. return 0;
    6. }
    7. 00D317B0 > 55 PUSH EBP ; 保存语句执行前的EBP
    8. 00D317B1 8BEC MOV EBP,ESP ; 将当前堆栈的ESP传给EBP
    9. 00D317B3 81EC CC000000 SUB ESP,0xCC ; 为函数的局部变量申请一段空间
    10. 00D317B9 53 PUSH EBX
    11. 00D317BA 56 PUSH ESI ; 保存入栈前的寄存器值
    12. 00D317BB 57 PUSH EDI
    13. 00D317BC 8D7D F4 LEA EDI,DWORD PTR SS:[EBP-0xC] ; 将局部变量的堆栈中开始地址值保存到EDI寄存器中
    14. 00D317BF B9 03000000 MOV ECX,0x3 ; ECX=3;计数器
    15. 00D317C4 B8 CCCCCCCC MOV EAX,0xCCCCCCCC ; 初始化 EAX
    16. 00D317C9 F3:AB REP STOS DWORD PTR ES:[EDI] ; EAX的值填充到EDI指向的地址,循环次数ECX
    17. 00D317CB B9 03C0D300 MOV ECX,堆栈平衡.00D3C003 ; ECX赋值
    18. 00D317D0 E8 32FBFFFF CALL 堆栈平衡.00D31307 ; CheckForDebuggerJustMyCode
    19. 00D317D5 6A 06 PUSH 0x6 ; 参数2=6
    20. 00D317D7 6A 05 PUSH 0x5 ; 参数1=5
    21. 00D317D9 E8 33FBFFFF CALL 堆栈平衡.00D31311 ; 调用 AFunc(int a,int b)函数
    22. 00D317DE 83C4 08 ADD ESP,0x8 ; 平衡堆栈
    23. 00D317E1 8945 F8 MOV DWORD PTR SS:[EBP-0x8],EAX ; 函数返回值EAX传递给局部变量x
    24. 00D317E4 33C0 XOR EAX,EAX ; EAX=0 return 0
    25. 00D317E6 5F POP EDI
    26. 00D317E7 5E POP ESI ; 寄存器出栈,恢复寄存器的值
    27. 00D317E8 5B POP EBX
    28. 00D317E9 81C4 CC000000 ADD ESP,0xCC ; 释放局部变量空间,平衡堆栈
    29. 00D317EF 3BEC CMP EBP,ESP ; 检查堆栈平衡
    30. 00D317F1 E8 3AFAFFFF CALL 堆栈平衡.00D31230 ; CheckESP
    31. 00D317F6 8BE5 MOV ESP,EBP ; ESP=EBP,还原调用函数前
    32. 00D317F8 5D POP EBP ; ESP=EBP,还原调用函数栈底 EBP
    33. 00D317F9 C3 RETN ; 返回调用函数指令的下一条指令 EIP
    1. 00D31311 /E9 2A040000 JMP 堆栈平衡.AFunc

    00D31740= 00D31311+ 0000042A+ 5

    1. int AFunc(int a, int b)
    2. {
    3. int n =a+b;
    4. return n;
    5. }
    6. -
    7. 00D31740 > 55 PUSH EBP ; 保存语句执行前的EBP
    8. 00D31741 8BEC MOV EBP,ESP ; 将当前堆栈的ESP传给EBP
    9. 00D31743 81EC CC000000 SUB ESP,0xCC ; 为函数的局部变量申请一块内存空间
    10. 00D31749 53 PU
    11. 00D3174A 56 PUSH ESI ; 保存寄存器环境
    12. 00D3174B 57 PU
    13. 00D3174C 8D7D F4 LEA EDI,DWORD PTR SS:[EBP-0xC] ; 将局部变量的堆栈中开始地址值保存到EDI寄存器中
    14. 00D3174F B9 03000000 MOV ECX,0x3 ; ECX=3
    15. 00D31754 B8 CCCCCCCC MOV EAX,0xCCCCCCCC ; 初始化 EAX
    16. 00D31759 F3:AB REP STOS DWORD PTR ES:[EDI] ; EAX的值填充到EDI指向的地址,循环次数ECX
    17. 00D3175B B9 03C0D300 MOV ECX,堆栈平衡.00D3C003 ; ECX赋值
    18. 00D31760 E8 A2FBFFFF CALL 堆栈平衡.00D31307 ; CheckForDebuggerJustMyCode
    19. 00D31765 8B45 08 MOV EAX,DWORD PTR SS:[EBP+0x8] ; EAX=参数1
    20. 00D31768 0345 0C ADD EAX,DWORD PTR SS:[EBP+0xC] ; EAX=参数2+参数1
    21. 00D3176B 8945 F8 MOV DWORD PTR SS:[EBP-0x8],EAX ; 局部变量var=EAX
    22. 00D3176E 8B45 F8 MOV EAX,DWORD PTR SS:[EBP-0x8] ; return eax
    23. 00D31771 5F P
    24. 00D31772 5E POP ESI ; 恢复寄存器环境
    25. 00D31773 5B P
    26. 00D31774 81C4 CC000000 ADD ESP,0xCC ; 释放局部变量空间,平衡堆栈
    27. 00D3177A 3BEC CMP EBP,ESP ; 检查堆栈平衡
    28. 00D3177C E8 AFFAFFFF CALL 堆栈平衡.00D31230 ; CheckESP
    29. 00D31781 8BE5 MOV ESP,EBP ; ESP=EBP
    30. 00D31783 5D POP EBP ; 还原调用函数栈底 EBP
    31. 00D31784 C3 RETN ; 返回调用函数指令的下一条指令 EIP