一、堆栈结构
可以看出一个函数局部变量和实参的位置
二、易语言例子解析
带上下文
00000000 - 60 pushad
00000001 - 8B 4D 10 mov ecx, dword [ebp+0x10]
00000004 - 8B 7D 08 mov edi, dword [ebp+0x08]
00000007 - 8B 75 0C mov esi, dword [ebp+0x0C]
0000000A - F3 A4 rep movsb
0000000C - 61 popad
这里牵涉到其他寄存器,需要保存环境,默认ebp esp 自动还原
pushad是将8个32位寄存器压入栈中,压入的顺序依次为 EAX,ECX,EDX,EBX,ESP,EBP,ESI,EDI
popad 相反
带局部变量
置入代码 ({ 139, 69, 8, 139, 0, 137, 69, 252 })
' mov eax , dword ptr [ebp+08h] //第一个参数地址赋值给eax
' mov eax , dword ptr [eax] //把上步骤参数的地址的值取出来
' mov dword ptr [ebp-04h] , eax //再把值传给临时变量ret
这里没看到小数的转换,只是取值而已, 说明类型无关
无局部变量
末尾的返回值无效的,只是为了保证编辑器语法通过,可以任意值
这里没有后面的俩个ret
三、易语言进阶例子
数据8字节的(长整数,双精度浮点数)
置入代码 ({ 139, 69, 8, 255, 48, 143, 69, 248, 255, 112, 4, 143, 69, 252 })
' mov eax , dword ptr [ebp+08h] ’第一个参数翻入eax
' push dword ptr [eax] 把参数所指的值并入栈 4个字节 低位
' pop dword ptr [ebp-08h] 把参数的值赋值给第二个局部变量
' push dword ptr [eax+04h] 4个字节 高位
' pop dword ptr [ebp-04h] 高位对应局部变量高位
末尾多俩个ret指令,无用,但看着别扭
低地址存低位,高地址存高位,栈是往低地址扩展的
从汇编看这个函数stdcall调用方式
四、c/c++例子
__cdecall:参数右到左入栈,调用方维持堆栈平衡(默认)
__stdcall:参数右到左入栈,被调用方堆栈平衡
SS:stack segment register 栈段暂存器
SP:stack pointer 栈指针
在VS编辑器中中堆栈会自动帮你配平衡,只需要关键汇编代码,比如下例子
eax保存函数返回值,edx保存高位
无局部变量
int add(int a, int b)
{
_asm
{
mov eax, dword ptr[ebp + 08h] //第一个参数
add eax, dword ptr[ebp + 0Ch] //第二个参数,堆栈的地址高到低,先入栈
}
//return (a + b);//K&R推荐return都打括号
}
标准调用方式
int __stdcall add(int a, int b)
{
_asm
{
mov eax, dword ptr[ebp + 08h]
add eax, dword ptr[ebp + 0ch]
}
//return (a + b);//K&R推荐return都打括号
}
RETN 8看到没,被调用方维持堆栈平衡,2个参数8个字节
c调用方式
int add(int a, int b)
{
//int c = 10;
_asm
{
mov dword ptr[ebp - 04h], 10
mov eax, dword ptr[ebp + 08h]
add eax, dword ptr[ebp + 0ch]
add eax, dword ptr[ebp -04h]
}
//return (a + b + c);//K&R推荐return都打括号
}
数据8字节的(长整数,双精度浮点数)
long long add(long long a, long long b)
{
return (a + b);//K&R推荐return都打括号
}
long long 是8个字节,低位低位相加,高位高位相加
增加一个临时变量
long long add(long long a, long long b)
{
long long c = 10L;
return (a + b + c);//K&R推荐return都打括号
}
五、相关指令
leave 指令
等价于
mov esp, ebp
pop ebp
call 指令
1、段内转移的CALL指令等价于:
push eip
jmp 目的位置
2、段间转移的CALL指令等价于:
push CS
push eip
jmp 目的位置
RETN/RETF 指令
RETN指令用于从段内CALL;
RETF指令用于从段间CALL。
俩种格式:
RETN/RETF
RETN/RETF 操作数
RETN等价于:POP eip
RETF等价于:
POP eip
POP CS
而带有操作数的RETN/RETF指令则是在POP之后,执行ESP=ESP+操作数
六、总结
1、汇编指令结合od ,分析了易语言中的置入代码.
2、VS编辑器内联汇编分析c/c++代码, 快捷键alt +8 查看反汇编窗口.
3、坑就是VS编译要禁止优化, od出现大量db时, 删除分析即可
终于完结了, 通过实践果然对汇编与内存的印象更加深刻~