若干个游戏辅助的分析手记(一)

  一年一度的重装系统“大工程”又浩浩荡荡的开工了。

  整理去年一年的工具及资料,今天起陆续放一点以前给客户做的游戏辅助的手记。(一年多了,客户应该不会介意吧)

  废话不多说,今天是第一篇。

  《龙翔密传》分析手记

  

选怪

突破口:

  CE搜索变化值,不停选怪取消怪,定位到如下代码:

00413b5e - 89 be b0 00 00 00 - mov [esi+000000b0],edi

  稍微回溯一下

00435BDC   /E9 4B090000     jmp     0043652C
00435BE1   |8B0D 08536300   mov     ecx, dword ptr [635308]
00435BE7   |8B11            mov     edx, dword ptr [ecx]
00435BE9   |8B46 0C         mov     eax, dword ptr [esi+C]
00435BEC   |8B52 34         mov     edx, dword ptr [edx+34]
00435BEF   |6A 00           push    0                                ; 固定参数
00435BF1   |50              push    eax                              ; 怪物ID
00435BF2   |FFD2            call    edx                              ; 选怪CALL edx = 0048ac00

  该CALL就是选怪CALL。

该CALL 可以直接调用实现选怪
_asm
{
	push 0    // 固定参数
	push id    // 怪物ID
	call 0x0048ac00
}

 

怪物列表
突破口:
  根据选怪的反汇编代码,跟进00435BF2这个CALL发现了另一个CALL:

00413AA9  |.  53            push    ebx                              ;  怪物ID
00413AAA  |.  FFD2          call    edx                              ;  根据怪物ID,取对象基址
再跟进此CALL发现另一个CALL:
00412CA5  |.  50            push    eax                              ;  怪物ID指针
00412CA6  |.  8D4C24 14     lea     ecx, dword ptr [esp+14]
00412CAA  |.  51            push    ecx                              ;  上层CALL的返回地址
00412CAB  |.  E8 30260000   call    004152E0                         ;  获得怪物列表
//
00412CAB  |.  E8 30260000   call    004152E0                         ;  获得怪物列表

  这个CALL很关键了,是遍历怪物列表的关键,以下是该CALL的分析:

004152E0  /$  55            push    ebp
004152E1  |.  8BEC          mov     ebp, esp
004152E3  |.  8B4F 18       mov     ecx, dword ptr [edi+18]          ;  超级像怪物列表(ecx = [01c398b8 + 18] = 01c45390)
004152E6  |.  8B41 04       mov     eax, dword ptr [ecx+4]           ;  eax = [[[635210]+90+18]+4] = 20904618
004152E9  |.  83EC 10       sub     esp, 10
004152EC  |.  8078 15 00    cmp     byte ptr [eax+15], 0
004152F0  |.  53            push    ebx
004152F1  |.  56            push    esi
004152F2  |.  8BF1          mov     esi, ecx
004152F4  |.  75 1E         jnz     short 00415314
004152F6  |.  8B4D 0C       mov     ecx, dword ptr [ebp+C]           ;  怪物ID
004152F9  |.  8B09          mov     ecx, dword ptr [ecx]
004152FB  |.  EB 03         jmp     short 00415300
004152FD  |   8D49 00       lea     ecx, dword ptr [ecx]
00415300  |>  3948 0C       /cmp     dword ptr [eax+C], ecx
00415303  |.  7D 05         |jge     short 0041530A
00415305  |.  8B40 08       |mov     eax, dword ptr [eax+8]
00415308  |.  EB 04         |jmp     short 0041530E
0041530A  |>  8BF0          |mov     esi, eax                        ;  esi 将作为本CALL 的返回值
0041530C  |.  8B00          |mov     eax, dword ptr [eax]
0041530E  |>  8078 15 00    |cmp     byte ptr [eax+15], 0
00415312  |.^ 74 EC         \je      short 00415300

  经过查看00415300 到00415312代码所访问的内存模型,判断怪物列表为一颗二叉树. 上面首先获得eax,即怪物二叉树的根节点. eax = [[[[635210]+90+18]+4] 然后从00415300 到00415312是重点,这里遍历二叉树找出对应ID的对象. 左树是小([eax]),右树是大([eax+8]).[eax+4]是父节点.[eax+c]是ID. [eax+15]有点特别,是一个bool类型,通过反汇编代码可以看出,此变量为整个遍历的结束条件. 所以,大胆推测[eax+15]标示了该节点的前一个节点是否是选中状态.如果为选中状态则为true.

  通过调用此CALL的上层CALL : 00412CEF  |> \8B47 10       mov     eax, dword ptr [edi+10]          ; 可以看出[eax+10]就是怪物对象地址 所以可以声明一个数据结构如下:

typedef struct _NPC
{
	NPC* LeftSmall;
	NPC* Parent;
	NPC* RightBig;
	int id;
	DATA* pData;
}NPC;

  根据反汇编代码004152EC到00415312写出大致的模拟算法如下:

NPC* GetAddressByID(int nID)
{
	NPC* a = A.Head;
	NPC* temp = NULL;
	NPC* find = NULL;
	if(a->selected == false)
	{
		int id = nID;
		do
		{
			if(a->id < id)// 如果当前ID小于给定的ID,则往大的方向遍历
			{
				temp = a->RigthBig;
			}
			else
			{
				find = a;
				a = a->LeftSmall;
			}
		}while(a->selecte != false);
	}
	return find;
}

  下面是根据分析资料实现遍历怪物列表的算法:(前序递归)

CArray<DATA*,DATA*&> NPCArray;
void PreOrder(NPC* Tree)
{
    if(Tree->id != 0)
    {
        NPCArray.Add(Tree->pData);
        PreOrder(Tree->LeftSmall);
        PreOrder(Tree->RightBig);
    }
}

  注意用此根节点遍历出来包括了怪物对象,还包括其他一些无用的对象,需要过滤.
  这里过滤的方式是物品的话,最大血量及当前血量都是0,可以以此作为判断是否是物品的根据.肯定二叉树叶子节点有一个变量是标明该对象的类型的,是背包还是怪物还是地上的物品等等.目前没有分析了.暂时用最大血量跟当前血量做判断.

 

打怪

突破口:

  OD在GameClient模块搜索"skill"查找到Send_Skill... 顺藤摸瓜,回溯几层CALL后,发现一个switch分支,当为7的分支的时候就是打怪

00423317  |.  E8 14200000   call    00425330
0042331C  |.  5F            pop     edi
0042331D  |.  5E            pop     esi
0042331E  |.  8BE5          mov     esp, ebp
00423320  |.  5D            pop     ebp
00423321  |.  C3            retn
00423322  |>  56            push    esi                              ;  esi = 上层ECX (DWORD [esi] 恒定为5e6e68); Case 7 of switch 0042327E
00423323  |.  E8 D81C0000   call    00425000                         ;  **************真正的打怪CALL 距离不够会自动靠近一步
00423328  |.  5F            pop     edi
00423329  |.  5E            pop     esi
0042332A  |.  8BE5          mov     esp, ebp
0042332C  |.  5D            pop     ebp
0042332D  |.  C3            retn

  直接CALL 00425000 就会对当前选定的怪进行攻击.
  esi = 上层ECX。

  以下是上层代码:

00418F0F  |.  8B8B CC010000 mov     ecx, dword ptr [ebx+1CC]         ;  ecx = [上层ecx + 1cc] 上层ECX = 1EB35128
00418F15  |.  85C9          test    ecx, ecx
00418F17  |.  74 06         je      short 00418F1F
00418F19  |.  8B01          mov     eax, dword ptr [ecx]
00418F1B  |.  8B10          mov     edx, dword ptr [eax]
00418F1D  |.  FFD2          call    edx                              ;  edx = 00423210,内含真正的打怪CALL

  

  该层的ECX又等于[该层上层ECX + 1cc],经过实测该层上层ECX恒定为1EB35128(此值非常眼熟,挺像怪物列表里的一个值,有待考证)

经过以上分析,打怪CALL的调用代码如下:

_asm
{
	mov eax,[1EB35128+1CC]
	push eax
	call 0x00425000
}

  上面的ECX = 1EB35128,此值非恒定,经过跟踪发现此为一个NEW出来的地址.
  寻找过程:
  发现DWORD [esi]恒定为56e6e8,直接搜索常数,发现有两个引用的地方,一个是构造函数,一个是析构函数,用构造函数来突破.构造函数附近代码如下:

0041FC91  |.  E8 08771300   call    <jmp.&MSVCR90.operator new>      ;  !!!!!!!!!!!!!这里NEW了一个对象,打怪CALL需要用到
0041FC96  |.  83C4 04       add     esp, 4
0041FC99      8945 F0       mov     dword ptr [ebp-10], eax
0041FC9C      C645 FC 01    mov     byte ptr [ebp-4], 1
0041FCA0      3BC7          cmp     eax, edi
0041FCA2  |.  74 09         je      short 0041FCAD
0041FCA4  |.  56            push    esi
0041FCA5  |.  50            push    eax
0041FCA6  |.  E8 D5330000   call    00423080

  OK,既然是NEW出来的,那就给它做个内存补丁:

  在下面找一空白处(关键是见缝插针,不能覆盖原来的代码)
  填写如下代码:

  

0041FD31      A3 0AFD4100   mov     dword ptr [41FD0A], eax
0041FD36      8945 F0       mov     dword ptr [ebp-10], eax
0041FD39      E9 15010000   jmp     0041FE53

0041FE53      C645 FC 01    mov     byte ptr [ebp-4], 1
0041FE57    ^ E9 44FEFFFF   jmp     0041FCA0

  修改完毕后,原来的代码变成了这样:

0041FC91  |.  E8 08771300   call    <jmp.&MSVCR90.operator new>      ;  !!!!!!!!!!!!!这里NEW了一个对象,打怪CALL需要用到
0041FC96  |.  83C4 04       add     esp, 4
0041FC99      E9 93000000   jmp     0041FD31
0041FC9E      90            nop
0041FC9F      90            nop
0041FCA0  |.  3BC7          cmp     eax, edi
0041FCA2  |.  74 09         je      short 0041FCAD
0041FCA4  |.  56            push    esi
0041FCA5  |.  50            push    eax
0041FCA6  |.  E8 D5330000   call    00423080                         ;  此CALL里eax填充56E6E8
0041FCAB  |.  EB 02         jmp     short 0041FCAF

  注意:因为新增的代码是在.text区段,而该区段是不能写的,所以在测试的时候用PE Explorer修改该区段为可写.否则
004236D9      A3 D8364200   mov     dword ptr [4236D8], eax这句代码将出错.用代码实现的时候注意修改内存属性。

(更新)在第一个测试版本中发现该CALL因为会判断怪的距离,不够的话会自动靠近.经过实测,当此CALL尚未执行完毕(即正在自动靠近怪物的时候),又调用此CALL的时候会让游戏崩溃,由此判定此CALL不是线程安全的函数,需要调用者确保线程安全.所以这个CALL太鸡肋!继续找个完美的CALL。。。

004235B5  |> \8B42 08       mov     eax, dword ptr [edx+8]           ;  Case 3 of switch 00423555
004235B8  |.  8B4A 0C       mov     ecx, dword ptr [edx+C]
004235BB  |.  8B52 10       mov     edx, dword ptr [edx+10]
004235BE  |.  52            push    edx                              ;  edx = 1eb怪物ID
004235BF  |.  51            push    ecx                              ;  1
004235C0  |.  50            push    eax                              ;  0
004235C1  |.  E8 9A120000   call    00424860                         ;  

 之前的CALL实际上是消息循环自动调用的,检查某个标记存在则自动调用之前找到的打怪CALL.而这里这个CALL可以理解为正是设定标志的.设定后可以让消息循环自动调用.
注意!!!!!!!!!!!调用此CALL,需要内部的ESI赋值

以下是上个CALL的ESI赋值

0048B85A  |. /74 54         je      short 0048B8B0
0048B85C  |. |8B0D 10526300 mov     ecx, dword ptr [635210]
0048B862  |. |8B51 50       mov     edx, dword ptr [ecx+50]
0048B865  |. |8B8A CC010000 mov     ecx, dword ptr [edx+1CC]         ;  为打怪CALL,提供的ESI
0048B86B  |. |C74424 50 020>mov     dword ptr [esp+50], 2
0048B873  |. |BA 03000000   mov     edx, 3
0048B878  |. |66:895424 08  mov     word ptr [esp+8], dx
0048B87D  |. |8B10          mov     edx, dword ptr [eax]

  打怪代码实现:  

_asm
{
	mov ecx,[635210]
	mov edx,[ecx+50]
	mov esi,[edx+1cc]
	push 0x6d//怪物ID
	push 1
	push 0
	call 00424860
}

怪物对象:
+14 ID范围的最大值
+48 怪物ID
+11C X坐标
+120 Y坐标
名字:[[+158]+8]+8+4
突破口:CE搜名字,找到对应的怪后,下内存访问断点
血量:
当前:[[[+158]+8]+80]
最大:[[[+158]+8]+80+8]

004191A2  |.  8B86 58010000 mov     eax, dword ptr [esi+158]         ;  读名字的关键开始
004191A8  |.  8B40 08       mov     eax, dword ptr [eax+8]
004191AB  |.  83C0 08       add     eax, 8
004191AE  |.  8378 18 10    cmp     dword ptr [eax+18], 10
004191B2  |.  72 05         jb      short 004191B9
004191B4  |.  8B40 04       mov     eax, dword ptr [eax+4]
004191B7  |.  EB 03         jmp     short 004191BC
004191B9  |>  83C0 04       add     eax, 4
004191BC  |>  8D50 01       lea     edx, dword ptr [eax+1]
004191BF  |.  90            nop
004191C0  |>  8A08          /mov     cl, byte ptr [eax]
004191C2  |.  40            |inc     eax
004191C3  |.  84C9          |test    cl, cl
004191C5  |.^ 75 F9         \jnz     short 004191C0

  当前选中对象的分析:

00436848  |.  8B4424 10     mov     eax, dword ptr [esp+10]
0043684C  |.  8BF0          mov     esi, eax
0043684E  |.  75 0F         jnz     short 0043685F
00436850  |.  C780 E8000000>mov     dword ptr [eax+E8], 3
0043685A  |.  E9 8E010000   jmp     004369ED
0043685F  |>  C780 E8000000>mov     dword ptr [eax+E8], 2
00436869  |.  D903          fld     dword ptr [ebx]
0043686B  |.  D998 A0000000 fstp    dword ptr [eax+A0]               ;  当前选中的物品ID
00436871  |.  D943 08       fld     dword ptr [ebx+8]
00436874  |.  C780 A8000000>mov     dword ptr [eax+A8], 1
0043687E  |.  D998 A4000000 fstp    dword ptr [eax+A4]               ;  当前选中的怪ID eax = 01c36c80 恒定
00436884  |.  E9 64010000   jmp     004369ED
00436889  |>  8B17          mov     edx, dword ptr [edi]             ;  Case 3 of switch 004367E7
0043688B  |.  8B42 18       mov     eax, dword ptr [edx+18]
0043688E  |.  8BCF          mov     ecx, edi
00436890  |.  FFD0          call    eax

  此段代码会不停在浮点寄存器中压栈出栈,eax = 01c36c80正是当前所选对象的基址.
eax = 01c36c80恒定,为确保以后游戏升级后能顺利查找到该地址,下面是eax的特征码
首先eax = [esp] + 10
查看函数开头,实际是ebx的压栈,继续回溯ebx
则得到下列代码

0051FF10  |.  395D C0       cmp     dword ptr [ebp-40], ebx
0051FF13  |.  0F85 87020000 jnz     005201A0
0051FF19  |.  8B0D 34526300 mov     ecx, dword ptr [635234]
0051FF1F  |.  8B1D 1C526300 mov     ebx, dword ptr [63521C]          ;  当前所选对象基地址
0051FF25  |.  8B01          mov     eax, dword ptr [ecx]
0051FF27  |.  8B33          mov     esi, dword ptr [ebx]             ;  GameClie.005E7EFC
0051FF29  |.  8B50 40       mov     edx, dword ptr [eax+40]
0051FF2C  |.  83C6 4C       add     esi, 4C
0051FF2F  |.  FFD2          call    edx

  

如上所示:[63521C]即为eax的基地址

+A0 当前选中物品的ID

+A4 当前选中怪的ID

 

物品列表

突破口

 CE监控 改写 地址 01c36c80 + A0的代码:

0043686b - d9 98 a0 00 00 00 - fstp dword ptr [eax+000000a0]// 排除掉了
0042fbf5 - 89 47 08 - mov [edi+08],eax
0041ff3e - c7 40 08 00 00 00 00 - mov [eax+08],00000000// 排除掉了

 0042fbf5 - 89 47 08 - mov [edi+08],eax是关键

  一路回溯,发现遍历物品列表的函数 跟 遍历怪物的函数是一样的.
注意用此根节点遍历出来包括了怪物对象,还包括其他一些无用的对象,需要过滤.
这里过滤的方式是物品的话,最大血量及当前血量都是0,可以以此作为判断是否是物品的根据.肯定二叉树叶子节点有一个变量是标明该对象的类型的,是背包还是怪物还是地上的物品等等.目前没有分析了.

 

技能

 

004235EC  |.  53            push    ebx                              ;  -1
004235ED  |.  8B5C24 18     mov     ebx, dword ptr [esp+18]
004235F1  |.  53            push    ebx                              ;  -1
004235F2  |.  8B5C24 18     mov     ebx, dword ptr [esp+18]
004235F6  |.  53            push    ebx                              ;  -1
004235F7  |.  57            push    edi                              ;  怪物ID
004235F8  |.  52            push    edx                              ;  -1
004235F9  |.  51            push    ecx                              ;  2
004235FA  |.  50            push    eax                              ;  0x16 技能代码
004235FB  |.  56            push    esi                              ;  esi = 01c28BE8 恒定
004235FC  |.  E8 0F0B0000   call    00424110                         ;  这个就是技能CALL

 

  

 

 

 

 

 

转载于:https://www.cnblogs.com/lijianglidedaxia/archive/2013/05/14/3077004.html

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值