外挂制作之思路总结和基址与偏移量

从今天起开始学习如何做外挂了 , 很久之前了解过一点皮毛。 无非是读写游戏进程中的内存数据。
再读写内存数据之前首先要做的就是找到游戏进程中在内存中的地址。在基地址中,利用CE寻找到某一数据对应的内存地址。计算出偏移量。然后每次都可以通过内存的基地址加上偏移量找到想要的内存地址,进行读写。
那么步骤就出现了
①通过游戏界面找到游戏进程id
②通过进程id找到进程数据块,找到进程基地址
③利用CE寻找某一属性的内存地址
④计算出进程基地址和数据对应的偏移量,然后保存偏移量
⑤修改地址数据
这样就完成了外挂的制作。
待解决:
①是否能通过进程id找到进程基地址
②是否能通过进程基地址与数据偏移量计算出某属性的内存地址
经过我的摸索与网上搜集资料。 有些思路是错误的。比如说找到进程基址后找到偏移量,是不完全正确的。通过OpenProcess()获取进程对象句柄,然后用ReadProcess和write对内存地址的数据进行读写。改写的过程就是修改数据的过程。在此基础上整理可得:
①通过游戏界面找到进程ID
②通过进程ID获取进程对象句柄
③通过句柄读取属性内存地址中存放的数据
需要注意的是基址与偏移。
所有的程序都有基址.我的理解就是对于一个进程而言,需要一些结构体存放某个属性如怪兽的血量、等级。如果这个struct太大了,就需要动态分配,结构体成员都是指针,指针指向一片动态分配的内存。存放这个struct。现在我们可以知道,指针是全局的,它的地址不变,但是它的内容是变化的(因为是动态分配的),所以我们搜到的目标地址就是变化的了。但是如果我们每次都先找到指针,就能找到想要的数据地址了。
基本上其他的都是上面这种指针式的扩展了:
比如说某游戏采用了 一关一关的数据结构。每关都会动态分配一块儿内存,但是金钱在这块内存的固定偏移处。这样的话,我们需要找到指针对应的关struct对应的地址,然后再加一个固定的偏移。

再组合一下,可能金钱不在这个关卡结构的固定偏移处,可能关卡内部一个指针,指向一个角色,然后金钱在这个角色的固定偏移处。这就是两重指针。
比如:
struct mission{
//关结构体
int missNum;
struct character* charPtr;
};
//角色结构体
struct character{
char name[MAXTITL];//一个字符串表示角色名字;
flot gold;
};
外挂制作之思路总结和基址与偏移量_第1张图片
当然也可能更复杂。但是总是可以找到一个方式来寻找到目标地址。
可以这样想,游戏本身肯定需要一个方式来访问金钱。这个方式就是 指针 和 固定地址、固定偏移 的组合。也就是说一个mission结构体的首地址一旦确定,那么他的成员变量一定是首地址加上一定的偏移量!
基址伴随着一个加到基上的偏移值来确定信息准确的位置(绝对地址)。这一概念与街道地址系统雷同。例如:“大街2010号”由基(大街2000街段)加上偏移值(从街段开始的10号)。
为什么基址是不会改变的?
因为各个进程内存空间是隔离的,存在于用户模式的内存空间中。在内核模式内存空间中,有一个进程地址是虚拟的,在使用的时候需要转换为真实地址(另外,每个进程的内部地址是虚拟的,在使用的时候需要转换为真实地址据内核对象中此进程真实的地址范围以及进程的虚拟的地址的基址)。(根据内核对象中此进程真实的地址范围以及进程的虚拟的地址的基址)。进程的虚拟地址范围远大于真实的地址空间范围,这需要运用虚拟内存技术,即将进程内暂时不用的内存页换出到外存,需要用到的时但由这个基址转换为真实的地址是不同的,所以内存不会冲突。如果我编的那个很紧凑的例子一样,我们一次性看到了所有相关代码,那自然好。可是如果不是这样(基本上都是这样)。我们就要看某些值是从哪来的。但是关键点就在于,我们是用CE搜的相关值,用CE查的相关代码,谁也不知道是不是驴头不对马嘴,有可能找到的不是同一套访问方式的中间代码和中间值。 这是很有几率的。不过一般来说大量值和中间代码都是类似的,比如说寄存器从eax换成了ecx,但是访问方式还是不变。这就是这个不严谨方式几乎总是能成功的一大部分原因。
(后记:多用了几次以后发现,OD查反汇编虽然理论上来说是最准的,但太费事了,万一某一条线 路上跟丢了简直崩盘,还会时不时用一个不知道哪里赋值的寄存器的值,真累。。有时候还是直接CE强搜比较方便。)
另外一个要提醒的是,有的复杂情况可能中间的偏移不是固定值,而是 +ecx 这种变量。只需要查这个ecx存放的地址再新建一个ce 扫描即可。找到目标地址后,看谁访问了,然后直接转到反汇编,看整个上下文,这样保证总是对的。比如说用CE查到目标地址,然后用OD开开,下访问断点,然后看访问的这个东西,如何访问的,就能直接找到一整套的访问方式。
然后就是看反汇编的能力了。找到底如何访问的目标地址。
另外,之前我们找到的基址是[[0x00001234]+4]+8,我们的猜测是 0x00001234指向关卡,关卡+4是角色,角色+8是金钱
那我们就可以去试一试,看一看,说不定角色+12是血,+16是蓝呢?
说不定关卡+8是关卡名呢?
另外说一句,一般字符串是C字符串。也就是说是一个指针,指向一个char型数组,以0结尾,查看的时候要注意。

为什么会有动态基址,有基址不就行了吗,为什么要弄个动态地址出来?
这涉及动态内存管理的问题,简单举个例子吧,假设我们现在建立了一个全局对象,下面有几个不同的属性,比如对象名称之类的,那么既然是全局对象 ,那么就必须要有一个固定的位置来描述,以便程序每次在不同的位置都能找到它,但这个对象下面的属性的值却不是固定了,比如对象名称吧,它可能是三个字符,可能是三百个,也可能是三千个,那程序如何去分配足够的内存给这个属性呢?动态地址就是解决这个问题的。一般在程序中,约定一个基址作为数据的地址入口,在程序运行过程中因为数据变化导致原有分配给数据的地址不够而需要新的地址时,就将新地址的位置写入基址,这样,既能让程序能够保证在任何情况下数据都可使用,也避免了因数据溢出而造成的错误,同时还可以减少内存的使用量。

以上单机游戏外挂的基本原理就全部解释完了,剩下就是实际的代码,在下一篇文章中。

你可能感兴趣的:(外挂制作之思路总结和基址与偏移量)