都不够给力,我来个给力的,手把手一起写现在某热门游戏的跑钱外挂(三)
都不够给力,我来个给力的,手把手一起写现在某热门游戏的跑钱外挂(三)
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++来写程序.
