前言
今天在WinXpSp3上逆向扫雷游戏,希望得到雷区数据表在内存中的地址, 并分析雷区数据格式.
试验记录
将winmine.exe拷贝一份到工程目录用于调试
用OD载入winmine.exe.
查看导入表, 看到有RegisterClassW
在RegisterClassW上下断点.
将扫雷跑起来,断在RegisterClassW, 按照WNDCLASSEXW定义查看参数.
找到 wcex.lpfnWndProc的函数地址.
到WndProc函数中,随便找一个消息处理, e.g. 右键按下(插地雷旗标)
开始是判断用户点击的雷区行列数和雷区预定义的行列相比是否越界, 这时就用到了雷区数据表开始地址.
取出雷区数据表开始地址,在数据区进行分析.
OD数据区是一行16列,为了便于分析, 将游戏设定为14行*14列的雷区.正好可以将OD数据区和雷区UI对应起来.
接下来,右击不同的雷区行列,看雷区数据表的值变化.
再试着点几下,看会不会被地雷炸死.
如果没有被炸死,雷区对应数据是啥?
如果被炸死,雷区对应数据是啥?
插不同的旗标(一共有3种), 雷区对应数据是啥?
如果没有被炸死,打开了一片雷区, 雷区对应数据是啥?
最关心的数据还是哪个数据值代表地雷, 看到地雷字节数据为0x8f.
直接双击不是地雷的数据,雷区很快就能被扫光.
不同行列的雷区数据读取, 可能需要判断详细些. 没有测试全, 简单的写了一下.
支持默认的3种(低,中,高)和最大行列的雷区(24行*30列)
BOOL CWinMineInjectDllApp::ReadMineRowColData(int iRow, int iCol, BYTE& ucData) {
BOOL bRc = FALSE;
DWORD dwMineCnt = 0;
DWORD dwMineRows = 0;
DWORD dwMineCols = 0;
SIZE_T stRdBack = 0;
/*
最大雷区的数据分析(24行,30列)
开始数据为16个字节, 固定的
第一行是挡板 左右各一个字节的挡板 + 列数
第2行是数据 左右各一个字节的挡板 + 列数个数据
*/
int iOneRowBytes = 0; // 算上左右墙(1个字节*2)后, 每行的数据字节数
int iDataOffsetBegin = 0; // 第一个数据距离表头的偏移
int iDataOffsetRowAndRow = 0; // 行数据之间间隔, e.g. 数据(1,1)和数据(2,1)之间,间隔的字符数
DWORD dwAddrObj = 0; // 要取的字节数的地址, 找到后, 取一个字节到ucData
bRc = ReadProcessMemory(m_hProcess, (LPVOID)MINE_DATA_ADDR_MINE_CNT,
&dwMineCnt, sizeof(DWORD), &stRdBack);
if (!bRc) {
return FALSE;
}
bRc = ReadProcessMemory(m_hProcess, (LPVOID)MINE_DATA_ADDR_MINE_ROW,
&dwMineRows, sizeof(DWORD), &stRdBack);
if (!bRc) {
return FALSE;
}
bRc = ReadProcessMemory(m_hProcess, (LPVOID)MINE_DATA_ADDR_MINE_COL,
&dwMineCols, sizeof(DWORD), &stRdBack);
if (!bRc) {
return FALSE;
}
if ((iRow <= 0) || (iCol <= 0) || ((DWORD)iRow > dwMineRows) || ((DWORD)iCol > dwMineCols)) {
return FALSE;
}
if ((30 == dwMineRows) && (24 == dwMineCols)) {
iOneRowBytes = dwMineCols + 2;
iDataOffsetBegin = MINE_DATA_TBL_ADDR + sizeof(DWORD) * 4 + iOneRowBytes + 1;
dwAddrObj = (DWORD)iDataOffsetBegin + iOneRowBytes * (iRow - 1) + (iCol - 1);
} else {
iOneRowBytes = 32;
iDataOffsetBegin = MINE_DATA_TBL_ADDR + sizeof(DWORD) * 4 + iOneRowBytes + 1;
dwAddrObj = (DWORD)iDataOffsetBegin + iOneRowBytes * (iRow - 1) + (iCol - 1);
}
bRc = ReadProcessMemory(m_hProcess, (LPVOID)dwAddrObj,
&ucData, sizeof(BYTE), &stRdBack);
return bRc;
}