吾爱破解 - LCG - LSG |安卓破解|病毒分析|www.52pojie.cn

 找回密码
 注册[Register]

QQ登录

只需一步,快速开始

查看: 38737|回复: 58
收起左侧

[原创] 简单的扫雷内存挂

[复制链接]
别说见过我 发表于 2016-9-10 20:47
本帖最后由 别说见过我 于 2016-9-12 00:37 编辑

这么多年来玩扫雷从未赢过,这次拿它来练手也算是圆了我多年来玩赢一盘的梦想。


动手之前,我们先来熟悉几个windows的API函数:
“OpenProcess”函数用来打开一个已存在的进程对象,并返回进程的句柄。
函数原型:
HANDLE OpenProcess(
    DWORD dwDesiredAccess, //访问权限
    BOOL bInheritHandle,  //是否继承句柄
    DWORD dwProcessId); //进程pid


“VirtualQueryEx”用来查询进程所使用的内存块的信息。
函数原型为:
DWORD VirtualQueryEx(
     HANDLE hProcess, //进程句柄
     LPCVOID lpAddress, //查询的地址
     PMEMORY_BASIC_INFORMATION lpBuffer, //指向MEMORY_BASIC_INFORMATION结构的指针,用于在本进程中接收内存信息
     DWORD dwLength); //MEMORY_BASIC_INFORMATION结构的大小

“ReadProcessMemory” 内存读取函数, 根据进程句柄和指定位置读取其内存数据。
函数原型为:
BOOL ReadProcessMemory(
     HANDLE hProcess, //进程句柄
     LPCVOID lpBaseAddress, //读取的地址
     LPVOID lpBuffer, //本进程的内存地址指针,指向读取到的数据
     DWORD nSize, //指定从地址处读出多少数据,单位字节
     LPDWORD lpNumberOfBytesRead);  //实际读出的空间大小,单位字节

“WriteProcessMemory” 写入某一进程的内存区域。入口区必须可以访问,否则操作将失败。
函数原型为:
BOOL WriteProcessMemory(
    HANDLE hProcess,  //进程句柄
    LPVOID lpBaseAddress, //写入的起始地址
    LPVOID lpBuffer, //要写入的数据的指针
    DWORD nSize, //写入数据的大小,单位字节
    LPDWORD lpNumberOfBytesWritten);  //实际写入的数据大小,单位字节

我们所要用到的就是这四个函数,那么接下来我们来理一理大致思路:
扫雷每次的地雷分布是随机的,那么我们可以肯定它是进程执行过程中,在内存中生成了雷区信息数据,并非是写在执行文件中段内固定的。
我们可以首先用ReadProcessMemory来查找数据,找到这片雷区的信息,然后通过WriteProcessMemory把地雷的数据给覆盖掉,
或许能实现“不踩到雷,顺利过关”。我用的是“或许”这个词,因为有几点其实我们都是不确定的:
1.扫雷的获胜条件是什么?
2.将地雷信息给复写掉,会不会引发程序异常?
3.地雷怎样识别?如何避免错误识别?

前两条需要使用汇编知识来分析程序逻辑才能得知,我们先不考虑这些问题(完全无能为力,听天由命吧...);
但第三条不能忽略,没有雷区的特征,我们完全就无从下手,
好在扫雷游戏早就被众多高手分析过了,我找到了其中一篇文章中的描述:
1.png
文章出自:http://www.ghoffice.com/bbs/read-htm-tid-46053.html

在以上的信息中我们得到两个重要信息:
1.扫雷对于雷区的绘制是通过一个二维的BYTE数组,那么我们等下查内存数据的时候就知道了数据格式。
2.地雷是用0x8f;安全区是通过0x0f;墙壁边界是0x10;

有了以上知识做铺垫,那么我们可以细化先前的思路,如下:
2.png

最终实现代码如下:
[C] 纯文本查看 复制代码
#include <windows.h>
#include <stdio.h>

const unsigned char MINE = 0x8f;        //地雷
const unsigned char SAFE_AREA = 0x0f;   //安全位置
const unsigned char WALL = 0x10;        //墙壁

static void removeMine(HANDLE hProcess, PVOID addr){
    DWORD dwNumberOfBytesRead;
    PBYTE pAddress = NULL;
    /*
     修改“地雷”的数据,两种可选方案:
     1.用SAFE_AREA覆盖:只要先前将旗帜插完,随便点一下即过关
     2.用WALL覆盖:在界面中有地雷的格子就会呈现出不可选的样子,然后在上面插旗、随便点哪也可以过关
     */
    unsigned char replace = WALL;
    if (WriteProcessMemory(hProcess, addr,&replace, sizeof(unsigned char),&dwNumberOfBytesRead) != 0){
        printf("成功修改内存数据\n");
    }else{
        printf("修改内存数据失败\n");
    }
}

static unsigned char getValue(HANDLE hProcess, PVOID pos){
    DWORD pvBufferLocal;
    DWORD dwNumberOfBytesRead;
    if(ReadProcessMemory(hProcess, pos, &pvBufferLocal, sizeof(unsigned char), &dwNumberOfBytesRead) != 0){
        return (unsigned char)pvBufferLocal;
    }
    return 0;
}

static void searchMine(HANDLE hProcess, PVOID addr, SIZE_T len){
    if(len < sizeof(unsigned char))
        return;
    
    PVOID pos = addr;
    while(TRUE){
        if((SIZE_T)((PBYTE)pos - (PBYTE)addr) == len)
            break;
        
        unsigned char val = getValue(hProcess, pos);
        if(MINE == (unsigned char)val){
            //判断这个疑似地雷的字节旁边是什么数据
            //如果旁边是 “地雷”、“安全位置”、“墙壁”,那么这个就“极有可能”真的是“地雷”
            //有点撞运气的成分,听天由命吧 :)
            //完美的方式是要判断当前这块区域是否是绘制雷区的二维数组,确定了数组边界后遍历数组修改数据。
            //完整实现起来代码量比较大,不符合简单教学的目的;根据“够用就好”的原则来说,目前这样也能用,那就这么干吧。
            unsigned char preVal = getValue(hProcess, (PBYTE)pos-1); //+1 -1都没有做范围判断,这么干是不行的,不过先就这样吧
            unsigned char nextVal = getValue(hProcess, (PBYTE)pos+1);
            if((MINE == preVal  || SAFE_AREA == preVal  || WALL == preVal) ||
               (MINE == nextVal || SAFE_AREA == nextVal || WALL == nextVal)){
                printf("找到疑似地雷: %p\n", pos);
                removeMine(hProcess, pos);
            }
        }
        pos = (PBYTE)pos + 1;
    }
    
}

BOOL searchMem(DWORD pid){
    HANDLE hProcess = OpenProcess(PROCESS_ALL_ACCESS, FALSE, pid); //以最大的权限打开进程句柄
    if(NULL == hProcess)
        return FALSE;
    
    MEMORY_BASIC_INFORMATION mbi;
    PBYTE pAddress = NULL;
    SYSTEM_INFO siSysInfo;
    GetSystemInfo(&siSysInfo);
    while(pAddress < (DWORD)siSysInfo.lpMaximumApplicationAddress){
        if(VirtualQueryEx(hProcess, pAddress, &mbi, sizeof(mbi)) != sizeof(mbi)){
            break;
        }
        /*
         检查内存块的保护类型是否为可读写(PAGE_READWRITE);
         检查内存是否被使用(MEM_COMMIT),申请的空间如果没被使用,那么是不会产生缺页异常为其映射到物理页;
         */
        if((mbi.Protect & PAGE_READWRITE) && (MEM_COMMIT == mbi.State)){
            searchMine(hProcess, mbi.BaseAddress, mbi.RegionSize);
        }
        pAddress = ((PBYTE)mbi.BaseAddress + mbi.RegionSize);
    }
    CloseHandle(hProcess);
    return TRUE;
}

int main(){
    printf("先运行扫雷游戏,然后在进程管理器中找到winmine.exe的进程pid,将其输入到控制台,然后回车。\n");
    printf("运行本程序只能作弊一次,想再次作弊,就再重新运行一次吧。\n");
    printf("当程序运行结束后,记得插完所有的旗帜!位置随便,总之要把旗帜用光,否则不算赢!\n");
    printf("插完旗帜后,随便点个格子就通关了游戏。\n");
    printf("祝你装逼愉快 :)\n");
    unsigned int pid = 0;
    printf("你的扫雷游戏pid是?:");
    scanf("%d", &pid);
    if(NULL == pid || 0 == pid){
        printf("错误的pid\n");
        return 1;
    }
    searchMem(pid);
    printf("已经完成了地雷清除工作。\n");
    return 0;
}


运行结果:
使用SAFE_AREA覆盖效果:过关界面还是会显示雷没正确标记,也没有数字(数字应该是动态生成的,没了雷也就不生成了)
好在也算胜利了:
3.png

使用WALL覆盖效果:
4.png

这个外挂我是在xp 32位的系统上编译测试的,win7没有试过,不敢保证效果。
还有上面的判断雷的前后位置的时候指针+1 -1我都并没有做范围判断,这是比较危险的,理论上可能会导致非法访问,
还好我没遇到过,大家可以尝试自己动手来完善(判断边界);
还有判断雷区的逻辑也是,也有待完善(依靠特征识别二维数组,确定边界、遍历数组修改)。

希望这篇分享对大家有一定的帮助。





点评

更正一下,是右键单击消息,左键直接就炸了-_-||  发表于 2016-9-12 11:13
既然读取到雷的分布了,干嘛不直接在对应有雷的按钮上发送单击消息呢,这样不就可以实现完美扫雷成功。  发表于 2016-9-12 11:11
启动扫雷游戏后输入 xyzzy,然后回车,再按一下shift键 ,注意看你的“扫雷”界面最左上角,一旦出现个“黑点”,就表明当前方格下是地雷。如果没出现个黑点的话,说明就是安全的。  发表于 2016-9-11 14:25

免费评分

参与人数 11热心值 +11 收起 理由
Hmily + 1 感谢发布原创作品,吾爱破解论坛因你更精彩!
yeyulang + 1 热心回复!
灬俯瞰天空 + 1 欢迎分析讨论交流,吾爱破解论坛有你更精彩!
杯具加杯具 + 1 已答复!
loooooooong + 1 用心讨论,共获提升!
xymngh + 1 技术贴
ll1103945660 + 1 谢谢@Thanks!
fgp282 + 1 热心回复!
201235865 + 1 热心回复!
jion + 1 热心回复!
qwerttqqaz + 1 我很赞同!

查看全部评分

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

 楼主| 别说见过我 发表于 2016-9-11 12:27
zhousai998 发表于 2016-9-11 10:11
怎么用.这东西.

先运行扫雷,然后不要动它,在任务管理器中找到扫雷的进程pid,
使用vc++ 贴入代码 然后编译运行,在运行之后会有提示,输入pid然后回车,
雷就被清理了,在扫雷游戏中使用完所有的旗帜(随便乱放就好了,游戏过关的要求,必须用完旗帜)。
最后随便点几一个格子,就过关了。
抱歉啊 程序我就没上传了,因为运行时360会报拦截,未免误会所以我就只贴了代码。
MAXtoDEATH 发表于 2016-9-23 11:49
不是dev的标准。。。71        39        C:\Documents and Settings\Administrator\My Documents\saokei.cpp        [Error] ISO C++ forbids comparison between pointer and integer [-fpermissive]
MosterGay丶 发表于 2016-9-10 20:54
 楼主| 别说见过我 发表于 2016-9-10 21:03
MosterGay丶 发表于 2016-9-10 20:54
666扫雷挂,膜拜大神

大神不敢当,一起交流,共同进步 :)
冰楓丶殘瀷 发表于 2016-9-10 21:05
扫雷还要开挂???
SoVieTing 发表于 2016-9-10 21:07
现在还玩儿扫雷啊。。。。
woaidky 发表于 2016-9-10 21:07
表示扫雷手动到高级..
黑龍 发表于 2016-9-10 21:12
多谢分享  最近在学C语言
大.闻道 发表于 2016-9-10 21:14
很厉害的样子
吾爱-宿命 发表于 2016-9-10 21:15
7楼的签名很亮

点评

666这你都看出来了 哈哈  发表于 2016-9-11 14:09
在你不远的距离 发表于 2016-9-10 21:30
大婶 请收下我的膝盖
您需要登录后才可以回帖 登录 | 注册[Register]

本版积分规则 警告:本版块禁止灌水或回复与主题无关内容,违者重罚!

快速回复 收藏帖子 返回列表 搜索

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

GMT+8, 2024-5-18 09:59

Powered by Discuz!

Copyright © 2001-2020, Tencent Cloud.

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