外挂制作CALL是如何炼成的之二:实践篇
外挂制作CALL是如何炼成的之二:实践篇
学习各种外挂制作技术,马上去百度搜索"魔鬼作坊"点击第一个站进入、快速成为做挂达人。
前言:遇到一个CALL应该如何写?
这个是写一个内挂不可避免的问题.刚初学的朋友可能会不知道如何入手.想起刚学这方面的时候,绕过很多弯路,现在把一些经验写出来给大家参考参考吧,不是很高深的东西,但我觉得对某些人很有帮助.
写CALL的步骤!
我之前说过写一个程序的CALL其实就2个步骤,第一:找到相关地址,第二:传入适当参数.只要这2点你做到了这个CALL便能调用了.
其实写CALL就更简单了.只要传入适当的参数调用这个地址便可以了.
这里地址我们找到了,所以我们所做的就是传入适当的参数.
什么是适当的参数?
就是CALL所需要的数据.大家都知道,在WINDOW系统里,数据的传递是靠寄存器和堆栈.其中寄存器用的最多的指令就是MOV指令.
而堆栈则是用PUSH指令.通过[esp+*]或者先mov ebp,esp然后[ebp+*]指向堆栈数据.
学习各种外挂制作技术,马上去百度搜索"魔鬼作坊"点击第一个站进入、快速成为做挂达人。
说了那么多其实本文就是围绕着一个中心点来讲解,那就是教你如何构建一个CALL所需要的参数环境.
例子一:
这是某游戏的一个CALL.我们来看看CALL的内部
写一个CALL,首先要写堆栈.这里从调用CALL的图中我们可以看出只有一个堆栈,那么就是
push ecx
call5FA410
然后看调用的寄存器.
如何看CALL内部调用了哪些寄存器呢?
首先,明确一点,寄存器本身是空的.他并没有数据,只是一个用来存放数据的空间.
假如我们直接调用这个CALL那么,寄存器中,数据不是为0就是运行上一个CALL残留的数据.这些残留的数据我们暂且把他看成0.
那么调用这个CALL的时候寄存器都是0了.当然2个特殊的寄存器除外,一个是ESP 一个是EIP.
好了现在CPU执行call5FA410后
首先把当前的EIP压入堆栈,也就是call5FA410当前地址的下一条指令的地址.然后JMP5FA410
这个时候,假设所有寄存器值都为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
call5FA410
=============================
例子二:
从图中我们可以看出这个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
call5BD150
add esp,4
我们来看看ECX的值,1615d00
那么是不是就是push1615d00呢?
的确,压入这个值CALL是不会崩溃,但这个绝对不是我想要的答案.
记住:调用CALL就是传入适当的参数.也就是需要我们构造一个CALL所需要的参数环境
这里我们只有一个参数.我们来看看CALL内部中调用这个参数的指令
mov al,[esp+8]
al大家看过汇编都知道,这个是EAX的低位1个字节.也就是最大数字是FF
从这句话我们可以得知,这里读取了PUSH ECX的低位数的1个字节的数据!
既然只调用了一个字节的数据,我们为何要压入4字节的数据呢?
所以这里ECX数据是1615d00低位1字节数据也就是00
所以写成CALL就是
push0
call5BD150
add esp,4
学习各种外挂制作技术,马上去百度搜索"魔鬼作坊"点击第一个站进入、快速成为做挂达人。