帮忙看下这几段汇编
时间:2011-12-16
来源:互联网
Assembly code
还有,昨天和别人讨论的时候,有人说将cout << &z << endl;放在int z(4);下面会影响栈使z的地址发生改变,我觉得根本不可能改变,在调用cout << &z << endl;之前z的地址都已经确定了如何改变,我觉得就算在z前面调用函数也不会发生变化,因为函数的调用都会新开辟栈帧完了之后又清空了回到了下一条指令地址
打个比较傻的比方:
就像一根面条,每次拉长的时候做个记号,然后又缩短
遇到函数调用时,首先保存调用者的栈帧底部地址,做记号开始拉长
退出函数调用时,缩回原来的长度,执行下一条指令
Assembly code
我是搞c++的,很多人劝我说搞这个没意义,可我觉得很有意义,C++ISO标准那是公式,我觉得一个职业的C++程序员
不应该只是会用标准,要理解,要深层次理解,有本书叫深度探索C++对象模型,探讨对象模型如果不清楚堆栈模型我觉得那就是瞎扯,而这些问题在汇编层次应该是很简单的东西,掌握这些来更好的理解C++底层机制岂不是事半功倍吗?
说实话这两天一直在搞函数调用时的栈帧结构问题,也看了不少资料对于这个问题总算有些理解了 11: int func(int x,int y) 12: { 004015E0 push ebp // 保存调用者函数的栈帧底部地址 esp = esp-4 004015E1 mov ebp,esp // 然后新的栈帧开始,起始位置就是esp, 004015E3 sub esp,44h // 预留空间 esp = esp-44 004015E6 push ebx // 连续压入三个寄存器,我就不大理解了,每个函数里都有这种固定格式 004015E7 push esi // 变址寄存器 它们主要用于存放存储单元在段内的偏移量,用它们可实现多种存储器操作数的寻址方式,为以不同的地址形式访问存储单元提供方便。 没看出方便在哪里... 004015E8 push edi // 变址寄存器 004015E9 lea edi,[ebp-44h] // 保存这个有什么用? 004015EC mov ecx,11h 004015F1 mov eax,0CCCCCCCCh 004015F6 rep stos dword ptr [edi] // 这句更猫腻了,看不懂 13: int z(4); 004015F8 mov dword ptr [ebp-4],4 // 将4 放入 ebp-4的地址中 14: return 6; 004015FF mov eax,6 // 15: }
还有,昨天和别人讨论的时候,有人说将cout << &z << endl;放在int z(4);下面会影响栈使z的地址发生改变,我觉得根本不可能改变,在调用cout << &z << endl;之前z的地址都已经确定了如何改变,我觉得就算在z前面调用函数也不会发生变化,因为函数的调用都会新开辟栈帧完了之后又清空了回到了下一条指令地址
打个比较傻的比方:
就像一根面条,每次拉长的时候做个记号,然后又缩短
遇到函数调用时,首先保存调用者的栈帧底部地址,做记号开始拉长
退出函数调用时,缩回原来的长度,执行下一条指令
Assembly code
|-------------| 22ff30 | 3 | push 3 esp-4 | 2 | push 2 esp-4 | 返回地址 | push re-addr esp-4 | EBP | push EBP esp-4 <<------这就记号 ---------------- <<------- mov %esp,%ebp --->> 新栈帧开始,新栈帧总是从原栈帧顶部位置开始,调用结束后就清栈,结果又变为调用函数的栈帧结构了 | z | <<------- movl $0x4,-0x4(%ebp)恰好得到22ff1c | | |-------------| <<------- sub $0x10,%esp --->> ESP 22ff10
我是搞c++的,很多人劝我说搞这个没意义,可我觉得很有意义,C++ISO标准那是公式,我觉得一个职业的C++程序员
不应该只是会用标准,要理解,要深层次理解,有本书叫深度探索C++对象模型,探讨对象模型如果不清楚堆栈模型我觉得那就是瞎扯,而这些问题在汇编层次应该是很简单的东西,掌握这些来更好的理解C++底层机制岂不是事半功倍吗?
作者: qscool1987 发布时间: 2011-12-16
不是太明白究竟什么问题。
第一段代码里,压栈的三个寄存器是由于函数对通用寄存器使用上有个约定,即要保证这三个寄存器的不变。另外的 edx:eax 通常作为返回值,所以可能会被改动;ebp/esp 就是堆栈和局部变量参数方面的应用了。所以,在函数的入口便会有对这三个寄存器的压栈保存操作,函数末尾相应地有出栈恢复的指令。出于代码通用性的考虑,即便函数里没有使用到这三个寄存器,或仅仅是部分使用到,也会对它们进行这一操作。
该堆栈空出额外的空间,并全部填入 0CCCCCCCCh ,这应该是防止堆栈溢出的个措施吧。单字节的 0cc 是 int3h 指令,这样可以在执行流程误入栈里时,也有想当的可能被侦测到。
你这里,语句序列的变化,除非是对局部数据的定义顺序上有变化,否则是不会影响到变量的地址的。
第一段代码里,压栈的三个寄存器是由于函数对通用寄存器使用上有个约定,即要保证这三个寄存器的不变。另外的 edx:eax 通常作为返回值,所以可能会被改动;ebp/esp 就是堆栈和局部变量参数方面的应用了。所以,在函数的入口便会有对这三个寄存器的压栈保存操作,函数末尾相应地有出栈恢复的指令。出于代码通用性的考虑,即便函数里没有使用到这三个寄存器,或仅仅是部分使用到,也会对它们进行这一操作。
该堆栈空出额外的空间,并全部填入 0CCCCCCCCh ,这应该是防止堆栈溢出的个措施吧。单字节的 0cc 是 int3h 指令,这样可以在执行流程误入栈里时,也有想当的可能被侦测到。
你这里,语句序列的变化,除非是对局部数据的定义顺序上有变化,否则是不会影响到变量的地址的。
作者: zara 发布时间: 2011-12-16
引用 1 楼 zara 的回复:
不是太明白究竟什么问题。
第一段代码里,压栈的三个寄存器是由于函数对通用寄存器使用上有个约定,即要保证这三个寄存器的不变。另外的 edx:eax 通常作为返回值,所以可能会被改动;ebp/esp 就是堆栈和局部变量参数方面的应用了。所以,在函数的入口便会有对这三个寄存器的压栈保存操作,函数末尾相应地有出栈恢复的指令。出于代码通用性的考虑,即便函数里没有使用到这三个寄存器,或仅仅是部分使用到,也会……
不是太明白究竟什么问题。
第一段代码里,压栈的三个寄存器是由于函数对通用寄存器使用上有个约定,即要保证这三个寄存器的不变。另外的 edx:eax 通常作为返回值,所以可能会被改动;ebp/esp 就是堆栈和局部变量参数方面的应用了。所以,在函数的入口便会有对这三个寄存器的压栈保存操作,函数末尾相应地有出栈恢复的指令。出于代码通用性的考虑,即便函数里没有使用到这三个寄存器,或仅仅是部分使用到,也会……
004015EC mov ecx,11h
004015F1 mov eax,0CCCCCCCCh
004015F6 rep stos dword ptr [edi]
这几段汇编的作用不是很理解啊
作者: qscool1987 发布时间: 2011-12-16
引用楼主 qscool1987 的回复:
Assembly code
说实话这两天一直在搞函数调用时的栈帧结构问题,也看了不少资料对于这个问题总算有些理解了
11: int func(int x,int y)
12: {
004015E0 push ebp // 保存调用者函数的栈帧底部地址 esp = esp-4
004015E1 mov ……
Assembly code
说实话这两天一直在搞函数调用时的栈帧结构问题,也看了不少资料对于这个问题总算有些理解了
11: int func(int x,int y)
12: {
004015E0 push ebp // 保存调用者函数的栈帧底部地址 esp = esp-4
004015E1 mov ……
child proc
push ebp
mov ebp,esp
sup esp,44h
...
(期间ebp一直不变)
...
mov esp,ebp
pop ebp
ret
child endp
这是高级语言的通常用的结构。
高级语言调用函数时的参数传递以及函数所使用的局部变量通常是通过栈来实现的,所以上述的这种结构是C/C++应用程序反编译后常见的结构。只要你反编译C/C++写的Win32应用程序,举目都是此种结构。
push ebx ;进入函数时保存旧值
push esi
push edi
...
pop edi ;退出函数前要恢复旧值
pop esi
pop ebx
作用很直白,保存好进门时的现场值,以便返回时恢复。
sub esp,44h
...
lea edi,[ebp-44]
mov ecx,11h
mov eax,0CCCCCCCCh
rep stos dword ptr [edi]
这几句将局部变量缓冲区每个字节清为0CCh,其中ecx是循环记数器,它控制rep循环几次,eax持有填充值,es:edi指向填充的位置(不可段超越)。
rep stos dword ptr [edi]与 rep stosd 等效,即一次填写一个双字(填写什么由eax指定)。stos前加rep表示循环赋值,每填写一个双字就sub ecx,4 ; add edi,4(也可能是减少,这与方向标志相关),然后判断ecx是否为0,如果为0就跳出。
至于为什么用0CCh填充也是有讲究的。
更多的详情请查相关指令。
作者: gsy999 发布时间: 2011-12-16

引用 3 楼 gsy999 的回复:
引用楼主 qscool1987 的回复:
Assembly code
说实话这两天一直在搞函数调用时的栈帧结构问题,也看了不少资料对于这个问题总算有些理解了
11: int func(int x,int y)
12: {
004015E0 push ebp // 保存调用者函数的栈帧底部地址 esp = esp-4
004015E1 mov ……
child proc
……
引用楼主 qscool1987 的回复:
Assembly code
说实话这两天一直在搞函数调用时的栈帧结构问题,也看了不少资料对于这个问题总算有些理解了
11: int func(int x,int y)
12: {
004015E0 push ebp // 保存调用者函数的栈帧底部地址 esp = esp-4
004015E1 mov ……
child proc
……

作者: qscool1987 发布时间: 2011-12-16
相关阅读 更多
热门阅读
-
office 2019专业增强版最新2021版激活秘钥/序列号/激活码推荐 附激活工具
阅读:74
-
如何安装mysql8.0
阅读:31
-
Word快速设置标题样式步骤详解
阅读:28
-
20+道必知必会的Vue面试题(附答案解析)
阅读:37
-
HTML如何制作表单
阅读:22
-
百词斩可以改天数吗?当然可以,4个步骤轻松修改天数!
阅读:31
-
ET文件格式和XLS格式文件之间如何转化?
阅读:24
-
react和vue的区别及优缺点是什么
阅读:121
-
支付宝人脸识别如何关闭?
阅读:21
-
腾讯微云怎么修改照片或视频备份路径?
阅读:28