【干货】C++游戏外挂开发教程17:EIP指向注入

文摘   社会   2025-01-10 00:00   广西  


欢迎来到C++游戏外挂开发系列教程! 

游戏外挂开发是一个既有挑战性又充满乐趣的领域,它让我们有机会深入探索计算机程序设计和系统底层原理。本教程将带领你从零开始,逐步学习如何使用C++编程语言开发游戏外挂,包括各种常见游戏中的作弊功能,如自动射击、透视功能、无敌模式等。通过本系列教程,你将了解到游戏外挂的基本原理、常见开发工具、技术挑战和实际应用技巧,为你打造成一名优秀的游戏外挂开发者提供全面的指导。

本期教程我们来讲一下EIP注入。EIP是程序中的一个寄存器,它指向下一条指令的地址,而不是当前代码。在一个CPU中,除了八个通用计算器外,还有一个计算器是EIP。它的作用是指向下一条指令地址,是一种控制指令流程的关键寄存器。
在之前我们提到过一种注入方式,称为消息注入。但是这种注入方法存在一些问题,主要针对有窗口的程序。对于那些没有窗口的程序,比如游戏程序,它们的数据并不存储在窗口中,而是存在于程序内部。因此,使用消息注入就无法进行注入。
我们要讲的是如何利用EIP来进行注入。在学习EIP注入之前,我们需要了解几个重要概念。首先,我们需要找到一个没有窗口的程序。即使没有窗口,我们可以通过打开任务管理器来查看其进程分布。任务管理器会显示具有窗口的应用程序,而没有窗口的程序则会在详细信息中显示,这些程序通常是后台程序。
当我们打开一个游戏时,会立即看到一个窗口出现,然后加载游戏框架。我们需要注意这个程序的进程名称。如果找不到窗口,我们可以通过进程名称或PID来找到它。
第一步是找到进程名称,然后通过API枚举所有进程路径。接着,我们需要判断是否是我们要找的程序。如果进程名称和枚举到的进程路径匹配,说明我们找到了目标程序。
找到目标程序后,接下来我们需要获取其CAD以及调试句柄。获取了调试句柄后,我们就可以开始注入。在注入之前,我们需要编写一些代码来存放注入的方法。因为每个程序的EIP都指向下一跳需要执行的指令的地址,我们需要将EIP改为我们的注入方法。程序是不停地运行的,每个线程都有自己的EIP。在进行注入之前,我们需要获取每个线程的EIP。
我们不能一直去获取它的EIP,但有一个方法可以解决这个问题。我们可以利用录像的方法,将每一代的EIP都记录下来。一个程序实际上是一个循环,相当于一个圈,而EIP就像是围绕这个圈一直在执行。
我们可以暂停线程,让程序暂停执行。这样做是可行的。暂停了线程之后,我们再获取EIP,然后就可以改变它的执行流程了。获取到EIP之后,我们首先要保存一份EIP。然后,我们将其指向我们需要注入的方法。注入完成后,再将其改回原来的地址。这个方法与之前讲到的寄存器钩子有些相似,但是不同之处在于,寄存器钩子需要知道一个地址才能进行操作,而EIP注入则不需要。
EIP注入的优势在于,它可以随时暂停程序,随时挂起线程,然后进行注入。这样做的痕迹更小,更不容易被发现。接下来我们来编写代码。目前是一张空白的代码,我们还没有写任何内容。首先,我们需要编写一个必须的函数,名为 "设置进程"。该函数需要传入一个进程名,用于查找需要的进程。接下来是 "获取进程PID" 的函数。PID是进程的唯一标识符,是一个四字节的数字。
第三步是获取每个线程的EIP。我们首先需要定义几个变量,然后初始化一个进程结构。接着,我们开始枚举进程。在枚举进程之前,我们需要获取进程的快照。Windows可以通过一个系统快照来获取所有进程的信息。然后,我们开始枚举。我们将获取的快照句柄传递给进程结构,然后继续执行,直到获取完所有进程的信息。
接下来,我们需要定义一个调试句柄,用于注入时使用。我们打开每个进程,并传递两个权限参数:获取进程的所有权限和获取线程的所有权限。然后,我们可以开始获取每个进程的路径。
先来调用这个函数,现在我们可以随便传入一个进程,按下 Ctrl+F5 运行起来,看一下出了什么错。检查一下错误,看看哪里出了问题。我们插入一个参数,这样应该就可以了。现在我们已经枚举了所有进程的路径。我们打开了所有进程,但是有一个路径没找到,我们要用这个。
枚举完进程路径后,我们需要进行比对。如果在进程路径中找到了进程名,那么我们就保存下这个PAD。PAD就是当前进程结构的标识符。然后退出,这段代码应该没问题吧。
接下来我们开始写注入。我们首先要定义一个EIP字母。然后是第二个路径,写一个注入的第一步。我们现在要做什么?在注入之前,我们还没有输入进去。我们不能用六,或者不能用麦克,这两个我们不能用,因为我们还没有注意到它的空间。我们现在只是在别的进程想要做的。怎么办呢?我们有一个API可以远程申请内存空间,我们可以调用这个API来申请所需的内存空间。看起来很顺利。
现在我们已经申请了进程句柄,然后申请了我们需要的空间。接下来,我们写一个结构。第一个字节是0x68。我们要写汇编代码,首先要传递一个EIP,用于之后的内存执行完毕后返回。我们先把它保存下来,然后调用一个函数之间,尽量保证通用寄存器保存下来。然后再保存浮点寄存器。保存之后,我们把第二个参数填入,保存完成后,就可以调用call了。然后我们要扩大栈的大小,然后真正实现注入。完成之后,就可以恢复了。
直接写入内存为什么可以呢?这个原理可能还好,但可能你们还是会有点糊涂啊。让我画个图吧,比如说,这是我们的栈空间,但这和堆一点联系都没有啊,这是我们的栈空间。
现在,比如说我们的栈空间,下边写一下,填充,这是我们的大栈,下面已经被其他东西给占用了,可能被我们的一些局部变量啊,函数传参啊给占用了。我们pass了EIP,我们取一块下来。EIP嘛,放大一点,我们要是不是录入到最远,入到这里面好,这段空间就被我们占用了,对吧?没问题吧,入了之后我们还要保证,保证保存什么呢?再输个账啊,再出个栈。我们要学会用我们的栈。
他又进来了,他又把他进来,它保存的是什么呀?通用计算器啊,通用计算器。他又占了一大块空间,他又带来一个大怪物。看一下。好,然后move了,这个我就不写了,一因为它和这个一样。当然啊,这个木子呢哈,他不用占啊,不用占对吧?现在调用调用调用什么?调用我们的这个目录指令啊,然后保存第二路径。
然后pass EAX啊,怕写1xPJS,然后他在load啊,他在load LoadLibrary。这个就它自己就会平衡啊,然后我们恢复的时候,我们把这个这个再拿出来。再把它弹出来,再把它弹出来,是什么意思?就是说再把他这一段再取出来,取出来之后呢,我们擦掉了,擦掉了。现在,现在是什么呀?现在直接return就可以了。
Written是什么意思啊?瑞等中间那,NET内部实际上是是吧?方块当然,暂停和壁纸。现在这个就是从我们的栈顶当,当之前我们保存的这个EIP,现在返回又成我们现在的栈顶了,对吧?来调用LoadLibrary?不用不用,这个LoadLibrary哦,纯路径是需要的,一会儿会写,一会写。
等于他返回的地址,啊,他是个指针,应该把它做成一个指针啊。就行了,有正常的,好像有。最后不是有,一定有啊。最后我们要弹出来对吧,我们要弹起来好。我们先来写写party,根据我们的这个函数来写,怕是1P啊,给我的EP,他的地址啊,白头party,等于index 9C,然后他写AD,他写AD是什么呀?
这个这些都是硬编码啊,都是硬编码怪的。贝木木一AX它的一面码就是0xB。好DWord一个地址啊,EX,我也是啊,确实。保存函数参数地址对吧,没毛病吧,然后再写一个call扣1C诶,不是空啊。看一下啊,E4X1X,阿拉斯怕谁怕谁怕谁。什么帕奇EX,等于0x50。然后保存这个函数的指针呢,保存DLL的指针EB9,MOV EAX。然后占两个字节call啊,占两个字节call ECX,UCX,EAX等于0xD2F两个字节啊,然后弹出通用寄存器啊,啊弹出送的变器等于04461发布FD09D对吧,Linux是决定。
by return,返回来吧,返回来吧,C3C3就是返回了,C3,下面还有还要保持一个空间,保存一段空间,保持人物空间。申请了空间之后,我们再怎么处理呢?不是将它继续用掉吗?这用了之后,我们这里应该释放,关闭它,将其释放掉。啊,这时候我们就要用它了,重新创建一个快照。
现在这个快照不再是进程快照了,现在这个快照是什么呢?我们要将它改一下,改成线程快照。这干什么呢?我们要挂起它的主线程嘛。
我们挂起它的主线程,我们将这个主线程挂起,挂哪个线程线程其实无所谓啊,主要是看你因为挂主线程相对来说稳定一点。挂辅线程也可以,挂其他线程也可以,反正你挂了,挂了之后直接就可以注入进去,挂哪个线程都一样,这个没有什么好说的。都用它就行了,local再点点,这就定义一个,刚才写那个进程结构,现在写一个线程结构。
线程结构,线程结构我们应该在哪里初始化一下,再把初始化一下,它的大小等于,限制接口,再把它传进来。如果线程结构线程结构怎么样,线程结构的当前进程pad啊,性能结构大点的小H等于,所以调试pad就说明我找到了,那说这个PID,他这个线程结构可不是说只是一个当前进程的这个结构,它是所有程序的线程都记下来的。然后我们就退出了,好debug,那个,三个,我来这,还要写个这个剧本,还是他就打开线程了。
这是打开线程,打开线程,哎,over process,生存结构,这个就是他的这个主线程挂起了,主线程ID。然后开始挂挂他那边挂他了吧,对吧?super。把线程句柄挂进来,有大。现在县城已经暂停了吧,挂机。现在现在你在哪啊,然后我们要取他,取他EIP了吧,context。
这个线程上下啊,sir,com tips,现在三角,哦这个,先阻止一下一下他的标志,好,然后调用一个API,调用一个API GTHREADCONTEXT对吧,这样这个API,然后把它反过来,线程句柄啊传过去,这该说什么呢?获取线程上下啊,获取线程上下文。
获取线程上下文之后呢,我们把新人word就可以,先先那个,先申请一个这个结构啊保保存进来学,有可能,嗯话题,话题的EIP地址,就是我们当前的这个挂起之后的EIP地址。
这1P这个是什么备份备份,当前也还行对吧。然后他当前的EIP,那现在我们要把它EIP弄成什么样,弄弄成我们刚才要远程申请的这个空间,他要执行这对吧,转成四杠七,然后我看一下啊,然后他的木木一AX,这个就是我们保存函数参数的地址。
保证函数的参数地址我们怎么写啊,就是他的过来啊,他的这个地址加上这让我们取取结构里面了偏移嘛,是吧,其他的迁移,这个到这个位置啊,有多大啊,他就取出来很简单吧,然后转一下,加上大或者参数第二,这是需要注,需要注意点,保存下来了,好,然后要保存到他的这个函数地址了,保存函数地址。
Move 1CX,这个就是它保存函数地址,等于就是他弄的蓝本,因为我们传的是单字节,就low the library啊,如果你传到宽子直接用就用这个啊,但现在我们现在用A。然后下一步是什么?下一步我们就要把我们DLL路径给拷贝下来了。
现在我们没有进行成功或失败的判断,这是不规范的。以后我们再将它规范一下。然后我们现在把所有的寄存器都改回去。将上下文再改回去,count,改回去吧,不是改回去,是把它修改了,把它修改了,对吧?然后继续执行限制,关闭可乐是憨的。然后还要把这个也是上去,是收尾了啊,扫尾工作,我们现在试一下啊,试一下注入进来啊,注插进来。
Hunter加F4 B Two,我们把我们第二DL找出,存档点,my dear, dear, dear,试一下是否成功,必须返回,咳没住没住,这,看一下啊,关一个11,我们就要开始判断了啊,哪里出错了,哪里出错了。先提交,先看一下他聪明啊,如果,然后打印错误代码1%看看,是获取错误代码啊,获取错误代码,然后我们一步一步的,这个东西,咦都判断下来啊,都判断下来他哪哪一步错了,看一下啊,获取主线程句柄失败,挂起线程失败,这这一步就已经错了,获取主线程句柄失败,我看一下怎么回事啊。
这个写成等于了啊,写成等于几,等等于。重新试一下啊,注入成功了吧,注入成功了,看见我们的窗口了吧,注入的窗口再进去看一眼啊,成功了啊,看看读不读,读我们的坐标里的读者能读出来就成功了,好读出来吧。
坐标读出来了吧,关闭啊对吧,没问题吧,有有没有问题,那么我们只是找到坐标了啊,下节课我们就把其他的数值给找出来,其他的数据给找出来对吧,什么生命值啊,蓝啊,体力值啊,技能CD啊,对吧,人物的属性啊,角色这些东西啊,找出来.这个结构是固定的,是通用的通用的,你调用的时候只需要调用什么,只需要写写这个进程名就行了,其他什么都不用管,然后把第二路径传进来就行了,就注入了啊,对吧,就入了这个自动注入能注入大部分的游戏,能注入大部分。那么本期教程就到此结束了。



暮色的狐
这是一只高强度上网冲浪、高质量输出内容的狐狸。主打ACGN杂谈、技术干货分享、第九艺术鉴赏、网梗百科解析、情感树洞鸡汤、正能量价值观~
 最新文章