首页
社区
课程
招聘
[原创][分享]编写单机游戏连连看辅助的全过程
发表于: 2020-10-6 12:15 9299

[原创][分享]编写单机游戏连连看辅助的全过程

2020-10-6 12:15
9299

本人师从于15PB,以下分析的是一个小作业。分析过程中,遇到不懂的问题,参考了15PB薛老师录制的视频。

可能有很多更加便捷快速的方法可以制作辅助,直接通关游戏,但是这里主要目的是为了分析程序的功能和函数,目的还是学习。

打开游戏

打开qqllk.exe。查看进程列表:发现点击开始游戏之后会新建一个qqllk.ocx的进程,点击继续之后,该进程结束,并且打开了游戏程序

点击继续之后:qqllk.ocx进程结束了,kyodai.exe进程被打开,也就是游戏程序

也就是说qqllk.ocx的功能就是打开游戏程序。但是为什么我们自己却不能打开程序?

介绍下面的API:

以挂起的形式创建进程

 

以挂起的形式创建进程,此时创建出来的是一个4GB的虚拟空间,程序还没有跑起来,在这期间可以对这4GB的内存空间做一些操作。比如:修改内存,或者直接卸载这4GB的内存,将另一个程序的内存放入这4GB的空间中。

这里猜测可能是使用了挂起的方式创建进程,然后修改内存,将游戏的内存修改为正确的值之后,再恢复进程的执行

CrateProcessA下断点

writeprocessmemory下断点之后,点击qqllk窗口中的继续,断下来了。注意参数

使用010打开该程序,搜索地址43817a-400000=3817a

修改为程序要求的字节:00,另存为一个新的文件


双击打开程序:直接运行游戏了


1.1直接分析新文件,OD打开,点击练习,会随机生成新地图,猜测使用了随机函数rand

搜索rand函数,下断点,点击练习按钮,看看是否会断下来。结果证明确实断了下来。

1.2点击K,进入栈回溯窗口,查看调用堆栈,发现我们自己的程序0041A080调用了0041CAF2,0041CAF2调用了rand()

1.3双击进入,在这两个地方设置断点

1.4再次点击"练习按钮",断在了第一次调用的函数,发现这是一个thiscall,ECX时this指针,是一个C++对象的调用。单步F7进入查看

1.5发现了一个文件,在本地磁盘中打开该文件试听一下,发现这是点击练习之后会播放的音乐。继续往下走

1.6单步没有能发现什么能认识的东西,直到CALL了第二个调用,并且注释说明,调用了函数:memset(void*dest , const void* src ,  int n)

这几行代码的意思,猜测一下:首先调用随机数函数rand(),然后调用memcpy,将src的内容拷贝给dest,一共拷贝0xDC个字节的内容。

1.7接下来F8单步,然后查看内存,验证一下

1.8调用前EAX的内存情况                

调用前EDX的内存情况


调用后EAX的内存情况

1.9首先一看到这样的布局,比较容易联想到这是一张地图。多走几次,发现前8字节都是固定不变的,那么先不看前8字节,看一下能否与游戏地图对接上

地址栏CTRL+G跳转:EAX+8,直接运行起来,发现这就是游戏地图。应该是每一种图案都对应一个数字。并且在游戏中消除了方块之后,地图对应的位置也会清零

那么联想上面的操作:初始化地图,01就代表这个位置有物品,00代表空地,然后后面会对01位置进行赋值。

1.10继续往下走,发现紧跟着的一个函数调用就是对01位置的赋值操作,因为CALL了之后,01的位置全部都变化了

1.11由此可以确认EAX就是地图,但是前面还有8个字节的内容不知道是什么,但是不管是随机了多少次,前8个字节都是固定不变的,并且又是以偏移的形式得到的地图,那么前8字节很可能就是地图的基址。基址+偏移得到地图。

1.12看一下上图中的EAX是从哪里来的,一直向上查找,会发现ESI就是ECX赋值过来的,也就说ESI就是this指针

1.13继续分析,有一个疑问,上面分析得出,memcpy函数将地图从EDX中copy到EAX上,那么EDX又是从哪里来了?

查看的方法是下内存断点,继续点击练习,让它断下来,然后内存中跳转到EDX+8,选中一段地图,右键,断点,内存写入,然后重新点击练习按钮

1.14断下来之后,点击K查看栈回溯

1.15 逐个双击进去查看,发现又回到刚刚分析的地方了,说明在调用memcpy函数之前,程序就预先初始化了一张地图,这张地图里面存储的是0和1。

1.16 但是我发现,这个初始化之后的地图全都是固定不变的,不管初始化多少次,地图仍然是不会变,那么后面的地图为什么又会变呢?跟进去看看

1.17 发现使用了一个文件,在本地磁盘中打开看看,发现这个正是一个地图文件。所以程序应该是预先设置好了地图,然后随机数应该是将位置为1的设置为随机图案。

1.18 再看之前的记录:调用rand()随机数,执行CDQ指令,获取偏移EDX(随机值),加上基址,获取地图,再赋值给EAX。

1.19 到此为止,我们得到了内存中的地图位置,并且知道了游戏是在什么时候初始化地图的,什么时候随机地图的,可以直接修改地图内存,写一个简单的破解了:

只需要在游戏初始化地图之后,将地图设置为0000即可消除所有方块,这里我设置为如下:

在设置两个图案进去

运行起来,整个游戏就只剩下两个图案了,直接点击就可以完成游戏了。

在OD里面测试:以下代码其实可以优化,直接写JMP会比较简单,但是写完后才想起,就懒得改了

在地图赋值完成之后,插入一段代码,将地图的第一个位置和第二个位置设置为图案,其他的全部设置为00,设置完之后,jcc跳转,跳转到0044af44处执行原本的代码,执行完原本代码之后,再跳转回41cb4a处继续执行代码

44AF44地址是一块程序没有使用到的空间,在这里我利用这段空间作为一个跳板,执行原代码。由于插入之后,会覆盖原本的代码,所以需要先备份一下原本的代码

在44AF44处添加原本的代码:

在OD中修改完成之后:选中修改的代码,复制到可执行文件。这样会生成一个新的文件。我测试的时候一共生成了两次文件,第一次是在44AF44处添加原本代码的时候,选中修改的代码,生成了新的文件

第二次是在41CB25处,添加代码时生成的。

执行完之后,双击打开exe,效果图:点击"练习按钮"整个游戏就剩下一对相同的图案了

以上的就是一个简单的破解,直接修改原程序,在原程序中插入我们自己的代码,然后让程序继续正常跑起来。这种方法比较无脑简单,也没有太多的技术性,有点类似爆破。接下来我们继续分析程序的算法,编写一个辅助工具

我们发现,在游戏中可以使用指南针道具,直接找到两个相同的图案,那么尝试一下通过这个道具入手,达到自动无限次使用道具。

2.1 首先,指南针也不可能预先知道哪里有相同的图案,也是需要访问地图才能查找到相同的图案,那么思路有了,

程序运行起来之后,在内存中的地图下一个内存访问断点,然后使用指南针道具,看看程序是在哪里调用了指南针功能的函数:

2.2 断下来之后,点击K查看调用堆栈,一共有5个调用,全部下断点,断下来之后再分析哪个函数真正调用了指南针函数


2.3 第五层执行了多次,指南针函数应该不会调用多次,逻辑上应该是使用一次指南针,调用一次指南针函数,取消断点

2.4 当点击游戏图案时,第一层和第二层会断下来,肯定不是调用指南针的,取消断点。

从这里开始,分析函数的功能,首先看看这个函数有几个参数,具体是什么。查看函数CALL之前,PUSH了几个参数,以及函数RET时返回了多少个字节,有些时候有可能有一些参数很早就PUSH了,中间又调用了其他函数,这样就容易混淆到底哪个才是参数,所以通过返回值和PUSH的个数一起确认参数是最靠谱的。

一直往下翻,这个函数特别长,翻得我怀疑人生....最终总算找到了,返回0xC的字节,说明PUSH了3个参数进栈,查看堆栈

调用之前PUSH了3个参数

2.5 返回时,RET了12字节。可以确定函数的参数就是这三个外加一个this指针。

2.6 查看参数:通过测试,并且查看这三个参数,发现这三个参数都是固定的,不会动态变化,

2.7 也就是说,我们只要调用这个函数,并且传三个参数(0 , 0 , 0xF0),就可以模拟程序调用这个函数了,只要可以随心所欲的调用这个函数,就可以随心所欲的使用指南针道具了

继续向下分析,分析第四层:分析函数的关键:①查看返回值变化②查看参数变化③查看自身变化


[招生]科锐逆向工程师培训(2024年11月15日实地,远程教学同时开班, 第51期)

最后于 2020-11-29 12:49 被三一米田编辑 ,原因:
上传的附件:
收藏
免费 5
支持
分享
最新回复 (14)
雪    币: 3372
活跃值: (1465)
能力值: ( LV4,RANK:50 )
在线值:
发帖
回帖
粉丝
2
支持支持,眼熟的两个南瓜
2020-10-6 14:02
0
雪    币: 22
活跃值: (443)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
3
2020-10-6 17:17
0
雪    币: 22
活跃值: (443)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
4
分析的不错~源码也发一下吧 学习一下!
2020-10-6 17:19
0
雪    币: 7121
活跃值: (125793)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
5
支持楼主
2020-10-6 19:01
0
雪    币: 763
活跃值: (4923)
能力值: ( LV9,RANK:160 )
在线值:
发帖
回帖
粉丝
6
靴子 分析的不错~源码也发一下吧 学习一下!
void CMyDlg::OnBnClickedButton3()
{
	for (int i = 0; i < 100; i++)
	{
		int n = ::SendMessage(m_hWnd, WM_DATA2, 0, 0);
		if (n==-1)
		{
			break;
		}
	}
	
	// TODO: 在此添加控件通知处理程序代码
}


void CMyDlg::OnBnClickedButton4()
{
	::SendMessage(m_hWnd, WM_DATA4, 0, 0);
	// TODO: 在此添加控件通知处理程序代码
}


void CMyDlg::OnBnClickedButton5()
{
	::SendMessage(m_hWnd, WM_DATA5, 0, 0);
	// TODO: 在此添加控件通知处理程序代码
}

其他的都在上面附上了

上传的附件:
2020-10-7 18:18
0
雪    币: 22
活跃值: (443)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
7
三一米田 void&nbsp;CMyDlg::OnBnClickedButton3() { for&nbsp;(int&nbsp;i&nbsp;=&nbsp;0;& ...
3Q!
2020-10-7 20:11
0
雪    币: 1657
活跃值: (924)
能力值: ( LV3,RANK:20 )
在线值:
发帖
回帖
粉丝
8
不愧是大佬,比我以前写的那个直接检测数组用算法消除的强多了。
2020-10-11 09:58
0
雪    币: 1027
活跃值: (251)
能力值: ( LV2,RANK:15 )
在线值:
发帖
回帖
粉丝
9
拜读~~大佬
2020-10-11 14:07
0
雪    币: 763
活跃值: (4923)
能力值: ( LV9,RANK:160 )
在线值:
发帖
回帖
粉丝
10
夜航星 不愧是大佬,比我以前写的那个直接检测数组用算法消除的强多了。

大佬牛皮!~早我们N年呀

最后于 2020-10-11 16:41 被三一米田编辑 ,原因:
2020-10-11 16:36
0
雪    币: 12332
活跃值: (5103)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
11

都是大牛淫,学习了
喜欢视频式,好保存
2020-10-13 18:52
0
雪    币: 52
活跃值: (93)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
12
学习了!
2020-11-10 23:50
0
雪    币: 110
活跃值: (299)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
13
 一个简单的游戏,分析就这么复杂,好厉害
2020-11-17 18:03
0
雪    币: 2280
活跃值: (3823)
能力值: ( LV6,RANK:81 )
在线值:
发帖
回帖
粉丝
14

大佬牛逼,我写的是图像识别的辅助

最后于 2020-11-17 18:34 被KingSelyF编辑 ,原因:
2020-11-17 18:34
0
雪    币: 299
能力值: ( LV1,RANK:0 )
在线值:
发帖
回帖
粉丝
15
郁金香第一个WG教程。不过实现模式不一样
2021-8-24 19:52
0
游客
登录 | 注册 方可回帖
返回
//