天天看点

扫雷逆向分析1.样本概况2.具体分析过程3.总结

1.样本概况

1.1 应用程序信息

  ----------------------

应用程序名称:扫雷(基于Windows xp系统)

MD5值:16A4FD569A3EB5CEBEB3DA99EF1D17E1

SHA1值:31A1A89BA067EA95F117754818429D6D8E8E59CF

简单功能介绍:

1. 游戏模式:分为初级,中级,高级,自定义模式

2. 自定义:可以设置游戏界面的高度(max=24),宽度(max=30),雷数(max=667)

3. 游戏窗口:显示有雷数,计时,和需要探测点击的雷区

4. 游戏规则:鼠标点击不为雷的区域会显示数字,提示周围的雷数,右键点击可以下标记,当所有不为雷的区域被点击即成功,点击到雷则游戏失败,点击笑脸可以重置当前雷区

1.2 分析环境及工具

系统环境:win7 Professional 32位(15pb实验环境)

工具:

1. DIE(PE文件分析)

2. Cheat Engine(搜索数据)

3. VS2019(编写DLL程序)

4. MFCInject(dll注入工具)

5. DebugView(输出调试信息)

6. OllyDbg(动态调试分析)

7. Spy++(获取窗口信息)

8. Hash(计算MD5,SHA1)

1.3 分析目标

实现扫雷外挂:

    1. 鼠标移动悬停在雷区时,在窗口右上角显示雷区坐标及当前区域是否有雷

    2. 实现F5一键扫雷功能,模拟点击所有不为雷的区域完成游戏

2.具体分析过程

2.1 DIE查壳

2.1.1 样本基础信息

链接器版本:Microsoft Linker7.0

编写工具:从链接器版本判断为VS2003

类型:32位

扫雷逆向分析1.样本概况2.具体分析过程3.总结

2.1.2 样本详细信息

分析样本导入库,为使用SDK编写的程序

扫雷逆向分析1.样本概况2.具体分析过程3.总结

2.2 CE搜索数据

2.2.1 获取关键数据

获取数据基本步骤

扫雷逆向分析1.样本概况2.具体分析过程3.总结

多次修改获取到三个的高,宽,雷数的数据,且在CE中地址为绿色(静态地址,全局),有多份可能为(代码冗余,实际显示需要)

扫雷逆向分析1.样本概况2.具体分析过程3.总结

2.2.2 验证数据有效性

在VS2019中编写MFC DLL(静态编译,不依赖环境),关键调试代码

  1. //调式获取到的高度,宽度,雷数变量地址  
  2. PDWORD pHeigth = (PDWORD)0x01005338;  
  3. PDWORD pWidth = (PDWORD)0x01005334;  
  4. PDWORD pMineCount = (PDWORD)0x01005330;  
  5. //显示错误  
  6. BOOL CheckResult(BOOL result, WCHAR* str)  
  7. {  
  8.     if (!result)  
  9.     {  
  10.         OutputDebugString(str);  
  11.         return FALSE;  
  12.     }  
  13.     return TRUE;  
  14. }  
  15. //保存窗口句柄  
  16. HWND g_hWnd = 0;  
  17. //原来的回调函数  
  18. WNDPROC OldWinProc = 0;  
  19. //新的窗口回调函数  
  20. LRESULT CALLBACK NewWndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)  
  21. {  
  22.     switch (uMsg)  
  23.     {  
  24.         //如果是键盘消息  
  25.         //一键扫雷思路:遍历扫雷数组中的元素,判断不为雷的区域,模拟鼠标点击  
  26.         case WM_KEYDOWN:  
  27.         {  
  28.             //f5  
  29.             if (wParam == VK_F5)  
  30.             {                  
  31.                 CString str;  
  32.                 str.Format(L"高:%d,宽:%d,雷数:%d\n", *pHeigth, *pWidth, *pMineCount);  
  33.                 OutputDebugString(str);  
  34.              }  
  35.         }  
  36.     }  
  37.     //CallWindowProc 不处理的消息让原来的回调函数处理  
  38.     return OldWinProc(hWnd, uMsg, wParam, lParam);  
  39. // CMFCmineApp 初始化  
  40. BOOL CMFCmineApp::InitInstance()  
  41. {  
  42.     CWinApp::InitInstance();  
  43.     //1. 通过查找窗口获取窗口句柄 spy++  
  44.     //通过窗口名,窗口类名获取窗口句柄  
  45.     g_hWnd = ::FindWindow(L"扫雷", L"扫雷");  
  46.     //判断是否找到  
  47.     if (CheckResult(g_hWnd != 0, L"未找到扫雷窗口\n") == 0)  
  48.         return 0;  
  49.     //2. 设置窗口回调函数  
  50.     OldWinProc = WNDPROC(SetWindowLong(g_hWnd, GWL_WNDPROC, LONG(NewWndProc)));  
  51.     //判断是否修改成功  
  52.     if (CheckResult(OldWinProc != 0, L"修改回调函数失败\n") == 0)  
  53.         return 0;  
  54.     return TRUE;  

使用注入工具MFCInject注入编写的dll,并打开ViewDebug查看调试信息,判断数据有效

扫雷逆向分析1.样本概况2.具体分析过程3.总结

2.3 OD分析代码逻辑

2.3.1 CE中找到访问数据位置

这时可以跟踪数据,右键选择是谁访问了这个数据(原理:下断点)

扫雷逆向分析1.样本概况2.具体分析过程3.总结

这时笑脸重置按钮就发挥作用了,点击,在CE弹出的窗口中查看

扫雷逆向分析1.样本概况2.具体分析过程3.总结

2.3.2 OD中分析

在OD中跳转到访问关键数据的地址进行分析

一共三处,其他两处查看与界面相关,不做具体分析,分析地址为0x01002EE4

扫雷逆向分析1.样本概况2.具体分析过程3.总结

对代码逻辑进行分析,判断为获取高宽对雷区进行重置的操作,这里可以得到雷区数组的初始地址,雷区总大小

扫雷逆向分析1.样本概况2.具体分析过程3.总结

2.3.3 获取雷区信息

根据内存窗口对照扫雷窗口区分雷区属性(最好宽度对应)

雷区基地址:0x01005430

空白:0x0F

雷:0x8F

边界:0x10

点击后的周围雷数:0x40+周围雷数

爆炸的雷:0x8A

扫雷逆向分析1.样本概况2.具体分析过程3.总结
扫雷逆向分析1.样本概况2.具体分析过程3.总结

2.3.4 调式输出雷区数组

在DLL中编写遍历雷区数组的代码进行调试,关键代码

  1. //如果是键盘消息  
  2.        //一键扫雷思路:遍历扫雷数组中的元素,判断不为雷的区域,模拟鼠标点击  
  3.        case WM_KEYDOWN:  
  4.        {  
  5.            //f5  
  6.            if (wParam == VK_F5)  
  7.            {               
  8.                int count = 0;  
  9.                //循环遍历整个雷区数组  
  10.                for (int y = 1; y < *pHeigth + 1; y++)  
  11.                {  
  12.                    //保存每一行雷区信息  
  13.                    CString S = 0;  
  14.                    //遍历行  
  15.                    for (int x = 1; x < *pWidth + 1; x++)  
  16.                    {  
  17.                        //获取信息  
  18.                        PBYTE pCode = PBYTE(dwBaseAddr + y * 32 + x);  
  19.                        //雷计数  
  20.                        if (*pCode == MINE)  
  21.                            count++;  
  22.                        //存放每个位置信息  
  23.                        CString s;  
  24.                        s.Format(L"%02x ", *pCode);  
  25.                        S += s;  
  26.                    }  
  27.                    //调试输出  
  28.                    OutputDebugString(S);  
  29.                }  
  30.                //输出雷数  
  31.                str.Format(L"雷数:%d", count);  
  32.                OutputDebugString(str);  
  33.            }  
  34.            break;  
  35.        }  

ViewDebug调试信息如下

扫雷逆向分析1.样本概况2.具体分析过程3.总结

2.3.5 查找消息回调

SPY++查找

扫雷逆向分析1.样本概况2.具体分析过程3.总结

也可以使用OD中W窗口右键点击窗口查看ClassProc

扫雷逆向分析1.样本概况2.具体分析过程3.总结

或者在关键API下断点回溯找到

扫雷逆向分析1.样本概况2.具体分析过程3.总结

2.3.6 分析回调函数

在OD中找到回调函数的位置,可以初步判断消息的点击消息为WM_LBUTTONDOWN

技巧:Ctrl+A - 右键分析 - 假定参数选择WndProc

设置消息断点,选择需要设置断点的消息(WM_LBUTTONDWON)

扫雷逆向分析1.样本概况2.具体分析过程3.总结

回调函数原型为

LRUSULT CALLBACK WinProc(HWND hWnd, UINT uMes, Wparam wPAram, LPARAM lParam){}

参数2为消息类型,参数4为WM_LBUTTONDWON的鼠标坐标

高位y坐标HIWORD(lParam),低位x坐标LOWORD(lParam)

消息回调中初步查看操作了保存鼠标坐标lParam的代码

扫雷逆向分析1.样本概况2.具体分析过程3.总结

该处经查看分析为判断点击的代码,不做具体分析

扫雷逆向分析1.样本概况2.具体分析过程3.总结
扫雷逆向分析1.样本概况2.具体分析过程3.总结

再次找到操作WM_LBUTTONDOWN消息操作坐标的代码,对获取到的鼠标坐标处理

扫雷逆向分析1.样本概况2.具体分析过程3.总结

根据处理完的值与点击的雷区界面对比,猜测为此处操作将客户区坐标转换为数组坐标

扫雷逆向分析1.样本概况2.具体分析过程3.总结

与扫雷窗口点击的区域对应

扫雷逆向分析1.样本概况2.具体分析过程3.总结

2.4 完成功能的实现

2.4.1 鼠标悬停提示

在注入DLL中的消息回调响应鼠标移动,根据分析的转换算法将鼠标坐标转换成数组坐标,判断当前雷区数组没区域是否为雷,在标题栏提示

主要代码如下

  1. //鼠标移动显示信息  
  2.         case WM_MOUSEMOVE:  
  3.         {  
  4.             CString sCaption;  
  5.             //x,y坐标  
  6.             DWORD x = LOWORD(lParam);  
  7.             DWORD y = HIWORD(lParam);  
  8.             //转换为数组坐标  
  9.             x = (x + 4) >> 4;  
  10.             y = (y - 0x27) >> 4;  
  11.             //在雷区范围内  
  12.             if (0 < x && x <= *pWidth && 0 < y && y <= *pHeigth)  
  13.             {  
  14.                 //如果判断为雷  
  15.                 if (*(PBYTE(dwBaseAddr + y * 32 + x)) == MINE)  
  16.                     sCaption.Format(L"扫雷 (x:%d,y:%d) 有雷", x, y);  
  17.                 else  
  18.                     sCaption.Format(L"扫雷 (x:%d,y:%d) 无雷", x, y);  
  19.                 SetWindowText(g_hWnd, sCaption);  
  20.             }   
  21.         }  

2.4.2 F5一键扫雷

在注入DLL中的消息回调响应F5按键消息,根据分析的转换算法将雷区数组坐标转换成客户区坐标,判断当前区域是否为雷,如果不是则发送消息模拟点击完成游戏

主要代码如下,这里需要注意要同时发送鼠标左键按下与抬起的消息,窗口才能完成响应整个模拟点击的操作

  1.                    //如果不是雷 F5一键扫雷功能  
  2.                  if (*pCode != MINE)  
  3.                  {  
  4.                      //首先将x,y数组坐标转换为客户区坐标  
  5.                      int xPos, yPos;  
  6.                      yPos = (y << 4) + 0x27;  
  7.                      xPos = (x << 4) - 4;  
  8.                      //前面为低字节  
  9.                      DWORD lparam = MAKELPARAM(xPos, yPos);  
  10.                      //给扫雷发送消息 模拟点击  
  11.                      SendMessage(g_hWnd, WM_LBUTTONDOWN, 0, lparam);  
  12.                      SendMessage(g_hWnd, WM_LBUTTONUP, 0, lparam);  
  13.                  } 

2.5 实现效果

2.5.1 效果截图

扫雷逆向分析1.样本概况2.具体分析过程3.总结

3.总结

很久之前玩扫雷,就想过一键扫雷的情景,作为第一个逆向的程序,还是从中获取到了很多的。首先第一次系统性的分析一个文件,从链接器信息,编写语言到使用的库,关键的逻辑判断及调式到实现,整个流程下来算是比较完整的,感觉逆向测试的过程还是比较需要技术的。

对逆向分析中使用的工具的了解和掌握也是比较重要的,不同的工具可以完成特定的分析功能,熟练的使用可以节约大量的时间,要让他们成为分析时的左膀右臂,还是就是对回调函数的理解及分析,基于消息驱动使得消息处理尤为重要,是用户与程序交互的重要枢纽,很多逻辑判断也是在其中实现的。

作为第一次分析是没有什么思路的,经过视频的讲解还是在脑子里有了个大概的过程,这次的扫雷是通过使用CE从数据入手,找到使用数据的地方,从而在OD动态分析逻辑,找到关键位置,再基于消息的响应处理找到是实现坐标的转换算法,期间使用了注入和Debugview的功能完成了动态调试,从而实现了整个功能(实际还是利用原程序完成自己功能的实现,总结下就是用最小的开销骗过程序来实现自己想要的效果)。

整体感觉还是需要多练习,基础要扎实,这样才可以更快的分析(基于知识和经验的猜测)到关键点做判断。