之前逆过XP下的扫雷程序,感觉XP下的扫雷很简单,但是发现网上对于Win7下的扫雷逆向很少很少,于是就试着继续逆一下Win7下的扫雷。这一逆发现难度提升了不只一个等级啊,经过两天的努力,终于整个逆完了它的扫雷算法。

首先在Win7下的扫雷不再是像XP一样在一开始就布置好雷区,这样我们就可以在一开始就读取雷区内存,比较坑的是win7下的扫雷是在你点击第一块儿方块时才开始布置雷区。这样我首先在rand函数下断点,发现有好多地方会调用rand函数,我把每个调用rand函数的地方下了断点,然后把一直在调用的rand函数的那几个函数断点给去掉,这样我们就找到了程序的突破口。

不断退出当前调用,并在上层函数的call调用处下断点,直到找到了一个疑是算法入口的函数。

跟进函数,又发现一个call,继续跟进

我发现这个函数便是调用rand函数的地方,估计核心就在此了,开干

我们要对每个call都倍加小心,需要都看一下,我们发现这里好像是一个申请数组空间并填充的操作

经过多次循环后,发现数组填充完毕,之后观察一下申请的数组空间中存储的东西

发现没有了00 01 09 0A四个值。因为我是点击的第一个方块,我们可以重新调试,点击其他方块试一下,发现这个数组会将点击方块周围的的9个值去掉(包括点击方块自己),这样我们就理解了,程序不会在第一次点击方块周围产生雷。

这时候我们估计就对这个程序有了一点点理解了,在点击第一块儿方块的时候,程序开始申请内存。这里它会有一个结构体存储了随机雷数组已用大小和总空间,然后生成一个数组,并将各个雷进行编号存入数组中。之后rand函数产生的随机雷就在这些数组中产生。接下来验证我们的想法:

跟进下一个call,发现这里申请数组空间,并存储随机出来的雷值

继续单步,发现有个小循环比较有意思

看一下rax存储了什么?

这好像是存储了多个数组的首地址啊,正好我们现在设定了9x9的雷区,这里正好9个地址,我们再跟进去这些地址看一下

这里+10处又存储了一个地址,继续观察,发现有个byte数组,存储了雷的状态,有雷就是1,无雷就是0

这个时候我们就基本搞明白了这个Win7下扫雷是怎么布置的了。可是问题来了,最初的记录雷区各个数组的地址从哪得啊?我们逆着代码去溯源。我们发现这个值是rax+0x10处的存储的值,而rax是rsi+0x58处存储的值,这个rsi是rcx作为上层调用函数传过来的参,我们走出这个函数看看这个参数从哪里得到。

我们找到了这样一个值,在FFCFAA38中存储了我们所想要的rsi的值。我们知道,这是一个全局变量,存储了rsi地址。但是这个值由于RSLR机制而导致每次地址不一样。我们有一种方法得到这个值,我们先看当前模块加载基地址,然后用FFCFAA38(全局变量地址)-FFC500000(当前模块加载地址)= AAA38(相对当前模块偏移)。这样我们可以用GetModuleHandle函数得到当前模块加载基地址,然后加上这个偏移AAA38就得到了全局变量地址。

这样我们就有了得到数组地址的方法:

Address = [[[[hModule+0xAAA38]+0x18]+0x58]+0x10]

这时Address就是存储雷区数组的首地址,每个雷区地址+0x10处就是雷区列状态数组(byte)地址。

其实,到这里我们也开始明白了,它所使用的应该是C++的vector,一个个push才产生这样的内存空间的,不得不说,这C++功力已经炉火存青了,各种数据结构弄得头都大了。

找到了雷区布置数组就可以进行下一步动作了,我们通过计算鼠标坐标值来获得雷区格子,每个格子是17*17像素大小并加上1像素的边,所以每个格子大小为18像素,雷区边界为30像素。你问我这些怎么得到的?这些值肯定在某个内存存着,你可以下断点在GetCursorPos处,在你移动鼠标时会触发断点,然后跳出函数,发现下边有一个GetWindowRect函数,这个函数会传递窗口句柄,窗口句柄存储在一个全局内存中,我们可以得到这个窗口句柄。但是我用了更简单的方法,既然有窗口,我直接用工具测一下就知道每个格子大小了么。

这样我们就得到了鼠标坐标转换格子的公式:

int x = (xPos - 30) / 18;          //列

int y = (yPos - 30) / 18;          //行

最后,上辅助代码:

WNDPROC g_oldProc = NULL;
DWORD   dwArrOffset = 0xAAA38;   //这是重定向之前全局变量相对于模块基地址的位置
DWORD64 dwMineAddress = 0;       //雷区列数组位置
BYTE    *pMineMap;               //自定义一个数组存储雷的布局,便于访问
DWORD   rows = 0;                //行
DWORD   cols = 0;                //列

LRESULT CALLBACK WindowProc(_In_  HWND hwnd,_In_  UINT uMsg,_In_  WPARAM wParam,_In_  LPARAM lParam
)
{if (uMsg == WM_MOUSEMOVE){int xPos = GET_X_LPARAM(lParam);int yPos = GET_Y_LPARAM(lParam);int x = (xPos - 30) / 18;          //列int y = (yPos - 30) / 18;          //行int nWidth = 30 + 18 * cols;int nHight = 30 + 18 * rows;if (x>=0&&y>=0&&x<nWidth&&y<nHight){if (pMineMap[x*rows + y] == (BYTE)0x01){SetWindowText(hwnd, L"!!!有雷!!!");}else{SetWindowText(hwnd, L"扫雷");}}else{SetWindowText(hwnd, L"扫雷");}}//F12键一键扫雷if (wParam == VK_F12){int i = 0;do{for (int j = 0; j < rows; j++){if (pMineMap[i*rows + j] != (BYTE)0x01){int x = (i * 18) + 30 + 9;//定位到格子中心int y = (j * 18) + 30 + 9;LPARAM point = MAKELPARAM(x, y);//发送鼠标点击消息
                    PostMessage(hwnd, WM_LBUTTONDOWN, NULL, point);PostMessage(hwnd, WM_LBUTTONUP, NULL, point);}}i++;} while (i!=cols);}return CallWindowProc(g_oldProc, hwnd, uMsg, wParam, lParam);
}
// CMineSweeperWaiguaApp 初始化
BOOL CMineSweeperWaiguaApp::InitInstance()
{CWinApp::InitInstance();OutputDebugString(L"已加载!\n");//获得雷区数组地址HMODULE hModule = GetModuleHandle(NULL);// 雷区列数组获得公式 [[[[hModule+0xAAA38]+0x18]+0x58]+0x10]DWORD64 v1 = *(DWORD64*)((DWORD64)hModule + dwArrOffset);//v2+0x8处存储了雷总数(DWORD),v2+0x0C处存储了行总数(DWORD),v2+0x10处存储了列总数(DWORD)DWORD64 v2 = *(DWORD64*)(v1 + 0x18);//申请一个存储雷区的数组空间rows = *(DWORD*)(v2+0x0C);     //行cols = *(DWORD*)(v2+0x10);     //列pMineMap = (BYTE*)VirtualAlloc(NULL,rows*cols, MEM_COMMIT, PAGE_READWRITE);if (pMineMap ==NULL){OutputDebugString(L"申请内存失败!\n");}OutputDebugString(L"申请内存成功!\n");//v1处存储了列总数(DWORD)v1 = *(DWORD64*)(v2 + 0x58);dwMineAddress = *(DWORD64*)(v1 + 0x10);//存储雷区列数组地址,有多少列就有多少数组//数组地址首位显示的是行数for (int i=0;i<*((DWORD*)(v2+0x10));i++){//v1处前4字节(DWORD)储了行总数  ,后边两个内容没搞懂(10 10)v1 = *(DWORD64*)(dwMineAddress + i * 8);BYTE *v5 = (BYTE*)(*(DWORD64*)(v1 + 0x10));for (int j = 0; j < *((DWORD*)(v2 + 0x0C)); j++){//将雷区状态赋值到数组中去 pMineMap[i*rows +j] = *v5;  //第i列第j行       v5++;}}OutputDebugString(L"雷区赋值成功!\n");HWND hWnd = FindWindow(NULL, L"扫雷");if (hWnd == NULL){OutputDebugString(L"未找到目标窗口!\n");return FALSE;}//更改指定窗口的属性//返回值是之前的窗口函数g_oldProc = (WNDPROC)SetWindowLongPtr(hWnd, GWLP_WNDPROC, (LONG_PTR)WindowProc);return TRUE;
}

到此,我们大功告成,需要注意的是每次要点击一下一个格子再注入动态库。不过,我的F12一键扫雷并没成功有人知道是怎么回事么?希望不吝赐教帮我解决一下,嘻嘻~~

转载于:https://www.cnblogs.com/Toring/p/6818068.html

Win7下64位扫雷逆向以及辅助制作相关推荐

  1. CY7C68013A在WIN7下64位的USB驱动程序安装过程

    文章来源说明:CY7C68013A在WIN7下64位的USB驱动程序安装过程遇到好几个问题,在网络上查阅了不少资料,本人只是对网络上的资料进行了整理,不能算是原创,对引用部分均说明了来源,为避免大家重 ...

  2. 说说windows下64位程序和32位程序

    对于两者的区别先不说,先说说两者的影响吧. windows操作系统,尤其是win7下64位和32位的程序对一般用户来说影响都不是很大,只要程序能够正常运行就ok了,不需要关心那么多. 可是对于很多大型 ...

  3. linux cmake 编译64位,cmake编译win下64位obs

    obs是一款开源编码推流工具,简单易用,非常流行. 一次项目中,发现本台式机I3处理器下32位obs推流CPU使用率100%.而使用的第三方设备在64位下,性能较好. 所以需要编译64位obs并且编译 ...

  4. Win7系统64位环境下使用Apache——下载mod_jk

    转载请注明出处:http://blog.csdn.net/dongdong9223/article/details/70313329 本文出自[我是干勾鱼的博客] 之前在几篇文章: Win7系统64位 ...

  5. Win7安装64位CentOS 6.4双系统详细过程

    原文连接Win7安装64位CentOS 6.4双系统详细过程 本文是在两篇文章整合而成.分别是: Mr.Johness的 最清晰细致的教程!一步步教你打造Win7+CentOS双系统 和 cjh326 ...

  6. win7系统下载 ghost win7 Sp1 64位纯净3月版

    win7系统下载 ghost win7 Sp1 64位纯净3月版 软件名称:        Ghost Win7 Sp1 64位纯净3月版 软件语言:        简体中文 软件大小:       ...

  7. win10、win7系统64位oracle11g安装教程以及32位plsql连接教程

    win10.win7系统64位oracle11g安装教程以及32位plsql连接教程 转载cxin917 最后发布于2016-02-05 14:16:55 阅读数 24354  收藏 展开 win10 ...

  8. win7 32/64位系统安装ug nx4.0

    UG NX 4.0 for Win7 64 英文教程来源: http://www.aame.in/2010/07/install-run-unigraphics-nx4-or-older-in.htm ...

  9. 装机员 Ghost Win7 Sp1 64位纯净贺岁版2017

    软件名称: 装机员 64位纯净贺岁版2017 软件语言: 简体中文 软件大小: 4.83 大小: GB 发布日期: 2017-01-01 文件名称: ZJY_Ghost_win 7_X64_CJ201 ...

  10. (绝对防御勒索病毒)装机员 ghost win7 Sp1 64位纯净6月版

    (绝对防御勒索病毒)装机员 ghost win7 Sp1 64位纯净6月版 软件名称:        Ghost Win7 Sp1 64位纯净6月版 软件语言:        简体中文 软件大小:   ...

最新文章

  1. 白话Elasticsearch46-深入聚合数据分析之Cardinality Aggs-cardinality去重算法以及每月销售品牌数量统计
  2. 面对复杂业务,if-else coder 如何升级?
  3. C#中IEnumerableT.Distinct()将指定实体类对象用Lambda表达式实现多条件去重
  4. android return 如何跳出两个循环_关于不得不学的Android知识之消息机制
  5. 【通信】基于matlab语音信号仿真【含Matlab源码 957期】
  6. ca锁登录显示服务器验签失败,网上报名CA登录失败解决办法
  7. wsimport的使用
  8. python范围缩放_如何缩放到初始绘图/缩放的特定范围?
  9. 感谢贤心感谢layui
  10. UID/DID/SID总结
  11. 怎么用注册表来删除卸载程序
  12. 电商平台怎么解决税务问题?
  13. 从零开始搭建springboot框架
  14. 热点追踪 | 数据,想说爱你不容易
  15. 计算机硬件查看,电脑硬件配置怎么查看和测试
  16. oracle的学习规划路线
  17. 折叠手机,是伪命题,还是真有未来?
  18. EmguCv多边形包围
  19. R在作图时遇到的问题以及解决方法
  20. JavaScript的1000+篇文章总结

热门文章

  1. Linux 查询和文件名加引号,Linux下vim查看文件名
  2. 前端学习(基础介绍)
  3. [转][darkbaby]任天堂传——失落的泰坦王朝(下)
  4. 实验2 网络扫描--Nmap与X-Scan
  5. 迈信ep100交流伺服 含原理图、PCB图、C源代码等生产资 料。
  6. Java——猜数字游戏
  7. 深度Linux与永中集成Office相聚在未来
  8. linux中什么目录用来存放系统管理员使用的管理命令,linuxcentos系统目录的详细介绍...
  9. Python工程目录结构
  10. SQL SERVER 2012/2014 序列号密钥