【汇编逆向系列】八、函数调用包含混合参数-8种参数传参,条件跳转指令,转型指令,movaps 16字节指令
1. 汇编代码
本案例是通过函数传参传递8种不同的类型的参数,来巩固上两节所学习的知识。整型的传参和浮点型的传参和rsp栈空间的使用,影子空间的申请。
1.1 debug编译
eight_mixed_params:00000000000008A0: 44 88 4C 24 20 mov byte ptr [rsp+20h],r9b00000000000008A5: F2 0F 11 54 24 18 movsd mmword ptr [rsp+18h],xmm200000000000008AB: F3 0F 11 4C 24 10 movss dword ptr [rsp+10h],xmm100000000000008B1: 89 4C 24 08 mov dword ptr [rsp+8],ecx00000000000008B5: 57 push rdi00000000000008B6: 48 83 EC 20 sub rsp,20h00000000000008BA: 48 8B FC mov rdi,rsp00000000000008BD: B9 08 00 00 00 mov ecx,800000000000008C2: B8 CC CC CC CC mov eax,0CCCCCCCCh00000000000008C7: F3 AB rep stos dword ptr [rdi]00000000000008C9: 8B 4C 24 30 mov ecx,dword ptr [rsp+30h]00000000000008CD: 0F B6 44 24 50 movzx eax,byte ptr [rsp+50h]00000000000008D2: 85 C0 test eax,eax00000000000008D4: 74 10 je 00000000000008E600000000000008D6: F2 0F 10 05 00 00 movsd xmm0,mmword ptr [__real@3ff0000000000000]00 0000000000000008DE: F2 0F 11 44 24 18 movsd mmword ptr [rsp+18h],xmm000000000000008E4: EB 09 jmp 00000000000008EF00000000000008E6: 0F 57 C0 xorps xmm0,xmm000000000000008E9: F2 0F 11 44 24 18 movsd mmword ptr [rsp+18h],xmm000000000000008EF: F2 0F 2A 44 24 30 cvtsi2sd xmm0,dword ptr [rsp+30h]00000000000008F5: F3 0F 5A 4C 24 38 cvtss2sd xmm1,dword ptr [rsp+38h]00000000000008FB: F2 0F 58 C1 addsd xmm0,xmm100000000000008FF: F2 0F 58 44 24 40 addsd xmm0,mmword ptr [rsp+40h]0000000000000905: 0F BE 44 24 48 movsx eax,byte ptr [rsp+48h]000000000000090A: F2 0F 2A C8 cvtsi2sd xmm1,eax000000000000090E: F2 0F 58 C1 addsd xmm0,xmm10000000000000912: F2 0F 58 44 24 18 addsd xmm0,mmword ptr [rsp+18h]0000000000000918: 0F BF 44 24 58 movsx eax,word ptr [rsp+58h]000000000000091D: F2 0F 2A C8 cvtsi2sd xmm1,eax0000000000000921: F2 0F 58 C1 addsd xmm0,xmm10000000000000925: F2 0F 2A 4C 24 60 cvtsi2sd xmm1,dword ptr [rsp+60h]000000000000092B: F2 0F 58 C1 addsd xmm0,xmm1000000000000092F: 8B 44 24 68 mov eax,dword ptr [rsp+68h]0000000000000933: F2 48 0F 2A C8 cvtsi2sd xmm1,rax0000000000000938: F2 0F 58 C1 addsd xmm0,xmm1000000000000093C: F2 0F 11 44 24 10 movsd mmword ptr [rsp+10h],xmm00000000000000942: F2 0F 10 44 24 10 movsd xmm0,mmword ptr [rsp+10h]0000000000000948: F2 0F 5E 05 00 00 divsd xmm0,mmword ptr [__real@4020000000000000]00 000000000000000950: 48 83 C4 20 add rsp,20h0000000000000954: 5F pop rdi0000000000000955: C3 ret0000000000000956: CC int 30000000000000957: CC int 3
1.2 release编译
eight_mixed_params:0000000000000000: 80 7C 24 28 00 cmp byte ptr [rsp+28h],00000000000000005: 74 0A je 00000000000000110000000000000007: F2 0F 10 25 00 00 movsd xmm4,mmword ptr [__real@3ff0000000000000]00 00000000000000000F: EB 03 jmp 00000000000000140000000000000011: 0F 57 E4 xorps xmm4,xmm40000000000000014: 0F 57 DB xorps xmm3,xmm30000000000000017: 41 0F BE C1 movsx eax,r9b000000000000001B: F2 0F 2A D9 cvtsi2sd xmm3,ecx000000000000001F: 0F 57 C0 xorps xmm0,xmm00000000000000022: F3 0F 5A C1 cvtss2sd xmm0,xmm10000000000000026: 0F 57 C9 xorps xmm1,xmm10000000000000029: F2 0F 58 D8 addsd xmm3,xmm0000000000000002D: 0F 57 C0 xorps xmm0,xmm00000000000000030: F2 0F 2A C0 cvtsi2sd xmm0,eax0000000000000034: 0F BF 44 24 30 movsx eax,word ptr [rsp+30h]0000000000000039: F2 0F 58 DA addsd xmm3,xmm2000000000000003D: 0F 57 D2 xorps xmm2,xmm20000000000000040: F2 0F 2A 54 24 38 cvtsi2sd xmm2,dword ptr [rsp+38h]0000000000000046: F2 0F 58 D8 addsd xmm3,xmm0000000000000004A: F2 0F 2A C8 cvtsi2sd xmm1,eax000000000000004E: 8B 44 24 40 mov eax,dword ptr [rsp+40h]0000000000000052: F2 0F 58 DC addsd xmm3,xmm40000000000000056: F2 0F 58 D9 addsd xmm3,xmm1000000000000005A: 0F 57 C9 xorps xmm1,xmm1000000000000005D: F2 48 0F 2A C8 cvtsi2sd xmm1,rax0000000000000062: F2 0F 58 DA addsd xmm3,xmm20000000000000066: F2 0F 58 D9 addsd xmm3,xmm1000000000000006A: F2 0F 59 1D 00 00 mulsd xmm3,mmword ptr [__real@3fc0000000000000]00 000000000000000072: 0F 28 C3 movaps xmm0,xmm30000000000000075: C3 ret
2. 汇编分析
2.1 调用者压栈
调用这个函数的调用者需要先将函数压栈到rsp栈帧寄存器,通过
00000000000008A0: 44 88 4C 24 20 mov byte ptr [rsp+20h],r9b00000000000008A5: F2 0F 11 54 24 18 movsd mmword ptr [rsp+18h],xmm200000000000008AB: F3 0F 11 4C 24 10 movss dword ptr [rsp+10h],xmm100000000000008B1: 89 4C 24 08 mov dword ptr [rsp+8],ecx
代码可知,r9b 为第四个参数是整型,byte是1字节,所以第四个参数是char型
xmm2为浮点型的第3个参数,mmword是8字节指令,所以第三个参数是double型
xmm1为浮点型的第二个参数,dword是4字节指令,所以第二个参数是float型
ecx为整型的第一个参数,dowrd是4字节指令,所以第一个参数是int型
这里参考了相关章节的知识:
【汇编逆向系列】四、函数调用包含单个参数之Double类型-mmword,movsd,mulsd,addsd指令,总结汇编的数据类型
后面的参数应该是压入[rsp+28h]之后的地址,那么如何知道一共有多少参数?需要了解rsp是如何变化的。
2.2 rsp和参数地址变化
首先在前面章节我们知道函数传参的时候会创建一个0x20大小的影子空间[rsp]-[rsp+0x20],在使用call指令调用的时候rsp -= 0x8,由于栈的地址是向上增长的,影子空间会变化为[rsp+8]-[rsp+0x28], 所以2.1的第一个参数放入的地址变成了[rsp+8], 在调用如下两条指令后
00000000000008B5: 57 push rdi ;rsp-=800000000000008B6: 48 83 EC 20 sub rsp,20h ; rsp-=0x20h
rsp -= 0x28h, 所以影子空间地址变化为[rsp+0x30]-[rsp+0x50], 再看后续汇编
...0000000000000921: F2 0F 58 C1 addsd xmm0,xmm10000000000000925: F2 0F 2A 4C 24 60 cvtsi2sd xmm1,dword ptr [rsp+60h]000000000000092B: F2 0F 58 C1 addsd xmm0,xmm1000000000000092F: 8B 44 24 68 mov eax,dword ptr [rsp+68h]
可以看到最后一个获取的参数地址为[rsp+0x68h], 所有操作指令最大为8字节操作指令, 那么从影子空间的结尾[rsp+0x50]到[rsp+0x68],一共有[rsp+0x50][rsp+0x58][rsp+0x60][rsp+0x68]4个参数,所以一共有8个参数被传入到这个函数中来。
2.3 JE,JMP指令
在前面的章节中介绍了test和movzx指令:
【汇编逆向系列】五、函数调用单个参数之char型和布尔型-TEST,JNE,JMP,SET条件设置和跳转指令
这段汇编使用了JE指令:
00000000000008CD: 0F B6 44 24 50 movzx eax,byte ptr [rsp+50h]00000000000008D2: 85 C0 test eax,eax00000000000008D4: 74 10 je 00000000000008E6
movzx是零拓展,说明[rsp+0x50h]是一个bool值, 将第五个参数放到eax寄存器, test指令来判断第五个参数是否为0,为0的话将ZF标值寄存器置1.
2.3.1 JE 条件跳转
ZF标志位为1的时候,跳转到对应地址
00000000000008CD: 0F B6 44 24 50 movzx eax,byte ptr [rsp+50h]00000000000008D2: 85 C0 test eax,eax00000000000008D4: 74 10 je 00000000000008E600000000000008D6: F2 0F 10 05 00 00 movsd xmm0,mmword ptr [__real@3ff0000000000000]00 0000000000000008DE: F2 0F 11 44 24 18 movsd mmword ptr [rsp+18h],xmm000000000000008E4: EB 09 jmp 00000000000008EF00000000000008E6: 0F 57 C0 xorps xmm0,xmm000000000000008E9: F2 0F 11 44 24 18 movsd mmword ptr [rsp+18h],xmm000000000000008EF: F2 0F 2A 44 24 30 cvtsi2sd xmm0,dword ptr [rsp+30h]
当第五个参数为0的时候,跳转到00000000000008E6地址,
00000000000008E6地址执行为: xorps xmm0,xmm0
这个操作是生成一个浮点数0.0,再将0.0存入到[rsp+18]这个地址,这个地址是sub rsp 20h中的,说明是一个临时变量。
当第五个参数不为0的时候,继续向下执行:
00000000000008D6: F2 0F 10 05 00 00 movsd xmm0,mmword ptr [__real@3ff0000000000000]00 0000000000000008DE: F2 0F 11 44 24 18 movsd mmword ptr [rsp+18h],xmm0
给xmm0复制浮点数 1.0 <= [3ff0000000000000], 再存到临时变量[rsp+0x18h]
可以理解为这理就是一个c语言当中的?:语法
p5 == 0? 0.0 : 1.0;
2.3.2 JMP强制跳转
JMP指令为强制跳转指令,直接跳转到参数的地址的位置
2.4 cvtss2sd和cvtsi2sd指令
cvtss2sd和cvtsi2sd都是双精度浮点型的转化指令,区别在于cvtsi2sd是整型向双精度浮点型转化,而cvtss2sd是单精度浮点型向双精度浮点型的转化,以下表格是他们的区别:
2.4.1 cvtsi2sd指令
cvtsi2sd
(Convert Signed Integer to Scalar Double)
使用语法:
cvtsi2sd xmm_dest, src ; src 可为通用寄存器(如 eax)或内存地址(如 [mem])
将源操作数(32/64位有符号整数)转换为双精度浮点数,结果存入目标 XMM 寄存器的低64位,高64位保持不变
示例:
cvtsi2sd xmm0, eax ; 将 eax 中的整数转为 double 存入 xmm0 低位
2.4.2 cvtss2sd指令
cvtss2sd
(Convert Scalar Single to Scalar Double)
使用语法:
cvtss2sd xmm_dest, src ; src 可为 XMM 寄存器(如 xmm1)或内存地址(如 [mem])
将源操作数(32位单精度浮点数)扩展为双精度浮点数,结果存入目标 XMM 寄存器的低64位,高64位不变
示例:
cvtss2sd xmm0, xmm1 ; 将 xmm1 低位的 float 转为 double 存入 xmm0 低位
2.5 xorps指令
XORPS
是 x86/x64 架构中的一条 SIMD 浮点指令,属于 SSE 指令集。它执行按位逻辑异或(XOR)操作,用于对 XMM 寄存器中的浮点数进行位运算。
基础语法:
XORPS xmm_dest, xmm_src
- 操作:
xmm_dest = xmm_dest XOR xmm_src
- 操作数:两个 128 位 XMM 寄存器
- 效果:对目标寄存器和源寄存器的每一位进行异或运算
经典用途:
XORPS xmm0, xmm0 ; 将 xmm0 置为零
用于将寄存器快速置0
2.5 movaps指令
MOVAPS
是 x86/x64 架构中的一条 SIMD 浮点数据传输指令,属于 SSE(Streaming SIMD Extensions)指令集。
- 作用:在 XMM 寄存器之间或 XMM 寄存器与内存之间传输 128 位数据
- 要求 16 字节对齐的内存地址
- 操作对象是 4 个打包的单精度浮点数(但实际传输原始二进制位)
语法格式:
MOVAPS xmm1, xmm2/mem128 ; 加载模式:内存/寄存器 → 寄存器
MOVAPS mem128, xmm1 ; 存储模式:寄存器 → 内存
3. 汇编转化
3.1 debug编译
eight_mixed_params:; === 保存前4个参数到影子空间 ===; 参数4 (char) 从 r9b 存到 [rsp+20h]00000000000008A0: 44 88 4C 24 20 mov byte ptr [rsp+20h], r9b; 参数3 (double) 从 xmm2 存到 [rsp+18h]00000000000008A5: F2 0F 11 54 24 18 movsd mmword ptr [rsp+18h], xmm2; 参数2 (float) 从 xmm1 存到 [rsp+10h]00000000000008AB: F3 0F 11 4C 24 10 movss dword ptr [rsp+10h], xmm1; 参数1 (int) 从 ecx 存到 [rsp+8]00000000000008B1: 89 4C 24 08 mov dword ptr [rsp+8], ecx; === 函数序幕 ===; 保存 rdi 寄存器00000000000008B5: 57 push rdi; 分配 32 字节局部空间00000000000008B6: 48 83 EC 20 sub rsp, 20h; 初始化栈空间为 0xCC (调试模式特有的未初始化标记)00000000000008BA: 48 8B FC mov rdi, rsp00000000000008BD: B9 08 00 00 00 mov ecx, 800000000000008C2: B8 CC CC CC CC mov eax, 0CCCCCCCCh00000000000008C7: F3 AB rep stos dword ptr [rdi]; === 条件分支处理 ===; 加载参数1 (int) 到 ecx (实际未使用)00000000000008C9: 8B 4C 24 30 mov ecx, dword ptr [rsp+30h] ; [rsp+30h] = 影子空间中的参数1; 加载参数5 (bool) 并进行零扩展00000000000008CD: 0F B6 44 24 50 movzx eax, byte ptr [rsp+50h] ; [rsp+50h] = 参数5; 测试条件值00000000000008D2: 85 C0 test eax, eax; 如果为0则跳转到 false_branch00000000000008D4: 74 10 je false_branch; === 条件为真分支 (condition != 0) ===; 加载双精度常量 1.000000000000008D6: F2 0F 10 05 00 00 movsd xmm0, mmword ptr [__real@3ff0000000000000]00 00; 将 1.0 存入局部变量 [rsp+18h]00000000000008DE: F2 0F 11 44 24 18 movsd mmword ptr [rsp+18h], xmm000000000000008E4: EB 09 jmp after_condition; === 条件为假分支 (condition == 0) ===
false_branch:; 将 xmm0 清零 (生成 0.0)00000000000008E6: 0F 57 C0 xorps xmm0, xmm0; 将 0.0 存入局部变量 [rsp+18h]00000000000008E9: F2 0F 11 44 24 18 movsd mmword ptr [rsp+18h], xmm0; === 参数累加阶段 ===
after_condition:; 加载参数1 (int) 并转换为 double -> xmm000000000000008EF: F2 0F 2A 44 24 30 cvtsi2sd xmm0, dword ptr [rsp+30h] ; [rsp+30h] = 参数1; 加载参数2 (float) 并转换为 double -> xmm100000000000008F5: F3 0F 5A 4C 24 38 cvtss2sd xmm1, dword ptr [rsp+38h] ; [rsp+38h] = 参数2; 累加参数2: xmm0 += xmm100000000000008FB: F2 0F 58 C1 addsd xmm0, xmm1; 累加参数3 (double): xmm0 += [rsp+40h] ([rsp+40h] = 参数3)00000000000008FF: F2 0F 58 44 24 40 addsd xmm0, mmword ptr [rsp+40h]; 加载参数4 (char) 并进行符号扩展0000000000000905: 0F BE 44 24 48 movsx eax, byte ptr [rsp+48h] ; [rsp+48h] = 参数4; 将扩展后的值转换为 double -> xmm1000000000000090A: F2 0F 2A C8 cvtsi2sd xmm1, eax; 累加参数4: xmm0 += xmm1000000000000090E: F2 0F 58 C1 addsd xmm0, xmm1; 累加局部变量 (条件结果): xmm0 += [rsp+18h]0000000000000912: F2 0F 58 44 24 18 addsd xmm0, mmword ptr [rsp+18h]; 加载参数6 (short) 并进行符号扩展0000000000000918: 0F BF 44 24 58 movsx eax, word ptr [rsp+58h] ; [rsp+58h] = 参数6; 将扩展后的值转换为 double -> xmm1000000000000091D: F2 0F 2A C8 cvtsi2sd xmm1, eax; 累加参数6: xmm0 += xmm10000000000000921: F2 0F 58 C1 addsd xmm0, xmm1; 加载参数7 (int) 并转换为 double -> xmm10000000000000925: F2 0F 2A 4C 24 60 cvtsi2sd xmm1, dword ptr [rsp+60h] ; [rsp+60h] = 参数7; 累加参数7: xmm0 += xmm1000000000000092B: F2 0F 58 C1 addsd xmm0, xmm1; 加载参数8 (long) -> eax (低32位)000000000000092F: 8B 44 24 68 mov eax, dword ptr [rsp+68h] ; [rsp+68h] = 参数8的低位; 将参数8转换为 double (完整64位) -> xmm10000000000000933: F2 48 0F 2A C8 cvtsi2sd xmm1, rax; 累加参数8: xmm0 += xmm10000000000000938: F2 0F 58 C1 addsd xmm0, xmm1; === 结果处理 ===; 将累加结果存入局部变量 [rsp+10h]000000000000093C: F2 0F 11 44 24 10 movsd mmword ptr [rsp+10h], xmm0; 重新加载到 xmm00000000000000942: F2 0F 10 44 24 10 movsd xmm0, mmword ptr [rsp+10h]; 除以双精度常量 8.00000000000000948: F2 0F 5E 05 00 00 divsd xmm0, mmword ptr [__real@4020000000000000]00 00; === 函数收尾 ===; 释放局部空间0000000000000950: 48 83 C4 20 add rsp, 20h; 恢复 rdi 寄存器0000000000000954: 5F pop rdi; 返回结果在 xmm0 中0000000000000955: C3 ret; === 调试断点 ===0000000000000956: CC int 3
3.2 release编译
eight_mixed_params:; ===== 条件分支处理(基于第5个参数condition) =====0000000000000000: 80 7C 24 28 00 cmp byte ptr [rsp+28h],0 ; 比较第5个参数(condition)是否为00000000000000005: 74 0A je 0000000000000011 ; 若为0跳转false分支; -- 条件为真分支(condition != 0)--0000000000000007: F2 0F 10 25 00 00 movsd xmm4, mmword ptr [__real@3ff0000000000000] ; 加载1.0到xmm400 00000000000000000F: EB 03 jmp 0000000000000014 ; 跳过false分支; -- 条件为假分支(condition == 0)--0000000000000011: 0F 57 E4 xorps xmm4, xmm4 ; xmm4 = 0.0; ===== 开始累加参数值(全部转换为双精度浮点) =====0000000000000014: 0F 57 DB xorps xmm3, xmm3 ; 清零xmm3(用于累加); -- 转换并累加第4个参数(char p4)--0000000000000017: 41 0F BE C1 movsx eax, r9b ; 扩展r9b中的char参数(符号扩展)000000000000001B: F2 0F 2A D9 cvtsi2sd xmm3, ecx ; 转换第1个参数(int p1)到xmm3; -- 转换并累加第2个参数(float p2)--000000000000001F: 0F 57 C0 xorps xmm0, xmm0 ; 清零xmm00000000000000022: F3 0F 5A C1 cvtss2sd xmm0, xmm1 ; 转换float参数(p2)0000000000000026: 0F 57 C9 xorps xmm1, xmm1 ; 清零xmm1(暂存)0000000000000029: F2 0F 58 D8 addsd xmm3, xmm0 ; p1 + p2 → xmm3; -- 转换并累加第4个参数(char p4)--000000000000002D: 0F 57 C0 xorps xmm0, xmm0 ; 清零xmm00000000000000030: F2 0F 2A C0 cvtsi2sd xmm0, eax ; 转换char参数(p4); -- 转换并累加第6个参数(short p5)--0000000000000034: 0F BF 44 24 30 movsx eax, word ptr [rsp+30h] ; 加载第6个参数(栈偏移0x30,short类型); -- 累加第3个参数(double p3)--0000000000000039: F2 0F 58 DA addsd xmm3, xmm2 ; 累加p3; -- 转换第7个参数(int p6)--000000000000003D: 0F 57 D2 xorps xmm2, xmm2 ; 清零xmm20000000000000040: F2 0F 2A 54 24 38 cvtsi2sd xmm2, dword ptr [rsp+38h] ; 转换第7个参数(int p6); -- 累加第4个参数(p4)--0000000000000046: F2 0F 58 D8 addsd xmm3, xmm0 ; 累加p4; -- 转换并累加第6个参数(short p5)--000000000000004A: F2 0F 2A C8 cvtsi2sd xmm1, eax ; 转换short参数(p5); -- 加载第8个参数(int p7)--000000000000004E: 8B 44 24 40 mov eax, dword ptr [rsp+40h] ; 加载第8个参数(栈偏移0x40,int类型); -- 累加条件结果值(xmm4)--0000000000000052: F2 0F 58 DC addsd xmm3, xmm4 ; 累加condition结果; -- 累加第6个参数(p5)--0000000000000056: F2 0F 58 D9 addsd xmm3, xmm1 ; 累加p5; -- 转换第8个参数(int p7)- 存在设计缺陷 --000000000000005A: 0F 57 C9 xorps xmm1, xmm1 ; 清零xmm1000000000000005D: F2 48 0F 2A C8 cvtsi2sd xmm1, rax ; 将32位整数零扩展为64位后再转换(负数会出错); -- 累加第7个(p6)和第8个参数(p7)--0000000000000062: F2 0F 58 DA addsd xmm3, xmm2 ; 累加p60000000000000066: F2 0F 58 D9 addsd xmm3, xmm1 ; 累加p7; ===== 结果处理 =====000000000000006A: F2 0F 59 1D 00 00 mulsd xmm3, mmword ptr [__real@3fc0000000000000] ; 乘以0.125(1/8)00 000000000000000072: 0F 28 C3 movaps xmm0, xmm3 ; 结果存入返回寄存器xmm00000000000000075: C3 ret ; 返回结果
3.3 C语言转化
double eight_mixed_params(int p1, // 参数1: 整型float p2, // 参数2: 单精度浮点double p3, // 参数3: 双精度浮点char p4, // 参数4: 字符型bool condition, // 参数5: 布尔型short p6, // 参数6: 短整型int p7, // 参数7: 整型long p8 // 参数8: 长整型
)
{// 根据条件生成结果值double condition_result = condition ? 1.0 : 0.0;// 将所有参数转换为double并累加double sum = (double)p1; // 参数1sum += (double)p2; // 参数2sum += p3; // 参数3sum += (double)(signed char)p4; // 参数4(有符号扩展)sum += condition_result; // 条件结果sum += (double)p6; // 参数6sum += (double)p7; // 参数7sum += (double)p8; // 参数8// 除以8.0并返回结果return sum / 8.0;
}