Hacking Diablo II之完整性检查(Integrity Scan)
d2hackmap有一個完整性檢查的功能(Integrity Scan),用來檢查游戲進程的代碼有沒有被改過。這個功能在d2hackmap的“安全開地圖”中有所應用。所謂的“安全開地圖”,其原理大致是在游戲進程分配一塊空間,把“開地圖”的相關代碼(不是一個完整的DLL模塊)注入這塊空間,這段代碼會在游戲的主線程context下運行,調用游戲的內部函數實現“開地圖”邏輯,完事兒后再釋放分配的空間。這個過程時間很短,也不需要修改游戲進程的代碼,因此安全性比較高,不容易被warden抓到。下圖是“安全開地圖”代碼運行前的警告:
“開地圖”(Reveal Map)的代碼邏輯大致如下,紅色代碼行調用了游戲內部函數:
void?__stdcall?RemoteRevealAutomapAct(RevealMapContext?*pctx)
{
????AutomapLayer2?*pLayer;
????UnitAny*?unit?=?*pctx->p_D2CLIENT_PlayerUnit;
????if?(!unit?||?!unit->pPos->pRoom1)?return;
????DWORD?currlvl?=?unit->pPos->pRoom1->pRoom2->pDrlgLevel->nLevelNo;
????DWORD?act?=?0;
????BYTE?actlvls[]?=?{1,?40,?75,?103,?109,?133,?134,?135,?136,?137};
????do?{}?while?(currlvl?>=?actlvls[++act]);
????DWORD?lvl?=?currlvl;
????for?(lvl?=?actlvls[act-1];?lvl?<?actlvls[act];?lvl++)?{
????????DrlgLevel?*pDrlgLevel?=?pctx->GetDrlgLevel((*pctx->p_D2CLIENT_pDrlgAct)->pDrlgMisc,?lvl);
????????if?(!pDrlgLevel)
????????????pDrlgLevel?=?pctx->D2COMMON_GetDrlgLevel((*pctx->p_D2CLIENT_pDrlgAct)->pDrlgMisc,?lvl);
????????if?(!pDrlgLevel->pRoom2First)?{
????????????pctx->D2COMMON_InitDrlgLevel(pDrlgLevel);
????????}
????????pLayer?=?pctx->D2COMMON_GetDrlgLayer(lvl);
????????pctx->InitAutomapLayer(pLayer->nLayerNo,?(DWORD)pctx->D2CLIENT_InitAutomapLayer_I);
????????pctx->RevealAutomapLevel(pctx,?pDrlgLevel);
????}
????pLayer?=?pctx->D2COMMON_GetDrlgLayer(currlvl);
????pctx->InitAutomapLayer(pLayer->nLayerNo,?(DWORD)pctx->D2CLIENT_InitAutomapLayer_I);
}
由于“開地圖”需要調用到游戲的內部函數,這給warden檢測留下了一點可乘之機:如果warden截獲了這幾個內部函數中的一個,在調用發生時檢查調用者的身份(通過分析函數返回地址得到調用模塊信息),就可抓住外掛。在d2hackmap中,為了對付warden的這種檢測,“安全開地圖”代碼在執行前,d2hackmap會對游戲進程做完整性檢查,也就是檢查游戲進程的代碼有沒有被改過。這篇文章講講“完整性檢查”的實現。
首先要明白的是這里說的“完整性檢查”主要指的是檢查代碼的完整性。一個可執行程序的構成,大約可分為文件頭、代碼段和數據段幾部分。程序的代碼在運行時不會改變,一般裝載在只讀內存頁面,數據段又可分為只讀數據和可讀寫數據兩部分。可讀寫數據裝載在讀寫內存頁面,從通用的角度來說,這部分數據是沒法做完整性檢查的。d2hackmap的完整性檢查功能查的是可執行模塊(exe、dll)的只讀內存頁面,包括代碼段和只讀數據段。
一個windows的進程加載幾十個DLL是很常見的,加上EXE主程序模塊,完整性檢查需要檢測的數據大小一般在幾兆到幾十兆字節之間。對于這樣的數據量,一個好的檢測算法是很必要的。d2hackmap使用的策略是,對于每一個待掃描的模塊,構建出相應的“干凈”模塊,然后拿兩個模塊逐字節比較。在 x86下,內存比較有專用、高效的匯編指令cmpsd和cmpsb。
DWORD?_declspec(naked)?__fastcall?mymemcmpd(DWORD?nSize,?void*?pleft,?void*?pright)
{
????__asm
????{
????????push?esi;
????????push?edi;
????????shr?ecx,?2;
????????mov?eax,?edx;
????????mov?esi,?edx;?//?pleft
????????mov?edi,?[esp+0x0c];?//?pright
????????rep?cmpsd;
????????sub?eax,?esi;
????????neg?eax;
????????pop?edi;
????????pop?esi;
????????ret?4;
????}
}
DWORD?_declspec(naked)?__fastcall?mymemcmpb(DWORD?nSize,?LPBYTE?pleft,?LPBYTE?pright)
{
????__asm
????{
????????push?esi;
????????push?edi;
????????mov?eax,?edx;
????????mov?esi,?edx;
????????mov?edi,?[esp+0x0c];
????????rep?cmpsb;
????????test?ecx,?ecx;
????????jz?notfound;
????????sub?eax,?esi;
????????not?eax;
????????pop?edi;
????????pop?esi;
????????ret?4;
notfound:
????????xor?eax,?eax;
????????pop?edi;
????????pop?esi;
????????ret?4;
????}
}
現在問題的關鍵是如何構建一個“干凈”的模塊,這跟黑客的反擊中一文中提到的“模塊重建”是非常相似的,唯一的區別在于“模塊重建”的代碼運行在游戲進程中,和目標模塊在同一個內存空間。
構建一個“干凈”模塊的算法步驟和手工加載DLL的步驟是比較類似的,描述如下:
1,把目標模塊的數據完整復制一份到本地進程空間(ReadProcessMemory),以下稱為“臟”模塊;
2,分配一塊空間以存放“干凈”模塊。
3,把目標模塊的磁盤文件映射到本地進程空間(CreateFile/CreateFileMapping/MapViewOfFile),以下稱為磁盤文件映象;
4,把“臟”模塊數據再復制到“干凈”模塊空間(memcpy)-這樣保證了可寫數據段是相同的;
5,把磁盤文件映象的可執行文件頭(PE header)復制到“干凈”模塊(memcpy)-pe header需要檢測;
6,分析pe header,把磁盤文件映象中的只讀section逐一復制到“干凈”模塊-只讀section需要檢測;
7,接下來對“干凈”模塊做進一步的修正(fix-up),包括導入表(IAT)和重定位表(relocation table);
8,IAT的修正稍微有點兒繁瑣,也和普通的加載DLL不同,主要的問題是同一個DLL,在本地加載和在游戲進程加載的基地執有可能是不一樣的。對于IAT中鏈接到的DLL,修正時應該以該DLL在目標游戲進程中加載的基地址為基準;
9,重定位表的修正也類似,應該使用“臟”模塊的重定位數據-這和普通的加載DLL也不同。
經過這幾步以后,“干凈”模塊就構建好了。接下來的完整性檢查用前面給出的mymemcmpd和mymemcmpb函數就行了。使用這種方法,完整性檢查的效率還是比較高的,一般情況下掃描一個進程的時間在幾秒鐘(<5秒)以內。下圖是d2hackmap插件(d2hackmap.dll)注入后對游戲進程的完整性檢查的結果,可以看到d2hackmap.dll修改了很多處,視圖中的每一項列出了被修改的dll名稱(入d2win.dll),修改地址,修改長度,修改后的指令,如是跳轉指令,還給出了跳轉模塊的名稱(如圖中都是d2hackmap.dll,根據這點我們就可以判斷出該處是被 d2hackmap.dll修改的)。
完整性檢查還可以有很多其他用途,不僅僅限于游戲外掛方面。比如說有些流氓軟件可能會在一些敏感進程中截獲某些API來監控用戶的行為,完整性檢查可以把它檢測出來。另外,完整性檢查還可以用來分析那些依賴于代碼截獲技術的程序,比如說你想分析D2JSP.DLL的實現技術,那么通過觀測它的截獲點,以截獲點為起點進行逆向分析是一種很有效的方法。下圖是D2JSP加載后的完整性檢查結果:
總結
以上是生活随笔為你收集整理的Hacking Diablo II之完整性检查(Integrity Scan)的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: java中socket编程实例_Java
- 下一篇: opengl交叉编译