函数调用堆栈(二)

2014-11-24 12:03:54 · 作者: · 浏览: 1
p,8
清栈,清除两个压栈的参数10 90 调用者main负责
(所谓__cdecl调用由调用者负责恢复栈,调用者负责清理的只是入栈的参数,test函数自己的堆栈空间自己返回时自己已经清除,靠!一直理解错)

00401094 push eax 入栈,计算结果108入栈,即printf函数的参数之一入栈
00401095 push offset string "%d" (0042201c) 入栈,参数 "%d" 当然其实是%d的地址
0040109A call printf (004010d0) 函数调用 printf("%d",108) 因为printf函数时
0040109F add esp,8 清栈,清除参数 ("%d", 108)
19: return 0;
004010A2 xor eax,eax eax清零
20: }

main函数执行完毕 此时esp=0x12FF34 ebp=0x12FF80
004010A4 pop edi
004010A5 pop esi
004010A6 pop ebx
004010A7 add esp,40h //为啥不用mov esp, ebp 是为了下面的比较
004010AA cmp ebp,esp //比较,若不同则调用chkesp抛出异常
004010AC call __chkesp (00401150)
004010B1 mov esp,ebp
004010B3 pop ebp //ESP=0X12FF84 EBP=0x12FFC0 尘归尘 土归土 一切都恢复最初的平静了 :)
004010B4 ret

1. 如果函数调用方式是__stdcall 不同之处在于
main函数call 后面没有了 add esp, 8
test函数最后一句 是 ret 8 (由test函数清栈, ret 8意思是执行ret后,esp+8)

2. 运行过程中0x12FF28 保存了指令地址 00401091是怎么保存的?
栈每个空间保存4个字节(粒度4字节) 例如下一个栈空间0x12FF2C保存参数10
因此
0x12FF28 0x12FF29 0x12FF2A 0x12FF2B
91 10 40 00
little-endian 认为其读的第一个字节为最小的那位上的数

3. char a[] = "abcde"
对局部字符数组变量(栈变量)赋值,是利用寄存器从全局数据内存区把字符串“abcde”拷贝到栈内存中的

4. int szNum[5] = { 1, 2, 3, 4, 5 }; 栈中是如何分布的?
00401798 mov dword ptr [ebp-14h],1
0040179F mov dword ptr [ebp-10h],2
004017A6 mov dword ptr [ebp-0Ch],3
004017AD mov dword ptr [ebp-8],4
004017B4 mov dword ptr [ebp-4],5
可以看出来 是从右边开始入栈,所以是 5 4 3 2 1 入栈

int *ptrA = (int*)(&szNum+1);
int *ptrB = (int*)((int)szNum + 1);
std::cout<< ptrA[-1] << *ptrB << std::endl;
结果如何?
28: int *ptrA = (int*)(&szNum+1);
004017BB lea eax,[ebp]
004017BE mov dword ptr [ebp-18h],eax
&szNum是指向数组指针;加1是加一个数组宽度;&szNum+1指向移动5个int单位之后的那个地方, 就是把EBP的地址赋给指针
ptrA[-1]是回退一个int*宽度,即ebp-4
29: int *ptrB = (int*)((int)szNum + 1);
004017C1 lea ecx,[ebp-13h]
004017C4 mov dword ptr [ebp-1Ch],ecx
如果上面是指针算术,那这里就是地址算术,只是首地址+1个字节的offset,即ebp-13h给指针

实际保存是这样的
01 00 00 00 02 00 00 00
ebp-14h ebp-13h ebp-10h
注意是int*类型的,最后获得的是 00 00 00 02
由于Little-endian, 实际上逻辑数是02000000 转换为十进制数就为33554432
最后输出533554432


摘自 踏雪无痕