win7掃雷逆向&輔助實現
做這個事情是因為軟件工程上機課太無聊了,就開始玩掃雷,但是覺得原版界面有點太丑了,就想自己改一改。
參考的資料還挺多的,這個講xp掃雷的講的比較好(https://www.cnblogs.com/iBinary/p/7533292.html),但是不太適用於win7版本的掃雷。
首先在win10上運行win7的掃雷需要patch一下程序最開頭對信息的校驗。
然后使用ida分析的時候可以下載網絡上的pdb進行分析。
分析雷在內存中的存儲
首先通過的是Game::Load()這樣一個函數來定位到對寬高雷數量的判定:
然后繼續往下翻,發現Board::Board這樣一個構造函數,應該是對掃雷的板子進行初始化了:
直接跟進去,在里面找到了對掃雷的板子的具體初始化函數:
最后定位到是一個二重循環對Array<UITile>類型的游戲板子進行了初始化!
注:推測UITile繼承了NodeBase。
之后的分析就采用動靜結合的方式,有一個文章寫得比較好:https://www.itdaan.com/blog/2017/05/06/d8daff1e0db2e3334cefc7aec9c0e351.html
總的來說就是對創建雷的部分進行下斷點然后往上層函數進行分析。
關鍵部分:
上面這個部分為我們提供了關鍵判斷,方便我們通過v3(this指針)去找到雷的位置。
之后就寫了一個測試腳本去提出雷(上面省略了用ce繼續查基址的部分(很簡單的))
直接丟代碼了:
#include <windows.h>
#include <stdio.h>
#include <tlhelp32.h>
#include <string.h>
int main(){
// 獲取pid
HWND hWnd = FindWindow(NULL, "掃雷");
DWORD pid = NULL;
GetWindowThreadProcessId(hWnd, &pid);
HANDLE hProcess = OpenProcess(PROCESS_ALL_ACCESS, 0, pid);
printf("hProcess: %p \n", hProcess);
// 獲取模塊地址
DWORD modaddr = NULL;
MODULEENTRY32 modentry;
memset(&modentry, 0, sizeof(modentry));
HANDLE hSnapshot = CreateToolhelp32Snapshot(TH32CS_SNAPMODULE,pid);
modentry.dwSize = sizeof(MODULEENTRY32);
Module32First(hSnapshot, &modentry);
do{
if (strcmp(modentry.szModule, "MineSweeper.exe") == 0)
{
modaddr = (DWORD)modentry.hModule;
CloseHandle(hSnapshot);
break;
}
}while(Module32Next(hSnapshot, &modentry));
printf("modaddr: %p \n", modaddr);
int a[16][16] = {0};
int x = 0;
int y = 0;
//[[[[[ecx+17*4]+12]+列index]+12]+行index]
//ecx = [[MineSweeper.exe+868B4]+0x10]
DWORD pObj = modaddr;
ReadProcessMemory(hProcess, (LPCVOID)(pObj+0x868B4), &pObj, 4, 0);
ReadProcessMemory(hProcess, (LPCVOID)(pObj+0x10), &pObj, 4, 0);
printf("ecx: %p \n", pObj);
ReadProcessMemory(hProcess, (LPCVOID)(pObj+17*4), &pObj, 4, 0);
ReadProcessMemory(hProcess, (LPCVOID)(pObj+12), &pObj, 4, 0);// 此時pObj指向列array
DWORD lie; // 一列一列處理
BYTE tmp = 0;
for (y = 0; y < 16; y ++){
ReadProcessMemory(hProcess, (LPCVOID)(pObj+y*4), &lie, 4, 0);
ReadProcessMemory(hProcess, (LPCVOID)(lie+12), &lie, 4, 0);
for (x = 0; x < 16; x ++)
{
ReadProcessMemory(hProcess, (LPCVOID)(lie+x), &tmp, 1, 0);
//printf("%d ", tmp);
a[x][y] = tmp;
}
}
printf("下面是雷區:\n");
// 輸出雷區
for(x = 0; x < 16; x ++){
for (y = 0; y < 16; y ++){
printf("%d ", a[x][y]);
}
printf("\n");
}
CloseHandle(hProcess);
return 0;
}
效果圖:
接着就是實現一個外掛dll了(注入的部分就省略了反正怎么注入都一樣):
// apparent.cpp : Defines the entry point for the DLL application.
//
#include "stdafx.h"
#include <stdio.h>
extern "C" __declspec(dllexport) VOID __stdcall haha(){
MessageBox(0,"hahaha",0,0);
}
DWORD WINAPI ThreadProc(LPVOID lpParameter){
char buf[1000] = {0}; // 輸出緩沖區
//MessageBox(0,"Mz1dll注入成功,enjoy it!", "Welcome",0);
DWORD pid = GetCurrentProcessId();
HANDLE hProcess = OpenProcess(PROCESS_ALL_ACCESS, 0, pid);// 獲取當前句柄
HMODULE hModule = GetModuleHandle(NULL);
//sprintf(buf, "hModule: %p", hModule);
//MessageBox(0, buf, 0, 0);
//sprintf(buf, "pid: %p\r\n句柄: %p", pid, hProcess);
//MessageBox(0,buf,"提示", 0);
while(1){
Sleep(2000);
int a[16][16] = {0};
int x = 0;
int y = 0;
//[[[[[ecx+17*4]+12]+列index]+12]+行index]
//ecx = [[MineSweeper.exe+868B4]+0x10]
DWORD pObj = (DWORD)hModule;
ReadProcessMemory(hProcess, (LPCVOID)(pObj+0x868B4), &pObj, 4, 0);
ReadProcessMemory(hProcess, (LPCVOID)(pObj+0x10), &pObj, 4, 0);
printf("ecx: %p \n", pObj);
ReadProcessMemory(hProcess, (LPCVOID)(pObj+17*4), &pObj, 4, 0);
ReadProcessMemory(hProcess, (LPCVOID)(pObj+12), &pObj, 4, 0);// 此時pObj指向列array
DWORD lie; // 一列一列處理
BYTE tmp = 0;
for (y = 0; y < 16; y ++){
ReadProcessMemory(hProcess, (LPCVOID)(pObj+y*4), &lie, 4, 0);
ReadProcessMemory(hProcess, (LPCVOID)(lie+12), &lie, 4, 0);
for (x = 0; x < 16; x ++)
{
ReadProcessMemory(hProcess, (LPCVOID)(lie+x), &tmp, 1, 0);
//printf("%d ", tmp);
a[x][y] = tmp;
}
}
// 展現圖形化提示
int index = 0;
char tip[] = "下面是雷區提示:\r\n";
sprintf(buf, tip);
index += sizeof(tip);
index -= 1;
for (x = 0; x < 16; x ++){
for (y = 0; y < 16; y ++){
//buf[index] = a[x][y]+'0';
sprintf(buf+index, "%d ", a[x][y]);
index += 2;
}
buf[index] = '\r';
index ++;
buf[index] = '\n';
index ++;
}
MessageBox(0, buf, "tip", 0);
}
return 0;
}
BOOL APIENTRY DllMain( HANDLE hModule,
DWORD ul_reason_for_call,
LPVOID lpReserved
)
{
switch ( ul_reason_for_call)
{
case DLL_PROCESS_ATTACH:
// 執行需要的代碼
CreateThread(NULL,0,
(LPTHREAD_START_ROUTINE)ThreadProc,
NULL, 0,NULL);//創建新線程執行代碼
break;
case DLL_PROCESS_DETACH:
break;
case DLL_THREAD_ATTACH:
break;
case DLL_THREAD_DETACH:
break;
}
return TRUE;
}
這個dll會彈框顯示雷區內存。
做的比較粗糙,但是原理基本上就是這個亞子。
演示:
差不多就這樣~