關於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里為所欲為了,還有三方檢測等着你呢!
這些還需要靠大家自己多去練習,總結規律,其實你會發現,
再難的保護還都是這樣子的了(笑)......

附贈代碼(部分):

  1. void CAnitTP_AppDlg::OnBnClickedButtonAnit()
  2. {
  3.         DWORD pid = GetProcessIdByProcName(TEXT("DNF.exe"));
  4.         if (pid==0)
  5.         {
  6.                 MessageBox(TEXT("對不起,沒有找到指定游戲進程.(DNF.exe)"), TEXT("操作失敗"), MB_OK | MB_ICONERROR);
  7.                 return;
  8.         }
  9.         const BYTE code[8] = {0x90,0x90,0x90,0x90,0x90,0x55,0x8b,0xec};
  10.         const BYTE code2[13] = { 0xC3, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90 ,0xE9};
  11.         
  12.         DWORD trds[521];
  13.         int trdcount=0;
  14.         trdcount=GetProcessThreadId(pid, trds);
  15.         LPVOID pEntryPoint=NULL;
  16.         BYTE buf[13];
  17.         HANDLE hThread;
  18.         for (int i = 0; i < trdcount;i++)
  19.         {
  20.                 pEntryPoint=GetThreadEntryPointById(trds[i]);
  21.                 ReadProcessMemoryEx(pid, pEntryPoint, buf, 8);
  22.                 TCHAR ModuleName[256];
  23.                 GetProcessThreadModuleNameByTid(pid, trds[i], ModuleName);
  24.                 
  25.                 if (memcmp(buf, code, 8) == 0 || lstrcmp(ModuleName, L"TenSLX.dll")==0)
  26.                 {
  27.                         hThread = OpenThread(THREAD_ALL_ACCESS, FALSE, trds[i]);
  28.                         if (!hThread)continue;
  29.                         TerminateThread(hThread,0);
  30.                         CloseHandle(hThread);
  31.                         
  32.                 }
  33.                 
  34.                 ReadProcessMemoryEx(pid, (LPVOID)((int)pEntryPoint - 0xc), buf, 13);
  35.                 if (memcmp(buf, code2, 13) == 0)
  36.                 {
  37.                         hThread = OpenThread(THREAD_ALL_ACCESS, FALSE, trds[i]);
  38.                         if (!hThread)continue;
  39.                         SuspendThread(hThread);
  40.                         CloseHandle(hThread);
  41.                 }
  42.         }
  43.         
  44.         byte code3[7] = { 0x6A, 0x08, 0x68, 0x00, 0x00, 0x00, 0x00 };
  45.         LPVOID pDbgUiRemoteBreakin = (LPVOID)GetProcAddress(GetModuleHandle(_T("ntdll.dll")), "DbgUiRemoteBreakin");   //調試用
  46.         memcpy(&code3[3], (LPVOID)((int)pDbgUiRemoteBreakin + 3), 4);
  47.         WriteProcessMemoryEx(pid, pDbgUiRemoteBreakin, code3, 7);
  48.         byte code4[6] = { 0x8b, 0xff, 0x55, 0x8b, 0xec, 0xff };
  49.         LPVOID pLdrInitializeThunk = (LPVOID)GetProcAddress(GetModuleHandle(_T("ntdll.dll")), "LdrInitializeThunk");  //DLL注入用
  50.         WriteProcessMemoryEx(pid, pLdrInitializeThunk, code4, 6);
  51.         MessageBox(TEXT("操作完畢,開始調試吧!"),TEXT("OK"),MB_OK|MB_ICONINFORMATION);
  52. }
復制代碼

功能函數頭文件:

  1. #ifndef HANSHU
  2. #define HANSHU
  3. #include <TlHelp32.h>
  4. #include <psapi.h>
  5. #pragma comment(lib,"psapi.lib")
  6. typedef enum _THREADINFOCLASS {
  7.         ThreadBasicInformation,
  8.         ThreadTimes,
  9.         ThreadPriority,
  10.         ThreadBasePriority,
  11.         ThreadAffinityMask,
  12.         ThreadImpersonationToken,
  13.         ThreadDescriptorTableEntry,
  14.         ThreadEnableAlignmentFaultFixup,
  15.         ThreadEventPair_Reusable,
  16.         ThreadQuerySetWin32StartAddress,
  17.         ThreadZeroTlsCell,
  18.         ThreadPerformanceCount,
  19.         ThreadAmILastThread,
  20.         ThreadIdealProcessor,
  21.         ThreadPriorityBoost,
  22.         ThreadSetTlsArrayAddress,
  23.         ThreadIsIoPending,
  24.         ThreadHideFromDebugger,
  25.         ThreadBreakOnTermination,
  26.         MaxThreadInfoClass
  27. } THREADINFOCLASS;
  28. typedef struct _CLIENT_ID {
  29.         HANDLE UniqueProcess;
  30.         HANDLE UniqueThread;
  31. } CLIENT_ID;
  32. typedef CLIENT_ID *PCLIENT_ID;
  33. typedef struct _THREAD_BASIC_INFORMATION { // Information Class 0
  34.         LONG     ExitStatus;
  35.         PVOID    TebBaseAddress;
  36.         CLIENT_ID ClientId;
  37.         LONG AffinityMask;
  38.         LONG Priority;
  39.         LONG BasePriority;
  40. } THREAD_BASIC_INFORMATION, *PTHREAD_BASIC_INFORMATION;
  41. typedef LONG (__stdcall *fZwQueryInformationThread) (
  42.         IN HANDLE ThreadHandle,
  43.         IN THREADINFOCLASS ThreadInformationClass,
  44.         OUT PVOID ThreadInformation,
  45.         IN ULONG ThreadInformationLength,
  46.         OUT PULONG ReturnLength OPTIONAL
  47.         );
  48. fZwQueryInformationThread  ZwQueryInformationThread;
  49. DWORD GetProcessPidByWndName(LPCTSTR szWndName)
  50. {
  51.         HWND hWnd = FindWindow(NULL,szWndName);
  52.         if (IsWindow(hWnd))
  53.         {
  54.                 DWORD pid;
  55.                 GetWindowThreadProcessId(hWnd,&pid);
  56.                 return pid;
  57.         }
  58.         return 0;
  59. }
  60. DWORD GetProcessIdByProcName(LPCTSTR szProcName)
  61. {
  62.         HANDLE hSnapshot= CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS,0);
  63.         PROCESSENTRY32 pro;
  64.         pro.dwSize=sizeof(pro);
  65.         BOOL bMore=Process32First(hSnapshot,&pro);
  66.         while (bMore)
  67.         {
  68.                 if (lstrcmp(szProcName,pro.szExeFile)==0)
  69.                 {
  70.                         CloseHandle(hSnapshot);
  71.                         return pro.th32ProcessID;
  72.                 }
  73.                 bMore=Process32Next(hSnapshot,&pro);
  74.         }
  75.         CloseHandle(hSnapshot);
  76.         return 0;
  77. }
  78. BOOL ReadProcessMemoryEx(DWORD pid,LPVOID addr,LPVOID buffer,DWORD size)
  79. {
  80.         HANDLE hProcess=OpenProcess(PROCESS_ALL_ACCESS,FALSE,pid);
  81.         if (hProcess==0)
  82.         {
  83.                 return FALSE;
  84.         }
  85.         BOOL bResult=ReadProcessMemory(hProcess,addr,buffer,size,NULL);
  86.         CloseHandle(hProcess);
  87.         return bResult;
  88. }
  89. BOOL WriteProcessMemoryEx(DWORD pid,LPVOID addr,LPVOID buffer,DWORD size)
  90. {
  91.         HANDLE hProcess=OpenProcess(PROCESS_ALL_ACCESS,FALSE,pid);
  92.         if (hProcess==0)
  93.         {
  94.                 return FALSE;
  95.         }
  96.         BOOL bResult=WriteProcessMemory(hProcess,addr,buffer,size,NULL);
  97.         CloseHandle(hProcess);
  98.         return bResult;
  99. }
  100. int GetProcessThreadId(DWORD pid,DWORD *trds)
  101. {
  102.         HANDLE hSnapshot= CreateToolhelp32Snapshot(TH32CS_SNAPTHREAD,pid);
  103.         if (hSnapshot==INVALID_HANDLE_VALUE)
  104.                 return 0;
  105.         THREADENTRY32 trd;
  106.         trd.dwSize=sizeof(trd);
  107.         BOOL bMore=Thread32First(hSnapshot,&trd);
  108.         int i=0;
  109.         while (bMore)
  110.         {
  111.                 if (trd.th32OwnerProcessID==pid)
  112.                 {
  113.                         trds[i]=trd.th32ThreadID;
  114.                         i++;
  115.                 }
  116.                 bMore=Thread32Next(hSnapshot,&trd);
  117.         }
  118.         CloseHandle(hSnapshot);
  119.         return i;
  120. }
  121. LPVOID GetThreadEntryPointById(DWORD tid)
  122. {
  123.         HANDLE hThread=OpenThread(THREAD_ALL_ACCESS,FALSE,tid);
  124.         if (hThread==0)
  125.         {
  126.                 return NULL;
  127.         }
  128.         LPVOID Addr=NULL;
  129.         ZwQueryInformationThread=(fZwQueryInformationThread)GetProcAddress(GetModuleHandle(TEXT("ntdll.dll")),"ZwQueryInformationThread");
  130.         ZwQueryInformationThread(hThread,ThreadQuerySetWin32StartAddress,&Addr,4,NULL);
  131.         CloseHandle(hThread);
  132.         return Addr;
  133. }
  134. BOOL GetProcessThreadModuleNameByTid(DWORD pid, DWORD tid, LPWSTR pszModuleName)
  135. {
  136.         HANDLE hProcess = NULL;
  137.         LPVOID pStart = NULL;
  138.         TCHAR tmpStr[256];
  139.         hProcess = OpenProcess(PROCESS_ALL_ACCESS, FALSE, pid);
  140.         if (!hProcess)return FALSE;
  141.         pStart = GetThreadEntryPointById(tid);
  142.         if (!pStart) return FALSE;
  143.         GetMappedFileName(hProcess, pStart, tmpStr, 256);
  144.         for (int i = lstrlen(tmpStr); i >0; i--)
  145.         {
  146.                 if (tmpStr[i]== '\\')
  147.                 {
  148.                         lstrcpy(pszModuleName, &tmpStr[i+1]);
  149.                         break;
  150.                 }
  151.         }
  152.         CloseHandle(hProcess);
  153.         return TRUE;
  154. }
  155. void TerminateThreadEx(DWORD tid,DWORD exitcode=0)
  156. {
  157.         HANDLE hThread=OpenThread(THREAD_ALL_ACCESS,FALSE,tid);        
  158.         if (hThread!=NULL)
  159.         {
  160.                 TerminateThread(hThread,exitcode);
  161.                 CloseHandle(hThread);
  162.         }
  163.         
  164. }
  165. BOOL EnableDebugPrivilege()  
  166. {  
  167.         HANDLE token;  
  168.         //提升權限   
  169.         if(!OpenProcessToken(GetCurrentProcess(),TOKEN_ADJUST_PRIVILEGES,&token))  
  170.         {   
  171.                 return FALSE;  
  172.         }  
  173.         TOKEN_PRIVILEGES tkp;  
  174.         tkp.PrivilegeCount = 1;  
  175.         ::LookupPrivilegeValue(NULL,SE_DEBUG_NAME,&tkp.Privileges[0].Luid);  
  176.         tkp.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED;  
  177.         if(!AdjustTokenPrivileges(token,FALSE,&tkp,sizeof(tkp),NULL,NULL))  
  178.         {  
  179.                 return FALSE;  
  180.         }  
  181.         CloseHandle(token);  
  182.         return TRUE;  
  183. }  
  184. #endif
復制代碼


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM