第一节 -- 寻找当前地图的寻路call

第一部分 堆栈入门

1.封包断点

bp WS2_32.send

2.硬件写入断点:7E8C0040

堆栈数据:

0018F844 008182FB /CALL 到 send 来自 Game.008182F5
0018F848 00000898 |Socket = 898
0018F84C 7E8C0040 |Data = 7E8C0040
0018F850 00000042 |DataSize = 42 (66.)
0018F854 00000004 \Flags = MSG_DONTROUTE

2-1 为什么要对 7E8C0040 下硬件写入断点

游戏内核的处理机制:
Created with Raphaël 2.1.0 主线程 主线程 消息队列 消息队列 消息执行 消息执行 发送消息(将消息的内容写入:7E8C0040) 从消息循环弹出消息 (调用win32 提供的 WS2_32.send,将7E8C0040的内容发送给服务端 ) 查看消息队列是否有消息
名词解释:

消息队列:并不是windows里面的消息队列,而是游戏内部的消息队列,使用quene<msg> 的方式,
先进去的消息就先执行

3.获取调用堆栈

通过硬件写入断点,可以找到一个调用堆栈,如果在还没有移动的时候就已经断点了,
说明不是当前断点,一定要在进行移动之后断点下来的,才是真正执行的地方

小提示: 通过Ctrl+K 获取调用堆栈列表,可以复制到剪切板

地址堆栈函数过程 / 参数调用来自结构
0018F66C00716477包含Game.00819624Game.00716474
0018F724004569D7? Game.007163A0Game.004569D2
0018F81400457EBBGame.00456840Game.00457EB6
0018F954004598C9Game.00457D10Game.004598C40018F950
0018F9600043BAD4包含Game.004598C9Game.0043BAD10018F9AC

4.确认顶层调用点

如何确认是那一层是真正的调用点

可以从 ( 调用来自) 00716474->004569D2->00457EB6->004598C4->0043BAD1 一层一层的找

1.
在 00716474 下断点,移动后断点一次,说明是移动的断点
2.
在 004569D2下断点,移动后断点一次,说明是移动的断点
3.
在 00457EB6 下断点,移动后断点一次,说明是移动的断点,通过下一层04598C4 的死循环,判断出当前调用层是顶层调用点
4.
在 004598C4 下断点,一直断点,说明已经进入了游戏引擎的死循环,那么上一层就是顶层调用点

得到调用点 00457EB6
0018F814 00457EBB Game.00456840 Game.00457EB6 //顶层调用点

第二部分,汇编分析

注意 第一部分未完成的读者,请勿进行接下来的学习
前置思路介绍

分析游戏内部如何实现寻路

c++ 代码模拟

    //定义目标点结构体
    typedef tagPoint{
        float x;
        float y;
    }POINT,LPPOINT;
    // 游戏内部的用户对象
    Player player;
    POINT tagetPoint = {};
    targetPoint.x = 155;
    targetPoint.y = 253;
    //执行移动
    player.Move(targetPoint)

c++ 汇编为win32汇编后的代码:

    sub esp,20H
    mov eax,431b0000H  ;155 的float的值
    mov [esp],eax ; 不能直接给非通用寄存器赋值
    mov eax,437d0000 ;253 float 的值
    mov [esp+4],eax 
    mov ecx,[7EF30010] ;游戏内部的用户对象的内存地址,本章节不做用户基址的搜索
    push esp  ;结构体入栈
    call 03105121  ;游戏内部通常使用_stdcall ,不用自己进行add esp,n 堆栈平衡
    add esp,20H  ; 这个是为了恢复自己申请的栈空间

游戏执行移动的流程分析(正向猜测,非逆向分析)

Created with Raphaël 2.1.0 开始 界面点击坐标调用移动 移动函数开始 是否正在移动? 结束 移动 yes no

用c++ 代码模拟流程图

//假设游戏玩家类有这些字段,n[0xm]表示我们不知道的内容
class Player{
    char nothing[0x160];//占位0x160个字节,不知道前面放的是什么,只知道0x164是状态
    int state;  //占位4
    char nothing[0x18];// 占位 0x18
    bit isAlive ;第0x180位的状态
};
//流程图中的  `移动函数开始` 

    define MOVE_STATE  2
    if(player.state == MOVE_STATE && player.isAlive){
        ... dosomething
        player.Move(point);
    }

汇编模拟c++的代码


    cmp [ecx+164],3;汇编的数字都是16进制,164 表示 0x164(10进制356),而不是10进制的164
    je 07aa0110 ;判断用户的状态是不是移动,是移动就跳转到结束
    cmp [ecx+180],1;判断是否活着
    je 07aa0110 ;死亡就结束
    call move;判断执行完成之后,就执行移动

5.函数体内部分析:

现在就是通过第4节的 顶层call 00457EB6
在这里向上回溯,知道找到条件判断,进入一小段函数体的判断

两个判断,如果都成功才会进入走路call

00457D3B    8A86 64010000   MOV     AL,BYTE PTR DS:[ESI+164]
00457D41    84C0            TEST    AL,AL
00457D43    53              PUSH    EBX
00457D44    74 17           JE      SHORT 00457D5D
00457D46    83BF D8010000 0>CMP     DWORD PTR DS:[EDI+1D8],2
00457D4D    75 0E           JNZ     SHORT 00457D5D
00457D4F    8B87 E0010000   MOV     EAX,DWORD PTR DS:[EDI+1E0]
00457D55    85C0            TEST    EAX,EAX
00457D57    0F84 7C010000   JE      00457ED9
00457D5D    8DBE 4C010000   LEA     EDI,DWORD PTR DS:[ESI+14C]

判断内容 伪代码解析:

if(esi+0x164==1)//是否活着
{
    if(edi+0x1d8!=0x2)//可能是  人物移动状态
    {

        //具体移动代码

    }
}

6.分析esi+0x164 和 edi+0x1d8 的硬件写入断点

调用堆栈

地址堆栈函数过程 / 参数调用来自结构
0018F45C0045823F? Game.00457AA0Game.0045823A0018F458
0018F47C0045A1D1? Game.004581E0Game.0045A1CC0018F478
0018F4F80045983C包含 Game.0045A1D1Game.004598390018F4F4
0018F50C00459971Game.00459800Game.0045996C0018F508
0018F52400455D33包含 Game.00459971Game.00455D300018F520
0018F5C40056E231包含 Game.00455D33Game.0056E22E0018F5C0
0018F5E0004649D9包含 Game.0056E231Game.004649D60018F5DC
0018F960007ABB04包含 Game.004649D9Game.007ABB010018F95C
0018F9D4007AF7BFGame.007AB270Game.007AF7BA0018F9D0
1.通过第四步 确认顶层调用点 的方式 按照

Game.0045823A-> Game.0045A1CC->Game.00459839-> Game.0045996C -> Game.00455D30 -> Game.0056E22E -> Game.004649D6 ->Game.007ABB01 -> Game.007AF7BA
依次寻找,直到找到游戏引擎的循环体的前一步

2.找到顶层call:
地址堆栈函数过程 / 参数调用来自结构
0018F5C40056E231包含 Game.00455D33Game.0056E22E

第三部分,调用测试

7.顶层call 代码区

0056E200    55              PUSH    EBP
0056E201    8BEC            MOV     EBP,ESP
0056E203    8B0D C08CB300   MOV     ECX,DWORD PTR DS:[B38CC0]
0056E209    56              PUSH    ESI
0056E20A    8B75 08         MOV     ESI,DWORD PTR SS:[EBP+8]
0056E20D    56              PUSH    ESI
0056E20E    E8 BDB72300     CALL    007A99D0
0056E213    A1 D885B200     MOV     EAX,DWORD PTR DS:[B285D8]
0056E218    8B48 5C         MOV     ECX,DWORD PTR DS:[EAX+5C]
0056E21B    8B46 04         MOV     EAX,DWORD PTR DS:[ESI+4]
0056E21E    8B89 E4010000   MOV     ECX,DWORD PTR DS:[ECX+1E4]
0056E224    8B11            MOV     EDX,DWORD PTR DS:[ECX]
0056E226    6A 00           PUSH    0
0056E228    6A 01           PUSH    1
0056E22A    50              PUSH    EAX
0056E22B    8B06            MOV     EAX,DWORD PTR DS:[ESI]
0056E22D    50              PUSH    EAX
0056E22E    FF52 10         CALL    DWORD PTR DS:[EDX+10]
0056E231    FF15 84699000   CALL    DWORD PTR DS:[<&tEngine.TdGetTim>; tEngine.TdGetTimeTickCountSafe
0056E237    A3 389CB200     MOV     DWORD PTR DS:[B29C38],EAX
0056E23C    5E              POP     ESI
0056E23D    5D              POP     EBP
0056E23E    C2 0400         RETN    4

8.模拟调用

push 0
push 1
push 0x42bc0000
push 0x438a0000
mov ecx,[0B285D8]
mov ecx,[ecx+0x5c]
mov ecx,[ecx+0x1e4]
mov edx,[ecx]
mov edx,[edx+0x10]
call edx

经过测试,可以成功进行本图寻路

ecx 演算值

[[B285D8]+0x5C]+0x1E4

ecx特征码:

8B 11 6A 00 6A 01 50 8B 06 50

不在本节介绍的地方:

1.人物基址寻找(为了课程连贯性,人物基址查找和人物信息查找,单独做一节)

2.特征码寻找 ,(用来在游戏更新之后,我们可以根据特征码自动定位基址,而不是在重新找一遍)

AYA原创,转载请务必注明出处

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值