血战上海滩寻找英雄血量地址 实现无敌效果深入分析

继续上次研究,先整理一下上次的研究,英雄最多有八颗星的血量,分别用0x5 0x19 0x2d 0x41 0x55 0x69 0x7d 0x91表示,比如如果英雄有8颗星的时候就顺序发送0x5 0x19 0x2d 0x41 0x55 0x69 0x7d 0x91这8个参数,有6颗星的时候就顺序发送0x5 0x19 0x2d 0x41 0x55 0x69这三个参数

当有八颗星时,其实是来自5.0/0.625 当有七颗星时,其实是来自4.375/0.625,依次类推
上面的5.0 ,4.375等来自[ebp-0x18]=[[[esi+8]+0x18]+0xf4],那么下边就分析一下这个是怎么来的
.text:0048C4B4 ; DATA XREF: hero_blood_1+36o
.text:0048C4B4 mov ecx, [esi+8]
.text:0048C4B7 ; 25: v4 = *(_DWORD *)(v3 + 24);
.text:0048C4B7 mov eax, [ecx+18h]
.text:0048C4BA ; 26: v5 = *(float *)(v4 + 240);
.text:0048C4BA mov edx, [eax+0F0h]
.text:0048C4C0 ; 27: v6 = *(float *)(v4 + 244);
.text:0048C4C0 mov eax, [eax+0F4h]
.text:0048C4C6 mov [ebp+var_20], edx
.text:0048C4C9 mov [ebp+var_18], eax ; biggest is 5.000

开始。。。

既然[ebp+var_18]来自[eax+0F0h],那就对这个地址下断点,然后看看谁改写了这个地址

此时,eax+0xf4=0x08b1540c ,它的值是0x40a00000也就是浮点数5.000
下边看看什么改写了该地址:

然后就IDA分析一下了:
.text:0044F633 lea ecx, [ebp+var_10]
.text:0044F636 push eax
.text:0044F637 push 30Dh
.text:0044F63C push offset aEDevEnyaAmat_1 ; "E:\\dev\\Enya\\AMatterUnit.cpp"
.text:0044F641 push 0
.text:0044F643 call sub_4059F0
.text:0044F648
.text:0044F648 loc_44F648: ; CODE XREF: attack_hero+34j
.text:0044F648 ; DATA XREF: attack_hero+36o
.text:0044F648 fld dword ptr [esi+0F4h] ; 原始血量参考值 满血是5 7颗星是4.375 依次减0.625
.text:0044F64E fsub [ebp+arg_0] ; 受到攻击时减去0.625
.text:0044F651 fst dword ptr [esi+0F4h] ; 将新的血量写入地址
.text:0044F657 fcomp ds:flt_58DDB8
.text:0044F65D fnstsw ax
.text:0044F65F test ah, 41h
.text:0044F662 jz short loc_44F67C

可以看到每次收到攻击时,在0044F64E 处就减去一个数(这里这个数就是0.625),然后再写入原来的地址,这个数来自 [ebp+arg_0] 也就是函数的第一个地址,我们可以这样想,如果减去0,那么每次收到攻击时,血量是不变的,那么就可以实现无敌效果了

将该函数命名attack_hero,查看该函数的调用,因为有三处调用,那就结合动态调试的方法,查看栈回溯,确定调用点:
.text:0044DE47
.text:0044DE47 loc_44DE47: ; CODE XREF: attack_hero_1+BBj
.text:0044DE47 mov eax, [esi]
.text:0044DE49 push edi
.text:0044DE4A mov ecx, esi
.text:0044DE4C call dword ptr [eax+74h] ; ds:[0059103C]=00464870 (shanghai.00464870)
.text:0044DE4F ; 32: v9 = a2;
.text:0044DE4F fstp [ebp+arg_0]
.text:0044DE52 ; 33: attack_hero((void *)(v3 - 24), v9);
.text:0044DE52 mov ecx, [ebp+arg_0]
.text:0044DE55 push ecx ; float
.text:0044DE56 mov ecx, esi
.text:0044DE58 call attack_hero
.text:0044DE5D ; 34: (*(void (__thiscall **)(int, float))(*(_DWORD *)v4 + 136))(v3 - 24, LODWORD(v5));
.text:0044DE5D mov edx, [esi]
.text:0044DE5F push edi
.text:0044DE60 mov ecx, esi
.text:0044DE62 call dword ptr [edx+88h]

从地址0044DE58 往上推,attack_hero第一个参数来自ecx ecx来自[ebp+arg_0] [ebp+arg_0]来自sp0,然后上边有个动态调用的函数,这里边很可能就是修改sp0的地方了,在0044DE4C 处下断,确定函数地址为0x00464870(我将此处函数名称修改为attack_hero_st0),跟进去分析一下,看他是怎么改变sp0的,来到关键点:

对函数sub_43ca10来说,因为传入的参数是9,所以结果是固定的0x005BEF58
0x005BEF58+0x28=0x005BEF80 这个地址在.rdata 段,所以结果是固定的0x3f800000,也就是浮点数1.000

然后在004648EC处将结果传入[ebp+arg_0],
.text:004648F9 fld [ebp+arg_0]
.text:004648FC fmul ds:flt_591078
.text:00464902 fstp [ebp+arg_0]
上边三条很重要,将[ebp+arg_0]放入sp0,然后跟0x591078处的值相乘
0x591078的值固定为0.625,所以结果是0.625,最后经过一系列的操作,将结果传入sp0,返回函数

经过上边的介绍我们知道了attack_hero_st0的结果就是0.625,所以每次攻击时,就是减去0.625,然后在除以0.625获得星数

那么实现无敌的方法就很简单了,直接使用十六进制编辑器将0x591078处的值修改为0x00000000,然后保存文件,重新运行程序,这样就实现完全无敌了(上一次是通过动态调试,然后锁定一个内存地址的方法实现无敌,这次直接修改可执行程序的.rdata段实现无敌效果,更加彻底)

猜你喜欢

转载自blog.csdn.net/qq_35519254/article/details/79292322