中企动力 网站报价,韩国网站怎么打开,订制网站,临汾网络推广虽然编了几年程序#xff0c;但是对于程序到底是什么规则变成汇编代码的#xff0c;在这里搞了一个小程序。用VC查看了一下汇编代码。在此之前先介绍一下关于函数运行是堆栈变化的细节。
在高级语言编写程序时#xff0c;函数的调用是很常见的事情#xff0c;但是在函数调…虽然编了几年程序但是对于程序到底是什么规则变成汇编代码的在这里搞了一个小程序。用VC查看了一下汇编代码。在此之前先介绍一下关于函数运行是堆栈变化的细节。
在高级语言编写程序时函数的调用是很常见的事情但是在函数调用过程中堆栈的变化通常有几个细节
1.父函数将函数的实参按照从右至左的顺序压入堆栈
2.CPU将父函数中函数调用指令Call的下一条指令地址EIP压入堆栈
3.父函数通过Push Ebp指令将基址指针EBP的值压入堆栈并通过Mov Ebp,Esp指令将当前堆栈指针Esp值传给Ebp
4.通过Sub Esp,mm是字节数指令可以为存放函数中的局部变量开辟内存。函数在执行的时候如果需要访问实参或局部变量都可以通过EBP指针来指引完成。
windows系统下常用的函数调用通常有种__cdecl和__stdCall。
1.在VC、.net等开发环境中编写命令行程序时的Main或者_tmain函数以及大家自己定义的很多函数都是默认采用__cdecl调用方式
2.通过MFC编写图形界面程序的时候其主函数声明为extern C int WINAPI tWinMain(参数)该函数的调用约定是__stdCall。WINAPI和PASCAL等都是__stdCall的宏定义,是一个意思此外大家平时调用的API函数绝大多数都是采用__staCall的调用方式
3.__cdecl调用方式的函数父函数在调用子函数的时候先将子函数的实参按照从右至左的顺序压入堆栈中子函数返回后父函数通过Sub Esp,nn函数实参个数*4指令来恢复堆栈
4.__stdCall调用约定函数子函数调用时实参入栈顺序也是从左到右但是堆栈恢复是子函数返回时自己通过Ret n指令来完成的。
下边就是针对这些知识进行的部分实践 [cpp] view plaincopy #includestdio.h #includewindows.h #includestdlib.h int fun(char *szIn, int nTest) { char szBuf[9]; printf(%d\n,nTest); strcpy(szBuf,szIn); return 0; } int main(int argc, char *argv[]) { char sz_In[] 1234567; fun(sz_In,888); return 0; } 汇编代码 [plain] view plaincopy 00401003 int 3 00401004 int 3 ILT0(?funYAHPADHZ): 00401005 jmp fun (00401020) //进入fun函数 ILT5(_main): 0040100A jmp main (00401080) //进入main函数该位置是整段代码的入口 0040100F int 3 00401010 int 3 00401011 int 3 00401012 int 3 00401013 int 3 00401014 int 3 00401015 int 3 00401016 int 3 00401017 int 3 00401018 int 3 00401019 int 3 0040101A int 3 0040101B int 3 0040101C int 3 0040101D int 3 0040101E int 3 0040101F int 3 --- c:\project\heap1\heap1.cpp -------------------------------------------------------------------------------------------------------------------------------------- 1: #includestdio.h 2: #includewindows.h 3: #includestdlib.h 4: int fun(char *szIn, int nTest) 5: { 00401020 push ebp 00401021 mov ebp,esp //保存基址指针并将现在的栈顶保存为基址指针。 00401023 sub esp,4Ch //腾出一部分堆栈区用于存放局部变量。 00401026 push ebx 00401027 push esi 00401028 push edi //保存三个寄存器的值。 00401029 lea edi,[ebp-4Ch] 0040102C mov ecx,13h 00401031 mov eax,0CCCCCCCCh 00401036 rep stos dword ptr [edi] //将腾出的4Ch的空间初始化值为0xCC。 6: char szBuf[9]; 7: printf(%d\n,nTest); 00401038 mov eax,dword ptr [ebp0Ch] 0040103B push eax 0040103C push offset string %d\n (0042201c) //先后压入栈中两个地址nTest一个是一个字符串指针。 00401041 call printf (004011d0) //调用printf函数时它会自动做到堆栈平衡。 00401046 add esp,8 //由于刚才压入和两个参数所以在这里手动将两个参数弹出堆栈 8: strcpy(szBuf,szIn); 00401049 mov ecx,dword ptr [ebp8] 0040104C push ecx //压入szIn的指针。这个参数在高出基址的8位处也就是调用该函数前压入栈中的。 0040104D lea edx,[ebp-0Ch] 00401050 push edx //压入szBuf的指针这个函数在低于基址的OCh位处这是调用函数后分配的。局部变量的分配大 00401051 call strcpy (004010e0) //小都是按4的倍数分配的所以尽管szBuf[9]但是也分配在了0Ch处。 00401056 add esp,8 9: return 0; 00401059 xor eax,eax //返回值在EAX中。 10: } 0040105B pop edi 0040105C pop esi 0040105D pop ebx //弹出保存的数据。 0040105E add esp,4Ch //消除为局部变量腾出的空间。 00401061 cmp ebp,esp 00401063 call __chkesp (00401250) //检验是否在用户自定义汇编代码中修改了ebp和esp的相对关系。一般情况下EBPESP 00401068 mov esp,ebp //将原基址恢复给栈顶寄存器。 0040106A pop ebp //弹出原调用函数的堆栈基址。 0040106B ret //函数返回。 --- No source file -------------------------------------------------------------------------------------------------------------------------------------------------- 0040106C int 3 0040106D int 3 0040106E int 3 0040106F int 3 00401070 int 3 00401071 int 3 00401072 int 3 00401073 int 3 00401074 int 3 00401075 int 3 00401076 int 3 00401077 int 3 00401078 int 3 00401079 int 3 0040107A int 3 0040107B int 3 0040107C int 3 0040107D int 3 0040107E int 3 0040107F int 3 --- c:\project\heap1\heap1.cpp -------------------------------------------------------------------------------------------------------------------------------------- 11: int main(int argc, char *argv[]) 12: { 00401080 push ebp 00401081 mov ebp,esp //保存堆栈基址 00401083 sub esp,48h //腾出局部变量空间 00401086 push ebx 00401087 push esi 00401088 push edi //保存3个寄存器 00401089 lea edi,[ebp-48h] 0040108C mov ecx,12h 00401091 mov eax,0CCCCCCCCh //初始化局部变量空间 00401096 rep stos dword ptr [edi] 13: char sz_In[] 1234567; 00401098 mov eax,[string 1234567 (00422020)] 0040109D mov dword ptr [ebp-8],eax 004010A0 mov ecx,dword ptr [string 12345674 (00422024)] 004010A6 mov dword ptr [ebp-4],ecx //将字符串通过寄存器将字符拷贝到分配的空间中。 14: fun(sz_In,888); 004010A9 push 378h 004010AE lea edx,[ebp-8] 004010B1 push edx //从右至左将参数压入堆栈中数字直接压入数值字符串则压入字符串指针 004010B2 call ILT0(fun) (00401005) 004010B7 add esp,8 //恢复堆栈 15: return 0; 004010BA xor eax,eax //返回值在EAX中 16: } 004010BC pop edi 004010BD pop esi 004010BE pop ebx //恢复3个寄存器 004010BF add esp,48h //清除局部变量空间 004010C2 cmp ebp,esp 004010C4 call __chkesp (00401250) //检测堆栈指针与堆栈基址 004010C9 mov esp,ebp //恢复调用函数的栈顶 004010CB pop ebp //恢复调用函数的堆栈基址 004010CC ret //函数返回 --- No source file -------------------------------------------------------------------------------------------------------------------------------------------------- 004010CD int 3 004010CE int 3 关于这个程序的堆栈使用情况也做了一下分析如图