逆向实战 | win7扫雷逆向&辅助实现

win7扫雷逆向&辅助实现

做这个事情是因为软件工程上机课太无聊了,就开始玩扫雷,但是觉得原版界面有点太丑了,就想自己改一改。

参考的资料还挺多的,这个讲xp扫雷的讲的比较好(https://www.cnblogs.com/iBinary/p/7533292.html),但是不太适用于win7版本的扫雷。

首先在win10上运行win7的扫雷需要patch一下程序最开头对信息的校验。

然后使用ida分析的时候可以下载网络上的pdb进行分析。

分析雷在内存中的存储

首先通过的是Game::Load()这样一个函数来定位到对宽高雷数量的判定:
image

然后继续往下翻,发现Board::Board这样一个构造函数,应该是对扫雷的板子进行初始化了:
image

直接跟进去,在里面找到了对扫雷的板子的具体初始化函数:
image

最后定位到是一个二重循环对Array<UITile>类型的游戏板子进行了初始化!
注:推测UITile继承了NodeBase。
image

之后的分析就采用动静结合的方式,有一个文章写得比较好:https://www.itdaan.com/blog/2017/05/06/d8daff1e0db2e3334cefc7aec9c0e351.html
总的来说就是对创建雷的部分进行下断点然后往上层函数进行分析。
关键部分:
image

上面这个部分为我们提供了关键判断,方便我们通过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;
}

效果图:
image

接着就是实现一个外挂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会弹框显示雷区内存。
做的比较粗糙,但是原理基本上就是这个亚子。

演示:
image

差不多就这样~

posted @ 2021-10-12 08:43  Mz1  阅读(977)  评论(1编辑  收藏  举报