寻找CALL的教程

合集下载
  1. 1、下载文档前请自行甄别文档内容的完整性,平台不提供额外的编辑、内容补充、找答案等附加服务。
  2. 2、"仅部分预览"的文档,不可在线预览部分如存在完整性等问题,可反馈申请退款(可完整预览的文档不适用该条件!)。
  3. 3、如文档侵犯您的权益,请联系客服反馈,我们会尽快为您处理(人工客服工作时间:9:00-18:30)。

寻找CALL的教程
2009-04-03 12:49
说一说一个简单的找call原理
其实游戏中的call是有一定规律的,这个规律是什么?
1、调用之前,必定有call名入栈,什么,不知道如何看堆栈有哪些内容。

很简单啊,按alt+k就看到了
2、系统进程空间,一般都可以忽略,那么,哪些是系统进程呢,我自己也分不太清,但是NTDLL,USER32,WS_S32等一般都是系统进程空间,在od的状态栏上就可以分辨出来。

举个例子:ZX中找死亡回程call
一、在弄死小号后,先下了 BP SEND指令,od中断下来,按ALT+K,看到的堆栈情况是这样的:
地址堆栈函数过程 / 参数调用来自结构
029FFEC8 00572860 WS2_32.send elementc.0057285A
029FFECC 000006BC Socket = 6BC
029FFED0 06D1F2A8 Data = 06D1F2A8
029FFED4 00000003 DataSize = 3
029FFED8 00000000 Flags = 0
029FFEE8 00578BE7 包含elementc.00572860 elementc.00578BE4
029FFEF0 00578827 elementc.00578BB0 elementc.00578822
029FFF38 005785DE ? elementc.00578640 elementc.005785D9
029FFF48 00577128 包含elementc.005785DE elementc.00577125
二、然后F9让游戏正常,再回到游戏中,按下“回城”
三、od中断下来,再按alt+K,看到的堆栈是另外一个样子了:
地址堆栈函数过程 / 参数调用来自结构
0012F3B4 00581775 ? elementc.005898B0 elementc.00581770
0012F3C4 00583F75 ? elementc.00581740 elementc.00583F70
0012F400 005A8CF6 ? elementc.00583ED0 elementc.005A8CF1
0012F410 0057E701 ? elementc.005A8CD0 elementc.0057E6FC <<<<<<<<<<<<< 0012F420 00509E80 elementc.0057E6C0 elementc.00509E7B
0012F424 006C6527 包含elementc.00509E80 elementc.006C6524
0012F444 006C647C elementc.006C64B0 elementc.006C6477
0012F45C 006C8F0E 包含elementc.006C647C elementc.006C8F0B
0012F464 0054A432 elementc.006C8F00 elementc.0054A42D
0012F47C 006C236B 包含elementc.0054A432 elementc.006C2368
0012F494 006C269B elementc.006C22C0 elementc.006C2696
0012F4E0 006C93AA ? elementc.006C2380 elementc.006C93A5
0012F55C 00549BBB ? elementc.006C8F50 elementc.00549BB6
四、如果用CTRL+F9跟踪,很容易发现,前三个都是在WS32系统进程空间,所
以,第4个,做了个标记的那里,就是死亡回程call了
希望大家多多实践,注意观察。

也许我的这个方法有问题,也请提出来大家讨论OD寻找CALL 希望对新手有用
关键Call的找法一直都是个不大不小让人头痛的问题,需要大量的汇编代码分析,还需要很多测试工作,当然还有一种变态的方法,那就是用OD断下后,把前前后后所有的call都测试一边,也能找到关键call,不过面对大量的call 和无数次挂游戏,恐怕这个方法只能在理论上实现了。

那么是否有既不需要看大量的汇编代码,有能够经过有限的几个测试找到关键call的方法呢,下面我就把一种另类的找法送给和我一样的懒人和新手吧。

以50打坐和普通攻击的call查找为例。

启动OD加载50,进入游戏后,和传统方法一样,bp send下断,然后等待游戏断一次,按F9直到游戏正常运行(这里等待断一次主要是为了去掉游戏定时与服务器信息和其它信息的干扰),然后马上回到游戏,按0(默认0是打坐),游戏被断下,连续按4次ctrl+F9(通常游戏的前3层都是信息函数等东西,所以直接到第4层啦),然后按F8,此时按alt+k打开堆栈窗口,如下:
调用堆栈
地址堆栈函数例程/ 参数调用来自框架
0012F524 004542DB ElementC.0059E270
ElementC.004542D6
0012F528 0049959B ElementC.004542C0
ElementC.00499596
0012F538 005410C4 包含ElementC.0049959B ElementC.0054 10C1
0012F550 00540E87 ElementC.00540ED0
ElementC.00540E82
1、有很多行,不过我们只需要关系第一行就可以了,其它的不用管,记录下0059E270,如果已经知道打坐的call地址,一看就知道我们已经找到了,不过现在我们架设是第一次找不知道关键call的地址,所以把0059E270记录下来,继续ctrl+F9,F8再进入一层,仍然是按alt+k,查看堆栈窗口,仍然只记录第一行得到004542C0,还是ctrl+F9,F8,alt+k再记录一个0049959B,一般来说游戏的call多在4-6层中,很少有再深的,而且还有另外的判断方法,因为单你再使用ctrl+F9进入下一层时,出现的call就不是单纯的地址了,而是类似call
[xxxx+xxx]这样的形式,那么也就说明走过了,所以记录3层就够了。

2、第一步完成了,此时按F9让游戏继续,又断下来了,不过这个时候不用急着按ctrl+f9进入,直接按一个alt+k看看,
调用堆栈
地址堆栈函数例程/ 参数调用来自框架
035CFEC8 00568A60 WS2_32.send
ElementC.00568A5A
035CFECC 00000500 Socket = 500
035CFED0 0C622008 Data = 0C622008
035CFED4 0000000D DataSize = D (13.)
035CFED8 00000000 Flags = 0
035CFEE8 0056DE37 包含ElementC.00568A60 ElementC.0056 DE34
第一行显示的是 ws2_32.send,原来是发送函数,所以不用再进入了,看一下CPU窗口的标题,显示的是CPU-t线程 0000000xxxx,记一下那个xxxx 的数字,直接按F9运行游戏,又断下来了,此时看一下CPU窗口的标题,如果xxxx数字一样,说明还是发送函数一类的东西不用进入(你可以都打开堆栈窗口看看验证下),继续按f9运行游戏,如果游戏断下来了而且不是发送函数一类的就都进入看一下,和上面的一样记录下地址,最后直到不再连续断,删除断点,回到游戏,人物已经进入打坐状态,一般简单的操作只会有1到2个需要跟进的断,而复杂的操作可能多一些,不过如果你从简单的call入手,注意观察,复杂操作的call还可以通过总结再去除掉很多无关的断。

3、第三步,好了只有三个地址,而且都没有参数,直接调用测试吧,运气不错第一个0059E270就是了。

呵呵,没有分析汇编代码找到了打坐的call
顺便找找取消打坐的call吧,仍然bp send,然后等游戏断一次,按F9直到游戏正常,然后进入游戏,用鼠标在其它地方点一下,游戏断了下来,4次ctrl+f9后按f8,alt+k,直接记录第一行,
调用堆栈
地址堆栈函数例程/ 参数调用来自框架
0F19FD88 00466D4C ElementC.0059E2B0
ElementC.00466D47
0F19FE2C 00455D8E ElementC.004668C0
ElementC.00455D89
0F19FE30 0F19FE7C Arg1 = 0F19FE7C
0F19FE38 005604D9 包含
ElementC.00455D8E ElementC.0056 04D6
有了经验就不再继续了,直接测试0059E2B0,果然就是取消打坐。

顺便说一下,注意上面的
0F19FE2C 00455D8E ElementC.004668C0
ElementC.00455D89
0F19FE30 0F19FE7C Arg1 = 0F19FE7C
这两行,如果是有参数的call,就是这样的形态,这里也就是call 004668c0含有一个参数。

普通攻击call的找法,和上面的类似,先选择一个怪,然后切换到OD,bp send,等游戏断一次,按F9直到恢复,然后按数字1(普通攻击默认在1),游戏断下,然后同样的方法,你可以找到
调用堆栈
地址堆栈函数例程/ 参数调用来自框架
0F19FDCC 00475F71 ElementC.0059DC30
ElementC.00475F6C
0F19FDEC 00475B8E ElementC.00475CE0
ElementC.00475B89
0F19FDF0 10B2A500 Arg1 = 10B2A500
0F19FE20 004639EC 包含ElementC.00475B8E ElementC.0046 39E9
0F19FE98 0043A572 包含ElementC.004639EC ElementC.0043 A56F
还是在第一行看到了吗?0059DC30,直接就找到了普通攻击的call。

带参数的call的找法,和普通攻击的相似,无非就是记录下来的地址可能会多一些,需要测试的也多一些,不过如果你能够对找出来的地址处的汇编代码进行简单的分析,那么也就还可以再排除大部分地址,减少测试量。

这就是利用堆栈进行关键call的另类找法。

本来打算弄点图上去的,不过因为我很懒,也就算了,大家多动手测试下,相信很快就能够掌握一些东西,然后我们多交流共同提高。

比如找怪的call,bp send,等游戏断一下,然后f9恢复运行,切换到游戏,用鼠标点任何一个怪,OD断下来,然后按照上面的方法,马上就可以在堆栈中找到
ElementC.00573800 在第一行,而且这行的下面就在堆栈中显示这个call的参数,类似
Arg1 = XXXXXXXX
Arg2 = XXXXXXXX
眼睛尖的马上就会发现,其中一个参数不就是我选的怪的ID嘛,另外一个就是相关的偏移,马上就可以确定这个就是call了。

拣物品、使用物品等也是类似的,全都是在第一行出现,其实原理很简单,因为是在堆栈里面,必须满足堆栈的处理原则,所以么先入后出,后入先出,呵呵。

如果是一个新游戏可能一开始会觉得不太好弄,不过因为大多数程序员都有共同的毛病就是只要功能类似肯定就会选择对某一类动作调用共同的函数进行操作,只是传入的参数不同,所以看多了,很快就可以对哪个才是我们需要的真正call 地址做出判断。

有时候还有额外的收获,比如说,查找使用技能的call,由于距离怪还有一定的距离,所以要先移动,然后才打,于是会断下很多,其中大部分是w32_send,这个直接判断标题栏就剔除了,而剩下的断中,就包含移动的和使用技能的,注意观察堆栈中调用的参数,只用看第一个调用,很意外的还顺便把移动的call找到了
CALL笔记(转)
首先先说一个概念 call游戏函数,首先必须进入游戏的进程中,就像要偷人家的东西必须进入人家的家[呵呵比喻有点不恰当],如何进入呢,参照论坛上的其他的帖子,方法基本有三个hook钩子注入dll 注入代码,我这主要是说的注入代码的方式,
所谓注入代码可以分成两步,一是在游戏进程中创建自己可以控制的内存空间,将代码写入]
二是,创建远程线程运行注入的代码;
[具体的方法]
一无参数的call
比如我们用OD 查找到打坐的Call是$0059A960,因为是无参数的call 我们就不需要写代码,也就不需要把代码注入到目标进程,直接可以执行第二步TempppHandle:= CreateRemoteThread(Hid, nil, 0, Pointer($0059A960,), nil, 0, num);
// 返回的句柄 := 创建远程线程并执行(目标句柄,nil,0,执行$0059A960函数,nil,0,num);
WaitForSingleObject(TempppHandle, INFINITE);//等待线程结束CloseHandle(TempppHandle); //关闭远程线程句柄[偷玩东西记得关门]
到这里我们就可以控制游戏中人物打坐了,
这部分的学习重点是要理解一个概念用CreateRemoteThread 是将我们的外挂程序与目标程序进行连接,通过CreateRemoteThread进入到游戏的进程从而可以在游戏中操作我们想操作的任何事,[进了人家的家,那你不是想干什么就干什么]
先写到这, 下次写带参数注入的方法和详细讲解
[带参数Call] 以武林的拾取物体为例子
注: 我的OD水平也比较差这里就不讲解关于OD找call的步骤了,有兴趣的朋友可以查阅网上的教程
1)我们用OD查找到拾取物品的call的原型
004758E0 8B0D C4C98E00 mov ecx, dword ptr [8EC9C4] ; ElementC.008F04B8 004758E6 8B97 10010000 mov edx, dword ptr [edi+110] [物品地址+110=物品的ID]
004758EC 8B46 20 mov eax, dword ptr [esi+20] [物品地址+10C=物品的序列号]
004758EF 52 push edx [压入物品ID]
004758F0 8B49 20 mov ecx, dword ptr [ecx+20]
004758F3 50 push eax [压入物品序列号]
004758F4 81C1 D4000000 add ecx, 0D4
004758FA E8 51A40F00 call 0056FD50
从原型看到,我们必须传入两个参数给汇编代码,如果传入两个参数呢?
首先我们把两个参数转换成一个数据结构
先定义以数据结构
type
PParam = ^TParam; // PParam指向TParam数据的指针
Tparam=packed record//数据结构包含了 id sn 这两个dwod类型的变量
id:dword;
sn:dword;
end;
现在我们先些汇编代码
function PickCall(p: pparam):DWORD; Stdcall;//拾取物体
var
edx1, eax1: DWORD; //定义两个变量来传递物品的ID和序列号
address:Pointer; //call地址
begin
address:=Pointer($0056FD50);//call的地址
edx1 := p^.id;//将函数参数的P的数据结构中的ID复制个 EDX1
eax1 := p^.sn;//将函数参数的P的数据结构中的SN复制个 EaX1
asm //汇编代码(不会汇编??没有关系对照上面的原型抄)
pushad
push edx1 //压入参数物品ID
push eax1 //压入参数物品序列号
mov ecx, dword ptr [$8EC9C4]
mov ecx, dword ptr [ecx+$20]
add ecx, $D4
call address //call游戏拾取函数
popad;
end;
result:=0;
end;
好的拾取物体函数写好了下步是如何将代码注入到游戏进程中执行??
1) 小偷开门
PHND:= OpenProcess (PROCESS_ALL_ACCESS, False, PID);得到游戏窗口句柄获
得权限
2)小偷在房间搞个放作案方案的地方
TAdd := VirtualAllocEx(PHND, nil, 4096, MEM_COMMIT, PAGE_EXECUTE_READWRITE)
在游戏进程中申请4096字节的空间用来存放函数代码
3)小偷把作案方案放入作案空间
WriteProcessMemory(TPHND, TAdd,PickCall,4096 , WriteCount);
把代码写到游戏进程中开始的地址是第二步得到的位置
4)小偷在房间中搞个放作案工具的地方
PAdd := VirtualAllocEx(PHND, nil, 128, MEM_COMMIT, PAGE_EXECUTE_READWRITE)
在游戏进程中申请128字节的空间用来存放参数
5)小偷把作案工具放入房间
WriteProcessMemory(TPHND, TAdd,param,128 , WriteCount);
写入参数
6)
一起就就绪开始作案
TmpHandle := CreateRemoteThread(TPHND, nil, 0, TAdd, padd, 0, WriteCount);
WaitForSingleObject(TmpHandle, INFINITE);//等待作案完成
CloseHandle(TmpHandle); //关闭远程的句柄作案完成后关门闪人
到这里基本都搞清楚注入代码的用法了,学习重点是
1)理解CreateRemoteThread函数各个参数的意义
2)数据结构的用法。

相关文档
最新文档