本期作者/牛杰
前言
是一種防止逆向的方案。逆向人員如果遇到復(fù)雜的代碼混淆,有時會使用調(diào)試器動態(tài)分析代碼邏輯簡化分析流程。例如惡意軟件通常會被安全研究人員、反病毒廠商和其他安全專業(yè)人員分析和調(diào)試,以了解其行為和功能,并開發(fā)相應(yīng)的安全措施來保護系統(tǒng),這時,惡意軟件開發(fā)人員就會使用反調(diào)試技術(shù)阻礙逆向人員的分析,以達到增加自己惡意代碼的存活時間。此外,安全人員也需要了解反調(diào)試技術(shù),當(dāng)遇到反調(diào)試代碼時,可以使用相對應(yīng)的反反調(diào)試。在反調(diào)試技術(shù)上中,我們介紹了9種常見的反調(diào)試方法,本篇繼續(xù)介紹6種方式。
反調(diào)試
NtSetInformationThread 是 Windows 操作系統(tǒng)提供的一個函數(shù),用于設(shè)置線程的信息,該函數(shù)通常用來設(shè)置線程的優(yōu)先級,此外通過設(shè)置不同的 ThreadInformationClass 參數(shù),可以實現(xiàn)隱藏線程、禁止調(diào)試、設(shè)置調(diào)試狀態(tài)等操作,從而增加程序的安全性和防御性,該函數(shù)原型與枚舉信息如下。
__kernel_entry NTSYSCALLAPI NTSTATUS NtSetInformationThread( [in] HANDLE ThreadHandle, [in] THREADINFOCLASS ThreadInformationClass, [in] PVOID ThreadInformation, [in] ULONG ThreadInformationLength ); 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;
NtSetInformationThread通過ThreadInformationClass中ThreadHideFromDebugger來反調(diào)試 是 NtSetInformationThread 函數(shù)的一個參數(shù),用于將當(dāng)前線程隱藏起來,使得調(diào)試器無法對其進行調(diào)試。這個參數(shù)的作用是防止調(diào)試器附加到被隱藏的線程上進行調(diào)試操作。
當(dāng)線程被隱藏后,調(diào)試器無法訪問和控制該線程的執(zhí)行。這可以用作一種反調(diào)試技術(shù),防止惡意用戶或惡意軟件使用調(diào)試器來分析、修改或干擾程序的執(zhí)行。
通過隱藏線程,程序可以增加自身的安全性,防止被調(diào)試器進行逆向工程、代碼分析、內(nèi)存調(diào)試等操作。這對于一些需要保護知識產(chǎn)權(quán)、防止惡意調(diào)試的應(yīng)用程序或安全軟件來說特別有用。
NtSetInformationThread反調(diào)試demo如下。
#include#include typedefenum_THREADINFOCLASS { ThreadBasicInformation, ThreadTimes, ThreadPriority, ThreadBasePriority, ThreadAffinityMask, ThreadImpersonationToken, ThreadDescriptorTableEntry, ThreadEnableAlignmentFaultFixup, ThreadEventPair_Reusable, ThreadQuerySetWin32StartAddress, ThreadZeroTlsCell, ThreadPerformanceCount, ThreadAmILastThread, ThreadIdealProcessor, ThreadPriorityBoost, ThreadSetTlsArrayAddress, ThreadIsIoPending, ThreadHideFromDebugger, ThreadBreakOnTermination, MaxThreadInfoClass } THREADINFOCLASS; typedefNTSTATUS(WINAPI* pNtSetInformationThread)(HANDLE, THREADINFOCLASS, PVOID, ULONG); voidHideFromDebugger(){ HMODULE hNtDll = LoadLibrary(TEXT("ntdll.dll")); pNtSetInformationThread NtSetInformationThread = (pNtSetInformationThread)GetProcAddress(hNtDll, "NtSetInformationThread"); NTSTATUS status = NtSetInformationThread(GetCurrentThread(), ThreadHideFromDebugger, NULL, 0); } voidThreadHid(){ HideFromDebugger(); intx = 1; while(1) { printf("%d",x); Sleep(1000); x++; }; } intmain() { CreateThread(0, 0, (LPTHREAD_START_ROUTINE)ThreadHid, 0, 0, 0); getchar(); return0; }
使用調(diào)試器執(zhí)調(diào)試該程序,可以看到程序正常執(zhí)行。
然后在線程中下斷點,程序會自動關(guān)閉。
需要注意的是,雖然使用 ThreadHideFromDebugger 可以增加程序的安全性,但它并不是絕對的安全措施,因為仍然存在其他方法可以繞過或檢測到線程隱藏。因此,在設(shè)計安全應(yīng)用程序時,應(yīng)綜合考慮多種防護措施,并定期進行安全評估和更新。
2.SeDebugPrivilege
默認情況下,進程沒有調(diào)試權(quán)限(SeDebugPrivilege),除非自己主動開啟,但是調(diào)試器啟動程序并調(diào)試時,會從調(diào)試器繼承該權(quán)限。使用該方式需要先調(diào)用CsrGetProcessId獲取csrss.exe的pid,該函數(shù)在ntdll。獲取pid后,通過OpenProcess讀取句柄csrss.exe,如果能獲取則說明被調(diào)試,代碼如下。
HMODULEhMod = LoadLibrary(TEXT("ntdll.dll")); typedefint(*CSRGETPROCESSID)(); CSRGETPROCESSIDCsrGetProcessId = (CSRGETPROCESSID)GetProcAddress(hMod, "CsrGetProcessId"); DWORDpid = CsrGetProcessId(); HANDLEhandle = OpenProcess(PROCESS_QUERY_LIMITED_INFORMATION, FALSE, pid); if(handle) { returntrue; } else{ returnfalse; }
3.時間檢測
通過計算時間差,如果時間間隔過長,判斷當(dāng)前進程被反調(diào)試,常用API有API有:QueryPerformanceCounter、GetTickCount、GetSystemTime、GetLocalTime,這些API使用方法相似,我們使用GetTickCount舉例。
DWORDstarttime = GetTickCount(); DWORDendtime = GetTickCount(); if(endtime - starttime > 500) { returntrue; } else{ returnfalse; }
4.父進程
通過檢測自身父進程來判定是否被調(diào)試,原理非常簡單,我們的系統(tǒng)在運行程序的時候,絕大多數(shù)應(yīng)用程序都是由explorer.exe這個父進程派生而來的子進程,也就是說如果沒有被調(diào)試其得到的父進程就是explorer.exe的進程PID,而如果被調(diào)試則該進程的父進程PID就會變成調(diào)試器的PID值,通過對父進程的檢測即可實現(xiàn)檢測是否被調(diào)試的功能。
#include#include #include intIsDebug() { DWORD ExplorerId = 0; PROCESSENTRY32 pe32 = { 0}; DWORD ProcessId = GetCurrentProcessId(); GetWindowThreadProcessId(FindWindow(L"Progman", NULL), &ExplorerId); HANDLE hProcessSnap = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, NULL); if(hProcessSnap != INVALID_HANDLE_VALUE) { pe32.dwSize = sizeof(PROCESSENTRY32); Process32First(hProcessSnap, &pe32); do { if(ProcessId == pe32.th32ProcessID) { // 判斷父進程是否是 Explorer.exe if(pe32.th32ParentProcessID != ExplorerId) { returnTRUE; } } } while(Process32Next(hProcessSnap, &pe32)); } returnFALSE; } intmain(intargc, char* argv[]) { if(IsDebug()) { printf("正在被調(diào)試 "); } system("pause"); return0; }
5.NtYieldExecution
NtYieldExecution讓當(dāng)前線程主動放棄其剩余的時間片,并執(zhí)行下一個等待的線程。如果沒有線程被安排執(zhí)行或在使用調(diào)試器單步調(diào)試時線程無法切換,NtYieldExecution函數(shù)返回為STATUS_NO_YIELD_PERFORMED (0x40000024)。
#defineSTATUS_NO_YIELD_PERFORMED 0x40000024 typedefNTSTATUS(WINAPI* pNtYieldExecution)(); boolisDebug() { HMODULE hNtDll = LoadLibrary(TEXT("ntdll.dll")); pNtYieldExecution NtYieldExecution = (pNtYieldExecution)GetProcAddress(hNtDll, "NtSetInformationThread"); INT iDebugged = 0; for(inti = 0; i < 0x20; i++) ????{ ????????Sleep(0xf); ????????if?(NtYieldExecution() != STATUS_NO_YIELD_PERFORMED) ????????????iDebugged++; ????} ????if?(iDebugged <= 3) ????????return?false; ????else ????????return?true; ????system("pause"); }
但是這種方法其實并不可靠,因為它只顯示當(dāng)前進程中是否有一個高優(yōu)先級的線程。然而,它可以作為一種反跟蹤技術(shù)。
6.DbgUiRemoteBreakin
DbgUiRemoteBreakin打補丁可以反OD附加調(diào)試,當(dāng)我們用OD附加調(diào)試時,CreateRemoteThread函數(shù)在目標(biāo)程序中創(chuàng)建了一個遠程線程,然后在遠程線程中調(diào)用DbgUiRemoteBreakin函數(shù),DbgUiRemoteBreakin內(nèi)部調(diào)用了DbgBreakPoint函數(shù),DbgBreakPoint函數(shù)內(nèi)部下了一個int 3斷點,觸發(fā)異常讓操作系統(tǒng)運行異常處理程序,然后操作系統(tǒng)把控制權(quán)交管給調(diào)試器,因此可以創(chuàng)建TLS回調(diào)的方式hook DbgUiRemoteBreakin,內(nèi)部直接調(diào)用ExitProcess()退出程序,測試代碼如下。
# include# include # include intmain(intargc, char* argv[]) { BYTE bBuffer[0x10] = { 0}; DWORD dwBreakAddress; DWORD dwOldProtect; DWORD dwNum; dwBreakAddress = (DWORD)GetProcAddress(LoadLibrary(L"ntdll.dll"), "DbgUiRemoteBreakin"); bBuffer[0] = 0xE9; *((DWORD*)(bBuffer + 1)) = (DWORD)ExitProcess - dwBreakAddress; VirtualProtect((LPVOID)dwBreakAddress, 0x10, PAGE_EXECUTE_READWRITE, &dwOldProtect); WriteProcessMemory(GetCurrentProcess(), (LPVOID)dwBreakAddress, bBuffer, 5, &dwNum); VirtualProtect((LPVOID)dwBreakAddress, 0x10, dwOldProtect, &dwOldProtect); while(1) { staticintx = 0; printf("%d ",x); Sleep(1000); x++; } return0; }
當(dāng)OD附加,程序終止。
總結(jié)
本篇繼續(xù)介紹了其他反調(diào)試的方法,在自己的代碼中使用反調(diào)試技術(shù),可以增加逆向人員的分析難度,或是通過了解這些技術(shù)的原理,在分析惡意代碼時進行反反調(diào)試,在后續(xù)的文章中,將會介紹更多的反調(diào)試方法。
-
WINDOWS
+關(guān)注
關(guān)注
3文章
3545瀏覽量
88707 -
操作系統(tǒng)
+關(guān)注
關(guān)注
37文章
6825瀏覽量
123333 -
調(diào)試
+關(guān)注
關(guān)注
7文章
578瀏覽量
33943 -
函數(shù)
+關(guān)注
關(guān)注
3文章
4331瀏覽量
62622
原文標(biāo)題:反調(diào)試技術(shù)-下
文章出處:【微信號:蛇矛實驗室,微信公眾號:蛇矛實驗室】歡迎添加關(guān)注!文章轉(zhuǎn)載請注明出處。
發(fā)布評論請先 登錄
相關(guān)推薦
評論