文章目录
- debug - 打补丁 - 浮点数加法
- 概述
- 笔记
- demo
- 用CE查看汇编(x64debug)
- main()
- update_info()
- 快捷键 - CE中查看代码时的导航
- 打补丁的时机 - 浮点数加法
- 补丁代码
- 补丁效果
- 浮点数寄存器组的保存
- END
debug - 打补丁 - 浮点数加法
概述
在cm中, UI上显示的数值仅仅用来显示, 改这个显示值没用.
显示时, 是从一个结构体中取出值(浮点数), 然后转成整数显示.
写了一个demo(vs2019 + console + debug/x64), 模拟结构体信息中的浮点数赋值和浮点数的显示.
用CE来看, 和cm中取值显示的汇编实现很像.
拿自己写的demo, 来打个补丁, 让显示之前, 先对结构体中的浮点值加上一些额外的值, 然后再去显示
这样的话, 显示值和结构体中的值就一致了.
笔记
demo
/*!
* \file fDataOpt.cpp
* \brief 测试在汇编中的浮点数操作的打补丁(浮点加法)
*/
#include <windows.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <cstdint>
typedef struct _tag_info
{
int id;
float money;
} TAG_INFO;
void init();
int rand_ranged(int range_min, int range_max);
void update_info(TAG_INFO& info);
int main()
{
TAG_INFO info;
memset(&info, 0, sizeof(info));
init();
do {
Sleep(1000);
update_info(info);
printf("money = %d\n", (int)info.money);
if (info.money >= 9999) {
break;
}
} while (true);
system("pause");
return 0;
}
void update_info(TAG_INFO& info)
{
info.id = 0x1000;
info.money += (float)rand_ranged(1, 10);
}
void init()
{
srand(0215);
}
int rand_ranged(int range_min, int range_max)
{
// Generate random numbers in the interval [range_min, range_max], inclusive.
// Note: This method of generating random numbers in a range isn't suitable for
// applications that require high quality random numbers.
// rand() has a small output range [0,32767], making it unsuitable for
// generating random numbers across a large range using the method below.
// The approach below also may result in a non-uniform distribution.
// More robust random number functionality is available in the C++ <random> header.
// See https://learn.microsoft.com/cpp/standard-library/random
return (int)(((double)rand() / RAND_MAX) * (range_max - range_min) + range_min);
}
用CE查看汇编(x64debug)
main()
fDataOpt.main - 40 55 - push rbp
fDataOpt.main+2- 57 - push rdi
fDataOpt.main+3- 48 81 EC 08010000 - sub rsp,00000108 { 264 }
fDataOpt.main+A- 48 8D 6C 24 20 - lea rbp,[rsp+20]
fDataOpt.main+F- 48 8D 7C 24 20 - lea rdi,[rsp+20]
fDataOpt.main+14- B9 0A000000 - mov ecx,0000000A { 10 }
fDataOpt.main+19- B8 CCCCCCCC - mov eax,CCCCCCCC { -858993460 }
fDataOpt.main+1E- F3 AB - repe stosd
fDataOpt.main+20- 48 8B 05 F1B50000 - mov rax,[fDataOpt.__security_cookie] { (192) }
fDataOpt.main+27- 48 33 C5 - xor rax,rbp
fDataOpt.main+2A- 48 89 85 D8000000 - mov [rbp+000000D8],rax
fDataOpt.main+31- 48 8D 0D ED050100 - lea rcx,[fDataOpt.exe+22015] { (1) }
fDataOpt.main+38- E8 52F9FFFF - call fDataOpt.exe+1137F
fDataOpt.main+3D- 41 B8 08000000 - mov r8d,00000008 { 8 }
fDataOpt.main+43- 33 D2 - xor edx,edx
fDataOpt.main+45- 48 8D 4D 08 - lea rcx,[rbp+08]
fDataOpt.main+49- E8 92F8FFFF - call fDataOpt.exe+112D0 { memset()
}
fDataOpt.main+4E- E8 66F7FFFF - call fDataOpt.exe+111A9 { init()
}
fDataOpt.main+53- B9 E8030000 - mov ecx,000003E8 { 1000 }
fDataOpt.main+58- FF 15 B2F50000 - call qword ptr [fDataOpt._imp_Sleep] { ->KERNEL32.Sleep }
fDataOpt.main+5E- 48 8D 4D 08 - lea rcx,[rbp+08]
fDataOpt.main+62- E8 9DF7FFFF - call fDataOpt.exe+111F4 { update_info()
}
fDataOpt.main+67- F3 0F2C 45 0C - cvttss2si eax,[rbp+0C] { 浮点数强转整数
}
fDataOpt.main+6C- 8B D0 - mov edx,eax
fDataOpt.main+6E- 48 8D 0D 43920000 - lea rcx,[fDataOpt.exe+1ACA8] { ("money = %d
") }
fDataOpt.main+75- E8 30F7FFFF - call fDataOpt.exe+1119A { printf()
}
fDataOpt.main+7A- F3 0F10 45 0C - movss xmm0,[rbp+0C] { 载入浮点数
}
fDataOpt.main+7F- 0F2F 05 56920000 - comiss xmm0,[fDataOpt.exe+1ACCC] { (9999.00) }
fDataOpt.main+86- 72 02 - jb fDataOpt.main+8A
fDataOpt.main+88- EB 07 - jmp fDataOpt.main+91
fDataOpt.main+8A- 33 C0 - xor eax,eax
fDataOpt.main+8C- 83 F8 01 - cmp eax,01 { 1 }
fDataOpt.main+8F- 75 C2 - jne fDataOpt.main+53
fDataOpt.main+91- 48 8D 0D 30920000 - lea rcx,[fDataOpt.exe+1ACB8] { ("pause") }
fDataOpt.main+98- FF 15 3AF80000 - call qword ptr [fDataOpt._imp_system] { ->ucrtbased.system }
fDataOpt.main+9E- 33 C0 - xor eax,eax
fDataOpt.main+A0- 8B F8 - mov edi,eax
fDataOpt.main+A2- 48 8D 4D E0 - lea rcx,[rbp-20]
fDataOpt.main+A6- 48 8D 15 E3910000 - lea rdx,[fDataOpt.exe+1AC80] { (1) }
fDataOpt.main+AD- E8 79F8FFFF - call fDataOpt.exe+1131B
fDataOpt.main+B2- 8B C7 - mov eax,edi
fDataOpt.main+B4- 48 8B 8D D8000000 - mov rcx,[rbp+000000D8]
fDataOpt.main+BB- 48 33 CD - xor rcx,rbp
fDataOpt.main+BE- E8 05F7FFFF - call fDataOpt.exe+111B8
fDataOpt.main+C3- 48 8D A5 E8000000 - lea rsp,[rbp+000000E8]
fDataOpt.main+CA- 5F - pop rdi
fDataOpt.main+CB- 5D - pop rbp
fDataOpt.main+CC- C3 - ret
fDataOpt.exe+11ABD- CC - int 3
update_info()
fDataOpt.update_info - 48 89 4C 24 08 - mov [rsp+08],rcx { rcx是结构体指针
}
fDataOpt.update_info+5- 55 - push rbp
fDataOpt.update_info+6- 57 - push rdi
fDataOpt.update_info+7- 48 81 EC E8000000 - sub rsp,000000E8 { 232 }
fDataOpt.update_info+E- 48 8D 6C 24 20 - lea rbp,[rsp+20]
fDataOpt.update_info+13- 48 8D 0D 6B070100 - lea rcx,[fDataOpt.exe+22015] { (1) }
fDataOpt.update_info+1A- E8 D0FAFFFF - call fDataOpt.exe+1137F
fDataOpt.update_info+1F- 48 8B 85 E0000000 - mov rax,[rbp+000000E0] { rbp + 0xE0 也是结构体指针
}
fDataOpt.update_info+26- C7 00 00100000 - mov [rax],00001000 { 4096 }
fDataOpt.update_info+2C- BA 0A000000 - mov edx,0000000A { 10 }
fDataOpt.update_info+31- B9 01000000 - mov ecx,00000001 { 1 }
fDataOpt.update_info+36- E8 80F7FFFF - call fDataOpt.exe+1104B { 产生随机数(1~10)
}
fDataOpt.update_info+3B- F3 0F2A C0 - cvtsi2ss xmm0,eax { eax是返回的随机数
cvtsi2ss 是将4字节内容整型内容, 强转为浮点数(e.g. 1 => 1.0f)
}
fDataOpt.update_info+3F- 48 8B 85 E0000000 - mov rax,[rbp+000000E0] { rbp + 0xE0是结构体指针
}
fDataOpt.update_info+46- F3 0F10 48 04 - movss xmm1,[rax+04] { 将info.money给xmm1
}
fDataOpt.update_info+4B- F3 0F58 C8 - addss xmm1,xmm0 { 2个浮点数相加
}
fDataOpt.update_info+4F- 0F28 C1 - movaps xmm0,xmm1 { 浮点数拷贝
}
fDataOpt.update_info+52- 48 8B 85 E0000000 - mov rax,[rbp+000000E0]
fDataOpt.update_info+59- F3 0F11 40 04 - movss [rax+04],xmm0 { 浮点数转为2进制存储, 更新money
}
fDataOpt.update_info+5E- 48 8D A5 C8000000 - lea rsp,[rbp+000000C8]
fDataOpt.update_info+65- 5F - pop rdi
fDataOpt.update_info+66- 5D - pop rbp
fDataOpt.update_info+67- C3 - ret
fDataOpt.update_info+68- CC - int 3
看了汇编实现, 就大概知道怎么给显示前的值打补丁(浮点加法)
快捷键 - CE中查看代码时的导航
如果不知道导航键, 在查看代码时, 无法回到查看的原始点. 只能按F8执行一步, 那样太麻烦了, 耽误事.
打补丁的时机 - 浮点数加法
在浮点数更新完, 即将要显示的时候, 打补丁.
打完补丁后, 浮点数增加了, 再执行原始的取浮点数的汇编代码.
补丁代码
[ENABLE]
//code from here to '[DISABLE]' will be used to enable the cheat
alloc(newmem,2048,"fDataOpt.exe"+11A57)
label(returnhere)
label(originalcode)
label(exit)
newmem: //this is allocated memory, you have read,write,execute access
//place your code here
// save env
push rax
// do task
movss xmm1, [rbp+0x0C] // 将info.money给xmm1
// 在原始值上加100(CE的汇编写法中, 数字默认都是16进制)
mov eax, 0x64
cvtsi2ss xmm0,eax // eaxcvtsi2ss 是将4字节内容整型内容, 强转为浮点数(e.g. 1 => 1.0f)
addss xmm1,xmm0 // 2个浮点数相加
movss [rbp+0x0C], xmm1 // 更新改过的info.money到info结构体
// restore env
pop rax
originalcode:
cvttss2si eax,[rbp+0C]
exit:
jmp returnhere
"fDataOpt.exe"+11A57:
jmp newmem
returnhere:
[DISABLE]
//code from here till the end of the code will be used to disable the cheat
dealloc(newmem)
"fDataOpt.exe"+11A57:
db F3 0F 2C 45 0C
//cvttss2si eax,[rbp+0C]
补丁效果
money = 96
money = 99
money = 108
money = 112
money = 118 // 这里是原始的实现, 每次money增加1~10
money = 224 // < 从这开始, 加了补丁, 每次money额外增加了100
money = 333
money = 436
money = 543
money = 652
浮点数寄存器组的保存
模拟了一下, 只看到浮点数寄存器组的保存, 没看到浮点数寄存器组的恢复.
等后续再看看哪里能看到浮点数寄存器的恢复, 或者用CE写个补丁试试. 估计就是用movss就行了.
// 模拟一下函数外边有浮点数, 函数参数也传浮点数, 函数内部也进行浮点数操作
// 看看汇编代码如何保存和恢复浮点寄存器组?
f3 = f1 + f2;
f4 = f1 - f2;
f5 = test_xmmx(f1, f2, f3, f4);
printf("f5 = %.2f\n", f5);
} while (true);
system("pause");
return 0;
}
float test_xmmx(float& f1, float& f2, float f3, float f4)
{
float f_rc = (f1 + f2) * f3 / f4;
return f_rc;
}
// 模拟一下函数外边有浮点数, 函数参数也传浮点数, 函数内部也进行浮点数操作
// 看看汇编代码如何保存和恢复浮点寄存器组?
f3 = f1 + f2;
00007FF629391B48 movss xmm0,dword ptr [f1]
00007FF629391B4D addss xmm0,dword ptr [f2]
00007FF629391B52 movss dword ptr [f3],xmm0
f4 = f1 - f2;
00007FF629391B57 movss xmm0,dword ptr [f1]
00007FF629391B5C subss xmm0,dword ptr [f2]
00007FF629391B61 movss dword ptr [f4],xmm0
f5 = test_xmmx(f1, f2, f3, f4);
00007FF629391B69 movss xmm3,dword ptr [f4]
00007FF629391B71 movss xmm2,dword ptr [f3]
00007FF629391B76 lea rdx,[f2]
00007FF629391B7A lea rcx,[f1]
00007FF629391B7E call test_xmmx (07FF6293913B1h)
00007FF629391B83 movss dword ptr [f5],xmm0
printf("f5 = %.2f\n", f5);
00007FF629391B8B cvtss2sd xmm0,dword ptr [f5]
00007FF629391B93 movaps xmm1,xmm0
00007FF629391B96 movq rdx,xmm1
00007FF629391B9B lea rcx,[string "f5 = %.2f\n" (07FF62939AD50h)]
00007FF629391BA2 call printf (07FF62939119Ah)
} while (true);
--- D:\my_dev\my_local_git_prj\study\asm\case\fxmmxOpt\fDataOpt.cpp ------------
float test_xmmx(float& f1, float& f2, float f3, float f4)
{
00007FF6293918A0 movss dword ptr [rsp+20h],xmm3 // 保存浮点数寄存器
00007FF6293918A6 movss dword ptr [rsp+18h],xmm2
00007FF6293918AC mov qword ptr [rsp+10h],rdx
00007FF6293918B1 mov qword ptr [rsp+8],rcx
00007FF6293918B6 push rbp
00007FF6293918B7 push rdi
00007FF6293918B8 sub rsp,118h
00007FF6293918BF lea rbp,[rsp+30h]
00007FF6293918C4 lea rcx,[__E9A00158_fDataOpt@cpp (07FF6293A2015h)]
00007FF6293918CB call __CheckForDebuggerJustMyCode (07FF62939137Fh)
float f_rc = (f1 + f2) * f3 / f4;
00007FF6293918D0 mov rax,qword ptr [f1]
00007FF6293918D7 mov rcx,qword ptr [f2]
00007FF6293918DE movss xmm0,dword ptr [rax]
00007FF6293918E2 addss xmm0,dword ptr [rcx] // 浮点数加法
00007FF6293918E6 mulss xmm0,dword ptr [f3] // 浮点数乘法
00007FF6293918EE divss xmm0,dword ptr [f4] // 浮点数除法
00007FF6293918F6 movss dword ptr [f_rc],xmm0
return f_rc;
00007FF6293918FB movss xmm0,dword ptr [f_rc]
}
00007FF629391900 lea rsp,[rbp+0E8h]
00007FF629391907 pop rdi
00007FF629391908 pop rbp
00007FF629391909 ret