搜索背包物品基址
在游戏中,背包中包含了各种药品和装备等游戏道具,通过搜索背包基址,进而遍历背包,得到各种背包物品信息是非常重要的。
背包中的物品数量是一个比较容易更改的属性,我们通过改变物品的数量,用 CE 工具来定位物品数量的虚拟地址,然后有两种思路来定位物品数量的基址:
- 对物品数量虚拟地址下
硬件访问断点
,看是谁访问了这个地址量,看是哪里改写了这个地址 - 对物品数量虚拟地址下
硬件写入断点
,改变物品数
一、笑傲江湖背包基址分析(x32)
1 通过硬件访问断点追溯背包基址
首先我们用 CE 搜索出背包特定物品数量的虚拟地址,然后对其下一个硬件访问断点
,打开背包,在如下出断下:
00A5A4E8 | C78424 9C000000 | mov dword ptr ss:[esp+9C],FFFFFFFF |
00A5A4F3 | E8 28DB0800 | call xajh.AE8020 |
00A5A4F8 | 83C4 20 | add esp,20 |
00A5A4FB | 837F 70 01 | cmp dword ptr ds:[edi+70],1 | 11 edi 的值一直变化
因为游戏在循环遍历背包中不同物品的信息并将其显示在频幕上,所以上面代码中的 edi
值一直变化,我们往上追溯 edi
:
00A5A47D | 8BBC24 84000000 | mov edi,dword ptr ss:[esp+84] | 22 edi 来源于本call的第一个参数
00A5AA57 | 52 | push edx |
00A5AA58 | 51 | push ecx |
00A5AA59 | 53 | push ebx |
00A5AA5A | 50 | push eax | 33 第一个参数为 eax
00A5AA25 | 8B4C24 2C | mov ecx,dword ptr ss:[esp+2C] |
00A5AA29 | 6A 00 | push 0 |
00A5AA2B | 53 | push ebx | 物品的位置
00A5AA2C | E8 BF03B0FF | call xajh.55ADF0 | 44 eax 来源于此 call
00A5AA31 | 85C0 | test eax,eax |
经分析,随着 ebx
不断 +1,返回的 eax
值不断 +4,表明正在遍历背包,因此推测 eax
为背包中物品的地址,由于我们需要追踪背包基址,因此我们需要跟进 call 里面继续追踪 eax
的来源:
0055ADF0 | 8B4424 04 | mov eax,dword ptr ss:[esp+4] |
0055ADF4 | 85C0 | test eax,eax |
0055ADF6 | 7C 1D | jl xajh.55AE15 |
0055ADF8 | 3B41 28 | cmp eax,dword ptr ds:[ecx+28] |
0055ADFB | 7D 18 | jge xajh.55AE15 |
0055ADFD | 807C24 08 00 | cmp byte ptr ss:[esp+8],0 |
0055AE02 | 8B49 24 | mov ecx,dword ptr ds:[ecx+24] | 77 ecx==438AC898
0055AE05 | 8D0C81 | lea ecx,dword ptr ds:[ecx+eax*4] | 66 ecx==5597BFB0
0055AE08 | 8B01 | mov eax,dword ptr ds:[ecx] | 55 ecx==5597BFE4
0055AE0A | 74 0B | je xajh.55AE17 |
0055AE0C | C701 00000000 | mov dword ptr ds:[ecx],0 |
0055AE12 | C2 0800 | ret 8 |
0055AE15 | 33C0 | xor eax,eax |
0055AE17 | C2 0800 | ret 8 |
这是一个公用 call,不止用于遍历背包,ecx
的值一直在变化,eax
为遍历的位置,由于背包比其他的容器要大,观察 eax
值的取值范围来分辨是否在遍历背包,如果判断错误则会追踪到其他的路径,导致追踪错误,跳出公用 call 后继续追踪 ecx
:
00A5AA25 | 8B4C24 2C | mov ecx,dword ptr ss:[esp+2C] | 88 [esp+2C] 为局部变量
00A5A91C | 8D4F 08 | lea ecx,dword ptr ds:[edi+8] | 1010 edi==438AC890
00A5A91F | 6A 01 | push 1 |
00A5A921 | 894C24 34 | mov dword ptr ss:[esp+34],ecx | 99
00A5A87F | E8 9C3BA5FF | call xajh.4AE420 | 1313 eax 来源于此call
00A5A884 | 8B40 08 | mov eax,dword ptr ds:[eax+8] | 1212 eax==2C5D7EC0
00A5A887 | 8B78 14 | mov edi,dword ptr ds:[eax+14] | 1111 eax==38926A20
004AE420 | A1 D8825201 | mov eax,dword ptr ds:[15282D8] | 1616 追到基址
004AE425 | 85C0 | test eax,eax |
004AE427 | 74 0E | je xajh.4AE437 |
004AE429 | 8B40 24 | mov eax,dword ptr ds:[eax+24] | 1515 eax==01529788
004AE42C | 85C0 | test eax,eax |
004AE42E | 74 07 | je xajh.4AE437 |
004AE430 | 8B80 90000000 | mov eax,dword ptr ds:[eax+90] | 1414 eax==0BDC0FB8
004AE436 | C3 | ret |
004AE437 | 33C0 | xor eax,eax |
004AE439 | C3 | ret |
组合背包物品数量基址:[[[[[[[15282D8]+24]+90]+8]+14]+8+24]+eax*4]+70。
其中 eax
为背包中物品的位置,因此可以得到背包基址:[[[[[[15282D8]+24]+90]+8]+14]+8+24]。
我们查看背包基址的内存:
5597BFB0 411B38A8 ¨8.A &"p酻"
5597BFB4 54750538 8.uT
5597BFB8 411B35F0 ð5.A &"P怲"
5597BFBC 5474CBD8 ØËtT
5597BFC0 411B3FE8 è?.A &"P怲"
5597BFC4 411B3250 P2.A &"P怲"
5597BFC8 411B3338 83.A &"P怲"
5597BFCC 411B3990 .9.A &"P怲"
5597BFD0 00000000 ....
5597BFD4 00000000 ....
背包基址里面存放的是物品的地址,为了验证背包基址是否正确,我们通过挪动背包中的物品,则其对应的基址也会移动到相应的位置。
下面我们继续分析背包集中中物品地址的结构,很容易得到如下信息:
+48:最大物品数量
+70:物品数量
但是看不到物品名称,一般来说物品结构体中都会包含物体名称,我们通过 CE 工具来对其进行定位,我们首先用工具将要搜索的物品名称进行转码:
由于这个游戏中文使用的是 UNICODE 编码,将其复制到 CE 中,并将搜索类型更改为 字节数组
,并在尾部添加 00 00
字符串结束符:
将搜索出来的字节数组类型全部改为 UNICODE 字符串,然后挨个更改名字,看哪个数据会影响游戏显示:
找到该地址后和物品结构体中的值一一比对,找到值相等或相近的,进行分析,不难得出物品名字偏移:
[+D8]+4:药品物品名称
[+E8]+4:装备物品名称
当物品偏移 [+D8]+4 的值为 1
的时候,表明这件物品是一件装备。
2 通过硬件写入断点追溯背包基址
我们之前用 硬件访问断点
追溯血量基址的时候发现并没有追踪到人物的基址,而是通过硬件写入断点
才追溯到正确的人物基址,那物品背包基址是不是也通过硬件写入断点
来追溯基址会比较好呢?我们来试一下。
首先对物品数量虚拟地址下硬件写入断点
,然后拆分物品使得物品数量发生变化,断下后进行溯源:
00554CD1 | 75 10 | jne xajh.554CE3 |
00554CD3 | 0141 70 | add dword ptr ds:[ecx+70],eax | 11 ecx==411B3990
00554CD6 | 8B51 70 | mov edx,dword ptr ds:[ecx+70] |
00554CD9 | 8B41 48 | mov eax,dword ptr ds:[ecx+48] |
0055AE83 | 8BCE | mov ecx,esi | 22
0055AE68 | 8BF9 | mov edi,ecx | 55
0055AE6A | 7C 36 | jl xajh.55AEA2 |
0055AE6C | 3B5F 28 | cmp ebx,dword ptr ds:[edi+28] | edi+28:"氶粆"
0055AE6F | 7D 31 | jge xajh.55AEA2 |
0055AE71 | 8B47 24 | mov eax,dword ptr ds:[edi+24] | 44 edi==438AC898
0055AE74 | 56 | push esi |
0055AE75 | 8B3498 | mov esi,dword ptr ds:[eax+ebx*4] | 33 eax==5597BFB0
0055B33D | 8BCE | mov ecx,esi | 66
0055B2F9 | 8BF1 | mov esi,ecx | 77
0052B20D | 8BCB | mov ecx,ebx | 88
0052B1F5 | 3BD0 | cmp edx,eax |
0052B1F7 | 8D5A 08 | lea ebx,dword ptr ds:[edx+8] | 99 edx==438AC890
0052B1FA | 75 72 | jne xajh.52B26E |
0052B1CD | E8 CEB5FFFF | call xajh.5267A0 | 1111 eax来源于这个call
0052B1D2 | 0FB64C24 23 | movzx ecx,byte ptr ss:[esp+23] |
0052B1D7 | 51 | push ecx |
0052B1D8 | 8BCB | mov ecx,ebx |
0052B1DA | 8BD0 | mov edx,eax | 1010
0052B1DC | E8 BFB5FFFF | call xajh.5267A0 |
我们进入 call 里面追踪 eax
:
005267A0 | 8B4424 04 | mov eax,dword ptr ss:[esp+4] |
005267A4 | 83F8 14 | cmp eax,14 |
005267A7 | 77 07 | ja xajh.5267B0 |
005267A9 | 8B4481 0C | mov eax,dword ptr ds:[ecx+eax*4+C] | 1212 ecx==38926A20
005267AD | C2 0400 | ret 4 |
005267B0 | 33C0 | xor eax,eax |
005267B2 | C2 0400 | ret 4 |
此处是一个公用的函数,我们之前通过拆分物品来改变物品数量,但是此处不断地断下,我们无法通过改变物品数量来使其断下,但是我们发现 ecx
的值是不变的,我们继续跟踪就跳出了这个 call,来到下面的位置:
0052B1CB | 8BCB | mov ecx,ebx | 1313
跳出公用函数后,此处又只有当拆分物品时才会断下,而且 ecx
的值没有变化,我们可以继续追踪:
0052A5B0 | 55 | push ebp |
0052A5B1 | 8BEC | mov ebp,esp |
0052A5B3 | 83E4 F8 | and esp,FFFFFFF8 |
0052A5B6 | 6A FF | push FFFFFFFF |
0052A5B8 | 68 AEF80F01 | push xajh.10FF8AE |
0052A5BD | 64:A1 00000000 | mov eax,dword ptr fs:[0] |
0052A5C3 | 50 | push eax |
0052A5C4 | 64:8925 00000000 | mov dword ptr fs:[0],esp |
0052A5CB | 81EC D0010000 | sub esp,1D0 |
0052A5D1 | 53 | push ebx |
0052A5D2 | 8BD9 | mov ebx,ecx | 1414
ecx
追溯到函数头部发现来源于上层 call,但是回到上层发现 ecx
的值不对,路线跟错了,是哪里除了问题吗?原来其实 0052A5B0
这个地址并不是函数头部,还有其他地址跳到了这里,我们直接右键这条语句,然后点击查找引用
看是哪条语句引用了这里,果然有一条语句跳转到 0052A5B0
这个地址,我们跟过去:
00DBA5E0 | E8 3B3E6FFF | call xajh.4AE420 | 1616 eax 来源于此call
00DBA5E5 | 85C0 | test eax,eax |
00DBA5E7 | 75 05 | jne xajh.DBA5EE |
00DBA5E9 | 33C0 | xor eax,eax |
00DBA5EB | C2 0400 | ret 4 |
00DBA5EE | 8B48 08 | mov ecx,dword ptr ds:[eax+8] | 1515 eax==2C5D7EC0
00DBA5F1 | 85C9 | test ecx,ecx |
00DBA5F3 | 74 F4 | je xajh.DBA5E9 |
00DBA5F5 | E9 B6FF76FF | jmp xajh.52A5B0 |
继续进入 call 追溯 eax
:
004AE420 | A1 D8825201 | mov eax,dword ptr ds:[15282D8] | 1818 追到基址
004AE425 | 85C0 | test eax,eax |
004AE427 | 74 0E | je xajh.4AE437 |
004AE429 | 8B40 24 | mov eax,dword ptr ds:[eax+24] | 1717
004AE42C | 85C0 | test eax,eax |
004AE42E | 74 07 | je xajh.4AE437 |
004AE430 | 8B80 90000000 | mov eax,dword ptr ds:[eax+90] | 1616
004AE436 | C3 | ret |
004AE437 | 33C0 | xor eax,eax |
004AE439 | C3 | ret |
这也是一个公用 call,频繁中断,我们无法拆分物品,但已经追踪到了基址,不用再追踪寄存器的值了,接下来我们组合物品数量基址:[[[[[[[15282D8]+24]+90]+8]+eax*4+C]+8+24]+ebx*4]+70。
eax
的取值有2、3、4 和 5,而结合我们之前的分析结果,eax=2 的时候是背包基址,ebx为背包中的物品位置。
我们发现通过硬件写入断点
追溯出来的背包基址当中有两个变量,对于 eax
的取值我们只能进行推测验证来确定了,所以硬件写入断点
行不通的时候,不妨试试硬件访问断点
。