欢迎来到 嗅灵易学

零基础也能上手的脚本技术课,一对一答疑带你入门

都不够给力,我来个给力的,手把手一起写现在某热门游戏的跑钱外挂(三)

都不够给力,我来个给力的,手把手一起写现在某热门游戏的跑钱外挂(三)

都不够给力,我来个给力的,手把手一起写现在某热门游戏的跑钱外挂(三)
2010年11月1日...
补充昨天的调试部分和今天的内存搜索与补丁.高潮部分到了.想写加速的消化掉这篇你就成功了.
我即写即发的,难免会有错字或者其它错,大家要指出来哈.谢谢..
昨天调试,到了按钮部分.多次测试按钮和T按钮的分别,我们发现游戏功能性按钮是按钮那里的调用,而会改变的服务器选择和游戏内物品是T按钮.
人物对话框弹出的选择性语句,例如传送员的,里面也是T按钮,据此分析得出的结论不用说啦.哈哈.NPC对话和物品ID获取完毕.接着来的是关键点的,人物移动.与模型(NPC和游戏角色)的ID获取
记得我们之前的那个无敌脚本解析点么? 分析那里,得出这个游戏的指令是先串成文字脚本形式,再由那里把脚本解析掉做相应操作的.我们在上篇文章那里那里下条件记录断,建个角色,把记录保存到文件(记录会好多,保存下来好分析).
角色建好那我们开始OD加载,进入游戏.我们是要分析游戏其行为,所以条件记录断要一开始就让它生效来记录.哦,忘记了,这游戏新手村好多人,找个没人的房子进去闪一边那样会更容易分析.
随便让角色跑几步,我们开始分析记录文件,数据太多,我拿主要部分出来说.一句一句看吧,嘛,既然是先做成脚本串再解析的,那就试试搜索自己的角色名字看看有没发现,在log记录那里.
0046D2B5  COND: 04B0C610 "类名=t",B0,"",B4,"钮 鼠标=3 ",BD,"疟?,B6,"韵?c0FoWa1.fkEuxd2 ",BD,"疟?,B2,"问?0 x=102 y=195 图?,AC,"='' 颜?,AB,"=white 文",D7,"?'testdbyou' 尺",B4,"?11 ",B4,"窒",B8,"=400 alpha=0  ",B6,"云?4"
这句.哦,角色选择时的T按钮.类名=T按钮嘛..用ACSII来看..向下找自己角色名..OH NO 找到了..模型加载
0046D2B5  COND: 03F62AC8 "称?,BD,"='' 颜?,AB,"2='80C0FF' 名",D7,"?'testdbyou' 颜?,AB,"='00FF00' 速",B6,"?3 站?,A2,"=shape/char/0063/stand.tcp 站?,A2,"2=shape/char/0063/stand2.tcp 行",D7,"?shape/char/0063/walk.tcp ",B2,"",BD,"距=72 ",B2,"",BD,"速=24 跑",B2,
为何上面是模型加载?根据经验得出的,这个说不清楚,反正第1眼看到就觉得是重要的,还是猜的,哈哈..用上面语句,我们来在无敌记录断那里做个程序暂停的条件断点,为了得到这个模型的ID.看
设置条件断点,条件为dword ptr [edx] == 0bdcec6b3
0bdcec6b3='称谓'的ACSII逆序
04F64680  B3 C6 CE BD 3D 27 C1 F7 BD F0 CB EA D4 C2 D2 BB  称谓='流金岁月一
04F64690  D4 B1 27 20 D1 D5 C9 AB 32 3D 27 38 30 43 30 46  员' 颜色2='80C0F
04F646A0  46 27 20 C3 FB D7 D6 3D 27 74 65 73 74 64 62 79  F' 名字='testdby
04F646B0  6F 75 27 20 D1 D5 C9 AB 3D 27 30 30 46 46 30 30  ou' 颜色='00FF00
04F646C0  27 20 CB D9 B6 C8 3D 33 20 D5 BE C1 A2 3D 73 68  ' 速度=3 站立=sh
04F646D0  61 70 65 2F 63 68 61 72 2F 30 30 36 33 2F 73 74  ape/char/0063/st
04F646E0  61 6E 64 2E 74 63 70 20 D5 BE C1 A2 32 3D 73 68  and.tcp 站立2=sh
04F646F0  61 70 65 2F 63 68 61 72 2F 30 30 36 33 2F 73 74  ape/char/0063/st
04F64700  61 6E 64 32 2E 74 63 70 20 D0 D0 D7 DF 3D 73 68  and2.tcp 行走=sh
04F64710  61 70 65 2F 63 68 61 72 2F 30 30 36 33 2F 77 61  ape/char/0063/wa
04F64720  6C 6B 2E 74 63 70 20 B2 BD BE E0 3D 37 32 20 B2  lk.tcp 步距=72 
04F64730  BD CB D9 3D 32 34 20 C5 DC B2 BD 3D 73 68 61 70  剿?24 跑步=shap
04F64740  65 2F 63 68 61 72 2F 30 30 36 33 2F 72 75 6E 2E  e/char/0063/run.
04F64750  74 63 70 20 C5 DC B2 BD B2 BD BE E0 3D 31 30 38  tcp 跑步步距=108
04F64760  20 C5 DC B2 BD CB D9 B6 C8 3D 34 35 20 C5 E4 C9   跑步速度=45 配
04F64770  AB B7 BD B0 B8 3D 27 73 68 61 70 65 2F 63 68 61  桨?'shape/cha
04F64780  72 2F 30 30 36 33 2F 30 30 2E 70 70 27 00        r/0063/00.pp'.
哦,看见上面语句了吗?同学们,关键的东西来了,要搞破坏的赶快.跑步速度=45,要是修改掉这句话的这句参数的话,嘻嘻..一个外面跑步的加速挂就出现了..不过我们要的不是这种破坏式的,我们继续我们的.
看到这里,我们拿的是ID,那么我们取消掉刚刚的田间断,向下滚代码,找到RET就直接F9断,函数间隔用int3的,所以只要看到几个INT3就知道这个函数结束了.
1个返回点.继续,其实也可以用F8去单步,不过那么效率不够,嘻嘻..
0046D4DE  |.  8B4C24 1C     mov     ecx, dword ptr [esp+1C]
0046D4E2  |.  64:890D 00000>mov     dword ptr fs:[0], ecx
0046D4E9  |.  83C4 28       add     esp, 28
0046D4EC  |.  C2 0400       retn    4
0046D4EF  |>  2BF2          sub     esi, edx
2个..
0046D543  |.  5E            pop     esi
0046D544  |.  5D            pop     ebp
0046D545  |.  5B            pop     ebx
0046D546  |.  64:890D 00000>mov     dword ptr fs:[0], ecx
0046D54D  |.  83C4 28       add     esp, 28
0046D550  \.  C2 0400       retn    4
0046D553      CC            int3
0046D554      CC            int3
看见了int3了没?这个函数结束了,也就是返回点是这两个,我们Shift+f9跑起来.断到返回,F8继续跟出去.跟到哪里?记得上面的按钮和T按钮吗?
我们跟到函数返回那个位置出现浮点指令,那么那里的就是模型ID了,可以从那里获取到,嘻嘻..经过几次的ret,我们跟到了这里.
00480940   .  50            push    eax
00480941   .  56            push    esi
00480942   .  57            push    edi
00480943   .  E8 183FFEFF   call    00464860
00480948   .  894424 1C     mov     dword ptr [esp+1C], eax
0048094C   .  DB4424 1C     fild    dword ptr [esp+1C]
00480950   .  A1 3CA86200   mov     eax, dword ptr [62A83C]
00480955   .  83C4 0C       add     esp, 0C
00480958   .  DD5C24 10     fstp    qword ptr [esp+10]
0048095C   .  DD4424 10     fld     qword ptr [esp+10]
00480960   .  DD1C24        fstp    qword ptr [esp]
00480963   .  50            push    eax
00480964   .  E8 B7080200   call    004A1220
这里什么意思呢? 对比上面的按钮处理方式,这里就是保存人物ID的位置,mov     dword ptr [esp+1C], eax,也就是eax就是我们的角色ID.
为了验证一下?好,之前我们调试过哪个左键点击ID那里有ID值的,我们来下个断,点击一下自己的角色,哦,没错了吧?点击角色的ID就是上面返回的ID.到这里,上面的点,多次测试,是每个模型加载必进的定位点,也就是NPC的ID或者其它玩家的ID也在那里返回.
如何判断?名字='testdbyou' 他都告诉你了,汗.这还判断不了?人物ID我们得到了,接着是人物的移动问题,我们继续分析之前的log记录,发现
0046D2B5  COND: 0579F73C "终点X=489 终点Y=1841 跑=1"
在测试一次,这次用走路的,结果是'跑=0'.....
这个..角色移动的目标位置?还是猜测.试试吧,条件断在'终点',跟到外面看看它是干嘛的.
条件为dword ptr [edx] == 0e3b5d5d6
进去游戏走一步..断了...我们还是上面那招,让程序跑到返回那里
这次不像刚刚那样有先前的指令点,我们每返回一次,就在它的那个CALL那里下一个断点.大概返回3-4个吧,就够了,直接让程序跑起来.如果程序不断断在我们下断的CALL那里,我们就把断点去掉,排除掉那个走路的关键点,接着,排除得出只有一个CALL是要点击走路时才进入的,OK,开始分析.
看堆栈.也就是看这个CALL的压入参数和寄存器.发现了,
004813B1   .  55            push    ebp
004813B2   .  8D4C24 14     lea     ecx, dword ptr [esp+14]
004813B6   .  51            push    ecx
004813B7   .  51            push    ecx
004813B8   .  8BC4          mov     eax, esp
004813BA   .  896424 20     mov     dword ptr [esp+20], esp
004813BE   .  51            push    ecx
004813BF   .  8938          mov     dword ptr [eax], edi
004813C1   .  8BC4          mov     eax, esp
004813C3   .  8930          mov     dword ptr [eax], esi
004813C5   .  8B0D 403A6000 mov     ecx, dword ptr [603A40]
004813CB   .  C74424 30 000>mov     dword ptr [esp+30], 0
004813D3   .  896424 24     mov     dword ptr [esp+24], esp
004813D7   .  E8 9429FFFF   call    00473D70
堆栈
0012F8EC   5131B483
0012F8F0   B8FA9C1F
0012F8F4   0012F90C
0012F8F8   02201A90
压入了4个参数,看,第1个参数就是我们的角色ID,接着跟进看看其它参数,发现第3个参数.里面dword的指向值就是.
05941BEC  D6 D5 B5 E3 58 3D 31 30 33 31 20 D6 D5 B5 E3 59  终点X=1031 终点Y
05941BFC  3D 32 36 36 38 20 C5 DC 3D 31                    =2668 跑=1.
好了,到这里已经知道角色的跑位值,接下来,我们试试修改终点值,看看它能不能跑到预想的位置,,我改成0看看,哦,可以,不断向地图边缘跑,不过由于游戏的关系,在场景卡死了,哈哈.没问题,知道这里是控制角色移动的就行,经过多次断,发现其它角色的移动也是通过这里的
我们只要判断第1个参数是不是我们需要的ID,接着更改路线就行了.
跑的部分完成.这个游戏要做自动的话要从人物提示那里获取x,y值,但那些x,y和这个终点的坐标不同,差距值是乘20,Y值的对应是逆向的,发挥你们的智慧吧,要解决是很简单的.
跑,对话,两部分已经完成,差不多要写个测试程序去看看成果了,接下来,我们来搜索定位内存和给程序打补丁.
送大家一段我自己的内存定位搜索代码,用SEH错误处理搜索的,来,上代码,不止用在这程序,用在其它用途也很大的.
__________________________________________________________________________________________________________
SStart    dd  401000h
_Handler  proc  _lpExceptionRecord,_lpSEH,_lpContext,_lpDispatcherContext
    pushad
    mov  edi,_lpContext
    mov  eax,pERRORGO
    mov  [edi.CONTEXT].regEip,eax
    mov  VSize,999h
    popad
    mov  eax,ExceptionContinueExecution
    ret
_Handler  endp
_SMem    proc  _pV1,_Size  ;ebx返回搜索开始位置,edi返回搜索结束位置+1   by:nowhy
    
    assume  fs:nothing
    push  offset _Handler
    push  fs:[0]
    mov  fs:[0],esp
    lea  eax,_ERRORTONEXT
    mov  pERRORGO,eax
    mov  ebx,SStart
    _ERRORTONEXT:
    add  ebx,VSize
    mov  VSize,0
    _NEXTGO1:
    mov  ecx,_Size
    mov  esi,_pV1
    inc  ebx
    mov  edi,ebx
    _NEXTCMP:
    mov  al,byte ptr [edi]
    cmp  al,byte ptr [esi]
    jnz  _NEXTGO1
    inc  esi
    inc  edi
    dec  ecx
    test  ecx,ecx
    jnz  _NEXTCMP
    pop  fs:[0]
    pop  eax
  ret
_SMem endp
_____________________________________________________________________________________________________________
调用方式,_pV1指向要搜索的内存判断地址,_Size为该段的长度,由于没写搜索失败后的参数返回.所以小心使用,哈哈.上面有说明返回值的返回方式
上面代码慢慢消化吧,不算复杂. 接着关键点<如何在程序中准确定位到内存>
我就拿调试的'按钮'那里开始说明.开OD,看代码.介绍我的变态定位方式.
00475654  |.  E8 37BA0200   call    004A1090
00475659  |.  8B4C24 4C     mov     ecx, dword ptr [esp+4C]
0047565D  |.  83C4 34       add     esp, 34
00475660  |.  53            push    ebx
00475661  |.  6A 07         push    7
00475663  |.  51            push    ecx
00475664  |.  894424 30     mov     dword ptr [esp+30], eax
00475668  |.  E8 23BA0200   call    004A1090
0047566D  |.  8B5424 28     mov     edx, dword ptr [esp+28]
00475671  |.  8B4C24 30     mov     ecx, dword ptr [esp+30]
00475675  |.  83C4 08       add     esp, 8
00475678  |.  50            push    eax
00475679  |.  8B4424 28     mov     eax, dword ptr [esp+28]
0047567D  |.  52            push    edx
0047567E  |.  57            push    edi
0047567F  |.  56            push    esi
00475680  |.  50            push    eax
00475681  |.  51            push    ecx
00475682  |.  8D5424 2C     lea     edx, dword ptr [esp+2C]
00475686  |.  68 68B15500   push    0055B168
0047568B  |.  52            push    edx
0047568C  |.  E8 2F900400   call    004BE6C0
00475691  |.  83C4 24       add     esp, 24
00475694  |.  8D4C24 28     lea     ecx, dword ptr [esp+28]
00475698  |.  C64424 6C 01  mov     byte ptr [esp+6C], 1
0047569D  |.  E8 6E840400   call    004BDB10
就上面那段说明,0047568C 我们要定位到这里,怎么定位好呢? 我给的建议是,避开全局变量,因为重新编译程序那会改动的几率太大了,接着避开局部变量,也就是lea     edx, dword ptr [esp+2C]这种,那里函数定义的局部变量一改变就变化了,
那么,我们定位的主要就只要.
0047565D  |.  83C4 34       add     esp, 34
00475660  |.  53            push    ebx
00475661  |.  6A 07         push    7
00475663  |.  51            push    ecx
这里的几个字节,对于程序来说,是不容易改变的. 
83 C4 34 53 6A 07 51
我们用这个来CALL上面的搜索函数.
incoke  _SMem,lp,7
返回值ebx是搜索开始的点,edi是搜索结束也就是搜索内存尾部的位置.直接edi+28h 那样的话,就能定位到0047568C 如果实在找不到那些指令点,看见了我上面定义的那个SStart,按照出现次数压入覆盖调用就能定位到了
接着要做什么?呵呵,对它写入呢...把它的CALL HOOK到我们的地址.我随便说明一下,
call    004BE6C0
比如要HOOK这个地址.我们的替换函数地址为edi,这个地址位置为ebx.
mov  eax,dword ptr [ebx+1]
lea  eax,dword ptr [eax+ebx+5]
这样就能从地址得到它的目标要CALL的地址,eax=4BE6C0,将EAX到HOOK函数返回地址那里位置,预留5字节,先0e9h(jmp)之后计算目标位置.填充进后面的4字节,那么程序HOOK处理完成后就JMP跳到游戏原来CALL的位置.
接着把CALL的地址替换为我们的处理地址位置.
sub  edi,ebx
sub  edi,5
mov  dword ptr [ebx+1],edi
接着程序跑到我们HOOK的地址,该干嘛干嘛去,记得寄存器不要改变,堆栈也是.
pushad
处理....
popad
db 0e9h
dd 0  ;这句是返回计算压入的位置,就是上面eax计算得出的
搞掂...那么大家努力,明天给大家发针对这个游戏更加细化的处理方式.
喜欢汇编的理由?? 够自由啊,,我爱怎么改就怎么改,喜欢怎么搞寄存器就怎么搞.比那些高级语言的自由度大多了,总体来说也比高级语言更简单,直接,有人要反驳我也没办法.我只是想简单明白的知道程序到底在干什么,要怎么做,有可能出现哪些问题.
这些高级语言是做不到的.不过要考虑的也比高级语言的多很多,所以,个人喜欢啦.哈哈. 其实我也会用C/C++来写程序.

注意:上传附件及图片大小不得大于30M。

⚠️ 版权声明:
本博客所有内容(含教程、源码、工具)仅供个人技术学习与研究交流使用,严禁商用、倒卖、二次分发及非法用途。
未经作者书面授权,任何组织或个人不得转载、复制或用于其他平台,违者将追究相关责任。

0 0 0 举报
复制成功