CALL是如何炼成的 之二:实践篇

前言:遇到一个CALL应该如何写? 
 
这个是写一个内挂不可避免的问题.刚初学的朋友可能会不知道如何入手.想起刚学这方面的时候,绕过很多
弯路,现在把一些经验写出来给大家参考参考吧,不是很高深的东西,但我觉得对某些人很有帮助. 
 
写CALL的步骤! 
 
我之前说过写一个程序的CALL其实就2个步骤,第一:找到相关地址,第二:传入适当参数.只要这2点你做到
了 这个CALL便能调用了. 
 
其实写CALL就更简单了. 只要传入适当的参数调用这个地址便可以了. 
 
这里 地址我们找到了,所以我们所做的就是传入适当的参数. 
 
什么是适当的参数? 
 
就是CALL所需要的数据.  大家都知道,在WINDOW系统里,数据的传递是靠 寄存器和堆栈.其中寄存器用
的最多的指令就是 MOV 指令. 
 
而堆栈则是用PUSH指令.通过[esp+*] 或者先mov ebp,esp  然后[ebp+*] 指向堆栈数据. 
 
 
说了那么多其实本文就是围绕着一个中心点来讲解,那就是教你如何构
建一个CALL所需要的参数环境. 
 
例子一: 

 

这是某游戏的一个CALL.我们来看看CALL的内部

 

写一个CALL,首先要写堆栈.这里从调用CALL的图中我们可以看出只有一个堆栈,那么就是 
 
push ecx 
call 5FA410 
 
然后看调用的寄存器. 
 
如何看CALL内部调用了哪些寄存器呢? 
 
首先,明确一点,寄存器本身是空的.他并没有数据,只是一个用来存放数据的空间. 
 
假如我们直接调用这个CALL  那么,寄存器中,数据不是为0就是运行上一个CALL残留的数据.这些残留的
数据我们暂且把他看成0. 
 
 
那么 调用这个CALL的时候 寄存器 都是0了.  当然2个特殊的寄存器除外,一个是ESP 一个是EIP. 
好了 现在CPU执行 call 5FA410  后  
 
首先把当前的EIP压入堆栈, 也就是 call 5FA410 当前地址的下一条指令的地址.然后 JMP 5FA410  
 
这个时候,假设所有寄存器值都为0 
 
有哪些指令会读取寄存器的值呢?  最常见的就是mov  , push , lea , 其实很多汇编指令都会读取寄存器
 比如说 add ebx,eax 
 
读取EAX的值 加上EBX 所得的值放入EBX里. 
 
第一条指令为 push ebx 
 
我们刚刚说过push 也是传递寄存器的一种指令.它读取了EBX的值 开辟一个堆栈空间 也就是指令 sub
esp,4 然后存放. 
 
这里EBX是不是我们所说的CALL所需要寄存器呢? 
 
其实这里不是的.这里涉及到一个寄存器环境保护的机制. 
 
什么是寄存器环境保护机制? 
 
当ECX存放着一个重要的数据时候 ,这个时候需要运行一个CALL,而CALL的内部需要用ECX存放东西.
那么原有的ECX重要数据该怎么办? 
 
这个时候,肯定要找一个空间存放起来,等CALL内部临时用完寄存器后在放回. 
 
这就好像你家里有一个仓库, 堆满了玉米,但你邻居家要用你的仓库临时存放大米.你没办法,只好先将玉米
放入一个临时空间.然后给你邻居家使用.用完之后你在放回去. 
 
这里的临时空间就是指window系统里的堆栈.首先PUSH EBX 把数据保存到堆栈里,等下面的指令使用完
寄存器,然后POP EBX. 
 
在尾部我们可以看到 有POP EBX 对应上面的PUSH EBX. 
 
这里我们发现 下面也有几个PUSH 指令 
push ebx 
push ebp 
push esi 
push edi 
 
在CALL尾部我们可以看到 
 
pop edi 
pop esi  
pop ebp 
pop ebx 
 
 
这里的4个寄存器就是我上面指的 寄存器环境保护.所以 这4个寄存器就可以被排除了. 
 
然后是第二句,mov ebx,[esp+8] 
 
讲调用CALL之前的最后一个堆栈读取并存放到EBX.  呵呵 ,刚刚把寄存器里的数据存放这里就被用到了,
指令执行完后 原有的EBX数据被覆盖. 
 
所以这里的EBX 是被用来当做临时空间来使用的. 
 
mov esi,[ebx+a8] 
 
这里的EBX,上面那条指令已经赋值了,所以这里的EBX就不用理会了,[EBX+A8] 读取后存放到ESI,这里的
ESI也是用来当做临时空间使用. 
 
XOR EBP,EBP 
 
EBP置0. 即使没有上面的PUSH EBP  只要遇到这种指令 EBP也是被用来当做临时空间的而不是当做
参数传递的. 你都清0了 还怎么传递参数? 
 
下面就是一个对比,然后一个CALL了. 
 
这些都不是我们改理会的东西. 
 
最后我们来看看 MOV [ESP+14],EAX 
 
这一句调用了EAX的值.如果之前没有赋值的情况下,也就是我们假设等于0的情况下  ,那么EAX就是一
个参数传递的寄存器.而这里我们在上面发现了 
lea eax,[EBX+c]  执行完这条指令后 EAX的值便不是空的了.既然不是空的也就是不是参数传递的寄存
器. 
 
 
下面的MOV [ESP+14],ECX  也是如此. 
 
上面有一条指令给寄存器赋值了. 
 
可以这么说 ,在假设寄存器都是空的情况下,  CALL内部调用了空的寄存器那么这个寄存器就是参数传递
的指令. 
 
从这里我们可以看出,整个CALL的内部都没有调用寄存器.也就是没有用寄存器传递参数,故这个CALL的
写法就是 
 
 
 
push ecx 
call 5FA410 
 
 
 
==================================================================== 

例子二:

从图中我们可以看出这个CALL是一个比较好写的CALL 
 
首先我们来看堆栈部分.拿到一个CALL,首先处理堆栈,从图中我们可以看出这个CALL只有一个堆栈. 
 
从 mov al,[esp+8]我们可以看出 这里的 [ESP+8]指向了  CALL上面压入的堆栈PUSH ECX 
 
处理完堆栈后,我们来看看 寄存器的处理. 
 
首先 PUSH ESI   我们可以看到尾部有POP ESI 相对应. 所以这个ESI是环境数据保护的寄存器,用来被
CALL内部作为临时寄存器使用. 
 
下面的MOV ESI,EAX  中的EAX则是上面一个CALL的返回值.所以不必要做考虑 
 
而 mov [esi+2],al  中 EAX则是由上面的指令mov al,[esp+8]  赋值.所以这里是临时寄存器. 
 
而 mov ecx,[ecx+20]  中的ECX则由上面的 指令赋值了.所以这里也不用考虑. 
 
从上面的分析得到,这里我们不需要任何寄存器. 
 
然后我们来看看,CALL尾部 ,是RETN,  后面没有跟随数字,这里没有自动平衡堆栈.所以这里需要我们来加
上恢复指令恢复堆栈 
 
所以这个CALL的写法是 
push ecx 
call   5BD150 
add esp,4 
 
我们来看看ECX的值,1615d00 
 
那么是不是就是 push 1615d00呢 ? 
 
的确,压入这个值CALL是不会崩溃,但这个绝对不是我想要的答案. 
记住:调用CALL就是传入适当的参数.也就是需要我们构造一个CALL所需要的参数环境 
 
这里我们只有一个参数.我们来看看  CALL内部中调用这个参数的指令 
 
mov al,[esp+8] 
 
al 大家看过汇编都知道,这个是 EAX的低位 1个字节.也就是最大数字是 FF 
 
从这句话我们可以得知,这里读取了 PUSH ECX 的低位数的1个字节的数据! 
 
既然只调用了一个字节的数据 ,我们为何要压入4字节的数据呢? 
 
所以这里ECX数据是 1615d00  低位1字节数据也就是 00 
 
所以写成CALL就是  
 
push 0 
call   5BD150 
add esp,4 

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值