您好,登錄后才能下訂單哦!
這篇文章主要講解了“FPS游戲反作弊系統設計方法是什么”,文中的講解內容簡單清晰,易于學習與理解,下面請大家跟著小編的思路慢慢深入,一起來研究和學習“FPS游戲反作弊系統設計方法是什么”吧!
目前大部分游戲外掛不再是以前那種createremotethread + loadlibary注入方式了,因為大部分反作弊有自己的minifilter文件過濾驅動與imageloadcallback鏡像加載回調做判斷,大部分反作弊軟件在這種過濾鉤子中做這種操作:
if(!CheckFileCertificateByR3(FilePatch)){ //把文件路徑傳回r3,r3判斷文件數字簽名是否在白名單數字簽名里面(比如微軟數字簽名),如果是白名單文件,就放行,如果不是白名單文件,就攔截 //不是白名單文件...攔截 block; } //放行 pass;
所以,外掛是特別難通過dll直接注入到游戲里面.因此大部分外掛通過一種 無文件落地注入方式 所謂無文件落地注入方式,就是直接在游戲進程里面開辟一個內存空間,把外掛的dll的shellcode寫入,之后手動修復輸入表,然后解析pe文件頭拿到dllmain,再通過createremotethread,apc或者hook方式讓游戲執行這塊內存地址,這樣子外掛就注入了
具體代碼如下(抄自google):
//以下代碼來自與谷歌搜索 void InjectorDLLByManualMap(const char* filepath, HANDLE hProcess) { LPVOID lpBuffer; HANDLE hFile; DWORD dwLength; DWORD dwBytesRead; DWORD dwThreadId; ULONG_PTR lpReflectiveLoader; LPVOID lpRemoteDllBuffer; //打開文件 hFile = CreateFileA(filepath, GENERIC_READ, 0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL); //得到文件大小 dwLength = GetFileSize(hFile, NULL); lpBuffer = HeapAlloc(GetProcessHeap(), 0, dwLength); //讀入文件 ReadFile(hFile, lpBuffer, dwLength, &dwBytesRead, NULL); //修復導入表 dwReflectiveLoaderOffset = GetReflectiveLoaderOffset(lpBuffer); //給游戲進程分配一段內存空間 lpRemoteDllBuffer = VirtualAllocEx(hProcess, NULL, dwLength, MEM_RESERVE|MEM_COMMIT, PAGE_EXECUTE_READWRITE); //寫入文件shellcode到分配的內存空間 WriteProcessMemory(hProcess, lpRemoteDllBuffer, lpBuffer, dwLength, NULL) lpReflectiveLoader = (ULONG_PTR)lpRemoteDllBuffer + dwReflectiveLoaderOffset; //啟動進程 CreateRemoteThread(hProcess, NULL, 1024*1024, (LPTHREAD_START_ROUTINE)lpReflectiveLoader, NULL, NULL, &dwThreadId) }
其特點是:內存標志為PAGE_EXECUTE_READWRITE,MEM_PRIVATE,無文件,無模塊,不會觸發minifilter和imageloadcallbacks,無法通過正常方式枚舉到外掛模塊,隱蔽性非常高.
之前的方法看起來非常的"無敵"實際上也是可以對抗的,因為其特征也非常明顯:
內存屬性為MEM_PRIVATE,內存標志為PAGE_EXECUTE_READWRITE.大小會很大.
所以檢測方法也有幾個:
1.暴力搜索PE頭,大部分這種內存加載的dll都有pe頭.一個內存屬性為mem_private居然還有pe頭,就說明是外掛了.目前大部分反作弊都有這個機制
外掛反制: 抹掉pe頭.不止pe頭,還可以抹掉一切pe特征.
2.createthreadcallbacks得到線程地址,判斷線程地址是否在一個內存屬性的mem_private的內存里面.如果是,說明就是外掛了.
外掛反制:不創建線程,使用hook方啟動外掛.
3.api調用回溯.顧名思義,外掛總要調用一些api地址的,我們可以通過回溯是誰調用了api地址,然后判斷這個調用地方內存屬性是不是mem_private.有兩種方法,一個是hook所有關鍵api,在hook部位用_returnaddres()得到調用地址(其實是讀ESP/RSP寄存器)第二種通過int3斷點觸發異常,使用異常處理函數處理這個異常,判斷調用者.
外掛反制: 第一種內聯hook方式,直接寫跳轉跳過hook,比如你hook的時候:
jmp 你的hook地址
push ebp
push eax
call xxxx;
外掛可以直接從push ebp調用,不再調用你jmp ,就可以繞過
第二種外掛反制目前沒有特別的能反制的地方.除非外掛自己構造api函數調用更底層的api.當然我們可以混淆原底層api的地址(無限套娃),具體以后在說.
為了實現調用回溯,我們需要實現如下步驟:
1. 設置異常處理程序去捕獲異常,代碼如下:
AddVectoredExceptionHandler
2. 拷貝原API地址到自己的內存區域,然后填充原API地址為int,代碼如下:
LPVOID pHOOKAdress; pHOOKAdress = Megrez_GetProAdress(pszModuleName, pszProcName); vecInt3HookedAdress.push_back((DWORD)pHOOKAdress); //用于檢測 if (pHOOKAdress == 0) { return 0; } DWORD dProSize = 0; LPBYTE pTemp = (LPBYTE)pHOOKAdress; BYTE bTemp = 0; for (dProSize = 0; ; ) { bTemp = *pTemp++; dProSize++; if (bTemp == 0xcc) { break; } } DWORD dFileSize = dProSize - 1; PVOID pNewAddr = VirtualAlloc(NULL, dFileSize, MEM_COMMIT, PAGE_EXECUTE_READWRITE); if (pNewAddr == NULL) { return 0; } Megrez_SetMemoryAttr(pHOOKAdress, dProSize); memcpy(pNewAddr, pHOOKAdress, dProSize - 1); memset(pHOOKAdress, 0xcc, 1); memset((PBYTE)pHOOKAdress + 1, 0xc3, 1); memset((PBYTE)pHOOKAdress + 2, 0x90, dProSize - 1 -2); memset((PBYTE)pHOOKAdress + 2 + dProSize - 1 - 2 - 1, 0xcc, 1); //memset((PBYTE)pHOOKAdress + 2 + dProSize - 3 - 2 , 0xcc, 2); mapAdress.insert(pair<DWORD, DWORD>((DWORD)pHOOKAdress, (DWORD)pNewAddr)); Megrez_SetMemoryAttr(pHOOKAdress, dProSize); Megrez_SetMemoryAttr(pNewAddr, dFileSize);
這樣子原api函數就會變成int3 當調用時候就回觸發int3異常 然后被我們的異常處理捕獲
3. 查詢異常位置內存信息,如果是meme_private者調用的代碼,則報告給服務端,代碼如下(記住,x32位下保存調用者地址的是esp,x64位下保存調用者地址的是rsp,):
size_t sizeQuery = VirtualQuery((PVOID)caller_function, lpBuffer, sizeof(MEMORY_BASIC_INFORMATION)); bool non_commit = lpBuffer->State != MEM_COMMIT; bool foreign_image = lpBuffer->Type != MEM_IMAGE && lpBuffer->RegionSize > 0x2000; bool spoof = *(PWORD)caller_function == 0x23FF; // jmp qword ptr [rbx],這是為了防止被欺騙 return sizeQuery || non_commit || foreign_image || spoof; //返回
處理完異常后,我們要跳到原來的保存的api內存里面正常調用(設置eip保存的內存地址)
ExceptionInfo->ContextRecord->Eip = mapAdress[(DWORD)ExceptionInfo->ExceptionRecord->ExceptionAddress]; #ifdef DEBUG WCHAR _buf[256] = { 0 }; swprintf_s(_buf, 256, L"eIP:0x%08X\n", ExceptionInfo->ContextRecord->Eip); OutputDebugStringW(_buf); #endif //已經處理了異常要再調用下一個異常處理來處理此異常 return EXCEPTION_CONTINUE_EXECUTION; } //調用下一個處理器 return EXCEPTION_CONTINUE_SEARCH;
可以看到,這樣子就得到了api調用者的信息,從而做出判斷.
這樣,一個能檢測出絕大部分內存加載外掛的東西就做好了(誰調用誰就會被檢測)
感謝各位的閱讀,以上就是“FPS游戲反作弊系統設計方法是什么”的內容了,經過本文的學習后,相信大家對FPS游戲反作弊系統設計方法是什么這一問題有了更深刻的體會,具體使用情況還需要大家實踐驗證。這里是億速云,小編將為大家推送更多相關知識點的文章,歡迎關注!
免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。