1. 汇编代码
本案例是通过函数传参传递8种不同的类型的参数,来巩固上两节所学习的知识。整型的传参和浮点型的传参和rsp栈空间的使用,影子空间的申请。
1.1 debug编译
eight_mixed_params:
00000000000008A0: 44 88 4C 24 20 mov byte ptr [rsp+20h],r9b
00000000000008A5: F2 0F 11 54 24 18 movsd mmword ptr [rsp+18h],xmm2
00000000000008AB: F3 0F 11 4C 24 10 movss dword ptr [rsp+10h],xmm1
00000000000008B1: 89 4C 24 08 mov dword ptr [rsp+8],ecx
00000000000008B5: 57 push rdi
00000000000008B6: 48 83 EC 20 sub rsp,20h
00000000000008BA: 48 8B FC mov rdi,rsp
00000000000008BD: B9 08 00 00 00 mov ecx,8
00000000000008C2: B8 CC CC CC CC mov eax,0CCCCCCCCh
00000000000008C7: 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,eax
00000000000008D4: 74 10 je 00000000000008E6
00000000000008D6: F2 0F 10 05 00 00 movsd xmm0,mmword ptr [__real@3ff0000000000000]
00 00
00000000000008DE: F2 0F 11 44 24 18 movsd mmword ptr [rsp+18h],xmm0
00000000000008E4: EB 09 jmp 00000000000008EF
00000000000008E6: 0F 57 C0 xorps xmm0,xmm0
00000000000008E9: F2 0F 11 44 24 18 movsd mmword ptr [rsp+18h],xmm0
00000000000008EF: 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,xmm1
00000000000008FF: 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,eax
000000000000090E: F2 0F 58 C1 addsd xmm0,xmm1
0000000000000912: 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,eax
0000000000000921: F2 0F 58 C1 addsd xmm0,xmm1
0000000000000925: F2 0F 2A 4C 24 60 cvtsi2sd xmm1,dword ptr [rsp+60h]
000000000000092B: F2 0F 58 C1 addsd xmm0,xmm1
000000000000092F: 8B 44 24 68 mov eax,dword ptr [rsp+68h]
0000000000000933: F2 48 0F 2A C8 cvtsi2sd xmm1,rax
0000000000000938: F2 0F 58 C1 addsd xmm0,xmm1
000000000000093C: F2 0F 11 44 24 10 movsd mmword ptr [rsp+10h],xmm0
0000000000000942: 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 00
0000000000000950: 48 83 C4 20 add rsp,20h
0000000000000954: 5F pop rdi
0000000000000955: C3 ret
0000000000000956: CC int 3
0000000000000957: CC int 3
1.2 release编译
eight_mixed_params:
0000000000000000: 80 7C 24 28 00 cmp byte ptr [rsp+28h],0
0000000000000005: 74 0A je 0000000000000011
0000000000000007: F2 0F 10 25 00 00 movsd xmm4,mmword ptr [__real@3ff0000000000000]
00 00
000000000000000F: EB 03 jmp 0000000000000014
0000000000000011: 0F 57 E4 xorps xmm4,xmm4
0000000000000014: 0F 57 DB xorps xmm3,xmm3
0000000000000017: 41 0F BE C1 movsx eax,r9b
000000000000001B: F2 0F 2A D9 cvtsi2sd xmm3,ecx
000000000000001F: 0F 57 C0 xorps xmm0,xmm0
0000000000000022: F3 0F 5A C1 cvtss2sd xmm0,xmm1
0000000000000026: 0F 57 C9 xorps xmm1,xmm1
0000000000000029: F2 0F 58 D8 addsd xmm3,xmm0
000000000000002D: 0F 57 C0 xorps xmm0,xmm0
0000000000000030: F2 0F 2A C0 cvtsi2sd xmm0,eax
0000000000000034: 0F BF 44 24 30 movsx eax,word ptr [rsp+30h]
0000000000000039: F2 0F 58 DA addsd xmm3,xmm2
000000000000003D: 0F 57 D2 xorps xmm2,xmm2
0000000000000040: F2 0F 2A 54 24 38 cvtsi2sd xmm2,dword ptr [rsp+38h]
0000000000000046: F2 0F 58 D8 addsd xmm3,xmm0
000000000000004A: F2 0F 2A C8 cvtsi2sd xmm1,eax
000000000000004E: 8B 44 24 40 mov eax,dword ptr [rsp+40h]
0000000000000052: F2 0F 58 DC addsd xmm3,xmm4
0000000000000056: F2 0F 58 D9 addsd xmm3,xmm1
000000000000005A: 0F 57 C9 xorps xmm1,xmm1
000000000000005D: F2 48 0F 2A C8 cvtsi2sd xmm1,rax
0000000000000062: F2 0F 58 DA addsd xmm3,xmm2
0000000000000066: F2 0F 58 D9 addsd xmm3,xmm1
000000000000006A: F2 0F 59 1D 00 00 mulsd xmm3,mmword ptr [__real@3fc0000000000000]
00 00
0000000000000072: 0F 28 C3 movaps xmm0,xmm3
0000000000000075: C3 ret
2. 汇编分析
2.1 调用者压栈
调用这个函数的调用者需要先将函数压栈到rsp栈帧寄存器,通过
00000000000008A0: 44 88 4C 24 20 mov byte ptr [rsp+20h],r9b
00000000000008A5: F2 0F 11 54 24 18 movsd mmword ptr [rsp+18h],xmm2
00000000000008AB: F3 0F 11 4C 24 10 movss dword ptr [rsp+10h],xmm1
00000000000008B1: 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-=8
00000000000008B6: 48 83 EC 20 sub rsp,20h ; rsp-=0x20h
rsp -= 0x28h, 所以影子空间地址变化为[rsp+0x30]-[rsp+0x50], 再看后续汇编
...
0000000000000921: F2 0F 58 C1 addsd xmm0,xmm1
0000000000000925: F2 0F 2A 4C 24 60 cvtsi2sd xmm1,dword ptr [rsp+60h]
000000000000092B: F2 0F 58 C1 addsd xmm0,xmm1
000000000000092F: 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,eax
00000000000008D4: 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,eax
00000000000008D4: 74 10 je 00000000000008E6
00000000000008D6: F2 0F 10 05 00 00 movsd xmm0,mmword ptr [__real@3ff0000000000000]
00 00
00000000000008DE: F2 0F 11 44 24 18 movsd mmword ptr [rsp+18h],xmm0
00000000000008E4: EB 09 jmp 00000000000008EF
00000000000008E6: 0F 57 C0 xorps xmm0,xmm0
00000000000008E9: F2 0F 11 44 24 18 movsd mmword ptr [rsp+18h],xmm0
00000000000008EF: 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 00
00000000000008DE: 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, rsp
00000000000008BD: B9 08 00 00 00 mov ecx, 8
00000000000008C2: B8 CC CC CC CC mov eax, 0CCCCCCCCh
00000000000008C7: 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_branch
00000000000008D4: 74 10 je false_branch
; === 条件为真分支 (condition != 0) ===
; 加载双精度常量 1.0
00000000000008D6: 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], xmm0
00000000000008E4: 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 -> xmm0
00000000000008EF: F2 0F 2A 44 24 30 cvtsi2sd xmm0, dword ptr [rsp+30h] ; [rsp+30h] = 参数1
; 加载参数2 (float) 并转换为 double -> xmm1
00000000000008F5: F3 0F 5A 4C 24 38 cvtss2sd xmm1, dword ptr [rsp+38h] ; [rsp+38h] = 参数2
; 累加参数2: xmm0 += xmm1
00000000000008FB: 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 -> xmm1
000000000000090A: F2 0F 2A C8 cvtsi2sd xmm1, eax
; 累加参数4: xmm0 += xmm1
000000000000090E: 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 -> xmm1
000000000000091D: F2 0F 2A C8 cvtsi2sd xmm1, eax
; 累加参数6: xmm0 += xmm1
0000000000000921: F2 0F 58 C1 addsd xmm0, xmm1
; 加载参数7 (int) 并转换为 double -> xmm1
0000000000000925: F2 0F 2A 4C 24 60 cvtsi2sd xmm1, dword ptr [rsp+60h] ; [rsp+60h] = 参数7
; 累加参数7: xmm0 += xmm1
000000000000092B: 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位) -> xmm1
0000000000000933: F2 48 0F 2A C8 cvtsi2sd xmm1, rax
; 累加参数8: xmm0 += xmm1
0000000000000938: F2 0F 58 C1 addsd xmm0, xmm1
; === 结果处理 ===
; 将累加结果存入局部变量 [rsp+10h]
000000000000093C: F2 0F 11 44 24 10 movsd mmword ptr [rsp+10h], xmm0
; 重新加载到 xmm0
0000000000000942: F2 0F 10 44 24 10 movsd xmm0, mmword ptr [rsp+10h]
; 除以双精度常量 8.0
0000000000000948: 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)是否为0
0000000000000005: 74 0A je 0000000000000011 ; 若为0跳转false分支
; -- 条件为真分支(condition != 0)--
0000000000000007: F2 0F 10 25 00 00 movsd xmm4, mmword ptr [__real@3ff0000000000000] ; 加载1.0到xmm4
00 00
000000000000000F: 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 ; 清零xmm0
0000000000000022: 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 ; 清零xmm0
0000000000000030: 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 ; 清零xmm2
0000000000000040: 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 ; 清零xmm1
000000000000005D: F2 48 0F 2A C8 cvtsi2sd xmm1, rax ; 将32位整数零扩展为64位后再转换(负数会出错)
; -- 累加第7个(p6)和第8个参数(p7)--
0000000000000062: F2 0F 58 DA addsd xmm3, xmm2 ; 累加p6
0000000000000066: 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 00
0000000000000072: 0F 28 C3 movaps xmm0, xmm3 ; 结果存入返回寄存器xmm0
0000000000000075: 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; // 参数1
sum += (double)p2; // 参数2
sum += p3; // 参数3
sum += (double)(signed char)p4; // 参数4(有符号扩展)
sum += condition_result; // 条件结果
sum += (double)p6; // 参数6
sum += (double)p7; // 参数7
sum += (double)p8; // 参数8
// 除以8.0并返回结果
return sum / 8.0;
}