QQ连连看6.0 MFC程序
1、找到原程序exe
1 、加壳:脱壳
2、多进程:使用进程遍历工具分析;调试 ,CreateProcessA/W下断分析
2、去广告
API:DialogBoxA/W、CreateWindowExA/W、WinExec、CreateProcessA/W
ShellExecuteA/W WriteProcessMemory
3、写外挂
1、分析+算法+模拟点击
找到连连看数组,使用算法完成自动连接
模拟点击对应位置,实现一键秒杀
2、利用游戏本身功能函数完成一键秒杀
分析道具,使用指南针完成自动连接
找到消除CALL,实现一键秒杀
需要分析的数据及代码:
连连看数组、指南针call、消除call
需要使用的工具:
Ollydbg、Cheat Engine、PEiD、Vistual Studio
需要使用的技术:
1、MFC DLL(新建-->MFC动态链接库-->DLL类型:静态链接)
使用MFC DLL,不需要自己写DLLMain的case,直接写在Initstance函数中即可,且调试时使用Cstring比较方便
2、SetWindowLong
修改窗口回调函数,在自己的窗口回调函数中处理快捷键响应。
3、CallWindowProc
调用指定窗口回调函数
4、多线程
分析思路:
找到原程序
先去广告
分析数据
搭建框架测试
分析道具call,测试
分析消除call,测试
1.样本概况
1.1 应用程序信息
应用程序名称:QQ连连看
MD5值:05C759844703E7D4822DDE966284ABAB
SHA1值:48C1D825C230196BD962AC294FA1990B6D41AD7F
1.2 分析环境及工具
系统环境:win7 32位
工具:Ollydbg、Cheat Engine、010Editor、PEiD、PCHunter、Vistual Studio
1.3 分析目标
启动qqllk.exe
1、找到程序本身的exe
2、去掉程序中的广告
3、写一个QQ连连看的外挂
2.具体分析过程
2.1 分析过程
2.1.1 找到程序本身的exe
启动qqlik.exe,点击开始游戏,会有弹窗弹出,同时使用PCHunter查看进程,发现出现一个qqllk.ocx的进程:
使用PEiD查看qqllk.ocx信息,发现其加了壳: ASPack 2.12 -> Alexey Solodovnikov
点击继续之后,会直接进入游戏,这时候我们使用PCHunter查看进程,发现刚才的qqllk.ocx自动退出了,有一个新的进程和文件中的一个一样:kyodai.exe
关闭qqllk.exe后,游戏还在运行,kyodai.exe应该是游戏本身的exe,但是直接点击该程序,无法运行:
使用PEiD对kyodai.exe进行查看,发现其连接器版本是6.0,是一个MFC的程序:
2.1.2 去掉程序中的广告
启动qqllk.exe,点击开始游戏之后,首先是弹出一个小窗口,游戏是在点击小窗口上的继续之后开始运行的,直接点击对应exe文件却无法运行。猜测是点击继续后,qqllk.ocx对kyodai.exe进行了修改,修改完之后,游戏才正常运行。
使用OD对qqllk.exe进行分析,因为开始游戏后会多一个进程qqllk.ocx,所以会用到CreateProcessA/W函数,在Ctrl+G搜索该函数,然后在函数处下断点,运行程序,点击开始游戏,看是否在断点处停止。
程序在CreateProcessW处停下了,单步运行到CALL,弹出了广告弹窗,说明该函数创建了进程qqllk.ocx。点击继续,该进程自动退出,创建了进程kyodai.exe,直接开始了游戏,并没有在创建进程的地方停下来。
再用OD附加qqllk.ocx进行分析,因为点击继续之后出现了一个新的进程:kyodai.exe,同样会用到CreateProcessA/W函数
搜索到两个函数,都下断点,然后运行程序,点击继续,程序是在CreateProcessA断下来的,说明在此创建了游戏进程。再看栈区,发现CreationFlags=CREATE_SUSPENDED,说明创建后进程挂起了。
挂起之后,应该是对kyodai.exe进行了修改,然后才能继续运行,搜索能读写远程进程内存的API:WritePorcessMemory,下断点,程序运行后在断点处停下来:
观察栈区,发现要修改的内存首地址是:0x43817A,要写入的大小为一个字节。
修改完内存后,应该使用唤醒函数ResumeThread将刚才挂起的进程唤醒,先在该函数处下断点,便于后续分析。
使用OD附加kyodai.exe,找到该地址,发现该地址上的数据是01,进入函数查看。
继续运行qqllk.ocx,观察kyodai.exe中0x43817A上数据的变化,发现其变成了0,而且进入函数,函数也发生了变化,已经写入成功。
此时进程还是挂起状态,继续运行qqllk.ocx,唤醒线程,在kyodai.exe的入口点断了下来,继续运行程序,游戏开始运行。
根据上面的分析,我们发现程序能运行起来,就是将0x0043817A上的数据1修改为了0。我们要想去掉广告直接运行kyodai.exe程序的话,可以使用010Editor对其进行手动修改。
使用LordPE找到偏移量,然后使用010Editor打开kyodai.exe程序,另存一份,在对应的偏移的位置将1修改为0,然后保存。
保存后,打开修改后的程序,能够直接进入游戏。
2.1.3 写一个QQ连连看的外挂
该游戏写外挂的时候,有两种思路:1、找到连连看数组,然后用算法自动连接,模拟点击,完成一键秒杀。2、利用游戏本身的函数,分析游戏道具,使用道具完成自动连接,找到消除CALL,完成一键秒杀。第一个是侧重算法,第二种是侧重逆向,这里采用第二种方法。
首先我们需要先找到游戏的数组,每次点击练习,地图都会进行刷新,程序应该使用了随机函数rand,搜索该API下断点,然后运行程序点击练习,会在断点处停下:
去K窗口中看栈回溯,看函数的调用关系,进入相应的函数,然后下断点,将之前rand处的断点进行删除。
断点下完之后运行程序,点击练习刷新地图,程序会在第一个断点(0x41A080)停下来。F7进去之后,有Start.wav注释,以及Sound字符串,应该是初始化游戏的。再往下跟踪,发现到了第二个断点处,第二个断点调用了rand函数。随机函数执行后,往下跟踪,在0x0041cb20进入该函数,发现里面循环调用了rand函数,应该是对地图进行随机填充操作。
该函数只push了一个参数1,又因为该程序是MFC程序,是使用C++代码写的,观察寄存器ECX的值,ECX会传递一个this指针,右键ECX数据窗口中跟随:
第一个值里面是函数地址,第二个是成员变量,看0x0012BB50这个地址,看其里面的内容,发现是有大量00、01
猜测可能是地图的数组,继续运行程序看其是否发生变化:
里面数据发生了变化,而且观察数据和游戏中的物体能够对应上,说明就是地图的数组,数组基址是0x0012BB50,地图的元素应该是从0x0012BB58开始的。
找到数组之后,就需要找调用指南针的CALL了。因为指南针要找两个一样的元素肯定会访问数组所在的内存,我们让程序运行起来,下内存访问断点,然后点击指南针,程序会断下来,在栈回溯中有CALL是调用指南针的CALL
将栈回溯中的调用都下断点,删除内存访问断点,将程序运行起来,其中0x004292A5一直中断,应该不是要找的,0x40CACA也不是。 在0x0041DE5C断点处,其栈中参数是:0,0,F0
这个CALL可能是调用指南针的CALL,继续运行程序,在Ox41E76C处中断,看栈中:
数据窗口中跟随,继续运行程序,观察其数据窗口中变化:
指南针的作用找到相同元素的两个坐标,我们猜测这里的数据是两个坐标(1,7)、(2,1),观察游戏中地图元素,发现这个两个坐标上的物体是一样的。因此确定0x0041DE5C处的CALL是使用指南针道具,Ox0041E76C处的CALL是获取两个可以连接的坐标。
我们可以写代码调用0x0041DE5C这个地址的CALL,即:CALL 0x0041E691,使用指南针道具完成自动连接。我们需要对ECX进行溯源,再传入三个参数:0,0,F0。
使用Cheat Engine,搜索ESI的地址:
找到三个绿色的基址:0x0045DCF8、0x0045DEBC、0x0047FDE0,在OD中分别查找常量,发现0x0045DEBC用的比较多,应该是个全局对象,编写代码进行测试。
同样的方法,我们去找到调用炸弹道具的CALL。当游戏中两个炸弹被消除后,道具栏会出现炸弹道具,下内存访问断点,然后点击炸弹道具,程序会断下来,我们看栈回溯找调用炸弹的CALL:
结果发现,调用炸弹的CALL也是0x41DE5C地址上的CALL,说明这个CALL是使用道具,调用炸弹时传入的参数:0,0,F4。
指南针的自动连接完成了,下面需要找到消除CALL,方式和上面差不多。因为消除的时候会访问数组内存,将相同的元素清为0,所以可以在要消除元素的地方下内存写入断点,消除的时候会断下来:
找到了清0的操作指令,看栈回溯,下断点,然后运行程序:
测试之后可以找到3个有用的消除CALL ,分别看其传入参数的个数。0x0041B4B7上的CALL有七个参数:
先push了2个0,由将当前方块数量减去2后push入栈,又push了1,2,又push了0x0035DBC8,该地址上有要消除的两个方块的坐标,最后push了一个0。有的参数不确定什么意思,继续看下一个。在0x0041AB34上的CALL有六个参数:
先push了一个固定值4,又push了一个的地址,右键跟随数据窗口,发现该地址上保存的是两个点的坐标(测试后发现是用于消除时的连接效果),然后分别push有两个地址:00129BEC、00129BF4,地址上分别是两个点的坐标,又push了地图数组的地址0x0012BB50,最后push了一个0。我们可以写代码调用这个6个参数的消除CALL,来进行消除操作。
将获取两个相同方块坐标的CALL和消除CALL的操作一起用,就能达到一键消除的效果,再写入循环,就能实现一键秒杀的功能。
2.1.4 功能展示
功能一:无限指南针
功能二:单次消除
功能三:一键秒杀
功能四:无限炸弹
2.2 测试代码(如有代码验证贴出关键代码
//回调函数
LRESULT CALLBACK WindowProc(_In_ HWND hWnd, _In_ UINT Msg, _In_ WPARAM wParam, _In_ LPARAM lParam)
{
//无限指南针
if (Msg == WM_DATA1)
{
__asm
{
mov eax, 0x0044CE70 // [0x0012A1F4 + 0x494]=[0x0012A688]=0044CE70
mov ecx, 0x0045DEBC // 该地址上的值是:0x0012A1F4
mov ecx, [ecx] // ecx=0012A1F4
lea ecx, [ecx + 0x494] // ecx=0x0012A688
push 0xF0
push 0x0
push 0x0
mov eax, 0x0041E691 //[0x0044CE70 + 0x28]=[0x0044CE98]=0x0041E691
call eax
}
return DefWindowProc(hWnd, Msg, wParam, lParam);
}
//无限炸弹
else if (Msg == WM_DATA3)
{
__asm
{
mov ecx, 0x0045DEBC
mov ecx, [ecx]
lea ecx, [ecx + 0x494]
push 0xF4
push 0x0
push 0x0
mov eax, 0x0041E691
call eax
}
return DefWindowProc(hWnd, Msg, wParam, lParam);
}
//消除
else if (Msg == WM_DATA2)
{
//? 模拟两个局部变量,获取两坐标点
POINT p1 = { 0 };
POINT p2 = { 0 };
_asm
{
//ecx
mov ecx, 0x0045DEBC //该地址上值是:0x0012A1F4
mov ecx, [ecx] //ECX=0x0012A1F4
lea ecx, [ecx + 0x494] //传地址,ECX=[0x0012A1F4 + 0x494]= 0x0012A688
mov ecx, [ecx + 0x19F0] //程序运行时,中间有一次操作:ESI=ECX,
//所以这里的是:ECX=[ECX + 0x19F0]=[0x0012C078]=每次不一样
//两个坐标
lea eax, p1.x
push eax
lea eax, p2.x
push eax
mov eax, 0x0042923F
call eax
}
//? 获取点后,调用消除CALL进行消除
if (p1.x == 0 && p1.x == p1.y)
{
return -1;
}
_asm
{
//构造ECX
mov ecx, 0x0045DEBC
mov ecx, [ecx] // [ECX] = 0x0012A1F4
//1、固定值
push 0x4
//2、这个地址一直在变,保存的是两个点的坐标,用于消除时的连线。
//已经知道ECX,需要由ECX构造该地址。
lea eax, [ecx + 0x494] //eax = 0x0012A688
mov eax, [eax + 0x19F0] //eax=[0x12A688+0x19F0]上的值,每次都在变化
add eax, 0x30 //再加上0x30,才和源程序的一样
push eax
//3,4、两个坐标
lea eax, p1.x
push eax
lea eax, p2.x
push eax
//5、地图数组地址 0x0012bb50 固定??
// lea eax, [ecx + 0x494] //eax = 0x0012A688
// mov eax, [eax + 0x19F0] //eax=[0x12A688+0x19F0]上的值,跟随数据窗口
// mov eax, [eax+0x4] //跟踪后,第二个是0x0012bb50,所以加4
// push eax
mov eax,0x0012BB50
push eax
//6、固定值 0
push 0
//调用call
mov eax, 0x0041C68E
call eax
}
return DefWindowProc(hWnd, Msg, wParam, lParam);
}
return CallWindowProc(g_OldProc, hWnd, Msg, wParam, lParam);
}
3.总结
在去广告的时候,涉及到了多线程的问题,分析过程主要是通过搜索关键API:CreateProcessA/W、WritePorcessMemory、ResumeThread等,分析后发现是对游戏程序进行了远程内存修改,观察栈,找到了修改的位置以及修改的大小,验证后找出了修改的数据,并通过010Editor手动修改相应的位置,去掉了广告。对外挂辅助的编写,主要是对CALL的分析。主要思路是利用游戏本身的指南针道具的功能,通过下内存访问断点,找到调用指南针的CALL, 找到能连接两个相同方块的坐标的CALL,通过下内存写入断点,再找到消除的CALL。然后模拟编写代码,最终达到目标。
总的来说,连连看比较复杂的一个逆向分析项目,需要耐心去分析。以后还要不断练习,才能提升分析能力。