一、堆栈结构
入栈
方式一
定义EBP寄存器存储栈底地址,ESP寄存器存储栈顶地址。在存入数据之前,栈底和栈顶位置一样。
mov edp,0x0019FEFC
mov ebp,0x0019FEFC
向堆栈中存入数据,存入4字节数据,存入edx-4的四个内存地址中。
mov dword ptr ds:[EDX-4],AAAAAAAA
4字节数据存入4个内存地址之后,栈顶地址应该向上移动四个地址,堆栈是从高地址向低地址存入数据,因此栈顶地址-4。
方式二
参数存入堆栈之后,直接修改栈顶的位置。
lea edx,dword ptr ds:[edx-4]
堆栈查数据
mov esi,dword ptr ds:[ebx-4]
mov esi,dword ptr ds:[ebx-8]
出栈
方式一
先取出数据,后修改栈顶,栈顶+4。
mov esi,dword ptr ds:[edx+0x4]
lea edx,dword ptr ds:[edx+0x4]
方式二
先修改栈顶地址,栈顶+4,然后取出数据。
lea edx,dword ptr ds:[edx+0x4]
mov esi,dword ptr ds:[edx+0x4]
汇编语句
mov ebx,dword ptr ss:[esp]
将寄存器中存储的内存地址对应的值放入ebx中。
lea eax,dword ptr ss:[esp]
将esp中的内存地址,直接存放到eax中。
mov dword ptr ss:[esp],ebx
将ebx中的值,存入到esp寄存器中存放到内存地址中。
堆栈读写数据
1、使用EBX存储栈底地址,EDX存储栈顶地址,连续存储5个不同的数
004039F0 t> BA 08FF1900 mov edx,0x19FF08
004039F5 BB 08FF1900 mov ebx,0x19FF08
004039FA C742 FC AAAAA>mov dword ptr ds:[edx-0x4],0xAAAAAAAA
00403A01 83EA 04 sub edx,0x4
00403A04 C742 FC BBBBB>mov dword ptr ds:[edx-0x4],0xBBBBBBBB
00403A0B 83EA 04 sub edx,0x4
00403A0E C742 FC CCCCC>mov dword ptr ds:[edx-0x4],0xCCCCCCCC
00403A15 83EA 04 sub edx,0x4
00403A18 C742 FC DDDDD>mov dword ptr ds:[edx-0x4],0xDDDDDDDD
00403A1F 83EA 04 sub edx,0x4
00403A22 C742 FC EEEEE>mov dword ptr ds:[edx-0x4],0xEEEEEEEE
00403A29 83EA 04 sub edx,0x4
2、分别使用栈底加偏移、栈顶加偏移的方式读取这5个数,并存储到寄存器中
栈底加偏移
00403A2C 8B43 FC mov eax,dword ptr ds:[ebx-0x4]
00403A2F 8B4B F8 mov ecx,dword ptr ds:[ebx-0x8]
00403A32 8B63 F4 mov esp,dword ptr ds:[ebx-0xC]
00403A35 8B6B F1 mov ebp,dword ptr ds:[ebx-0xF]
二、堆栈图
EIP寄存器
EIP寄存器主要存储当前程序所在的内存地址。
call指令
call指令用来调用一个函数。在od中使用F7来跟进。执行call指令会分两步:
-
将call指令的下一条指令地址压入堆栈,因为call指令执行完毕之后,还要回到原来的地址。 -
首先会跳转到一个JMP指令所在的地址,然后进入子程序
堆栈分析
存储栈底
将程序开始的栈底存入堆栈中,用于函数执行完毕之后,恢复堆栈。
00401070 /> 55 push ebp
执行指令之前,观察ebp的值为
执行push指令之后,将ebp即栈底的值入栈。
堆栈平衡
设置栈顶和栈底。
00401071 |. 8BEC mov ebp,esp
开辟缓冲区
将栈顶的地址向低地址移动,开辟指定大小的内存空间,用来存储函数中要使用到的数据。
00401073 |. 83EC 48 sub esp,0x48
保留现场
相关寄存器中存储函数调用所需要的信息,因此将相关寄存器内容存入堆栈中,以便函数调用的过程中使用。
00401076 |. 53 push ebx
00401077 |. 56 push esi
00401078 |. 57 push edi ; test222.<ModuleEntryPoint>
填充缓冲区
使用硬编码CCCCCCCC来填充缓冲区,CC是断点int3的硬编码。当前堆栈是用来存储数据,以供函数使用,防止编译器将当前堆栈内容当作指令来执行。
00401079 |. 8D7D B8 lea edi,dword ptr ss:[ebp-0x48]
0040107C |. B9 12000000 mov ecx,0x12
00401081 |. B8 CCCCCCCC mov eax,0xCCCCCCCC
实现函数功能
将两个参数相加存入eax寄存器中。
mov eax,dword ptr ss:[ebp+0x8]
add eax,dword ptr ss:[ebp+0xC]
恢复现场
将栈中的值取出,恢复到指定寄存器中。
平衡堆栈
函数结束之后,栈底和栈顶不会发生变化。
平衡堆栈分为外平栈和内平栈
外平栈即平栈指令在函数外部,由函数的调用者执行平栈指令。
内平栈即平栈指令在函数内部,由函数本身执行平栈指令。
RETN
等价于pop eip,即将此时栈顶的值存储到EIP中。
程序经过一系列指令之后,在函数末尾执行retn指令,将此时栈顶的值即call指令的下一条指令地址取出,存放到EIP中。
三、汇编
#include "stdio.h"
void Function(int x,int y)
{
int a;
a= x + y;
printf("%dn",a);
}
void main()
{
int a=1;
int b=3;
Function(1,3);
}
主函数
00401070 /> 55 push ebp //存储栈底
00401071 |. 8BEC mov ebp,esp //堆栈平衡
00401073 |. 83EC 48 sub esp,0x48 //开辟缓冲区
00401076 |. 53 push ebx //保留现场
00401077 |. 56 push esi
00401078 |. 57 push edi ; test222.<ModuleEntryPoint>
00401079 |. 8D7D B8 lea edi,[local.18] //开辟缓冲区
0040107C |. B9 12000000 mov ecx,0x12
00401081 |. B8 CCCCCCCC mov eax,0xCCCCCCCC
00401086 |. F3:AB rep stos dword ptr es:[edi]
00401088 |. C745 FC 01000>mov dword ptr ss:[ebp-0x4],0x1 //传递参数,局部变量。
0040108F |. C745 F8 03000>mov dword ptr ss:[ebp-0x8],0x3
00401096 |. 6A 03 push 0x3 //参数入堆
00401098 |. 6A 01 push 0x1
0040109A |. E8 66FFFFFF call test222.00401005 //调用函数
0040109F |. 83C4 08 add esp,0x8 //平衡堆栈
//恢复现场
004010A2 |. 5F pop edi ; kernel32.7769AA68
004010A3 |. 5E pop esi ; kernel32.7769AA68
004010A4 |. 5B pop ebx ; kernel32.7769AA68
004010A5 |. 83C4 48 add esp,0x48
004010A8 |. 3BEC cmp ebp,esp
004010AA |. E8 91000000 call test222.00401140 //调用printf函数
004010AF |. 8BE5 mov esp,ebp
004010B1 |. 5D pop ebp ; kernel32.7769AA68
004010B2 . C3 retn
Function函数
00401020 /> 55 push ebp //存储栈底
00401021 |. 8BEC mov ebp,esp //堆栈平衡
00401023 |. 83EC 44 sub esp,0x44 //开启填充缓冲区
00401026 |. 53 push ebx //保留现场
00401027 |. 56 push esi
00401028 |. 57 push edi
00401029 |. 8D7D BC lea edi,[local.17]
0040102C |. B9 11000000 mov ecx,0x11
00401031 |. B8 CCCCCCCC mov eax,0xCCCCCCCC
00401036 |. F3:AB rep stos dword ptr es:[edi]
00401038 |. 8B45 08 mov eax,dword ptr ss:[ebp+0x8] //取参数
0040103B |. 0345 0C add eax,dword ptr ss:[ebp+0xC]
//
[local.1] 是 ebp-4 , [local.2] 是 ebp-8
//
0040103E |. 8945 FC mov [local.1],eax
00401041 |. 8B4D FC mov ecx,[local.1] //参数相加
//参数传递给print函数
00401044 |. 51 push ecx ; /Arg2 = 00000000
00401045 |. 68 1C204200 push test222.0042201C ; |Arg1 = 0042201C ASCII "%d"
//调用printf函数
0040104A |. E8 71000000 call test222.004010C0 ; test222.004010C0
//外平栈
0040104F |. 83C4 08 add esp,0x8
00401052 |. 5F pop edi ; test222.0040109F
00401053 |. 5E pop esi ; test222.0040109F
00401054 |. 5B pop ebx ; test222.0040109F
//平衡堆栈
00401055 |. 83C4 44 add esp,0x44
00401058 |. 3BEC cmp ebp,esp
0040105A |. E8 E1000000 call test222.00401140
0040105F |. 8BE5 mov esp,ebp
00401061 |. 5D pop ebp ; test222.0040109F
00401062 . C3 retn
原文始发于微信公众号(土拨鼠的安全屋):逆向学习 | 堆栈基础小记
- 左青龙
- 微信扫一扫
- 右白虎
- 微信扫一扫
评论