吾爱破解 - 52pojie.cn

 找回密码
 注册[Register]

QQ登录

只需一步,快速开始

查看: 19286|回复: 62
收起左侧

[原创] 连连看辅助的实现以及关键算法的逆向

  [复制链接]
三年三班三井寿 发表于 2019-3-7 16:45
论坛上已经有不少这个小游戏的分析了,我也试着用了差不多两个周日的时间来分析它(萌新,分析的很慢TAT),有了一些不同的思路。
先说一下大概的思路吧,辅助工具的编写其实很简单,甚至都不用去看反汇编,利用游戏里的道具(炸弹)去实现就可以了。通过CE找出道具栏的位置,炸弹的值,发送消息模拟点击炸弹就行。
实现后完成,又感觉太投机取巧了,就又自虐了一下,把它判断如何链接的算法给逆了出来。


游戏版本:百度qq连连看单机版第一个
               
             在分析游戏之前,我们先去一个广告

             QQ图片20190307134720.png
            不对,应该是多个广告

             QQ图片20190307135154.png
            打开qqllk后直接会弹出第一个广告,然后第二个第三个,最后才会进入游戏。
            通过进程名的查看,真正游戏的进程名是kyodai.exe,我们直接去打开kyodai,会发现直接是打开不了的。
            观察后发现,游戏启动的过程中一共会有三个进程出现,一个就是一开始的qqllk.exe,然后会开启一个qqllk.ocx的进程,最后才会启动kyodai.exe
            也就是说kyodai.exe是qqllk.ocx这个进程启动的,而它肯定对游戏进程进行了解密,通过API断点,可以定位到解密的点
               QQ图片20190307140748.png
             仅仅是在0x43817A处修改了一个字节,
qqllk.ocx
在此之前已经创建了
kyodai.exe进程,不过是以挂起的方式创建的,解密完成后再恢复运行。

             最后通过16进制编辑器手动修复一个字节就可以了,当然也可以脱壳后dump出来。去广告就到这结束了,具体细节也不细说了。

            


             接下来,进入主题

             1.png

            从目标入手,想要实现这个小游戏的辅助。

            最直接的方法就是找到各个数据的基址(前提是它不会变)

            好吧,我们先找找看,假定它是不变的,至少这游戏没有随机基址

             timg.jpg

           
Cheat Engine
启动

           首次搜索,再次搜索......

           等等,我们要找啥

          基址

      地图基址 和 道具基址

          假定空地就是0,其他有具体的值,然后搜几次,发现,没有符合要求的地址

          这里默认的四字节是搜索不到的,切换到单字节就能找到。

          几番确认后,我们能够确定地图数组的基地址为0x0012BB58,道具栏的基地址为0x0012AC5E

         

          有了基址了不起啊?

          QQ图片20190307143535.png

         你们知道我要说啥了吧

         

        下一步,我们需要找到炸弹

         QQ图片20190307144004.png

        OllyDbg启动,ctrl+G,
0x0012AC5E


       观察道具基址的数值,我们猜测12AC5E处的值是道具(指南针)数量,下面6E处是第二个道具数量,7E,8E等还没有道具(初始会有3个指南针和3个重置道具)

        然后通过多次修改数据验证猜想,得出最后结论

      5E,6E,7E,8E,9E,AE,BE处为道具栏各个道具数量,

          每行末尾有一个标志位,通过F1,F2,F3...来标志下一格道具的种类,炸弹为F4(0表示下一个没有道具)

      还有一些其他的标志位,比如默认显示什么的,不过根我们这里没啥关系了

        


        我们已经找到了道具栏的位置,也找到了炸弹的标志

        接下来,就可以去写
外挂
了,实现起来也十分简单,将特定道具格子改成炸弹,然后模拟鼠标点击事件

        实现的方式也有很多种,我们在这里可以直接使用SetWindowLong来修改游戏本身的消息回调,来实现类似于HOOK的功能

         QQ图片20190307150752.png

        不过在测试的时候,发现即使已经修改了消息回调,也没法响应我们的键盘消息,可能是这游戏程序里本身就有钩子

        不过好在,鼠标点击事件是能够获取到的,不然我们就得去找其他方法了。
         QQ图片20190307151807.jpg
         我们直接在道具栏指定位置点击,得到真实点击事件得wParam(1)与lParam(高位和地位对应着鼠标坐标)
至此,基本就完成了,所有功能都围绕这个来实现就可以了。
修改道具栏道具后不会立马显示道具,需要鼠标移动到附近,所以也要模拟一个鼠标移动得消息
(将道具栏指定位置修改为炸弹(我是开启得时候直接将道具栏得道具顺序都给固定了),模拟鼠标点击炸弹)

[C++] 纯文本查看 复制代码
	BYTE old_count[1];
	LPARAM lParam = (LPARAM)0x00c802d5;
	ReadProcessMemory(INVALID_HANDLE_VALUE, (LPVOID)0x12AC9E, &old_count[0], 1, &dwSize);
	WriteProcessMemory(INVALID_HANDLE_VALUE,(LPVOID)0x12AC9E, "\xFF", 1, &dwSize);
	::SendMessage(g_hwnd,WM_LBUTTONDOWN, 1, (LPARAM)lParam);
	WriteProcessMemory(INVALID_HANDLE_VALUE, (LPVOID)0x12AC9E, &old_count[0], 1, &dwSize);
	::SendMessage(g_hwnd, WM_MOUSEMOVE, 0, (LPARAM)lParam);


         QQ图片20190307152938.png QQ图片20190307152929.png

            











爽过之后,进入
11timg.jpg
自虐模式
         虽然这只是一个小游戏,并没有涉及很多数据结构和算法,但要去逆它得的关键算法对于我这种小白来说,还是很不友好的。
           含泪打开OD
第一步,定位
           之前我们拿道具开刀,现在我们仍旧从道具下手,这次的主角是指南针,因为这个道具里面肯定会包含链接的算法,去判断两个点能否链接。
           有了前期的信息,定位其实很快。直接在指南针地址上下断,通过栈回溯去找调用的函数。
           


             待分析函数:   QQ图片20190307154819.png

            该函数传入了两个参数,用于接受返回的两个点坐标
            该函数实现:    QQ图片20190307155756.png
            它的内部实际上是两层循环,逻辑其实很简单(但还是看了好久),从地图数组的第一个点开始取,依次比较两个点类型(若是空地则跳过,若类型相同,则去比较判断能否链接)
           
              在下面一层的函数有判断类型的,检测类型的,还有判断两个点能否链接的,而最后一个其实就是整个游戏核心的算法,在这个函数内部仍有4个函数调用,不过后两个也都是基于前两个基础之上的

               判断能否链接的函数:   QQ图片20190307160436.png
              
首先它判断了两个点的y坐标是否相等,如果相等就进入到这个函数(其实其他情况也会进入这个函数,不过参数不一样),这种情况里面会判断两点中间有没有其他障碍物。

               QQ图片20190307160852.png



              接着返回上一层判断x坐标是否相等,然后进入另一个类似的函数。

              如果这两次判断都无效,则会进入到第三个算法,若第三个也无法判断正确,会进入到第四个算法

              其实后面两个算法也都是基于前两个上面的。

             QQ图片20190307161454.png             
             前两个一个是在横向的判断,一个是纵向
QQ图片20190307162845.png QQ图片20190307162853.png
               
         整理后的源码大概如下:
[C++] 纯文本查看 复制代码
BOOL IsXCheck(DWORD y, DWORD x1, DWORD x2, BOOL by1, BOOL by2)
{
	DWORD dwSmallx, dwBigx;
	dwSmallx = x2 < x1 ? (x2 + by2) : (x1 + by1);
	dwBigx = x2 < x1 ? (x1 - by1) : (x2 - by2);
	while (dwSmallx <= dwBigx)
	{
		if ((dwSmallx & 0x80000000) || dwSmallx >= 0x13 || y < 0 || y >= 0xB) return FALSE;
		BYTE dwAddr = *(PBYTE)((DWORD)dwAddrBase + y * 0x13 + dwSmallx);
		if (dwAddr > 0)return FALSE;
		++dwSmallx;
	}
	return TRUE;
}
BOOL IsYCheck(DWORD x,DWORD y1,DWORD y2,BOOL b1,BOOL b2)
{
	DWORD dwSmally, dwBigy;
	dwSmally = y2 < y1 ? (y2 + b2) : (y1 + b1);
	dwBigy = y2 < y1 ? (y1 - b1) : (y2 - b2);
	if (dwSmally > dwBigy)return TRUE;
	DWORD temp = dwSmally;
	dwSmally *= 0x13;
	do
	{
		if ((x & 0x80000000) || x >= 0x13 ||
			(dwSmally & 0x80000000) || dwSmally >= 0xD1)
			return FALSE;
		BYTE dwAddr = *(PBYTE)((DWORD)dwAddrBase + dwSmally + x);
		if (dwAddr > 0)return FALSE;
		dwSmally += 0x13;	++temp;
	} while (temp <= dwBigy);
	return TRUE;
}

后两个其实只是在调用前两个:
[C++] 纯文本查看 复制代码
BOOL AlgorithmA(POINT a, POINT b)
{
	for (DWORD i = 0; i < 0xB; i++)
	{
		BOOL loc1 = (i == a.y) ? TRUE : FALSE;
		BOOL loc2 = (i == b.y) ? TRUE : FALSE;
		if (!IsXCheck(i, a.x, b.x, loc1, loc2)||
			!IsYCheck(a.x, a.y, i, 1, loc1))
			continue;
		if (IsYCheck(b.x, b.y, i, 1, loc2))
			return TRUE;
	}
	return FALSE;
}
BOOL AlgorithmB(POINT a, POINT b)
{
	for (DWORD i = 0; i < 0x13; i++)
	{
		BOOL loc1 = (i == a.x) ? TRUE : FALSE;
		BOOL loc2 = (i == b.x) ? TRUE : FALSE;
		if (!IsYCheck(i, a.y, b.y, loc1, loc2) ||
			!IsXCheck(a.y, a.x, i, 1, loc1))
			continue;
		if (IsXCheck(b.y, b.x, i, 1, loc2))
			return TRUE;
	}
	return FALSE;
}

四个算法都分析出来后,再往上一层判断能否链接
[C++] 纯文本查看 复制代码
BOOL IsRemove(POINT a, POINT b)
{
	if (a.y == b.y&&IsXCheck(a.y, a.x, b.x, 1, 1)) return TRUE;
	if (a.x == b.x&&IsYCheck(a.x, a.y, b.y, 1, 1))return TRUE;
	if (AlgorithmA(a,b))return TRUE;
	if (AlgorithmB(a,b))return TRUE;
	return FALSE;
}

再往上就是那个循环遍历了
[C++] 纯文本查看 复制代码
BOOL GetPoint(POINT&a, POINT&b)
{
	for (DWORD i = 1 ,j=1; i < 0xD1; i++)
	{
		j = i;
		obj.a1.x = (i - 1) % 0x13;
		obj.a1.y = (i - 1) / 0x13;
		do
		{
			obj.b1.x = j % 0x13;
			obj.b1.y = j / 0x13;
			DWORD style1 = CheckStyle(obj.a1.x, obj.a1.y);
			DWORD style2 = CheckStyle(obj.b1.x, obj.b1.y);
			if (style1 == 0xff || style1==0|| style1==0x1)
				break;
			if (style2 == 0xff || style2 == 0 || style1 == 0x1)
			{
				j++;
				continue;
			}	
			if (style1 == style2 && IsRemove(obj.a1, obj.b1))
			{
				a = obj.a1;
				b = obj.b1;
				return TRUE;
			}
			j++;
		} while (j<0xD1);
	}
	return FALSE;
}




主要还原的代码都在按照反汇编看的,应该还有很多地方可以优化。

最终测试结果,与游戏本身指南针找到的是同样的两个点。
QQ图片20190307164345.png


1.png
QQ图片20190307154819.png
QQ图片20190307152938.png
QQ图片20190307152929.png
QQ图片20190307151807.jpg
QQ图片20190307143535.png
QQ图片20190307135154.png
QQ图片20190307134720.png
11timg.jpg
1.png
QQ图片20190307150752.png
QQ图片20190307144004.png
QQ图片20190307140748.png

免费评分

参与人数 42吾爱币 +46 热心值 +41 收起 理由
goupiluobo + 1 + 1 谢谢@Thanks!
conway + 1 + 1 谢谢@Thanks!
can0a + 1 用心讨论,共获提升!
gunxsword + 1 + 1 我很赞同!
2743793308 + 2 + 1 鼓励转贴优秀软件安全工具和文档!
西门吹菠萝啤 + 1 + 1 感谢发布原创作品,吾爱破解论坛因你更精彩!
↗↑↘↓↗↑↘ + 1 + 1 用心讨论,共获提升!
matali + 1 + 1 我很赞同!
siuhoapdou + 1 谢谢@Thanks!
Groza + 1 + 1 我很赞同!
k53247l2 + 1 谢谢@Thanks!
叁丫 + 1 + 1 谢谢@Thanks!
朔子哥哥 + 1 + 1 用心讨论,共获提升!
Avenshy + 2 + 1 用心讨论,共获提升!
ygzzhsn + 1 + 1 我很赞同!
Whitte + 1 + 1 我很赞同!
lglsad + 1 + 1 我很赞同!
ihappyes + 1 + 1 谢谢@Thanks!
dmxayjn + 1 + 1 谢谢@Thanks!
s13682621505 + 1 我很赞同!
黑龍 + 1 + 1 谢谢@Thanks!
x-kith + 1 + 1 我很赞同!
dreamlivemeng + 1 + 1 感谢发布原创作品,吾爱破解论坛因你更精彩!
WYWZ + 1 + 1 用心讨论,共获提升!
寒尘丶Coldust + 1 + 1 谢谢@Thanks!
410791007 + 1 我很赞同!
InMyMind + 1 + 1 欢迎分析讨论交流,吾爱破解论坛有你更精彩!
CrazyNut + 3 + 1 膜拜大佬
遗忘丶彻底 + 1 + 1 我很赞同!
wmsuper + 3 + 1 我很赞同!
ximendongkai + 1 + 1 我很赞同!
airwenlee + 1 + 1 用心讨论,共获提升!
iteamo + 1 + 1 热心回复!
m58758788 + 1 + 1 用心讨论,共获提升!
swjtu_ray + 1 用心讨论,共获提升!
reread + 1 + 1 我很赞同!
冷眸丶boocky + 1 + 1 我很赞同!
wadsq1081 + 1 + 1 竟然没有加分
Homely + 2 + 1 加油
涛之雨 + 2 + 1 没人评分?!破!
SxAni丶 + 2 + 1 我很赞同!
zyh666 + 1 + 1 大佬

查看全部评分

本帖被以下淘专辑推荐:

发帖前要善用论坛搜索功能,那里可能会有你要找的答案或者已经有人发布过相同内容了,请勿重复发帖。

涛之雨 发表于 2019-3-7 19:20
加精警告。。。
前排留名。
话说原来是这个原因一些小游戏打不开啊……
没分许过。。
话说既然知道原理了。回头试一下。。。估计原理差不多
大佬!收下一拜。先研究小游戏的去广告和启动还原。。。
算法回头再看。
zbr878458173 发表于 2019-4-9 12:27
JuncoJet 发表于 2019-3-8 10:52
大学的时候写过,三线程技能调用

你这个签名。。。。。。。。。。。是把所有的勋章都C下来了吗
viczc 发表于 2019-3-7 18:50
涛之雨 发表于 2019-3-7 19:22
码了这么多字楼主辛苦了。
楼主年龄呀,表情包都放上来了。

龙飞飞龙 发表于 2019-3-7 19:25
真诚的感谢楼主的辛苦分享
 楼主| 三年三班三井寿 发表于 2019-3-7 19:35
涛之雨 发表于 2019-3-7 19:20
加精警告。。。
前排留名。
话说原来是这个原因一些小游戏打不开啊……

不是大佬,贼萌
 楼主| 三年三班三井寿 发表于 2019-3-7 19:37

你楼下那位是大佬,俺是萌新
学会爱自己 发表于 2019-3-7 19:58
学习一下
艾莉希雅 发表于 2019-3-7 20:00
自摩尔庄园后……又一款童年被……
遇日不归 发表于 2019-3-7 20:18
这个可以学习下,收藏了!!
您需要登录后才可以回帖 登录 | 注册[Register]

本版积分规则

返回列表

RSS订阅|小黑屋|处罚记录|联系我们|吾爱破解 - LCG - LSG ( 京ICP备16042023号 | 京公网安备 11010502030087号 )

GMT+8, 2024-12-22 16:04

Powered by Discuz!

Copyright © 2001-2020, Tencent Cloud.

快速回复 返回顶部 返回列表