QQ连连看6.0 逆向分析

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。然后模拟编写代码,最终达到目标。

    总的来说,连连看比较复杂的一个逆向分析项目,需要耐心去分析。以后还要不断练习,才能提升分析能力。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值