反汇编——结构体作为局部变量的传参和返回值的本质测试

本文探讨了结构体作为参数传递及作为函数返回值时的不同实现方式。通过VS2017与VC++6.0的反汇编对比,详细分析了结构体参数传递过程中的寄存器使用与内存复制两种方法,以及结构体作为返回值时的地址传递机制。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

不同编译器生成的结果不同,但本质都是通过寄存器或内存复制,本测试只是为了了解结构体在作为局部变量时的参数传递与返回的本质,实际使用中,最好定义为全局变量

结构体传参:

# include<stdio.h>

struct st
{
	char a;
	short b;
	int c;
	int d;
	int e;
};
void Function(st s)
{

}
int main(int argc, char* argv[])
{
	st s;
	s.a = 1;
	s.b = 2;
	s.c = 3;
	s.d = 4;
	s.e = 5;

	Function(s);


	return 0;
}
反汇编:
1.vs2017测试结果,使用寄存器传递地址
Function(s);
008A1740  sub         esp,10h  
008A1743  mov         eax,esp  
008A1745  mov         ecx,dword ptr [s]  
008A1748  mov         dword ptr [eax],ecx  
008A174A  mov         edx,dword ptr [ebp-10h]  
008A174D  mov         dword ptr [eax+4],edx  
008A1750  mov         ecx,dword ptr [ebp-0Ch]  
008A1753  mov         dword ptr [eax+8],ecx  
008A1756  mov         edx,dword ptr [ebp-8]  
008A1759  mov         dword ptr [eax+0Ch],edx  
008A175C  call        Function (08A10A5h)  
008A1761  add         esp,10h  
2.vc++6.0测试结果,直接使用rep stos指令,在内存内复制
12:   void Function(st s)
00401020   push        ebp
00401021   mov         ebp,esp
00401023   sub         esp,40h
00401026   push        ebx
00401027   push        esi
00401028   push        edi
00401029   lea         edi,[ebp-40h]
0040102C   mov         ecx,10h
00401031   mov         eax,0CCCCCCCCh
00401036   rep stos    dword ptr [edi]

00401038   pop         edi
00401039   pop         esi
0040103A   pop         ebx
0040103B   mov         esp,ebp
0040103D   pop         ebp
0040103E   ret

结构体返回值:

# include<stdio.h>

struct st
{
	char a;
	short b;
	int c;
	int d;
	int e;

};
st Function()
{
	st s;
	s.a = 1;
	s.b = 2;
	s.c = 3;
	s.d = 4;
	s.e = 5;
	return s;
}
int main(int argc, char* argv[])
{
	st a = Function();


	return 0;
}

反汇编:
vs2017测试结果,把Function函数的返回值的地址放在eax
st a = Function();
0106171E  lea         eax,[ebp-104h]  //外层函数用来接收返回值的地址
01061724  push        eax  
01061725  call        Function (01061366h)  
0106172A  add         esp,4  
0106172D  mov         ecx,dword ptr [eax]  
0106172F  mov         dword ptr [ebp-0ECh],ecx  
01061735  mov         edx,dword ptr [eax+4]  
01061738  mov         dword ptr [ebp-0E8h],edx  
0106173E  mov         ecx,dword ptr [eax+8]  
01061741  mov         dword ptr [ebp-0E4h],ecx  
01061747  mov         edx,dword ptr [eax+0Ch]  
0106174A  mov         dword ptr [ebp-0E0h],edx  
01061750  mov         eax,dword ptr [ebp-0ECh]  
01061756  mov         dword ptr [a],eax  
01061759  mov         ecx,dword ptr [ebp-0E8h]  
0106175F  mov         dword ptr [ebp-10h],ecx  
01061762  mov         edx,dword ptr [ebp-0E4h]  
01061768  mov         dword ptr [ebp-0Ch],edx  
0106176B  mov         eax,dword ptr [ebp-0E0h]  
01061771  mov         dword ptr [ebp-8],eax 
vc++6.0测试结果,把Function函数的返回值的地址放在eax
     st a = Function();
0040D4D8   lea         eax,[ebp-30h]  //外层函数用来接收返回值的地址
0040D4DB   push        eax
0040D4DC   call        @ILT+20(Function) (00401019)
0040D4E1   add         esp,4
0040D4E4   mov         ecx,dword ptr [eax]
0040D4E6   mov         dword ptr [ebp-20h],ecx
0040D4E9   mov         edx,dword ptr [eax+4]
0040D4EC   mov         dword ptr [ebp-1Ch],edx
0040D4EF   mov         ecx,dword ptr [eax+8]
0040D4F2   mov         dword ptr [ebp-18h],ecx
0040D4F5   mov         edx,dword ptr [eax+0Ch]
0040D4F8   mov         dword ptr [ebp-14h],edx
0040D4FB   mov         eax,dword ptr [ebp-20h]
0040D4FE   mov         dword ptr [ebp-10h],eax
0040D501   mov         ecx,dword ptr [ebp-1Ch]
0040D504   mov         dword ptr [ebp-0Ch],ecx
0040D507   mov         edx,dword ptr [ebp-18h]
0040D50A   mov         dword ptr [ebp-8],edx
0040D50D   mov         eax,dword ptr [ebp-14h]
0040D510   mov         dword ptr [ebp-4],eax


<think>嗯,用户之前问过如何通过反汇编查看变量地址,现在又问怎么根据汇编语句判断变量位置。看来他可能是在学习逆向工程或者底层编程,需要理解变量在汇编中的表示方式。 首先,我需要回忆之前提供的步骤,是否已经涵盖了相关内容。之前的回答提到了使用GDBobjdump,以及如何定位局部变量、全局变量堆变量。现在用户的问题更具体,是如何从汇编代码本身来判断变量位置,而不只是使用工具。 用户可能是想在没有调试信息的情况下,或者静态分析二进制文件时,直接通过阅读汇编代码来识别变量。这时候需要详细解释不同变量类型在汇编中的表现,比如局部变量通过ebp或esp的偏移,全局变量用绝对地址,堆变量通过指针间接访问。 需要考虑不同架构的汇编语法是否有差异,比如IntelAT&T语法。用户之前的例子用的是Intel语法,所以这次也应该保持一致。同时,要提到优化级别的影响,因为高优化级别可能会改变变量的存储方式,比如寄存器分配。 另外,用户可能需要知道如何识别结构体或数组的访问模式,比如基址加偏移。还要注意指针变量的处理,比如lea指令用于取地址,mov用于存取值。 需要注意的事项包括优化带来的影响,比如变量被优化掉,或者使用寄存器存储,这样就没有内存地址。还有多线程环境中的TLS变量,使用段寄存器如%fs,这可能是一个难点,需要举例说明。 最后,要总结关键点,帮助用户系统地理解如何从汇编指令中提取变量位置信息。可能需要补充一些示例,展示不同的寻址方式,并解释如何结合上下文理解变量类型结构。</think>通过汇编语句判断变量位置需要结合内存寻址方式上下文语义分析,以下是系统化的判断方法: --- ### 一、关键寻址模式识别 #### 1. **栈变量定位**(局部变量) - **典型模式**:`[ebp±offset]` 或 `[esp±offset]` - **示例分析**: ```asm mov DWORD PTR [ebp-0x4], 10 ; int a = 10 lea eax, [esp+0x8] ; 取栈参数地址 ``` - **偏移计算**:`ebp-0x4` 表示当前栈帧内偏移4字节的变量 - **特点**: - 生命周期与函数执行同步 - 通常对应C代码中的局部变量或函数参数 #### 2. **全局变量定位** - **典型模式**:直接使用绝对地址 - **示例分析**: ```asm mov eax, DWORD PTR [0x804a010] ; 访问全局变量区 add DWORD PTR [0x804c000], 5 ; 修改全局变量 ``` - **特征**: - 无寄存器基址(如`ebp/esp`) - 地址通常位于`.data`或`.bss`段(如`0x804a000`附近) #### 3. **堆变量定位**(动态分配) - **典型模式**:通过指针间接访问 - **示例分析**: ```asm ; malloc调用后: mov [eax], 0x20 ; *(ptr) = 32 add edx, [ebx+0x4] ; 访问结构体成员 ``` - **关键指令**: - `call malloc` 后返回值在`eax`中 - 通过寄存器间接寻址(如`[eax]`, `[ebx+0x8]`) --- ### 二、结构特征分析 #### 1. **数组访问模式** - **计算公式**:`基地址 + 索引*元素大小` - **示例**: ```asm mov ecx, [edi+esi*4] ; int arr[...]; arr[i]访问 ``` #### 2. **结构体成员访问** - **固定偏移模式**: ```asm mov eax, [edx+0x10] ; struct.member2 偏移16字节 ``` #### 3. **指针链式访问** - **多级间接寻址**: ```asm mov eax, [ebx] ; 一级指针 mov ecx, [eax+0x8] ; 二级指针访问 ``` --- ### 三、上下文关联技巧 #### 1. **函数调用前后分析** - **参数传递模式**: ```asm push 0x20 ; 函数参数压栈 call func ``` #### 2. **寄存器跟踪法** - **生命周期追踪**: ```asm mov esi, [ebp-0x4] ; 加载局部变量到寄存器 add esi, [edi+0x8] ; 与其他内存数据运算 ``` #### 3. **数据流回溯**(以`eax`为例): ```asm lea eax, [ebp-0x10] ; eax = &local_var mov [eax], 0x55 ; *(&local_var) = 0x55 ``` --- ### 四、工具辅助验证 1. **交叉引用分析**: ```bash objdump -d -M intel program | grep '804a010' # 全局变量使用点追踪 ``` 2. **动态调试对比**: ```gdb (gdb) x/xw 0x804a010 # 查看内存实际值 (gdb) info symbol 0x804a010 # 验证符号信息 ``` --- ### 五、特殊场景处理 1. **优化代码分析**(-O2编译): - 变量可能被优化为寄存器操作(无内存地址) - 示例: ```asm mov ecx, 5 ; 原变量a被优化为立即数 add edx, ecx ; 直接使用寄存器值 ``` 2. **浮点变量定位**: ```asm fld DWORD PTR [ebp-0x8] ; 加载float/double类型变量 ``` 3. **线程局部存储(TLS)**: ```asm mov eax, DWORD PTR fs:[0x18] ; Windows TLS访问 mov ecx, [eax+0x10] ; 实际数据访问 ``` --- ### 六、综合判断流程 1. **确定变量作用域** → 2. **识别寻址模式** → 3. **计算偏移量** → 4. **验证内存区域属性** 通过系统化分析汇编指令的寻址模式上下文语义,可准确推断出变量的物理存储位置逻辑含义。实际应用中需结合反编译工具(如Ghidra)动态调试进行交叉验证。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值