關於Win7 x64下過TP保護(應用層)(轉)
非常感謝大家那麼支援我上一篇教程。
Win10 快出了,所以我打算儘快把應用層的部分說完。
除錯物件:DXF
除錯工具:CE、OD、PCHunter、Windbg
除錯先言:TP的應用層保護做得比較多,包括對偵錯程式的檢測,比如CE工具會被DXF報非法。有的保護還是核心與應用層交替保護。
應用層:
1、TP讓偵錯程式卡死(核心互動)
現象: <ignore_js_op>
如圖,TP會檢測偵錯程式讓偵錯程式暫停執行,實際上就是暫停了偵錯程式所有的執行緒而已。
這個保護是今年7月份新出的,所以我這裡重點分析下,我剛開始除錯的時候就發現OD會莫名其妙地卡死。
開啟PCHunter發現OD的程序執行緒全部被暫停了。
開始我認為是TP呼叫了SuspendThread(函式:暫停指定執行緒)來讓偵錯程式卡死的。
於是我就開啟Windbg附加並在這個函式上下斷點,發現沒有斷下來。
然後我認為是呼叫了介面函式NtSuspendThread(函式:暫停指定執行緒<核心介面>)
但是還是沒有斷下。所以排除了DXF在Ring3呼叫了暫停執行緒讓OD卡死。
於是我思考了一下,開啟虛擬機器,簡單過了雙機除錯保護(一段時間後還是會藍屏),在DXF啟動之後,
在Windbg輸入!process OD的程序ID 來檢視執行緒的呼叫堆載,我發現了很有意思的東西。
SuspendCount被置為了1,再看看呼叫堆載。
原來TP在Ring0中呼叫了KiSuspendThread來暫停OD的執行緒啊。怪不得斷不下來。
於是我在KiSuspendThread頭部下斷點,發現當OD開啟的時候會斷下,
這個是它的函式開頭
0: kd> u KiSuspendThread
nt!KiSuspendThread:
fffff800`03e6cc60 48895c2408 mov qword ptr [rsp+8],rbx
fffff800`03e6cc65 4889742410 mov qword ptr [rsp+10h],rsi
fffff800`03e6cc6a 57 push rdi
fffff800`03e6cc6b 4883ec30 sub rsp,30h
fffff800`03e6cc6f 8364245800 and dword ptr [rsp+58h],0
fffff800`03e6cc74 65488b1c2588010000 mov rbx,qword ptr gs:[188h]
fffff800`03e6cc7d 4885db test rbx,rbx
它還保留著用__stdcall的呼叫約定,在64位下一般都是__fastcall
通過對引數的分析,我發現這個函式的第一個引數也就是rbx,裡面存的是執行緒物件。
我在網上也沒有找到相關的資訊,於是我自己在頭部改成了ret。
之後執行OD就不會卡死了。
繼續深究,原來TP建立了一個核心回撥,就是CreateProcess的回撥,
自己可以開啟PCHunter檢視。
當發現是OD的程序被建立時,就會呼叫這個函式讓程序暫停。
哦,原來是這樣,那我們有什麼辦法解決它呢?怎麼才能讓偵錯程式正常執行呢?
方案:1、自己恢復偵錯程式的程序(推薦) 2、刪除核心回撥(驅動推薦)3、Hook KiSuspendThread 繞過(稍難)
在這裡我推薦第一種,因為我們是要在應用層下操作。
方法很簡單,當我們開啟OD工具時,開啟PCHunter選擇OD的程序,右鍵恢復程序執行即可。
也可以自己做一個工具,恢復OD的程序,但是你要確保自己的程式不會進入黑名單。
至此,我們的偵錯程式能正常打開了。
2、函式鉤子(Hook)
這個保護不能說是新鮮了的吧,在應用層裡很多遊戲都這麼幹。
其實就是把一些重要的除錯函式進行鉤子,導致程式崩潰或者無法除錯。
我們開啟PCHunter,來到如圖的位置,選擇DXF的程序->右鍵選擇掃描。
<ignore_js_op>
現在你只需要坐下等大約5分鐘吧,好像有一千多個鉤子(笑),要有耐心。
看到圖中3個鉤子了嗎,它就是我們要說的。
我這裡來說明下這3個函式的用途。
DbgUiRemoteBreakin:遠端中斷,附加偵錯程式時偵錯程式會發送資訊讓程序走這裡。
KiUserExceptionDispatcher:UEF異常處理函式,夢老大講解過的,這個我們不能隨便恢復,乾脆不用管它
因為DXF自己製造異常自己處理。
LdrInitalizeThunk:映像檔案鏈入口,當DLL載入時會經過這裡,如果我們不恢復它將無法注入DLL。
這3個鉤子有兩個是我們必須恢復的,就是一和三。
第三個比較好處理,PCHunter中已經給出了函式原來的機器碼。我們之間用PCHunter恢復也可以自己寫個程式恢復
但是第一個就不好了,我們必須用程式自己來恢復,因為:
<ignore_js_op>
紅線部分是需要重定位的,機器每次開機都會不同,我們可以通過獲取自己程式的這個位置的程式碼來恢復。
具體怎麼恢復這裡就不說了,我會貼出程式碼給大家。
那麼2個Hook搞定後我們就要來解決崩潰問題了。
3、異常崩潰
大家可以發現OD附加DXF後執行,遊戲會莫名其妙地崩潰,你可能會認為OD被DXF檢測到了,其實它是個通用
的反除錯的手法。
自己給自己製造異常,自己處理,如果OD搶著處理這個異常,反而會使程序崩潰。
這個就是它異常崩潰的原理。
其實是一個執行緒在自發異常的,怎麼把它揪出來呢?
開啟Windbg,附加DXF,執行,可以發現一段時間後,Windbg斷下
如圖所示:
<ignore_js_op>
執行緒ID:F08 傳送了一個記憶體訪問異常(0x80000002)它故意讓Windbg斷下。
它需要試探是否有偵錯程式,於是我們就找到它了,把f08換成十進位制發現是
3848的執行緒傳送異常的,在PCHunter中可以看到,如圖所示,它是由ntdll.dll傳送的。
唯一的辦法就是結束這個執行緒,右鍵->結束執行緒,搞定,OD附加DXF不會崩潰了。
但是你得自己做個程式來找到這條執行緒然後來結束掉,可以通過搜尋執行緒入口特徵碼的方式來
找到它。
那麼現在,我們除錯DXF再也沒有問題了。但是CE工具開啟久了也會被提示非法,怎麼辦?
4、檢測非法工具
也許大家非常想要知道怎麼辦。
它檢測非法工具的原理是:
使用ReadProcessMemory(函式:讀取程序記憶體)搜尋程序特徵碼,找到屬於非法工具的特徵碼後遊戲消失,提示非法重啟。
若想解決它,我們有以下方案:
1、刪除工具中的特徵碼 2、在核心中攔截NtReadVirtualMemory或KeStackAttachProcess來繞過搜尋
3、找到搜尋的執行緒,結束該執行緒。
其實我發現第3種是一勞永逸,所以我在這裡說下第三種方法。
開啟PCHunter找到如下幾條執行緒。選擇後同時右鍵結束,OK,CE再也不會非法了。
<ignore_js_op>
這幾條執行緒都是TenSLX.dll模組裡的,其實這個方法在騰Xun遊戲裡面通用。
別問我是怎麼知道的,其實我是一個個試過的(笑)。
5、關於CRC程式碼自校驗
通過上面的說明,大家應該可以總結一個結論
在應用層中游戲若想保護自己都是採用走執行緒或Hook方式。
所以大家可以自己找到CRC程式碼自校驗的執行緒吧?結束掉,OK,可以下軟體斷點(int 3)了。
結束語:
別以為這樣就能在DXF裡為所欲為了,還有三方檢測等著你呢!
這些還需要靠大家自己多去練習,總結規律,其實你會發現,
再難的保護還都是這樣子的了(笑)......
附贈程式碼(部分):
- void CAnitTP_AppDlg::OnBnClickedButtonAnit()
- {
- DWORD pid = GetProcessIdByProcName(TEXT("DNF.exe"));
- if (pid==0)
- {
- MessageBox(TEXT("對不起,沒有找到指定遊戲程序.(DNF.exe)"), TEXT("操作失敗"), MB_OK | MB_ICONERROR);
- return;
- }
- const BYTE code[8] = {0x90,0x90,0x90,0x90,0x90,0x55,0x8b,0xec};
- const BYTE code2[13] = { 0xC3, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90 ,0xE9};
- DWORD trds[521];
- int trdcount=0;
- trdcount=GetProcessThreadId(pid, trds);
- LPVOID pEntryPoint=NULL;
- BYTE buf[13];
- HANDLE hThread;
- for (int i = 0; i < trdcount;i++)
- {
- pEntryPoint=GetThreadEntryPointById(trds[i]);
- ReadProcessMemoryEx(pid, pEntryPoint, buf, 8);
- TCHAR ModuleName[256];
- GetProcessThreadModuleNameByTid(pid, trds[i], ModuleName);
- if (memcmp(buf, code, 8) == 0 || lstrcmp(ModuleName, L"TenSLX.dll")==0)
- {
- hThread = OpenThread(THREAD_ALL_ACCESS, FALSE, trds[i]);
- if (!hThread)continue;
- TerminateThread(hThread,0);
- CloseHandle(hThread);
- }
- ReadProcessMemoryEx(pid, (LPVOID)((int)pEntryPoint - 0xc), buf, 13);
- if (memcmp(buf, code2, 13) == 0)
- {
- hThread = OpenThread(THREAD_ALL_ACCESS, FALSE, trds[i]);
- if (!hThread)continue;
- SuspendThread(hThread);
- CloseHandle(hThread);
- }
- }
- byte code3[7] = { 0x6A, 0x08, 0x68, 0x00, 0x00, 0x00, 0x00 };
- LPVOID pDbgUiRemoteBreakin = (LPVOID)GetProcAddress(GetModuleHandle(_T("ntdll.dll")), "DbgUiRemoteBreakin"); //除錯用
- memcpy(&code3[3], (LPVOID)((int)pDbgUiRemoteBreakin + 3), 4);
- WriteProcessMemoryEx(pid, pDbgUiRemoteBreakin, code3, 7);
- byte code4[6] = { 0x8b, 0xff, 0x55, 0x8b, 0xec, 0xff };
- LPVOID pLdrInitializeThunk = (LPVOID)GetProcAddress(GetModuleHandle(_T("ntdll.dll")), "LdrInitializeThunk"); //DLL注入用
- WriteProcessMemoryEx(pid, pLdrInitializeThunk, code4, 6);
- MessageBox(TEXT("操作完畢,開始除錯吧!"),TEXT("OK"),MB_OK|MB_ICONINFORMATION);
- }
複製程式碼
功能函式標頭檔案:
- #ifndef HANSHU
- #define HANSHU
- #include <TlHelp32.h>
- #include <psapi.h>
- #pragma comment(lib,"psapi.lib")
- typedef enum _THREADINFOCLASS {
- ThreadBasicInformation,
- ThreadTimes,
- ThreadPriority,
- ThreadBasePriority,
- ThreadAffinityMask,
- ThreadImpersonationToken,
- ThreadDescriptorTableEntry,
- ThreadEnableAlignmentFaultFixup,
- ThreadEventPair_Reusable,
- ThreadQuerySetWin32StartAddress,
- ThreadZeroTlsCell,
- ThreadPerformanceCount,
- ThreadAmILastThread,
- ThreadIdealProcessor,
- ThreadPriorityBoost,
- ThreadSetTlsArrayAddress,
- ThreadIsIoPending,
- ThreadHideFromDebugger,
- ThreadBreakOnTermination,
- MaxThreadInfoClass
- } THREADINFOCLASS;
- typedef struct _CLIENT_ID {
- HANDLE UniqueProcess;
- HANDLE UniqueThread;
- } CLIENT_ID;
- typedef CLIENT_ID *PCLIENT_ID;
- typedef struct _THREAD_BASIC_INFORMATION { // Information Class 0
- LONG ExitStatus;
- PVOID TebBaseAddress;
- CLIENT_ID ClientId;
- LONG AffinityMask;
- LONG Priority;
- LONG BasePriority;
- } THREAD_BASIC_INFORMATION, *PTHREAD_BASIC_INFORMATION;
- typedef LONG (__stdcall *fZwQueryInformationThread) (
- IN HANDLE ThreadHandle,
- IN THREADINFOCLASS ThreadInformationClass,
- OUT PVOID ThreadInformation,
- IN ULONG ThreadInformationLength,
- OUT PULONG ReturnLength OPTIONAL
- );
- fZwQueryInformationThread ZwQueryInformationThread;
- DWORD GetProcessPidByWndName(LPCTSTR szWndName)
- {
- HWND hWnd = FindWindow(NULL,szWndName);
- if (IsWindow(hWnd))
- {
- DWORD pid;
- GetWindowThreadProcessId(hWnd,&pid);
- return pid;
- }
- return 0;
- }
- DWORD GetProcessIdByProcName(LPCTSTR szProcName)
- {
- HANDLE hSnapshot= CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS,0);
- PROCESSENTRY32 pro;
- pro.dwSize=sizeof(pro);
- BOOL bMore=Process32First(hSnapshot,&pro);
- while (bMore)
- {
- if (lstrcmp(szProcName,pro.szExeFile)==0)
- {
- CloseHandle(hSnapshot);
- return pro.th32ProcessID;
- }
- bMore=Process32Next(hSnapshot,&pro);
- }
- CloseHandle(hSnapshot);
- return 0;
- }
- BOOL ReadProcessMemoryEx(DWORD pid,LPVOID addr,LPVOID buffer,DWORD size)
- {
- HANDLE hProcess=OpenProcess(PROCESS_ALL_ACCESS,FALSE,pid);
- if (hProcess==0)
- {
- return FALSE;
- }
- BOOL bResult=ReadProcessMemory(hProcess,addr,buffer,size,NULL);
- CloseHandle(hProcess);
- return bResult;
- }
- BOOL WriteProcessMemoryEx(DWORD pid,LPVOID addr,LPVOID buffer,DWORD size)
- {
- HANDLE hProcess=OpenProcess(PROCESS_ALL_ACCESS,FALSE,pid);
- if (hProcess==0)
- {
- return FALSE;
- }
- BOOL bResult=WriteProcessMemory(hProcess,addr,buffer,size,NULL);
- CloseHandle(hProcess);
- return bResult;
- }
- int GetProcessThreadId(DWORD pid,DWORD *trds)
- {
- HANDLE hSnapshot= CreateToolhelp32Snapshot(TH32CS_SNAPTHREAD,pid);
- if (hSnapshot==INVALID_HANDLE_VALUE)
- return 0;
- THREADENTRY32 trd;
- trd.dwSize=sizeof(trd);
- BOOL bMore=Thread32First(hSnapshot,&trd);
- int i=0;
- while (bMore)
- {
- if (trd.th32OwnerProcessID==pid)
- {
- trds[i]=trd.th32ThreadID;
- i++;
- }
- bMore=Thread32Next(hSnapshot,&trd);
- }
- CloseHandle(hSnapshot);
- return i;
- }
- LPVOID GetThreadEntryPointById(DWORD tid)
- {
- HANDLE hThread=OpenThread(THREAD_ALL_ACCESS,FALSE,tid);
- if (hThread==0)
- {
- return NULL;
- }
- LPVOID Addr=NULL;
- ZwQueryInformationThread=(fZwQueryInformationThread)GetProcAddress(GetModuleHandle(TEXT("ntdll.dll")),"ZwQueryInformationThread");
- ZwQueryInformationThread(hThread,ThreadQuerySetWin32StartAddress,&Addr,4,NULL);
- CloseHandle(hThread);
- return Addr;
- }
- BOOL GetProcessThreadModuleNameByTid(DWORD pid, DWORD tid, LPWSTR pszModuleName)
- {
- HANDLE hProcess = NULL;
- LPVOID pStart = NULL;
- TCHAR tmpStr[256];
- hProcess = OpenProcess(PROCESS_ALL_ACCESS, FALSE, pid);
- if (!hProcess)return FALSE;
- pStart = GetThreadEntryPointById(tid);
- if (!pStart) return FALSE;
- GetMappedFileName(hProcess, pStart, tmpStr, 256);
- for (int i = lstrlen(tmpStr); i >0; i--)
- {
- if (tmpStr[i]== '\\')
- {
- lstrcpy(pszModuleName, &tmpStr[i+1]);
- break;
- }
- }
- CloseHandle(hProcess);
- return TRUE;
- }
- void TerminateThreadEx(DWORD tid,DWORD exitcode=0)
- {
- HANDLE hThread=OpenThread(THREAD_ALL_ACCESS,FALSE,tid);
- if (hThread!=NULL)
- {
- TerminateThread(hThread,exitcode);
- CloseHandle(hThread);
- }
- }
- BOOL EnableDebugPrivilege()
- {
- HANDLE token;
- //提升許可權
- if(!OpenProcessToken(GetCurrentProcess(),TOKEN_ADJUST_PRIVILEGES,&token))
- {
- return FALSE;
- }
- TOKEN_PRIVILEGES tkp;
- tkp.PrivilegeCount = 1;
- ::LookupPrivilegeValue(NULL,SE_DEBUG_NAME,&tkp.Privileges[0].Luid);
- tkp.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED;
- if(!AdjustTokenPrivileges(token,FALSE,&tkp,sizeof(tkp),NULL,NULL))
- {
- return FALSE;
- }
- CloseHandle(token);
- return TRUE;
- }
- #endif
複製程式碼