Chinaunix首页 | 论坛 | 博客
  • 博客访问: 248641
  • 博文数量: 65
  • 博客积分: 2758
  • 博客等级: 少校
  • 技术积分: 725
  • 用 户 组: 普通用户
  • 注册时间: 2006-02-25 00:23
文章分类

全部博文(65)

文章存档

2011年(4)

2010年(1)

2009年(60)

我的朋友

分类:

2009-11-19 15:22:36

作者:重楼
时间:2009.08.20


目标:游戏找CALL练习实例ONE

记:看这篇找CALL文章,并不是说要学会它的操作步骤,而是学会找步骤的原理,只有懂得原理,才算真正会了.不要因为模拟器的简单 而忽略这篇文章,也不要因为曾经写过这个模拟器的挂 而忽略这篇文章.希望这篇文章能给你带来帮助.


 

这是一位大牛做的模拟器器,今天就来找这个 模拟器的CALL

用OD 加载 模拟器

然后按F9 运行

下bp send 断点


 

P:为什么要下 send 断点?

*send 是微软提供的一个API 函数,可以用来发送数据包.  绝大部分游戏都是用这个函数来发包的,其他还有 WSASend  sendto  WSASendto  ==  send对应的收包函数是 recv  WSASend对应的收包函数是 WSARecv


下段 后 这里按 ALT+B  会显示 已经下段的 地址


 

*当你暂时不想断下来 而又不想删掉 就可以 按 空格 来禁止.

断下来后,我们点下  模拟器的 加血 按钮  OD立马就断了下来


 

*标题显示 模块- ws2_32  表明 我们还在系统领空
*游戏发送数据包是调用send函数 发送的,掉用以后 程序告诉系统 我要发包了,然后系统就开发发送封包,这个时候断下来后 我们就在 系统发送完封包 后.


按 CTRL+F9  返回

P:为什么要用 CTRL+F9 返回?而不是 前进?

*程序是 一层套着一层的,就像一个箱子 里面 包含一个箱子  ,而里面的箱子里又有一个箱子.而我们断的send  就是在最里面的箱子,所以我们需要返回到 我们所需要的代码层.




 



我们来看下 堆栈窗口

这里 第一行是  CALL
      第二行到第五行 是 CALL的 参数
写成函数就是 send(soket,data,datasize,flags)

这个就是 系统send所需要的参数 
DATA 这里存放着 send发送的封包内容
DATASIZE  表示 封包的大小


 

从MSDN查看一下 函数的参数 我们会 发现 他的参数 跟我们刚刚反汇编的一样.





 


*按照 __cdecl调用约定  参数 是从 右边 开始压入堆栈 


继续  返回

这里 我们已经到了 程序领空了  (标题显示 模拟器,而没有显示ws2_32)
在这里 OD已经 给我们标明了. 这是调用send 的汇编代码
因为这个模拟器并没有写接收 返回封包的 代码 所以 我们调用这个send 函数程序也是没有反应的

继续返回


 

这里有一个CALL  如果我们第一次找,我们并不能确认这个是否 是我们要找的

P:如何确认这个是否是我们需要的CALL呢?


我们先断下来 再说

继续返回



 

这里上面 有一个 retn 

*retn  表示  一段程序的结束.

这里从  JMP 00403814  开始 到下面的retn  代表 这个是连续的一段代码


继续返回



 

这里 我们又发现一个CALL


先断下来再说

在返回 一层



 

这里也有一个CALL  也断下来

好了  我们现在已经返回了6层 找到了3个CALL

到底哪个是我们需要找的呢 我们先来测试下我们找的

把 send 断点 删了 暂时没用了

按下 加血 我们发现 所有断点  都会断,  这个时候 我们发现 第二个CALL 附近有 "血"这种文本

当然一般的除了喊话CALL 以外不会有很明确的数值当做依据 这个时候就要靠 你的经验 去猜了.

我们在 点下 吃蓝  发现 只断下 第一个. 好了,第一个先不要管了.为啥?猜的...


好了 我们来看看第二个CALL

mov edx,00453028
call 00452E98
retn



*要写一个CALL,我们就要模拟出他所需要的寄存器,还有堆栈的环境

P:如果看一个CALL所 需要的 寄存器?

我们进入 call 00452E98  的内部

选中 call 00452E98  那行 按 回车 就会 跳到下面


00452E98  /$  55            push    ebp
00452E99  |.  8BEC          mov    ebp, esp
00452E9B  |.  83C4 F8      add    esp, -8
00452E9E  |.  53            push    ebx
00452E9F  |.  8955 FC      mov    dword ptr [ebp-4], edx
00452EA2  |.  8BD8          mov    ebx, eax
00452EA4  |.  8B45 FC      mov    eax, dword ptr [ebp-4]
00452EA7  |.  E8 1414FBFF  call    004042C0
00452EAC  |.  33C0          xor    eax, eax
00452EAE  |.  55            push    ebp
00452EAF  |.  68 7E2F4500  push    00452F7E
00452EB4  |.  64:FF30      push    dword ptr fs:[eax]
00452EB7  |.  64:8920      mov    dword ptr fs:[eax], esp
00452EBA  |.  8B45 FC      mov    eax, dword ptr [ebp-4]
00452EBD  |.  BA 942F4500  mov    edx, 00452F94
00452EC2  |.  E8 5513FBFF  call    0040421C


00452E98  /$  55            push    ebp
00452E99  |.  8BEC          mov    ebp, esp
00452E9B  |.  83C4 F8      add    esp, -8
00452E9E  |.  53            push    ebx


这里是 保存 堆栈环境  我们先不管他

00452E9F  |.  8955 FC      mov    dword ptr [ebp-4], edx


将 EDX 保存到 [EBP-4]
我们来找下 EDX的值


这段汇编代码 前面 没有给 EDX赋值  我们返回上一层  按小键盘  -

上一层 

mov edx,00453028

这句代码的意思是  将 453028赋值给EDX
也就是说EDX=00453028

找到EDX 的值后 继续往下面找


00452EA2  |.  8BD8          mov    ebx, eax

这里需要EAX的值,但是 我们找了这一层和上一层 并没有发现有给EAX赋值的代码

我们这里 先直接给EAX赋值

其他没有了

好了 我们现在可以确认了  这个CALL 调用了 EDX 和EAX的寄存器的值


 

这个CALL的写法就是 


mov edx,00453028
mov eax,00991FA8
call 00452E98



我们来测试下


 


CALL成功了

但是,我们拿到另外一台电脑  发现 居然不能用了?  调试了下 发现 EAX的值 跟刚刚的不一样~


P:如何取到 EAX的固定值呢?

答案很简单 用CE搜


 


看到那个绿色的值了么 那个就是 EAX的基址 ,无论 EAX的值 怎么变 都可以在这个地址读取到真正的值

代码如下

mov edx,00453028
mov eax,456d68
mov eax,[eax]
call 00452E98


好了  现在 可以再任意一台电脑上运行了



=======================================================



好了  先不管这个CALL


我们现在来找下 EAX的值。

在  call 00452E98  下断  按下 加血 OD 断下来了

然后我们按 CTRL+F9 返回

好了 到了这里



 


mov edx,ebx
mov eax,[ebx+124]
call [ebx+120]

这个时候 我们发现  EAX的值

EAX=[ebx+124]


我们发现 CALL地址 并不是 是直接的地址。



在这个CALL 下断  点下 加血 按钮

我们发现  [EBX+124]=[0099493C]=00991FA8
EAX=991FA8
刚刚 我们找过 EAX的基址了 

[ebx+120]=[00994938]=00453014

这里的CALL地址 写成代码是

mov eax,456d68
mov eax,[eax]
call 453014




 

CALL成功了`

P:为什么2个不同地址的CALL都会成功呢?




经过不断测试(如何测试?点击不同的按钮 看看CALL的地址)

发现 不同按钮断下来的地址 都是不同的。



 

补魔的地址



 

冰系的地址




==========


我们来测试下第三个CALL




 


mov edx,[ebx+214]
mov [eax+24c],edx
mov eax,ebx
call 4324d8



调试过程中 
[EBX+214]=0  那么 edx的值 =0

[EAX+24C]=EDX=0
EAX=00991FA8

EAX=EBX=994818
CALL 4324d8


寄存器的值都搞清楚了 然后 我们看看 CALL还调用了哪个寄存器

跟进CALL  (按 F7 进入CALL )


004324D8  /$  53            push    ebx
004324D9  |.  8BD8          mov    ebx, eax
004324DB  |.  66:83BB 22010>cmp    word ptr [ebx+122], 0
004324E3  |.  74 2D        je      short 00432512
004324E5  |.  8BC3          mov    eax, ebx
004324E7  |.  8B10          mov    edx, dword ptr [eax]
004324E9  |.  FF52 3C      call    dword ptr [edx+3C]
004324EC  |.  85C0          test    eax, eax
004324EE  |.  74 22        je      short 00432512
004324F0  |.  8BC3          mov    eax, ebx
004324F2  |.  8B10          mov    edx, dword ptr [eax]


004324E5  |.  8BC3          mov    eax, ebx
这里有一行 调用了 EBX  我们往上找找 发现 有给EBX 赋值的代码  那么 我们就不必理会了

004324E7  |.  8B10          mov    edx, dword ptr [eax]
这里调用了 EAX  在上一层 有给EAX赋值的 代码

下面就没了

好了  用代码注入器 写CALL



mov edx,[ebx+214]      ,[EBX+214]=0

那么 第一句就是 
mov edx,0

mov [eax+24c],edx  ,edx的值 =0  EAX=00991FA8
那么 第二句 就是 MOV [EAX+24C],0  但是 注入器不通过 这个时候 我们换一种写法
首先给 EAX赋值  mov eax,991FA8  这里 我们刚刚找过 这个基址 456d68 是EAX的基址

首先放入 基址的值 mov eax,456d68
然后在读取 基址  mov eax,[eax]
然后 加上偏移 24C  add [eax],24c   

地址写好后 我们把 EDX放到 地址里去 mov [eax],edx 

mov eax,ebx第三句  EBX=994818
因为 CALL没有调用EBX 所以我们不必给ebx  赋值
mov eax,994818


call 4324d8


放到一起 那么就是

mov edx,0
mov eax,456d68
mov eax,[eax]
add eax,24c
mov [eax],edx
mov eax,994818
call 4324d8



 


好了 CALL成功了~

经过调试 发现 不同的 按钮  他的EBX里的值也不一样


好了  我们发现这也可以调用



提问:

P:为什么3个不同地址的CALL都会成功呢?

总结

*retn  表示  一段程序的结束
*send 是微软提供的一个API 函数,可以用来发送数据包.  绝大部分游戏都是用这个函数来发包的,其他还有 WSASend  sendto  WSASendto  ==  send对应的收包函数是 recv  WSASend对应的收包函数是 WSARecv
*要写一个CALL,我们就要模拟出他所需要的寄存器,还有堆栈的环境
阅读(1350) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~