首页
社区
课程
招聘
[原创]Win10扫雷分析
发表于: 2023-12-20 23:36 8839

[原创]Win10扫雷分析

2023-12-20 23:36
8839

首先用x64dbg附加扫雷程序

依照路径把扫雷程序复制出来,因为这个文件夹咱们没有权限

复制出来之后发现有il2cpp_data这个文件夹,之后发现global-metadata.dat,没有加密
就可以推断出这个是unity写的il2cpp加密的程序,而且是没有经过变形的
下载Il2CppDumper,点击Il2CppDumper.exe 依次选择游戏的GameAssembly.dll 和data文件夹下的global-metadata.dat

自动解析之后会生成DummyDll文件夹,把里面的Assembly-CSharp.dll拖进dnspy反汇编软件

然后我们把GameAssembly.dll拖入IDA中来进行分析,方便接下来看代码

接下来就是寻找各个数据,首先我们先看一下各个名称,依照名字来对比,可以看出来,挑战模式、网格数据、经典模式等

这里我以经典模式为例子,先总体都看一下代码,然后我们看到Gameplay.Board.Data里面的BoardConfigData有一个get_TileCount获取网格数量的并且是有高度和宽度的,我们点击这个函数,并在提示的位置,设置断点,然后跟过来看一下,刚好看到了我想要的数据,并同时 确定了这个位置就是我们需要的宽和高,还有中间的numberOfMines (雷的数量)

接下来就是寻找雷区数据的分布了,上面的还可以用ce来确定,但是雷区数量不是非常的方便用ce来寻找,
首先我们可以看到public class ClassicModeTileData : TileData, IClassicModeTileData, ITileData

ClassicModeTileData继承了这3个我们挨个看一下父类的代码,TileData里面的get_IsMine也许我们用的到,接下来我们在这个位置设置断点

我们可以看到已经断下来了,接着我们堆栈寻找ecx是如何来的,这个位置会来好几次,有好几个调用这个位置的代码,我们需要多测试几次,每个断点仔细的看一下

代码观察一下,会看到这个位置来了两遍,在结合之前看的反编译的代码,我们可以知道这是链表,它的意思是每一行一个链表然后再把链表在组合起来(个人猜测)

最后就是开始标志位,这个位置我就没有在代码里面找了,以为之前的几个数据要是找到了的话,那么必然的标志位在某一个指针内部,然后我通过逐级查看每一级的指针内存,看到在0xD8的位置上如果是0就说明还没有点击过,点击过之后就会生成雷区了

以下是我简易实现了一下8乘8的版本

#include <Windows.h>
#include <TlHelp32.h>
#include <iostream>
#include <vector>
 
uintptr_t g_baseAddress = 0;
HANDLE g_hProcess = NULL;
 
 
uintptr_t GetModuleBaseAddress(DWORD processId, const wchar_t* moduleName) {
    HANDLE hSnapshot = CreateToolhelp32Snapshot(TH32CS_SNAPMODULE | TH32CS_SNAPMODULE32, processId);
 
    if (hSnapshot == INVALID_HANDLE_VALUE) {
        return 0;
    }
 
    MODULEENTRY32 moduleEntry;
    moduleEntry.dwSize = sizeof(MODULEENTRY32);
 
    if (Module32First(hSnapshot, &moduleEntry)) {
        do {
            if (_wcsicmp(moduleEntry.szModule, moduleName) == 0) {
                CloseHandle(hSnapshot);
                return reinterpret_cast<uintptr_t>(moduleEntry.modBaseAddr);
            }
        } while (Module32Next(hSnapshot, &moduleEntry));
    }
 
    CloseHandle(hSnapshot);
    return 0;
}
 
uintptr_t uIsStart(_In_ HANDLE hProcess, uintptr_t baseAddress)
{
    bool b = false;
    // 计算内存地址
    std::vector<uintptr_t> offsets = { 0x03116DF0, 0x430, 0xa0, 0xd8 };
    for (uintptr_t offset : offsets) {
        b = ReadProcessMemory(hProcess, reinterpret_cast<LPCVOID>(baseAddress + offset), &baseAddress, sizeof(baseAddress), NULL);
    }
 
    return baseAddress;
}
 
 
uintptr_t MinListPoint(_In_ HANDLE hProcess, uintptr_t baseAddress)
{
    // 计算内存地址
    std::vector<uintptr_t> offsets = { 0x03116DF0, 0x430, 0xa0, 0x70,0x10,0x10,0x10 };
    for (uintptr_t offset : offsets) {
        ReadProcessMemory(hProcess, reinterpret_cast<LPCVOID>(baseAddress + offset), &baseAddress, sizeof(baseAddress), NULL);
    }
 
    return baseAddress;
}
 
 
uintptr_t MinListOnePoint(_In_ HANDLE hProcess, uintptr_t baseAddress, int offsets)
{
 
    ReadProcessMemory(hProcess, reinterpret_cast<LPCVOID>(baseAddress + 0x10), &baseAddress, sizeof(baseAddress), NULL);
    ReadProcessMemory(hProcess, reinterpret_cast<LPCVOID>(baseAddress + 0x20 + offsets * 8), &baseAddress, sizeof(baseAddress), NULL);
 
    return baseAddress;
}
 
DWORD WINAPI MyThreadFunction(LPVOID lpParam)
{
    uintptr_t mapMinListPointOld = 0;
    uintptr_t mapMinListPoint = 0;
    //读取是否开始了
    while (true)
    {
        uintptr_t uStart = uIsStart(g_hProcess, g_baseAddress);
        if (uStart != 0)
        {
 
            mapMinListPoint = MinListPoint(g_hProcess, g_baseAddress);
            if (mapMinListPointOld != mapMinListPoint)
            {
                mapMinListPointOld = mapMinListPoint;
                printf("\n");
                printf("\n");
                for (int y = 7; y > -1; y--)
                {
                    uintptr_t mapMinListLinePoint = MinListOnePoint(g_hProcess, mapMinListPoint, y);
                    for (int x = 0; x < 8; x++)
                    {
                        uintptr_t mapMinListOnePoint = MinListOnePoint(g_hProcess, mapMinListLinePoint, x);
 
                        int nMin = 0;
                        ReadProcessMemory(g_hProcess, reinterpret_cast<LPCVOID>(mapMinListOnePoint + 0x1c), &nMin, sizeof(nMin), NULL);
                        if (nMin)
                        {
                            printf("1");
                        }
                        else
                        {
                            printf("0");
                        }
 
                    }
                    printf("\n");
                }
            }
 
 
        }
        Sleep(1000);
    }
 
 
    return 0;
}
int main() {
 
    const wchar_t* processName = L"Minesweeper.exe";
    const wchar_t* moduleName = L"GameAssembly.dll";
 
 
    DWORD processId = 0;
 
    HANDLE hProcessSnap = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
 
    if (hProcessSnap != INVALID_HANDLE_VALUE) {
        PROCESSENTRY32 processEntry;
        processEntry.dwSize = sizeof(PROCESSENTRY32);
 
        if (Process32First(hProcessSnap, &processEntry)) {
            do {
                if (_wcsicmp(processEntry.szExeFile, processName) == 0) {
                    processId = processEntry.th32ProcessID;
                    break;
                }
            } while (Process32Next(hProcessSnap, &processEntry));
        }
 
        CloseHandle(hProcessSnap);
    }
 
    if (processId != 0) {
        g_baseAddress = GetModuleBaseAddress(processId, moduleName);
 
        if (g_baseAddress != 0) {
            std::wcout << L"The base address of " << moduleName << L" in " << processName << L" is 0x" << std::hex << g_baseAddress << std::endl;
             
        }
        else {
            std::wcout << L"Module " << moduleName << L" not found in process " << processName << std::endl;
        }
    }
    else {
        std::wcout << L"Process " << processName << L" not found" << std::endl;
    }
 
 
    // 打开游戏进程
    g_hProcess = OpenProcess(PROCESS_ALL_ACCESS, FALSE, processId);
    if (g_hProcess == NULL) {
        std::cerr << "Failed to open the process. Error code: " << GetLastError() << std::endl;
        return 1;
    }
 
    //创建线程
    HANDLE  hThread = CreateThread(
        NULL,                   // default security attributes
        0,                      // use default stack size 
        MyThreadFunction,       // thread function name
        0,          // argument to thread function
        0,                      // use default creation flags
        0);   // returns the thread identifier
 
    WaitForSingleObject(hThread, INFINITE);
    CloseHandle(g_hProcess);
 
    return 0;
}
#include <Windows.h>
#include <TlHelp32.h>
#include <iostream>
#include <vector>
 
uintptr_t g_baseAddress = 0;
HANDLE g_hProcess = NULL;
 
 
uintptr_t GetModuleBaseAddress(DWORD processId, const wchar_t* moduleName) {
    HANDLE hSnapshot = CreateToolhelp32Snapshot(TH32CS_SNAPMODULE | TH32CS_SNAPMODULE32, processId);
 
    if (hSnapshot == INVALID_HANDLE_VALUE) {
        return 0;
    }
 
    MODULEENTRY32 moduleEntry;
    moduleEntry.dwSize = sizeof(MODULEENTRY32);
 
    if (Module32First(hSnapshot, &moduleEntry)) {
        do {
            if (_wcsicmp(moduleEntry.szModule, moduleName) == 0) {
                CloseHandle(hSnapshot);
                return reinterpret_cast<uintptr_t>(moduleEntry.modBaseAddr);
            }
        } while (Module32Next(hSnapshot, &moduleEntry));
    }
 
    CloseHandle(hSnapshot);
    return 0;
}
 
uintptr_t uIsStart(_In_ HANDLE hProcess, uintptr_t baseAddress)
{
    bool b = false;
    // 计算内存地址
    std::vector<uintptr_t> offsets = { 0x03116DF0, 0x430, 0xa0, 0xd8 };
    for (uintptr_t offset : offsets) {
        b = ReadProcessMemory(hProcess, reinterpret_cast<LPCVOID>(baseAddress + offset), &baseAddress, sizeof(baseAddress), NULL);
    }
 
    return baseAddress;
}
 
 
uintptr_t MinListPoint(_In_ HANDLE hProcess, uintptr_t baseAddress)
{
    // 计算内存地址
    std::vector<uintptr_t> offsets = { 0x03116DF0, 0x430, 0xa0, 0x70,0x10,0x10,0x10 };
    for (uintptr_t offset : offsets) {
        ReadProcessMemory(hProcess, reinterpret_cast<LPCVOID>(baseAddress + offset), &baseAddress, sizeof(baseAddress), NULL);
    }
 
    return baseAddress;
}
 
 
uintptr_t MinListOnePoint(_In_ HANDLE hProcess, uintptr_t baseAddress, int offsets)
{
 
    ReadProcessMemory(hProcess, reinterpret_cast<LPCVOID>(baseAddress + 0x10), &baseAddress, sizeof(baseAddress), NULL);
    ReadProcessMemory(hProcess, reinterpret_cast<LPCVOID>(baseAddress + 0x20 + offsets * 8), &baseAddress, sizeof(baseAddress), NULL);
 
    return baseAddress;
}
 
DWORD WINAPI MyThreadFunction(LPVOID lpParam)
{
    uintptr_t mapMinListPointOld = 0;
    uintptr_t mapMinListPoint = 0;
    //读取是否开始了
    while (true)
    {
        uintptr_t uStart = uIsStart(g_hProcess, g_baseAddress);
        if (uStart != 0)
        {
 
            mapMinListPoint = MinListPoint(g_hProcess, g_baseAddress);
            if (mapMinListPointOld != mapMinListPoint)
            {
                mapMinListPointOld = mapMinListPoint;
                printf("\n");
                printf("\n");
                for (int y = 7; y > -1; y--)
                {

[注意]传递专业知识、拓宽行业人脉——看雪讲师团队等你加入!

收藏
免费 5
支持
分享
最新回复 (2)
雪    币: 3070
活跃值: (30876)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
2
感谢分享
2023-12-21 09:41
1
雪    币: 7541
活跃值: (5382)
能力值: ( LV4,RANK:50 )
在线值:
发帖
回帖
粉丝
3
思路清晰,代码还行,好好学习,未来可刑
2023-12-22 00:43
0
游客
登录 | 注册 方可回帖
返回
//