注册 登录  
 加关注
   显示下一条  |  关闭
温馨提示!由于新浪微博认证机制调整,您的新浪微博帐号绑定已过期,请重新绑定!立即重新绑定新浪微博》  |  关闭

风雨夜归人

专业收集资料,个人爱好!

 
 
 

日志

 
 

【原创】代码注入实例教程  

2009-05-19 16:12:45|  分类: CCB的教程 |  标签: |举报 |字号 订阅

  下载LOFTER 我的照片书  |

代码注入实例教程

本教程由CCB原创,转载时请征得本人同意并注明。

代码注入(CODE INJECTION)是对付动态内存分配(DMA)游戏的常用方法。这个教程目的是想让你了解有关代码注入方面的相关知识,并以CE的TUTORIAL为实例来说明一下实际的操作。

选择CE的TUT来做实例是因为大家手头上基本上都有CE,并且TUT第六步使用的就是DMA,这样可以以最简单的方法让大家最快地接触和理解代码注入的原理和实际操作。这个教程面向那些已经掌握了CE操作的人,特别是按TUT的要求做完CE的教程的,并且要有一定的汇编语言基础。如果你还不能熟悉CE的操作,或者CE的教程你还没完成,或者你完全不懂汇编,可能很难看懂,请先去学些基础,学好了再来看这个教程。

我手头上用的是CE4.4,大家可能都在用5.2了吧,不过没关系,基本上都一样。好了,让我们开始吧!

第一步:

运行CE,运行TUT,然后输入密码098712直接进到第六步。

现在我们先来找TUT窗口上那个数值的地址,找到地址后,象原来做教程第六步一样,找到改写这个地址的指令。这一次,这条指令的所在内存地址对我们来说比较重要,找到指令后要把它的地址记下来。然后,我们还是象做教程第六步一样找到指针,并把它以指针方式加到地址列表上,因为后面我们还要来看看我们做代码注入的效果。

我这里找到的这条指令是:
004560CF MOV [eax], edx

这一步做的基本上就是CE教程里面的第六步,没什么特别的,只是指令的地址要记下来,因为后面还要用到。

第二步:

现在我们要为代码注入做些准备了。首先要做的就是找一个地方来放我们的代码,就是找一个CODE CAVE。在CE主菜单上点MEMORY VIEW,打开内存察看窗口。在内存察看窗口上点菜单:VIEW->MEMORY REGIONS,这个时候就会弹出一个MEMORY REGIONS(内存区域)的窗口,在这个窗口中我们可以看到TUT所使用的各个内存块。我们要找的地方应该是在某一个块的尾部,并且这个块应该是可读写的,就是在这个内存区域窗口上看PROTECT那一栏应该是Read+Write的,这样才不会导致出错。

在内存区域窗口中,我们可以看到第三个区域00459000,PROTECT那一栏写的是Read+Write,看来这个块就是我们要找的了,再看看它下面的一个块,是00460000,也就是说,我们找的00459000这个块,范围是从00459000-0045FFFF。那么我们就从它的尾部来找地方。

现在关了内存区域这个窗口,回到内存察看这个窗口。我们在上半部点右键->GO TO ADDRESS,输入00460000,然后再往上翻。嗯,不错,这个位置全是00 00,基本上可以确定是我们可以用的地方了。然后我们还要规划一下,特别是我们不太确定我们要写入的代码有多少,所以尽量留多一点,以免破坏下一个块(00460000),所以我们确定把代码放在0045FF10这里,另外我们要确定一个地址来放我们得到的数值的地址,就放在0045FF40吧。

这一步主要做的就是找地方放我们的代码,现在就基本确定了。

第三步:

这一步就要真正动手了,不过动手之前,要先看看原来的指令所在的位置那里(就是004560CF)后面的几条指令,并注意这些指令所在的地址。我这里看到的三条指令是这样的:
004560cf 89 10         mov [eax], edx
004560d1 8b 45 fc       mov eax, [ebp-04]
004560d4 8b 80 10 03 00 00   mov eax, [eax+00000310]

我们要把这里改成一条JMP指令,由于一条JMP指令是五个字节,所以这里要破坏两条指令,注意如果被破坏的指令总字节数超过五字节,后面多余的要用NOP补上。

现在我们要先写好我们的代码,就是0045FF10那里的代码,这个次序不能搞错,不管我们是在CE中直接汇编还是以后用外挂写入机器码,都要注意这个次序,因为如果在我们自己的代码没处理好之前就先改原始位置的代码,结果程序如果在这个时候刚好执行到那里的话,就会跳到我们还没准备好的这个位置来,结果出现不可预料的结果,所以要先写好我们的代码再说。

现在,在内存察看窗口上半部点右键->GO TO ADDRESS,输入0045FF10,然后我们在0045FF10这一行上点右键->ASSEMBLE,然后输入我们的代码,输入完后CE会问,我们输入的代码字节数和原来代码不兼容,是否要用NOP填掉,因为这里原来放的不是真正的代码,所以点NO就行了。输入一行完了之后再在下一行点右键->ASSEMBLE输入下一行代码,。我们要输入的代码其实很简单:
mov [0045ff40], eax ---------->这一行是把当前EAX的值,也就是那个数值当前的地址放到我们的地址
mov [eax], edx     ---------->这两行是恢复原始位置上被我们破坏了的原来的代码。
mov eax, [ebp-04]   ---------->
jmp 004560d4     ---------->这一行是跳回去执行。

四行代码输入完了,0045FF10这个位置上的情况应该是这样的:
0045ff10 89 05 40 ff 45 00   mov [0045ff40], eax
0045ff16 89 10         mov [eax], edx
0045ff18 8b 45 fc     mov eax, [ebp-04]
0045ff1b e9 b4 61 ff ff     jmp 004560d4

现在回过头来改原始位置的代码,同样是右键->GO TO ADDRESS,再输入004560CF,再点右键->ASSEMBLE,把原始位置上那两条代码改成JMP,改了之后的情况如下:
004560cf e9 3c 9e 00 00   jmp 0045ff10
004560d4 8b 80 10 03 00 00   mov eax, [eax+00000310]

从上面总共六行代码,我们可以看出,当程序执行到004560CF时,是跳到0045FF10执行我们的代码,而我们的代码首先是把EAX的值放到我们指定的内存地址0045FF40那里,然后就是恢复原始位置上被我们的JMP破坏掉的那两条代码。再然后是跳到原始位置我们那个JMP后面的一行代码去执行。注意,如果我们先恢复原始位置上那两行代码,再把EAX放到我们所指定的位置的话,就有问题了,因为原始位置上那个第二行代码MOV eax, [ebp-04]已经破坏了EAX的值了。还有跳回去是要跳到JMP后面的一行,否则会造成死循环。

这一步就是代码注入的关键,该解释的上面应该都解释清楚了吧。

第四步:

现在我们来检验我们代码注入的结果。

首先,我们在内存察看窗口的下半部点右键->GO TO ADDRESS,输入0045FF40,这样内存察看下半部会显示我们用来放数值地址的这个位置。然后调整内存察看这个窗口的位置,让CE主窗口上地址列表也同时能看到。然后我们回到TUT,点击CHANGE VALUE,这个时候在内存察看窗口上我们就可以看到0045FF40那里原来的00 00 00 00四个字节的内容变了,我们再看CE主窗口上的内存地址列表,我们刚开始以指针方式添加的内存地址,P->后面的数字就和0045FF40这里的地址是一样的(后者的四个字节的次序相反),现在我们再点TUT上面的CHANGE POINTER,这个时候CE主窗口上地址列表中的指针地址变了,而我们在内存察看窗口上看到的0045FF40这个位置也变了,而且数值和地址列表上显示的一样。这样就说明我们的代码注入成功了。

最后再补充一些内容:

一,上面这个代码注入的过程,其实就是让程序转到我们自己的代码,并且把当前保存数值的地址放到我们指定的静态地址0045FF40上,然后再跳回去执行。要注意的是它实现的功能仅仅是把变化后的数值地址给我们放到一个我们知道的,固定的内存地址,其实这样做离一个外挂还差很远,不过如果你要做对付DMA的游戏的外挂的话,这个代码注入就是最基本的东西,从某种意义上说,它就是变动态为静态,让使用DMA的游戏也能象静态内存分配的游戏一样容易处理。

二,关于代码注入后如何运用的问题,如果一个游戏经过我们象上面这样的代码注入之后,那么不管保存数值的内存地址怎么变,我们的代码都会把它保存在我们知道的位置上,那么我们就可以通过我们的外挂来在这个固定的位置(0045FF40)上读取到当前数值所在的地址,进而就能象对付静态内存分配的游戏一样,可以读取或者修改这个数值。

三,上面这个代码注入的过程,我们是手工在CE中完成的,在实际应用中当然这样不方便,我们可以把它也做到外挂上。反正在外挂初始化的时候把上面修改的这些指令的机器码写入到相应的地址上就行。当然写入同样要注意次序,就象上面说的。如果你用TMK来生成外挂,代码注入这一部分就是:
POKE 0045ff10 89 05 40 ff 45 00   89 10 8b 45 fc e9 b4 61 ff ff  
POKE 004560cf e9 3c 9e 00 00
这样就完成了代码注入了。很简单是吧?呵呵。

四,关于代码注入的地方,上面这个例子我们是选择注入到TUT改变数值的时候,也就是说当程序每次改变这个数值的时候就把它所在的位置放到我们指定的位置上。为什么不是在改变地址的时候呢?因为一般来说在游戏中,改变数值的时候比较多,而改变内存位置的时候相对少些,一般只是在重新运行游戏或重新开始游戏时才发生。所以,改变数值的时候比较多,我们比较容易找到这条指令,而且也因此让我们指定的这个地址更新更及时。而缺点只有一个就是当数值不改变的时候,我们也就读不到地址了。当然如果你能找到程序改变数值地址的那条指令,在那里做代码注入也是完全可以的。

总之,上面只是用CE的TUT给大家说个代码注入的基本原理和实例,在实际应用中,要涉及的内容和要考虑的因素还更多,不过只要大家掌握了基本的原理和方法,要在实践过程中去改进自己的做法和完善自己的外挂就不难了。

补充一:

代码,或者指令,其实意思都差不多,一般是指程序语句。因为经常看英文资料,所以习惯上也会被英语中的词汇所影响,呵呵。英语中的CODE就是代码的意思,一般是指一行或几行程序语句,指令英语中是INSTRACTION,也和代码的意思差不多,不过更多地是指汇编指令,或者说指令有时是指CPU执行的一次操作,一般例如一个MOV就是一条指令。

至于NOP,这个是一个汇编语句,在汇编语言中它标记为NOP,就是NO OPRATION的意思,意思是“不操作”,十六进制机器码是“90”,一般来说如果原来一条指令把它改为NOP就是让这条指令不操作或者说不发生作用。当程序执行到一条NOP指令的时候就什么也不做,接着执行下一条指令。CE教程中第五步其实就是由CE自动把那条修改数值的指令改为NOP。

最后,改写0045FF40的值没什么意义,它只是我们自己用的一个地址,程序本身并不使用。真正有意义的不是直接修改0045FF40这个地址的值,而是根据0045FF40这个地址里面的数值知道了TUT上数值现在在什么地址,修改那个地址才是真正的修改到它的数值。举例来说,例如现在0045FF40里面的值是00501230,那么我们就知道TUT上那个数值其实是存在00501230这个位置,那么我们直接修改00501230这个地址上的数值才会真正的修改了TUT上面那个数字。

 

补充二:

内存块:WINDOWS的PE文件(简单点说就是可执行文件)在调入内存时是按原来的磁盘文件的结构申请和使用内存的,申请的内存是一块一块的,并且不同的东西放在不同的块,比如说PE文件的可执行部分和资源部分(就是程序用到的位图,图标等)在内存中就会放在不同的块里面。
怎么知道是一个块的尾部:这个用眼睛看,加上用经验判断,因为从下一个块在什么地方开始就知道上一个块大概在什么地方结束,也就知道上一个块的尾大概在哪里了。比如你沿一条街道走,走到一个地方,再过去就是另一条街了,那么你凭经验应该知道你在的位置就是这条街道的尾部了吧?
还有,其实PE文件放在磁盘上也是以块为单位的,而放入内存也是以块为单位,不过它们都以一定的方式对齐,而且它们对齐的最小单位不一样,所以在内存中一个块真正结束后并不一定就是下一个块的开始,可能要到对齐位置后才开始下一个块。具体的磁盘文件和内存的对齐单位我忘了准确的是多少,我举个例,比如磁盘文件是以200K为对齐单位,内存是以400K为对齐单位吧,那么如果有一个块的实际内容是534K,那么在磁盘文件里面它会占600K,因为是以200K为单位,而这个块放进内存之后它是占800K,因为是以400K为单位。那么,一个534K的块在内存中就占用了800K,那么从这个块开头算起第535K-800K的这一块位置就是我们可以用的地方。上面只是说个简单的例子。另外国外的游戏工具有个CODE CAVE TOOL就是专门帮你找可用的内存位置并且还能告诉你找到的这个地方在什么位置,有多大的空间可以用等等。
如果你对上面说的仍然看不懂,或者更加疑惑了,那不要紧,不用再看下去,这只是说明你没有足够的基本知识来理解代码注入,需要学一些系统方面和汇编方面的基本知识:)


 

  评论这张
 
阅读(396)| 评论(0)
推荐 转载

历史上的今天

评论

<#--最新日志,群博日志--> <#--推荐日志--> <#--引用记录--> <#--博主推荐--> <#--随机阅读--> <#--首页推荐--> <#--历史上的今天--> <#--被推荐日志--> <#--上一篇,下一篇--> <#-- 热度 --> <#-- 网易新闻广告 --> <#--右边模块结构--> <#--评论模块结构--> <#--引用模块结构--> <#--博主发起的投票-->
 
 
 
 
 
 
 
 
 
 
 
 
 
 

页脚

网易公司版权所有 ©1997-2017