当前位置: 首页 > news >正文

【汇编逆向系列】九、函数传参之结构体 - SHL、SHR指令,小型结构体参数和返回值

目录

1. 汇编代码

1.1 debug编译

1.2 release编译

2. 汇编分析

2.1 结构体的头文件

 2.2 结构体传参

2.3 计算

2.4返回结构体

 2.5 汇编优化 - SHR指令

3. 汇编转化

3.1 debug编译

3.2 Release编译

3.3 C语言转化


1. 汇编代码

前面章节描述了参数传参的汇编写法,但是一般情况下如果参数非常多,也不会使用很多参数而是使用结构体,那么参数是一个结构体在汇编上是如何体现的呢?先来看一组汇编代码。

1.1 debug编译

struct_by_value_param:0000000000000A90: 48 89 4C 24 08     mov         qword ptr [rsp+8],rcx0000000000000A95: 57                 push        rdi0000000000000A96: 48 83 EC 40        sub         rsp,40h0000000000000A9A: 48 8B FC           mov         rdi,rsp0000000000000A9D: B9 10 00 00 00     mov         ecx,10h0000000000000AA2: B8 CC CC CC CC     mov         eax,0CCCCCCCCh0000000000000AA7: F3 AB              rep stos    dword ptr [rdi]0000000000000AA9: 48 8B 4C 24 50     mov         rcx,qword ptr [rsp+50h]0000000000000AAE: 8B 44 24 50        mov         eax,dword ptr [rsp+50h]0000000000000AB2: D1 E0              shl         eax,10000000000000AB4: 89 44 24 28        mov         dword ptr [rsp+28h],eax0000000000000AB8: 8B 44 24 54        mov         eax,dword ptr [rsp+54h]0000000000000ABC: D1 E0              shl         eax,10000000000000ABE: 89 44 24 2C        mov         dword ptr [rsp+2Ch],eax0000000000000AC2: 48 8B 44 24 28     mov         rax,qword ptr [rsp+28h]0000000000000AC7: 48 8B F8           mov         rdi,rax0000000000000ACA: 48 8B CC           mov         rcx,rsp0000000000000ACD: 48 8D 15 00 00 00  lea         rdx,[struct_by_value_param$rtcFrameData]000000000000000AD4: E8 00 00 00 00     call        _RTC_CheckStackVars0000000000000AD9: 48 8B C7           mov         rax,rdi0000000000000ADC: 48 83 C4 40        add         rsp,40h0000000000000AE0: 5F                 pop         rdi0000000000000AE1: C3                 ret

1.2 release编译

struct_by_value_param:0000000000000000: 8D 04 09           lea         eax,[rcx+rcx]0000000000000003: 48 C1 E9 20        shr         rcx,20h0000000000000007: 03 C9              add         ecx,ecx0000000000000009: 89 44 24 08        mov         dword ptr [rsp+8],eax000000000000000D: 89 4C 24 0C        mov         dword ptr [rsp+0Ch],ecx0000000000000011: 48 8B 44 24 08     mov         rax,qword ptr [rsp+8]0000000000000016: C3                 ret

2. 汇编分析

2.1 结构体的头文件

在此类汇编中,汇编语言是操作地址,编译器会将结构体的每个参数都转化为地址,所以一定需要头文件开看结构体的声明才能了解汇编做了什么,所以先找到该结构体的声明

typedef struct {int x;int y;
} Point;

可以看到该结构体只是非常简单两个4字节的有符号整型参数

 2.2 结构体传参

在 x64 调用约定中,小型结构体(如 8 字节的 Point)通过寄存器 RCX 传递(Windows)或拆分到多个寄存器(Linux)。

此处通过 mov qword ptr [rsp+8], rcx 保存到栈的 [rsp+8]位置

push rdi和sub rsp,40h两个指令将rsp的地址减了0x48h,所以[rsp+8]地址变为[rsp+50h]

后续通过 [rsp+50h] 访问:

  • [rsp+50h] → p.x(低 4 字节)
  • [rsp+54h] → p.y(高 4 字节)

2.3 计算

mov eax, dword ptr [rsp+50h]  ; 加载 p.x
shl eax, 1                    ; eax = p.x * 2
mov dword ptr [rsp+28h], eax   ; 存储到局部变量 result.x
mov         eax,dword ptr [rsp+54h]; 加载p.y
shl         eax,1             ; eax= p.yu *2
mov         dword ptr [rsp+2Ch],eax  ; 存储局部变量 result.y

 可以看到这一步操作将p.x和p.y 都乘以2 

SHL为左移指令,左移一位代表乘以2

将两个临时变量存储到了[rsp+0x28h]和[rsp+0x2ch]

2.4返回结构体

小型结构体通过 RAX 寄存器返回:

mov rax, qword ptr [rsp+28h]  ; 将 result.x 和 result.y 作为 8 字节值加载到 RAX
mov rdi, rax                  ; 临时保存返回值
call _RTC_CheckStackVars      ; 栈检查(调试用)
mov rax, rdi                  ; 恢复返回值到 RAX
ret                           ; 返回 RAX 中的 Point 结构体

 2.5 汇编优化 - SHR指令

lea         eax, [rcx+rcx]  // LEA指令计算 x*2:取RCX低32位(x)与自身相加,结果存入EAX(相当于x*2)
shr         rcx, 20h        // 将RCX右移32位(0x20),使原高32位(y)移动到低32位
add         ecx, ecx         // 将ECX(原y值)乘以2:ecx = ecx + ecx

 汇编优化操作,几个指令后,EAX存放X*2, ECX存放Y*2

3. 汇编转化

可以看到这个函数做了一个小型结构体的转化示例,但是大型结构体的操作是不一样的下一节,我们单独介绍大型结构体如何传参

3.1 debug编译

struct_by_value_param:; 函数入口:保存参数寄存器RCX(结构体指针)到栈上[rsp+8]0000000000000A90: 48 89 4C 24 08     mov  qword ptr [rsp+8], rcx  ; 保存RCX(结构体参数)到调用者栈帧; 函数序言:保存非易失寄存器并分配栈空间0000000000000A95: 57                 push rdi                     ; 保存RDI(非易失寄存器)0000000000000A96: 48 83 EC 40        sub  rsp, 40h                ; 分配64字节栈空间(含影子空间+局部变量); 调试模式栈初始化:用0xCC填充栈空间(调试器未初始化标记)0000000000000A9A: 48 8B FC           mov  rdi, rsp                ; RDI = 栈顶地址0000000000000A9D: B9 10 00 00 00     mov  ecx, 10h                ; 循环次数=16(64字节/4)0000000000000AA2: B8 CC CC CC CC     mov  eax, 0CCCCCCCCh         ; 填充值=0xCCCCCCCC0000000000000AA7: F3 AB              rep stos dword ptr [rdi]      ; 重复填充栈空间; 加载结构体参数并处理第一个字段(x)0000000000000AA9: 48 8B 4C 24 50     mov  rcx, qword ptr [rsp+50h] ; RCX = 原始结构体指针(参数区)0000000000000AAE: 8B 44 24 50        mov  eax, dword ptr [rsp+50h] ; EAX = 结构体第一个成员(x)0000000000000AB2: D1 E0              shl  eax, 1                  ; EAX = x * 20000000000000AB4: 89 44 24 28        mov  dword ptr [rsp+28h], eax ; 结果存局部变量1(x*2); 处理第二个字段(y)0000000000000AB8: 8B 44 24 54        mov  eax, dword ptr [rsp+54h] ; EAX = 结构体第二个成员(y)0000000000000ABC: D1 E0              shl  eax, 1                  ; EAX = y * 20000000000000ABE: 89 44 24 2C        mov  dword ptr [rsp+2Ch], eax ; 结果存局部变量2(y*2); 准备返回值(新结构体)0000000000000AC2: 48 8B 44 24 28     mov  rax, qword ptr [rsp+28h] ; RAX = 合并的x*2和y*2(8字节)0000000000000AC7: 48 8B F8           mov  rdi, rax                ; RDI临时保存返回值(RAX是返回寄存器); 调试检查:验证栈变量是否被破坏0000000000000ACA: 48 8B CC           mov  rcx, rsp                ; RCX = 栈帧基址0000000000000ACD: 48 8D 15 00 00 00  lea  rdx, [struct_by_value_param$rtcFrameData] ; 调试信息表000000000000000AD4: E8 00 00 00 00     call _RTC_CheckStackVars      ; 调用运行时栈检查(调试模式); 函数收尾:设置返回值并恢复栈0000000000000AD9: 48 8B C7           mov  rax, rdi                ; RAX = 返回值(新结构体)0000000000000ADC: 48 83 C4 40        add  rsp, 40h                ; 释放栈空间(64字节)0000000000000AE0: 5F                 pop  rdi                     ; 恢复RDI寄存器0000000000000AE1: C3                 ret                          ; 函数返回

3.2 Release编译

// 函数:struct_by_value_param
// 输入:RCX = 64位参数(低32位为x,高32位为y)
// 输出:RAX = 64位返回值(低32位为x*2,高32位为y*2)0000000000000000: 8D 04 09           lea         eax, [rcx+rcx]  // LEA指令计算 x*2:取RCX低32位(x)与自身相加,结果存入EAX(相当于x*2)0000000000000003: 48 C1 E9 20        shr         rcx, 20h        // 将RCX右移32位(0x20),使原高32位(y)移动到低32位0000000000000007: 03 C9              add         ecx, ecx         // 将ECX(原y值)乘以2:ecx = ecx + ecx0000000000000009: 89 44 24 08        mov         dword ptr [rsp+8], eax  // 将x*2的结果存入栈[rsp+8](低32位)000000000000000D: 89 4C 24 0C        mov         dword ptr [rsp+0Ch], ecx  // 将y*2的结果存入栈[rsp+0Ch](高32位)0000000000000011: 48 8B 44 24 08     mov         rax, qword ptr [rsp+8]  // 从栈中加载8字节数据(x*2和y*2)到RAX作为返回值0000000000000016: C3                 ret                         // 函数返回,RAX包含新结构体

3.3 C语言转化

Point struct_by_value_param(Point p) {Point result = {p.x * 2, p.y * 2};return result;
}

http://www.dtcms.com/a/267682.html

相关文章:

  • 《深度剖析:5G网络切片如何精准保障不同业务QoS需求》
  • PHP语法基础篇(九):正则表达式
  • 本机上搭一个HTTPS网站用什么工具?.NET self host支持吗?
  • Redis--黑马点评--达人探店功能实现详解
  • C++ 11中lock_guard和unique_lock的区别
  • 初识Linux:Linux开发工具gcc/g++和gdb以及Makefile的使用
  • Python小工具之PDF合并
  • Redux 扩展与标准化模板方案
  • LINUX75 LAMP
  • 字节一面整理
  • C++ 模板参数匹配、特化
  • 智能监控算法助力工厂高温高效管理
  • 淘系怎么做?
  • hiredis window之RFDMap
  • 基于大模型的肾积水全周期预测与诊疗方案研究报告
  • 如何使用backtrace定位Linux程序的崩溃位置
  • 【STM32】定时器中断 + 含常用寄存器和库函数配置(提供完整实例代码)
  • 洛谷 P11967 [GESP202503 八级] 割裂-普及+/提高
  • 百度文心 4.5 大模型详解:ERNIE 4.5 Technical Report
  • 水下航行器外形之变体式与回转体的区别
  • 线程锁和线程同步
  • 从“电话催维修“到“手机看进度“——售后服务系统开发如何重构客户体验
  • Linux网络配置与故障排除完全指南
  • 12 nacos配置中心
  • 使用Kahn算法处理节点依赖关系
  • ABB焊接机器人智能节气仪
  • 汽车制造车间检测机器人与PLC无线以太网实时控制方案
  • 数据库学习笔记(十七)--触发器的使用
  • Java SE--数组
  • 前端相关性能优化笔记