VC环境下对函数调用的汇编分析【原创】
时间:2007-03-06 来源:loughsky
前沿:对于我们平常编程中常出现一些细节,如__stdcall和__cdecl编译器如何为我们处理,函数中变量以及new出来的变量到底存放于哪些地方,等等一些列问题。本文将和大家一起分析程序执行的汇编语言,通过对此过程掌握使自己在开发中熟悉并优化自己的代码。作者:天衣有缝,联系邮件:[email protected],MSN:[email protected],我的QQ群3226292,转载请保留完整文档。
1.环境:我使用的开发环境是vc7.1,其release单步调试需要对项目属性作如下修改:
“C++”--》“常规”--》“调试信息格式” 改为:“用于“编辑并继续”的程序数据库(/ZI)”
“C++”--》“优化”--》“优化” 改为:禁用(/Od)
如果你是vc6环境,可如下修改release版属性:
选中Win32 Release然后
Project-》setting-》C/C++ -》Category-》General
-》Optimization-》Disable(Debug)
-》Debug Info-》Program DataBase
-》Link---》Generate Debug Info打上钩
2.c语言代码,如下:
/***开始*****************************************************/
#include "stdafx.h"
int __cdecl add(int a, int b)
{
int c;
c = a + b;
return c;
}
int _tmain(int argc, _TCHAR* argv[])
{
int iResult = add(123, 456);
printf(" ************ ");
return 0;
}
/***结束*****************************************************/
3.程序分析过程,F10单步启动程序:
/***开始*****************************************************/
int _tmain(int argc, _TCHAR* argv[])
{
00401020 push ebp 建立堆栈帧
00401021 mov ebp,esp 存入栈基地址,运行后EBP = 0012FEE4
(表明默认堆栈大小约为1兆)
00401023 sub esp,44h 空出一块堆栈区,不知道是干什么的,可有高人指点?
×××××××××××××××××××××
在一篇vc6的汇编调试文章中看到main中变量存储于此堆栈中,但是我在vc7调试发现iresult地址根本不在堆栈范围之内,不知作何解释
×××××××××××××××××××××
00401026 push ebx 保护现场
00401027 push esi
00401028 push edi
int iResult = add(123, 456);
00401029 push 1C8h 参数入栈
0040102E push 7Bh
00401030 call add (401000h) 执行函数调用,按F11跳到本文蓝色处
×××××××××××××××××××××
call指令详解:
call指令是push和jmp的结合,先执行push eip将当前地址入栈(函数调用完毕需要用这个地址返回),然后调用jmp指令。因为普通指令无法对eip操作,所以在很多病毒程序中常有如下语句:
call @@get_eip
@@get_eip:
pop ebp ;取得eip
×××××××××××××××××××××
00401035 add esp,8 释放123和456变量所占堆栈
00401038 mov dword ptr [iResult],eax 从eax取出计算结果
printf(" ************ "); 下面是一个函数的测试
0040103B push offset string " ************ " (4060FCh) 变量地址入栈
00401040 call printf (401051h) 执行call调用函数
00401045 add esp,4 变量地址出栈
return 0;
00401048 xor eax,eax 使eax为0,eax就是返回给操作系统的值
}
0040104A pop edi
0040104B pop esi
0040104C pop ebx 恢复现场
0040104D mov esp,ebp 平衡堆栈
0040104F pop ebp 释放堆栈帧
00401050 ret 返回操作系统调用处
函数定义:
int __cdecl add(int a, int b)
{
00401000 push ebp 建立堆栈帧
00401001 mov ebp,esp 存入栈基地址
00401003 sub esp,44h 开辟变量使用的堆栈区,供函数内部变量使用
执行前ESP = 0012FE84,执行后ESP = 0012FE40
×××××××××××××××××××××
此处可以打开内存0x0012FE8C,看到 7b 00 00 00 c8 01 00 00,这就是我们传入的123(0x0012fe8c处)和456(0x0012fe90处)变量
×××××××××××××××××××××
00401006 push ebx 保护现场
00401007 push esi
00401008 push edi
int c;
c = a + b;
00401009 mov eax,dword ptr [a] 第一个参数,也就是[ebp+8]
0040100C add eax,dword ptr [b] 第二个参数,也就是[ebp+c]
0040100F mov dword ptr [c],eax c变量在栈中,地址为0x0012fe80,就是变量堆栈区顶部
return c;
00401012 mov eax,dword ptr [c] 计算结果存入eax
}
00401015 pop edi 回复现场
00401016 pop esi
00401017 pop ebx
00401018 mov esp,ebp 平衡堆栈,回收变量堆栈区
0040101A pop ebp 释放堆栈帧
0040101B ret 回到调用地址,读者从这里转到粉红色处接着看
/***结束*****************************************************/
1.环境:我使用的开发环境是vc7.1,其release单步调试需要对项目属性作如下修改:
“C++”--》“常规”--》“调试信息格式” 改为:“用于“编辑并继续”的程序数据库(/ZI)”
“C++”--》“优化”--》“优化” 改为:禁用(/Od)
如果你是vc6环境,可如下修改release版属性:
选中Win32 Release然后
Project-》setting-》C/C++ -》Category-》General
-》Optimization-》Disable(Debug)
-》Debug Info-》Program DataBase
-》Link---》Generate Debug Info打上钩
2.c语言代码,如下:
/***开始*****************************************************/
#include "stdafx.h"
int __cdecl add(int a, int b)
{
int c;
c = a + b;
return c;
}
int _tmain(int argc, _TCHAR* argv[])
{
int iResult = add(123, 456);
printf(" ************ ");
return 0;
}
/***结束*****************************************************/
3.程序分析过程,F10单步启动程序:
/***开始*****************************************************/
int _tmain(int argc, _TCHAR* argv[])
{
00401020 push ebp 建立堆栈帧
00401021 mov ebp,esp 存入栈基地址,运行后EBP = 0012FEE4
(表明默认堆栈大小约为1兆)
00401023 sub esp,44h 空出一块堆栈区,不知道是干什么的,可有高人指点?
×××××××××××××××××××××
在一篇vc6的汇编调试文章中看到main中变量存储于此堆栈中,但是我在vc7调试发现iresult地址根本不在堆栈范围之内,不知作何解释
×××××××××××××××××××××
00401026 push ebx 保护现场
00401027 push esi
00401028 push edi
int iResult = add(123, 456);
00401029 push 1C8h 参数入栈
0040102E push 7Bh
00401030 call add (401000h) 执行函数调用,按F11跳到本文蓝色处
×××××××××××××××××××××
call指令详解:
call指令是push和jmp的结合,先执行push eip将当前地址入栈(函数调用完毕需要用这个地址返回),然后调用jmp指令。因为普通指令无法对eip操作,所以在很多病毒程序中常有如下语句:
call @@get_eip
@@get_eip:
pop ebp ;取得eip
×××××××××××××××××××××
00401035 add esp,8 释放123和456变量所占堆栈
00401038 mov dword ptr [iResult],eax 从eax取出计算结果
printf(" ************ "); 下面是一个函数的测试
0040103B push offset string " ************ " (4060FCh) 变量地址入栈
00401040 call printf (401051h) 执行call调用函数
00401045 add esp,4 变量地址出栈
return 0;
00401048 xor eax,eax 使eax为0,eax就是返回给操作系统的值
}
0040104A pop edi
0040104B pop esi
0040104C pop ebx 恢复现场
0040104D mov esp,ebp 平衡堆栈
0040104F pop ebp 释放堆栈帧
00401050 ret 返回操作系统调用处
函数定义:
int __cdecl add(int a, int b)
{
00401000 push ebp 建立堆栈帧
00401001 mov ebp,esp 存入栈基地址
00401003 sub esp,44h 开辟变量使用的堆栈区,供函数内部变量使用
执行前ESP = 0012FE84,执行后ESP = 0012FE40
×××××××××××××××××××××
此处可以打开内存0x0012FE8C,看到 7b 00 00 00 c8 01 00 00,这就是我们传入的123(0x0012fe8c处)和456(0x0012fe90处)变量
×××××××××××××××××××××
00401006 push ebx 保护现场
00401007 push esi
00401008 push edi
int c;
c = a + b;
00401009 mov eax,dword ptr [a] 第一个参数,也就是[ebp+8]
0040100C add eax,dword ptr [b] 第二个参数,也就是[ebp+c]
0040100F mov dword ptr [c],eax c变量在栈中,地址为0x0012fe80,就是变量堆栈区顶部
return c;
00401012 mov eax,dword ptr [c] 计算结果存入eax
}
00401015 pop edi 回复现场
00401016 pop esi
00401017 pop ebx
00401018 mov esp,ebp 平衡堆栈,回收变量堆栈区
0040101A pop ebp 释放堆栈帧
0040101B ret 回到调用地址,读者从这里转到粉红色处接着看
/***结束*****************************************************/
相关阅读 更多 +