无限阳光、自动收集阳光CALL、阳光产生速度

news2024/12/26 22:09:41

简单实现无限阳光

 

本次实验内容:通过逆向分析植物阳光数量的动态地址找到阳光的基址与偏移,从而实现每次启动游戏都能够使用基址加偏移的方式定位阳光数据,最后我们将通过使用C语言编写通用辅助实现简单的无限阳光外挂,在教程开始之前我们先来说一下为什么会有动态地址与基址的概念! 大部分编程语言都会有局部变量和全局变量,相对于局部变量来说是在游戏运行后动态分配的默认由堆栈存储,而全局变量则是我们所说的基址其默认存储在全局数据区,全局数据区里面的数据则是在编译的时候就写入到程序里了,所以不会变化,而游戏的开发都会使用面向对象技术,我们可以推测游戏中的阳光很可能就是类中的一个数据成员,而数据成员的地址就是通过new动态分配的,如下代码:

#include <stdio.h>
  class SunClass{
  public:
	int SunTime;
	int SunValue;
	int SunAttr;
  };
  int main()
  {
	SunClass *Sun=new SunClass;
	Sun->SunValue=100;javascript:void(0)
	printf("SunValue: %d
  ",Sun->SunValue);
	return 0;
  }

如上代码定义了`SunClass`类,在主函数中我们为Sun实例指针动态分配了内存,分配的内存存储在栈中,而栈地址每次都会发生变化,所以分配的内存地址是不固定的,从而导致阳光的地址是动态的 好!现在我们就进入正题,开始挖掘游戏数据,先从最简单的阳光地址找起来吧,首先你需要运行游戏并附加植物大战僵尸进程,然后我们开启新的游戏,首次扫描我们先来遍历4字节的50,也就是搜索当前阳光的数量,当然你也可以尝试搜索金钱数量等,道理都是一样的,这里就拿阳光的搜索方法作为演示目标。 

 接着我们需要让阳光发生变化,这样才可以让我们继续更加精确的确定这个局部变量在内存中的地址是多少,此处我手动种植了一颗向日葵则阳光变为了0,我们就输入0然后再次扫描,由于这款游戏比较简单,基本上经过两次筛选就能定位到阳光的内存地址了,在遍历一些大型游戏的时候,读者应该有耐心,经过多次筛查直到最终找到正确的\(动态\)内存地址为止。  观察上图`13C66448`地址,会发现CE显示该地址是一个灰色地址,在CE中灰色就表示是动态地址而绿色则表示基址,此处的动态地址则相当于我们上方代码中给一个类动态new开辟的内存空间的首地址,由于该地址是系统为我们动态开辟的,所以每次重启游戏该地址都会发生变化,为了能够制作外挂我们必须要找到阳光的基址。 我们继续将地址栏中的地址双击加入到最底部的地址栏,然后在地址上右键,选择`查找改写地址`当我们选择查找改写地址的时候,其实CE就为我们在这个地址上下了硬件写入断点,这个下断点的功能我们同样可以使用X64dbg来完成,此时回到游戏等待阳光出现并点击阳光,则此时会出现以下汇编指令。 上图中我们可以得知`add [eax+5560],ecx`这条指令是加法运算,最右侧ECX里面就是我们当前需要增加的阳光数,将ECX中的阳光数赋值给`[eax+5560]`这个内存地址,那么我们的阳光就会增加,此时我们需要知道EAX寄存器指向的地址是多少,CE中已经为我们分析出了EAX寄存器当前值是`13C60EE8`我们此时需要记下它的一级偏移`5560`,然后去搜索`13C60EE8`这个内存地址。  上图搜索结果可以看到有非常多的数据,那我们该如何判断应该选择那一个呢?这里就是一个技巧的问题了,我们需要尽量选择地址不同的,比如标红处的位置是我们重点关注的对象,其中13C60EE8这个内存地址就相当于我们`SunClass`类实例化的基地址,而5560则是阳光在类中的偏移地址,此处我们需要分析谁给EAX赋值了,直接在`00FE82E8`右键,查找访问地址,然后会看到以下截图内容:  此处会出现一大堆指令,这里也需要一个遍历技巧,我们可以排除CMP之类的对比指令,因为我们是增加阳光所以不可能出现对比的代码,此外我们需要关注操作数左侧是EAX的,因为我们要找的是谁给EAX赋值的,我们选择`mov eax,[ecx+00000768]`这条汇编指令,然后发现二级偏移是`768`,我们继续查找谁给ECX赋值的,这里直接记下ECX寄存器中的地址`00FE7B80`  继续搜索十六进制数`00FE7B80`如下搜索结果可以看到有绿色的地址,这些绿色的地址都属于全局变量,到此说明我们已经找到了这个阳光的基地址了,这里我们可以随意选择绿色的地址作为基址使用,此处我选择的是`006A9EC0`来当作基址使用,前面找到的地址每次启动游戏都会发生变化,而这个基址是永远不会变化的。  最后我们通过查找到的基址与偏移相加的形式,就可以定位到动态地址了,具体公式应该是`阳光= [[[006a9ec0]+768]+5560]`,我们可以直接在CE中添加这个指针,用于进行测试,如下图所示:  最后我们再来总结一下查找思路,其基址查找过程可以描述为以下流程,如果用正向的思路来理解的话应该从后向前来看,会发现正向思路来看会非常的清晰,而我们找基址则是从逆向的角度来分析,也就是从前向后来理解这个过程。 已知阳光的动态地址ECX的值就是增加的阳光 将增加值ECX赋值给 [eax+5560] 我们就得到了阳光 00430A11 - 01 88 60550000 - add [eax+00005560],ecx 我们需要继续找出EAX是多少? 由第二条汇编指令可知EAX的值来自于[ecx+768]这个地址 0045B6FD - 8B 81 68070000 - mov eax,[ecx+00000768\] 最后我们继续跟随查找ECX里面存储的数据得到 \[006A9EC0\] 该数据明显属于全局数据区 00467B00 - 8B 0D C09E6A00 - mov ecx,[006A9EC0] 最后总结出定位静态基址公式 【阳光= [[[006a9ec0]+768]+5560]】 通过编程的方式读取并修改我们的阳光数量,如下这样一段代码,它可以实现读取动态地址并修改阳光数量。

 #include <iostream>
  #include <Windows.h>

  int GetDyAddr(int Pid,int Base, int Offset[], int len)
  {
	int temp;
	HANDLE Process;
	Process = OpenProcess(PROCESS_ALL_ACCESS, false, Pid);
	ReadProcessMemory(Process, (LPVOID)Base, &temp, 4, NULL);
	for (int i = 0; i < len; i++)
	{
		if (i == len - 1)
			temp += Offset[i];
		else
			ReadProcessMemory(Process, (LPVOID)(temp + Offset[i]), &temp, 4, NULL);
	}
	return temp;
  }

  int main()
  {
	int base;
	int offset[3];
	int PID = 5772;
	base = 0x006a9ec0;
	offset[0] = 0x768;
	offset[1] = 0x5560;

	int addr = GetDyAddr(PID, base, offset, 2);
	printf("进程地址:%x
  ", addr);
	HANDLE Process = OpenProcess(PROCESS_ALL_ACCESS, false, PID);
	WriteProcessMemory(Process, (LPVOID)addr,&PID,4,0);
  }

实现自动收集阳光

本次实验内容:通过阳光增加的值为切入点,找到自动收集阳光的关键判断并实现自动收集阳光,首先我们猜测当阳光出现后,我们是否会去点击,这个过程必然是由一个判断和一个时钟周期事件来控制的,那么当我们点击下落的阳光以后,则该判断条件实现,会执行收集阳光的CALL,否则的话继续执行阳光下落的过场动画,这正是正向开发的一种开发手段,此时我们也仅仅是猜测,接下来我们将去验证这个大胆的想法。 为了找到阳光自动收集的关键跳转,我们需要以阳光增加作为切入点,为啥以它作为切入点呢?我们可以这样思考,当我们点击阳光后阳光增加了,说明已经完成了判断,下一步就是写入变量从而增加阳光,那么我们先来找到阳光的动态地址,并在该动态地址上按下F6键查找写入,然后回到游戏等待阳光出现并点击阳光,此时CE会出现以下代码,我们只需要记下`00430A11`这个内存地址,然后直接关闭CE。 

 接着打开X64dbg附加到游戏进程,附加完成后,游戏会被X64dbg暂停运行,此时我们直接按下F12让游戏运行起来,然后按下`Ctrl + G`输入`00430A11`跳转到刚才找到的代码位置,过去以后直接F2下一个断点。  此时我们需要逆向思考一个问题`add dword ptr ds:[eax+0x5560],ecx`这条指令是在我们阳光被点击后执行的,也就是说我们已经点击了阳光现在开始赋值了,那判断阳光是否被回收肯定是在这条指令之前出现,所以我们向上找,观察代码我们不难看出执行`add dword ptr ds:[eax+0x5560],ecx`指令之前有一个无条件跳转`jmp 0x00430A0E`跳过来的。  继续向上查找跳转来源,可知在jmp跳转之前有一个`je 0x004309EF`跳转,经过测试这个地方具体控制阳光是否增加,在向上找就到段首了,此处代码中并没有出现自动收集阳光的关键跳转,因此推断这里应该是一个控制阳光是否增加的子过程\(子过程:过程中调用的过程,称为子过程\),所以我们继续回朔到上一层。  返回到上一层以后,可以看到我们正是在`call <plantsvszombies.sub_4309D0>` 这里出来的,而上方就有一个`jne plantsvszombies.4313FD`关键跳,此处的关键跳转也并不是控制是否回收阳光的关键跳转,而此处的代码量比较少,因此判断此处还是一个子过程,我们继续回溯到上一层。  我们直接单步F8运行到返回,并出这个CALL,出CALL以后会看到`call <plantsvszombies.sub_430E40>`没错!我们正是从这个子过程里出来的,接着向上找跳转会看到有一个`jne plantsvszombies.431599`此处如果将其改为`jmp`的话即可实现自动收集阳光,也就是说如果jne跳转实现则执行收集阳光,否则继续执行阳光下落的过场动画。  注意:如果我们在关键跳`jne plantsvszombies.4313FD`处下断点时,会发现当阳光出现后程序会被无限的断下,这说明是有一个定时器线程在不断的执行判断代码,每次都会判断你是否点击了阳光,所以X64dbg才会被一直断下。 知道了修改流程,那我们就通过编程的方式来实现修改程序的硬编码,首先我们可以通过以下代码完成字节集的读取。

#include <stdio.h>
  #include <Windows.h>

  byte *ReadByteSet(DWORD Pid, DWORD Base, DWORD Size)
  {
	HANDLE handle = OpenProcess(PROCESS_ALL_ACCESS, 0, Pid);
	byte *buf = new byte[Size];
	ReadProcessMemory(handle, (LPVOID)Base, buf, Size, NULL);
	return buf;
  }

  int main()
  {
		byte *Buff = new byte[10];
		Buff = ReadByteSet(2232, 0x00401000, 10);
		for (int i = 0; i < 10; i++)
			printf("%02X ", Buff[i]);
	return 0;
  }

既然有读取内存字节集,那么就有写入字节集,如下代码就是一种字节集写入的实现方式。

#include <stdio.h>
  #include <Windows.h>

  BOOL WriteByteSet(DWORD Pid, DWORD Base, unsigned char *ShellCode, DWORD Size)
  {
	BYTE *Buff = new BYTE[Size];
	memset(Buff, *ShellCode, Size);
	HANDLE handle = OpenProcess(PROCESS_ALL_ACCESS, 0, Pid);
	BOOL Ret = WriteProcessMemory(handle, (LPVOID)Base, Buff, Size, NULL);
	if (Ret)
		return TRUE;
	else
		return FALSE;
  }

  int main()
  {
	unsigned char shell[] = { 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90 };
	BOOL temp = WriteByteSet(3772, 0x00401010, shell, 8);
	return 0;
  }

想要实现阳光自动收集,只需要将`0x0043158F`机器码`0x75 0x08`修改为`0xEB 0x08`即可实现效果。

unsigned char shell[] = { 0xEB };
BOOL temp = WriteByteSet(9744, 0x0043158F, shell, 1);

寻找葵花生产速度

本次实验内容:通过CE修改器遍历出控制太阳花吐出阳光的时间变量,太阳花吐出阳光是由一个定时器控制的,首先我们找到第一个太阳花的基址与偏移,然后找出第二个太阳花的动态地址,并通过公式计算得到太阳花结构长度的相对偏移,最后我们通过C语言编程实现,遍历并修改所有图中的太阳花吐出阳光的时间,最终实现全图吐阳光。 从本次实验开始将接触到关于分析定时器的相关技巧,一般的定时器分为递增定时器与递减定时器,不过大多数游戏都会使用递减定时器,因为递减定时器更好编程判断,本游戏中的太阳花生产速度使用的就是递减定时器,太阳花生产阳光一定是一个周期性的事件,我们只要找到该定时器并改写它的时间即可实现无限吐阳光,如下是太阳花定时器的遍历技巧: > 首先种下第一个太阳花 -> 然后CE马上搜索 -> 未知的初始值 > 回到游戏短暂等待(时钟发生变化) -> 然后马上切回CE -> 搜索减少的数值 -> 掉一点搜一点 > 如果中途太阳花吐出了阳光 -> 则需要搜索增加的数值(1次) -> 然后再搜索减少的数值 > 最终找到一个动态地址(范围:0-5000) -> 锁定该变量范围在1至10即可 -> 实现无限出阳光 修改太阳花时钟有两种方式,第一种找到基址与偏移然后分别修改每一个定时器的时钟,第二种方式则是找到汇编跳转并进行改写,第一种方式要找植物相对偏移,首先我们先来猜测以下游戏作者会用什么方式存储不同植物的栏位。 * * * 如下图: 我们可先来猜测,游戏作者会使用二维结构体来存储植物位置,通过结构体链表将不同植物进行连接,当我们铲除植物的时候,只需要在链表中摘除相应节点,而太阳花的的地址一定是连续存储在内存中的线性空间,此游戏的矩阵可能就是`5*9`这么一个范围,假设在横坐标X轴如果两个植物之间的相对偏移是14C\(14C就是太阳花结构体的实际长度\),那么我们找到第一个植物的基址与偏移,每次相加14C的偏移量,则可遍历到下一个植物的内存地址,同理如果相减14C则就可遍历出上一个植物的内存地址,而纵坐标Y可能就是由一个1C偏移来控制的,此时我们也仅仅只是猜测。 

 如果我们按照上图中的方式进行推理,其计算每一个阳光时钟公式就可总结为如下,但真的是这样吗? > X坐标下的第1个植物:基地址 + 偏移1 + 偏移2 + 768 > X坐标下的第2个植物:基地址 + 偏移1 + 偏移2 + 768 + 14C > X坐标下的第3个植物:基地址 + 偏移1 + 偏移2 + 768 + 14C + 14C > Y坐标下的第1个植物:基地址 + 偏移1 + 偏移2 + 768 + 1C > Y坐标下的第2个植物:基地址 + 偏移1 + 偏移2 + 768 + 1C + 14C 其实并不是!经过我对具体坐标的分析,在本游戏中太阳花与太阳花之间,可能使用了一维结构体来存储的植物与植物之间的属性,每次相加偏移都会遍历到下一个植物的属性上面,也就是说无论太阳花种植到在什么位置,只要相加偏移就可以遍历到下一个植物的冷却数据,而需要遍历的次数则取决于太阳花的种植数量。 首先我们种植一颗太阳花,并通过上方的遍历技巧找到当前第一个植物的动态地址,排查到最后可发现剩余11条结果,此时我们可猜测这个定时器应该在0-10000之间,应该不会大于这个参数,如下图我找到了`13D65160`这个地址,将该地址锁定为10就可以实现第一个太阳无限吐阳光。  接着我们在第一个太阳花的旁边种植第二个太阳花,然后还是使用前面的遍历技巧找到第二个太阳花的动态地址`13D652AC`,找到以后我们可以猜测第一个与第二个在内存中的布局应该是连续的,所以我们可以使用`13D652AC \- 13D65160 = 14C`此处得到的14C其实就是太阳花结构的实际长度,也可以说是两个太阳花之间的偏移值。  既然知道了太阳花之间的相对偏移,那么我们下一步就是来找一个固定的地址,我们在第一个太阳花地址上,右键选择查找改写地址,然后可看到`0045FA48 \- 83 47 58 FF \- add dword ptr [edi+58],-01 <<` 这条汇编指令,此汇编指令乍一看是一条加法指令,但其相加的操作数是-1也就是相减,此处就是太阳花的定时器,其每次减1直到为0则吐出阳光,这里我们就可知该定时器是一个递减定时器,我们只需要记下偏移为`58`下一个地址是`13D65108`即可。  回到CE我们继续搜索十六进制地址`13D65108`然后找到偏移为`AC`下一个地址为`13D08948`  继续搜索`13D08948`得到下一个偏移数据为`768`,继续搜索`00FE7B80`  最终经过我们的搜索得到了一个绿色的基址00599F75 389F6A00 mov eax,[006A9F38]  最后使用CE添加这个基地址与偏移数据来验证一下,公式为 `[[[006A9F38+768]+AC]+58]]` 此时我们就可以定位到第一个太阳花的动态地址了。  根据上面的理论,我们知道太阳花的结构体大小为`14C`,那么我们在第一个太阳花动态地址的基础上加上14C就可以得到第二个太阳花的动态地址。  既然找到了基址与偏移,接下来就是通过C语言编程实现修改全图太阳花的冷却时间,此处贴出我实现的代码.

int main()
  {
	int base;
	int offset[4];
	int PID = 1292;
	base = 0x006a9f38;
	offset[0] = 0x768;
	offset[1] = 0xac;
	offset[2] = 0x58;
	int addr = GetDyAddr(PID, base, offset, 3);
	printf("阳光吐出动态地址:%x
  ", addr);
	HANDLE Process = OpenProcess(PROCESS_ALL_ACCESS, false, PID);
	int SunOffset = 0;
	int SunNum = 10;
	while (TRUE)
	{
		for (int i = 0; i < 5; i++)
		{
			WriteProcessMemory(Process, (LPVOID)(addr + SunOffset), &SunNum, 4, NULL);
			SunOffset = SunOffset + 0x14c;
			printf("搞事 fuck ok
  ");
		}
		SunOffset = 0;
		Sleep(1000);
	}
	return 0;
  }

当我们进入游戏种植好太阳花以后,我们开启这个辅助,即可实现让前四个太阳花吐出阳光,最后种植的太阳花则不修改,为了防止程序出现假死我通过sleep函数让太阳花每一秒吐出一个阳光,这样修改的话会很有节奏感。 

 上述方法,虽然可以修改达到无线吐阳光的作用,但是这种修改方式,显然是不太合理,如果图中有10个太阳花,那么我们则只能循环十次,这种效率还是太低,其实我们可以通过直接修改硬编码的方式来实现一劳永逸的效果,之所以是一劳永逸,是因为所有太阳花的吐阳光判断都是共用一个判断函数执行的,阳光的递减时钟都会走一个地方`add dword ptr [edi+58],-01` 我们只需要定位到这里,然后分析出阳光产生的关键键跳转并改掉其硬编码即可。  上图是经过测试后备注的一些细节,我们只需要将图中的`0045FA7D`处的指令集,替换为nop即可实现全图的植物无线吐阳光啦,其C语言修改代码如下,代码中使用了上面封装好的的写内存字节集函数。

int main()
  {
	int PID = 3612;
	unsigned char Auto[] = { 0xEB };
	unsigned char Suns[] = { 0x90, 0x90, 0x90, 0x90, 0x90, 0x90 };

	BOOL ret = WriteByteSet(PID, 0x0043158F, Auto, 1);
	BOOL ret1 = WriteByteSet(PID, 0x0045FA7D, Suns, 6);
	if (ret != 0 & ret1 != 0)
	{
		printf("您的向日葵已打包,请注意查收
  ");
	}
	return 0;
  }

最终配合自动收集阳光,即可实现如下图所示的变态功能,其实这也不算变态,毕竟还有更加变态的,其实这种修改方式并不完美,因为我们的阳光数量可能是一个整数类型,如果不加以控制,当整数变量到达所能承载的最大范围时,则程序会发生整数溢出,轻则阳光变为负数,重则直接崩溃卡死。 

寻找阳光掉落Call

本次实验内容:本次实验将接触到Call调用这个概念,什么是Call调用\? Call相当于你在编程时所编写的函数,而高级语言中的函数最终也是会被编译器转换为汇编格式的Call调用,这些关键Call普遍都会存在各种参数,关于Call的作用,打个比方有些网游外挂可以实现自动寻路,自动吃药,自动打怪,甚至是全屏秒杀,这些功能是通过修改数值也无法做到的,Call就可做到。 其实关键Call就是作者开发过程中写的一个个处理不同事件的独立的处理函数,这些函数包括了各种独立的游戏功能,而我们可以在远程进程中开辟线程,并通过汇编形式动态的调用这些关键Call,从而实现一些变态功能。 本次实验我们将通过查找阳光的掉落的定时器,并通过定时器变量顺藤摸瓜找到生成阳光的关键Call,通过给此Call传递不同参数实现掉落阳光,钻石,零秒通关等,阳光遍历技巧如下: > 进入游戏等待阳光出现 -> 当阳光出现后 -> 马上搜索未知初始数值 > 返回游戏等待时钟发生变化 -> 马上切回CE -> 搜索减少的数值 -> 阳光下落一点搜一点 > 经过上方步骤反复排查 -> 最终能找到一个值范围(0-700) -> 锁定1即可实现无限掉落 基址与偏移的找法,在文章开头就已经分享了查找的技巧,此处控制阳光掉落公式 `[[[006A9F38 + 768] + 5538]]` > 00413B7C - 83 86 38550000 FF - add dword ptr [esi+00005538],-01 << > 004524F4 - 8B 86 68070000 - mov eax,[esi+00000768] << > 00599F75 - A1 389F6A00 - mov eax,[PlantsVsZombies.exe+2A9F38] << 我们要找关键Call只需要等待阳光出现,当阳光出现后,CE会检测到一条数据 `add dword ptr [esi+00005538],-01` 说明此时定时器开始工作了,我们只需要记下这个内存地址`00413B7C`,然后关闭CE,并打开X64DBG附加到游戏。 

 打开X64DBG附加到游戏进程,然后按下`ctrl+G`定位到`00413B7C` 此时我们需要关注`add dword ptr ds:[esi+0x5538],0xFFFFFFFF`这条计时器指令下方,就是一个大跳转该跳转跳向了结束,那么我们可以猜测`jne 0x413BF1`跳过的内容很有可能就是跳过了阳光的生成过程,也就是可能跳过了阳光生成Call。  玩过此游戏的一定知道,游戏屏幕中不止可以掉落出阳光,还可以掉出其他的东西,比如钻石,金钱,奖牌等,那么我们有理由相信,该游戏中调用的Call应该有很多参数传递,比如掉落属性,掉落坐标,掉落类型等,而我们已经找到了阳光计时器每次递减的汇编代码,故猜测调用Call应该就在附近,向下查找发现有很多Call调用,但有一个比较特别的`call 0x40CB10`,之所以说它特别是因为该Call在调用之前,通过push传递了多了参数,此处很可疑,我们需要具体分析。  此时我们在`00413BE4`处下断点,然后回到游戏发现每当阳光出现的时候,此处就会断下,那么我们尝试给它传递不同的参数,例如给`eax`传递一个2,观察效果发现原本应该是出现阳光的地方,现在居然出现了金币。  经过上方的测试,发现我们猜测是合理的,那么这段代码,转换为C语言后应该是这样的: push 0 普通掉落 push 2-3 其他掉落方式 push 4 自动收集阳光 push 6 右侧滑出掉落 eax=2 金钱 eax=3 钻石 eax=4 普通阳光 eax=5 小的阳光 eax=6 大的阳光 eax=8 自动通关神器 sub_00413BE4(push 0= 物品飞出状态,push eax= 掉落物品,push 0x3c,push edi= 对象状态,ecx= 游戏基地址) { return 0; } 到此,我们还没有办法完成外部注入,因为据我观察程序中的edi寄存器与ecx寄存器中的数据是动态的,每次游戏重新运行都会发生变化,如果想要在外部调用这个Call函数,我们需要找到这两个寄存器的基址,或者说找到他们的来源。 我们先来分析第一个地址如何定位,第一个动态地址是EDI寄存器,这个寄存器每次存储一个整数,此处无法直接找基址,我采取的方式是从后向前逆推代码,观察那些指令改写了该寄存器,然后将这些改写寄存器的指令拼凑起来。  这些调用分别是上图中的,赋值1-赋值3外加一个`call 0x005AF400`,程序中正是通过这种方式计算出动态数据的,我们直接将其提取出来,提取后代码如下:

mov eax,226
call 0x005AF400
mov edi,eax
add edi,64

再来分析第二个,第二个相对于第一个来说就好找许多了,因为它是一个动态地址,如下图我们记下 `13DCE000` 这个动态地址,然后直接在X64dbg中脱离游戏,这里不能结束游戏,如果结束了下次该地址又会发生变化。 

 CE直接附加游戏,然后我们来搜索十六进制数据`13DCE000`然后,你懂的,就是找基址,前面已经找过很多次了。  通过查找访问代码,就能找到一级偏移是768,继续记下`00FE4170`然后回到CE继续搜索。  到这里基地址出来了,其公式为`[6A9EC0+768]`,从这里也可以看出,这应该是一个通用对象地址。  最后,我们整合并写出以下汇编代码,然后使用注入器注入到游戏测试。

pushad
  mov eax,226
  call 0x005AF400
  mov edi,eax
  add edi,64

  mov esi,dword ptr ds:[ 006a9ec0 ]
  mov esi,dword ptr ds:[ esi + 768 ]

  push 0
  push 4
  push 3c
  push edi
  mov ecx,esi
  call 0x40cb10
  popad

发现每当我们点击注入以后,程序中就会多出一个太阳,说明我们的汇编代码没有问题 

 为了能够写出自己的外挂,我们需要实现一个简单的注入器,并能够通过远程的方式调用阳光生成Call,使用C语言编写如下代码即可实现注入效果,需要注意调用约定,此处我在Win10上测试没有问题,在Xp系统上会出现异常退出的情况。

#include <stdio.h>
  #include <Windows.h>

  void AddSun()
  {
	__asm{
		pushad
			mov eax, 226
			mov ebx, 0x005AF400
			call ebx
			mov edi, eax
			add edi, 64
			mov esi, dword ptr ds : [0x006a9ec0]
			mov esi, dword ptr ds : [esi + 0x768]
			push 0h
			push 0x4
			push 0x3c
			push edi
			mov ecx, esi
			mov edx, 0x40cb10
			call edx
		popad

	}
  }

  void InjectCode(DWORD dwProcId, LPVOID mFunc)
  {
	HANDLE hProcess,hThrea?
	LPVOID mFuncAddr,ParamAddr;
	DWORD NumberOfByte;

	hProcess = OpenProcess(PROCESS_ALL_ACCESS, FALSE, dwProcId);
	mFuncAddr = VirtualAllocEx(hProcess, NULL, 128, MEM_COMMIT, PAGE_EXECUTE_READWRITE);
	WriteProcessMemory(hProcess, mFuncAddr, mFunc, 128, &NumberOfByte);
	hThread = CreateRemoteThread(hProcess, NULL, 0, (LPTHREAD_START_ROUTINE)mFuncAddr, ParamAddr, 0, &NumberOfByte);
	WaitForSingleObject(hThread, INFINITE);
	VirtualFreeEx(hProcess, mFuncAddr, 128, MEM_RELEASE);
	CloseHandle(hThread);
	CloseHandle(hProcess);
  }

  int main()
  {
	for (int i = 0; i < 10;i++)
		InjectCode(3644, AddSun);
  }

原文链接:游戏逆向分析入门:无限阳光、自动收集阳光CALL、阳光产生速度-端游外挂-游戏安全实验室

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/609224.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Vue Router路由管理器

目录&#xff1a; 相关理解基本路由几个注意事项嵌套&#xff08;多级&#xff09;路由路由的query参数命名路由路由的params参数路由的props配置路由跳转的replace方法编程式路由导航缓存路由组件activated和deactivated路由守卫路由器的两种工作模式 相关理解 vue-route…

博学谷学习记录】超强总结,用心分享 | 架构师 敏捷开发 学习总结

文章目录 敏捷开发1. 概述2. 敏捷开发 敏捷开发 1. 概述 随着软件开发技术的不断发展&#xff0c;现在出现了很多种不同的开发模式&#xff0c;其实敏捷开发已经成为现在很多企业开发应用程序都想要选择的开发方案&#xff0c;那么什么是敏捷开发呢&#xff1f;1.1 四种开发模…

Linux 配置Java环境(一)

Linux 配置Java环境 一、配置Java环境1、查看系统是否有java环境2、卸载系统自带的jdk3、创建一个文件夹用于存放java的压缩包4、包下载好的jdk拖到java文件夹5、安装jdk6、配置环境变量7、让配置生效8、验证是否配置成功 一、配置Java环境 1、查看系统是否有java环境 输入指…

nginx中location和rewrite

常用的Nginx 正则表达式 ^ &#xff1a;匹配输入字符串的起始位置 $ &#xff1a;匹配输入字符串的结束位置 * &#xff1a;匹配前面的字符零次或多次。如“ol*”能匹配“o”及“ol”、“oll” &#xff1a;匹配前面的字符一次或多次。如“ol”能匹配“ol”及“oll”、“olll…

0-1背包问题:动态规划的经典应用

文章目录 引言背包问题简介0-1背包问题定义0-1背包问题的限制条件 动态规划解决思路状态定义状态转移方程 背包问题的Java实现示例与分析 总结 引言 背包问题是在给定一组物品和一个背包容量的情况下&#xff0c;如何选择物品放入背包&#xff0c;以使得放入背包的物品总价值最…

高边功率开关参数Load current(ISO)和Nominal current

1. IL(nom)是没有加散热片的情况下&#xff0c;考虑RON和BTS6133D与环境热阻计算得到的电流值&#xff1b; 2. IL(iso)是有散热片的情况下计算得到的电流值&#xff1b; 3. IL12(SC)是如果负载电流达到75A以上&#xff0c;BTS6143D会通过不断重启来来限制电流在75A以下。

【论文简述】GeoMVSNet: Learning Multi-View Stereo with Geometry Perception(CVPR 2023)

一、论文简述 1. 第一作者&#xff1a;Jie Zhu 2. 发表年份&#xff1a;2023 3. 发表期刊&#xff1a;CVPR 4. 关键词&#xff1a;MVS、级联结构、几何感知、频域增强、高斯混合模型 5. 探索动机&#xff1a;基于级联的结构以从粗到细的方式计算不同分辨率的深度图&#x…

数据结构与算法练习(二)数组模拟队列

文章目录 1、队列2、数组实现队列3、数组实现循环队列 1、队列 队列是一个有序列表&#xff0c;可以用数组或链表来实现 遵循先入先出的原则 在队尾插入元素叫做入队&#xff0c;对头删除元素叫做出队。2、数组实现队列 思路&#xff1a; 用front和rear记录队列前后的下标&a…

【MCS-51单片机汇编语言】期末复习总结⑤——定时器中断方式与查询方式程序设计(题型五)

文章目录 知识准备工作方式寄存器TMODTCON寄存器IE寄存器 定时/计数器的使用初始化 常考题型例题1题目描述题目解析题解 例题2题目描述题目解析题解 知识准备 工作方式寄存器TMOD D7D6D5D4D3D2D1D0TF1TR1TF0TR0IE1IT1IE0IT0 [注]&#xff1a;TMOD高4位与T1相关&#xff0c;低…

iptables和防火墙

文章目录 1.防火墙2.Iptables基本介绍2.1 什么是iptables2.2 什么是包过滤防火墙2.3 包过滤防火墙如何实现 1.防火墙 Linux防火墙主要工作在网络层&#xff0c;针对 TCP/IP 数据包实施过滤和限制&#xff0c;典型的包过滤防火墙&#xff0c;基于内核编码实现&#xff0c;具有非…

Qt(C++)绘制指针仪表盘显示当前温度

一、功能介绍 当前文章要实现的功能: 使用Qt绘制一个仪表盘,用来显示当前的温度,绘制刻度、绘制数字、绘制温度指针。仪表盘全程使用QPainter进行绘制,QPainter是Qt框架中非常重要的一个类,绘制功能的实现离不开它。如果想要使用Qt进行高质量的绘图或UI设计,必须掌握QP…

Groovy 基本语法

一、简介 类型转换:当需要时,类型之间会自动发生类型转换: 字符串&#xff08;String&#xff09;、基本类型(如int) 和类型的包装类(如Integer) 类说明&#xff1a;如果在一个groovy 文件中没有任何类定义&#xff0c;它将被当做script 来处理&#xff0c;也就意味着这个文件将…

DragGAN-点对点的手动P图

原文网站link以及论文链接&#xff1a; https://vcai.mpi-inf.mpg.de/projects/DragGAN/ https://arxiv.org/pdf/2305.10973.pdf 简介 深度生成模型近年在合成随机真实感图像方面取得了不错的成绩。在现实世界的应用中&#xff0c;这种基于学习的图像合成方法中一个关键功能是…

文心一言 VS 讯飞星火 VS chatgpt (31)-- 算法导论5.2 3题

三、利用指示器随机变量来计算掷n 个骰子之和的期望值。 文心一言&#xff1a; 为了计算掷n个骰子之和的期望值&#xff0c;我们需要先了解一个重要的概念&#xff1a;指示器随机变量。 指示器随机变量是一种特殊的随机变量&#xff0c;它只有两个取值&#xff1a;0和1。通常…

《计算机网络——自顶向下方法》精炼——3.7(2)

读书有三到&#xff1a;谓心到&#xff0c;眼到&#xff0c;口到。——明朱熹 文章目录 对链接吞吐量的简化描述高带宽路径的TCP公平性 对链接吞吐量的简化描述 为了简化对一条TCP连接吞吐量的描述&#xff0c;我们首先忽略连接过程中处于慢启动状态的时间&#xff0c;因为这一…

AWD练习平台搭建 附虚拟机资源

文章目录 前言基本环境平台搭建系统安装平台部署环境启动实现效果 参考后记 前言 最近需要准备线下AWD了&#xff0c;但是没有那么多金币每天上BugKu的PVP在线AWD对战进行练习&#xff0c;于是想着可以自己搭建一个团队内部使用的AWD攻防平台&#xff0c;浅浅的记录一下 基本…

基于SSM框架的学生信息管理系统

项目介绍 主要功能&#xff1a; 基于Spring、SpringMVC、Mybatis框架LayUI完成了这么一套系统 ①角色登录&#xff1a;管理员登录、教师登录、学生登录 ②基本管理&#xff1a;学生管理、教师管理、班级管理、专业管理、教室管理 ③教务管理&#xff1a;课程管理、开课管理、课…

Packet Tracer - 综合技能练习

Packet Tracer - 综合技能练习 拓扑 地址分配表 32-1913 2^132^8*2^5 256*32 32个C 16 16 (1)172.16.128.0~172.16.143.255 255.255.240.0 (2)172.16.144.0~172.16.159.255 255.255.240.0 G0/0 172.16.159.254 G0/1 172.16.143.254 设备 接口 IP 地址 子网掩码 …

深入理解API网关Kong:初识Kong网关

深入理解API网关Kong&#xff1a;初识Kong网关 介绍 Kong是一个开源的、可扩展的、微服务 API 网关&#xff0c;它已经在全球范围内的各种规模的组织中广泛使用。Kong的设计原则是为了处理服务到服务的通信&#xff0c;通过提供功能丰富的平台&#xff0c;实现对API的全面管理…

Verilog基础之一、触发器实现

目录 一、触发器简介 1.1 触发器作用 1.2 触发器信号 1.3 使用规范 1.4 Vivado综合触发器 二、代码实现 三、综合结果 一、触发器简介 1.1 触发器作用 触发器是工程中基础的单元结构也是相当重要的组成&#xff0c;时序电路的实现离不开触发器。触发器是具有存储功能的器件&…