静态反调试技术(2)
文章目錄
- NtQueryInformationProcess()
- `ProcessDebugPort`(0x7)
- CheckRemoteDebuggerPresent()
- ProcessDebugObjectHandle(0x1E)
- ProcessDebugFlags(0x1F)
- 調試程序代碼
- NtQuerySystemInformation()
- `SystemKernelDebuggerInformation`(`0x23`)
- 破解之法
- NTQueryObject()
- 破解之法
- 反調試技術系列:
NtQueryInformationProcess()
下面介紹一種利用NtQuertInformationProcess()API探測調試器的技術。通過NtQuertInformationProcess()API可以獲取各種與進程相關的信息。函數定義如下
NtQueryInformationProcess ( IN HANDLE ProcessHandle, // 進程句柄 IN PROCESSINFOCLASS InformationClass, // 信息類型 OUT PVOID ProcessInformation, // 緩沖指針 IN ULONG ProcessInformationLength, // 以字節為單位的緩沖大小 OUT PULONG ReturnLength OPTIONAL // 寫入緩沖的字節數 );第一個參數是希望操作的進程句柄,這個句柄必須以PROCESS_QUERY_INFORMATION模式存取。為了取得一個句柄,我們必須用OpenProcess函數:
HANDLE hProcess = OpenProcess(PROCESS_QUERY_INFORMATION,FALSE,dwProcessID);第二個參數是請求信息的類型,這個參數可以有許多個值,為這個參數指定特定值并去調用函數,相關信息就會設置到第三個參數PVOID ProcessInformation。
因此,如果第二個參數是ProcessBasicInformation的話,則第三個參數必須為一個指針指向結構PROCESS_BASIC_INFORMATION。
typedef struct { DWORD ExitStatus; // 接收進程終止狀態 DWORD PebBaseAddress; // 接收進程環境塊地址 DWORD AffinityMask; // 接收進程關聯掩碼 DWORD BasePriority; // 接收進程的優先級類 ULONG UniqueProcessId; // 接收進程ID ULONG InheritedFromUniqueProcessId; //接收父進程ID } PROCESS_BASIC_INFORMATION;PROCESSINFOCLASS是枚舉類型,擁有的值如下所示:
enum PROCESSINFOCLASS {ProcessBasicInformation = 0,ProcessQuotaLimits,ProcessIoCounters,ProcessVmCounters,ProcessTimes,ProcessBasePriority,ProcessRaisePriority,ProcessDebugPort = 7, //0x7ProcessExceptionPort,ProcessAccessToken,ProcessLdtInformation,ProcessLdtSize,ProcessDefaultHardErrorMode,ProcessIoPortHandlers,ProcessPooledUsageAndLimits,ProcessWorkingSetWatch,ProcessUserModeIOPL,ProcessEnableAlignmentFaultFixup,ProcessPriorityClass,ProcessWx86Information,ProcessHandleCount,ProcessAffinityMask,ProcessPriorityBoost,MaxProcessInfoClass,ProcessWow64Information = 26,ProcessImageFileName = 27,ProcessDebugObjectHandle = 30, //0x1EProcessDebugFlags = 31, //0x1FSystemKernelDebuggerInformation = 35 };以上代碼中與調試器探測相關的成員為ProcessDebugPort(0x7),ProcessDebugObjectHandle(0x1E),ProcessDebugFlags(0x1F)
ProcessDebugPort(0x7)
進程處于調試狀態時,系統就會為它分配一個調試端口(Debug Port)。PROCESSINFOCLASS參數的值設置為 ProcessDebugPort(0x7)時,調用NtQueryInformationProcess()函數就能獲取調試端口。若進程處于非調試狀態,則變量 ProcessDebugPort的值設置為0;若進程處于調試狀態,則變量 ProcessDebugPort的值設置為0xFFFFFFFF
ProcessDebugPort=0x7; DWORD dwDebugPort = 0;pNtQueryInformationProcess(GetCurrentProcess(),ProcessDebugPort,&dwDebugPort,sizeof(dwDebugPort),NULL);printf("NtQueryInformationProcess(ProcessDebugPort) = 0x%X\n", dwDebugPort);if( dwDebugPort != 0x0 ) printf(" => Debugging!!!\n\n");else printf(" => Not debugging...\n\n");CheckRemoteDebuggerPresent()
CheckRemoteDebuggerPresent() API與IsdebuggerPresent() API類似,用來檢測進程是否處于調試狀態。CheckRemoteDebuggerPresent()函數不僅可以用來檢測當前進程,還可以用來檢測其它進程是否處于被調試狀態。
CheckRemoteDebuggerPresent()內部代碼如下:
這個參數7即 ProcessDebugPort
ProcessDebugObjectHandle(0x1E)
調試進程時會生成調試對象(Debug Object)。函數的第二個參數值為 ProcessDebugObjectHandle(0x1E)時,調用函數后通過第三個參數就能獲取調用對象句柄。進程屬于調試狀態時,調試對象句柄就存在;若進程處于非調試狀態,則調試對象句柄值為NULL。
ProcessDebugObjectHandle=0x1E; HANDLE hDebugObject = NULL;pNtQueryInformationProcess(GetCurrentProcess(),ProcessDebugObjectHandle,&hDebugObject,sizeof(hDebugObject),NULL);printf("NtQueryInformationProcess(ProcessDebugObjectHandle) = 0x%X\n", hDebugObject);if( hDebugObject != 0x0 ) printf(" => Debugging!!!\n\n");else printf(" => Not debugging...\n\n");ProcessDebugFlags(0x1F)
檢測Debug Flags(調試標志)的值也可以判斷進程是否處于被調試狀態,函數的第二個參數設置為ProcessDebugFlags(0x1F)時,調用函數后通過第三個參數即可獲取調試標志的值:若為,則進程處于被調試狀態;若為1,則進程處于非調試狀態。
ProcessDebugFlags=0x1F;BOOL bDebugFlag = TRUE;pNtQueryInformationProcess(GetCurrentProcess(),ProcessDebugFlags,&bDebugFlag,sizeof(bDebugFlag),NULL);printf("NtQueryInformationProcess(ProcessDebugFlags) = 0x%X\n", bDebugFlag);if( bDebugFlag == 0x0 ) printf(" => Debugging!!!\n\n");else printf(" => Not debugging...\n\n"); }調試程序代碼
#include "stdio.h" #include "windows.h" #include "tchar.h"enum PROCESSINFOCLASS {ProcessBasicInformation = 0,ProcessQuotaLimits,ProcessIoCounters,ProcessVmCounters,ProcessTimes,ProcessBasePriority,ProcessRaisePriority,ProcessDebugPort = 7,ProcessExceptionPort,ProcessAccessToken,ProcessLdtInformation,ProcessLdtSize,ProcessDefaultHardErrorMode,ProcessIoPortHandlers,ProcessPooledUsageAndLimits,ProcessWorkingSetWatch,ProcessUserModeIOPL,ProcessEnableAlignmentFaultFixup,ProcessPriorityClass,ProcessWx86Information,ProcessHandleCount,ProcessAffinityMask,ProcessPriorityBoost,MaxProcessInfoClass,ProcessWow64Information = 26,ProcessImageFileName = 27,ProcessDebugObjectHandle = 30,ProcessDebugFlags = 31,SystemKernelDebuggerInformation = 35 };void MyNtQueryInformationProcess() {typedef NTSTATUS (WINAPI *NTQUERYINFORMATIONPROCESS)(HANDLE ProcessHandle,PROCESSINFOCLASS ProcessInformationClass,PVOID ProcessInformation,ULONG ProcessInformationLength,PULONG ReturnLength);NTQUERYINFORMATIONPROCESS pNtQueryInformationProcess = NULL;pNtQueryInformationProcess = (NTQUERYINFORMATIONPROCESS)GetProcAddress(GetModuleHandle(L"ntdll.dll"), "NtQueryInformationProcess");// ProcessDebugPort (0x7)DWORD dwDebugPort = 0;pNtQueryInformationProcess(GetCurrentProcess(),ProcessDebugPort,&dwDebugPort,sizeof(dwDebugPort),NULL);printf("NtQueryInformationProcess(ProcessDebugPort) = 0x%X\n", dwDebugPort);if( dwDebugPort != 0x0 ) printf(" => Debugging!!!\n\n");else printf(" => Not debugging...\n\n");// ProcessDebugObjectHandle (0x1E)HANDLE hDebugObject = NULL;pNtQueryInformationProcess(GetCurrentProcess(),ProcessDebugObjectHandle,&hDebugObject,sizeof(hDebugObject),NULL);printf("NtQueryInformationProcess(ProcessDebugObjectHandle) = 0x%X\n", hDebugObject);if( hDebugObject != 0x0 ) printf(" => Debugging!!!\n\n");else printf(" => Not debugging...\n\n");// ProcessDebugFlags (0x1F)BOOL bDebugFlag = TRUE;pNtQueryInformationProcess(GetCurrentProcess(),ProcessDebugFlags,&bDebugFlag,sizeof(bDebugFlag),NULL);printf("NtQueryInformationProcess(ProcessDebugFlags) = 0x%X\n", bDebugFlag);if( bDebugFlag == 0x0 ) printf(" => Debugging!!!\n\n");else printf(" => Not debugging...\n\n"); }int _tmain(int argc, TCHAR* argv[]) {MyNtQueryInformationProcess();printf("\npress any key to quit...\n");_gettch();return 0; }
strongOD->Options中的*KernelMode可以繞過。
NtQuerySystemInformation()
這是基于調試環境檢測的反調試技術。
注意:
前面所介紹的反調試技術,我們通過探測調試器來判斷自己的進程是否處于被調試狀態,這是一種非常直接的調試器探測方法。除此之外,還有間接探測調試器的方法,借助該方法可以檢測調試環境,若顯露出調試器的端倪,則立刻停止執行程序。
運用這種反調試技術可以檢測當前OS是否在調試模式下運行。
OS的調試模式:
為了使用winDbg工具調試系統內核(Kernel Debugging),需要先準備2個系統(Host Target)并連接(Serial,1394,USB,Direct Cable)。其中,Target的OS以調試模式運行,連接到Host系統的WinDbg上臺后即可調試。
設置調試模式方法:
windows XP:編輯"C:\boot.ini"
windows 7:使用bcdedit.exe實用程序
ntdll!NtQuerySystemInformation()API是一個系統函數,用來獲取當前運行的多種OS信息。
typedef NTSTATUS (__stdcall *NTQUERYSYSTEMINFORMATION) (IN SYSTEM_INFORMATION_CLASS SystemInformationClass,IN OUT PVOID SystemInformation,IN ULONG SystemInformationLength,OUT PULONG ReturnLength OPTIONAL);NTQUERYSYSTEMINFORMATION NtQuerySystemInformation;第一個參數是dwRecordType,這個參數指定了我們所查詢的系統信息類型,為了查詢系統HANDLE列表,我們定義一個常量#define NT_HANDLE_LIST 16(這個數值我是查資料得到的,如果誰有更詳細的資料,也請讓我共享一下)。
第二個參數是一個指針,這個指針用來返回系統句柄列表,在調用NtQuerySystemInformation函數之前,必須為這個指針分配足夠的內存空間,否則函數調用會出錯。
第三個參數是指定你為HandleList所分配的內存空間大小,單位是byte。
第四個參數是NtQuerySystemInformation返回的HandleList的大小;如果NtQuerySystemInformation函數調用成功,返回值將是0,否則可以使用GetLastError()獲得詳細的錯誤代碼。
SYSTEM_INFORMATION_CLASS 是枚舉類型,擁有的值如下:
typedef enum _SYSTEM_INFORMATION_CLASS { SystemBasicInformation,// 0 Y N SystemProcessorInformation,// 1 Y N SystemPerformanceInformation,// 2 Y N SystemTimeOfDayInformation,// 3 Y N SystemNotImplemented1,// 4 Y N // SystemPathInformation SystemProcessesAndThreadsInformation,// 5 Y N SystemCallCounts,// 6 Y N SystemConfigurationInformation,// 7 Y N SystemProcessorTimes,// 8 Y N SystemGlobalFlag,// 9 Y Y SystemNotImplemented2,// 10 YN // SystemCallTimeInformation SystemModuleInformation,// 11 YN SystemLockInformation,// 12 YN SystemNotImplemented3,// 13 YN // SystemStackTraceInformation SystemNotImplemented4,// 14 YN // SystemPagedPoolInformation SystemNotImplemented5,// 15 YN // SystemNonPagedPoolInformation SystemHandleInformation,// 16 YN SystemObjectInformation,// 17 YN SystemPagefileInformation,// 18 YN SystemInstructionEmulationCounts,// 19 YN SystemInvalidInfoClass1,// 20 SystemCacheInformation,// 21 YY SystemPoolTagInformation,// 22 YN SystemProcessorStatistics,// 23 YN SystemDpcInformation,// 24 YY SystemNotImplemented6,// 25 YN // SystemFullMemoryInformation SystemLoadImage,// 26 NY // SystemLoadGdiDriverInformation SystemUnloadImage,// 27 NY SystemTimeAdjustment,// 28 YY SystemNotImplemented7,// 29 YN // SystemSummaryMemoryInformation SystemNotImplemented8,// 30 YN // SystemNextEventIdInformation SystemNotImplemented9,// 31 YN // SystemEventIdsInformation SystemCrashDumpInformation,// 32 YN SystemExceptionInformation,// 33 YN SystemCrashDumpStateInformation,// 34 YY/N SystemKernelDebuggerInformation,// 35 YN //0x23 SystemContextSwitchInformation,// 36 YN SystemRegistryQuotaInformation,// 37 YY SystemLoadAndCallImage,// 38 NY // SystemExtendServiceTableInformation SystemPrioritySeparation,// 39 NY SystemNotImplemented10,// 40 YN // SystemPlugPlayBusInformation SystemNotImplemented11,// 41 YN // SystemDockInformation SystemInvalidInfoClass2,// 42 // SystemPowerInformation SystemInvalidInfoClass3,// 43 // SystemProcessorSpeedInformation SystemTimeZoneInformation,// 44 YN SystemLookasideInformation,// 45 YN SystemSetTimeSlipEvent,// 46 NY SystemCreateSession,// 47 NY SystemDeleteSession,// 48 NY SystemInvalidInfoClass4,// 49 SystemRangeStartInformation,// 50 YN SystemVerifierInformation,// 51 YY SystemAddVerifier,// 52 NY SystemSessionProcessesInformation// 53 YN } SYSTEM_INFORMATION_CLASS;向SystemInformationClass參數傳入SystemKernelDebuggerInformation值(0x23)即可判斷當前OS在調試模式下運行
SystemKernelDebuggerInformation(0x23)
void MyNtQuerySystemInformation() {//檢測當前OS是否運行在調試模式下,WinDbgtypedef NTSTATUS (WINAPI *NTQUERYSYSTEMINFORMATION)(ULONG SystemInformationClass,PVOID SystemInformation,ULONG SystemInformationLength,PULONG ReturnLength);typedef struct _SYSTEM_KERNEL_DEBUGGER_INFORMATION {BOOLEAN DebuggerEnabled;BOOLEAN DebuggerNotPresent;} SYSTEM_KERNEL_DEBUGGER_INFORMATION, *PSYSTEM_KERNEL_DEBUGGER_INFORMATION;NTQUERYSYSTEMINFORMATION NtQuerySystemInformation;NtQuerySystemInformation = (NTQUERYSYSTEMINFORMATION) GetProcAddress(GetModuleHandle(L"ntdll"), "NtQuerySystemInformation");ULONG SystemKernelDebuggerInformation = 0x23;ULONG ulReturnedLength = 0;SYSTEM_KERNEL_DEBUGGER_INFORMATION DebuggerInfo = {0,};NtQuerySystemInformation(SystemKernelDebuggerInformation, (PVOID) &DebuggerInfo, sizeof(DebuggerInfo), // 2 bytes&ulReturnedLength);printf("NtQuerySystemInformation(SystemKernelDebuggerInformation) = 0x%X 0x%X\n", DebuggerInfo.DebuggerEnabled, DebuggerInfo.DebuggerNotPresent);if( DebuggerInfo.DebuggerEnabled ) printf(" => Debugging!!!\n\n");else printf(" => Not debugging...\n\n"); }在上述代碼中調用NtQuerySystemInformation()API時,第一個參數(SystemInformaticationClass)的值設置為SystemKernelDebuggerInformation(0x23),第二個參數(SystemInformation)為SYSTEM_KERNEL_DEBUGGER_INFORMATION結構體的地址。當API返回時,若系統處在調試模式下,則SYSTEM_KERNEL_DEBUGGER_INFORMATION.DebuggerEnable的值設置為1(SYSTEM_KERNEL_DEBUGGER_INFORMATION.DebuggerEnable的值恒為1)
破解之法
在Windows XP 系統中編輯boot.ini文件,刪除“/debugport=coml /baudrate=115200 /debug”值。在windows 7系統的命令行窗口中執行“bcdedit /debug off”命令即可。并且,若重啟系統則要以正常模式啟動。
NTQueryObject()
系統中的某個調試器調試進程時,會創建1個調試對象類型的內核對象。檢測該對象是否存在即可判斷是否有進程正在被調試。
ntdll!NTQueryObject() API用來獲取系統各種內核對象的信息,NTQueryObject()函數的定義如下
NTSTATUS NtQueryObject(_In_opt_ HANDLE Handle,_In_ OBJECT_INFORMATION_CLASS objectInformationClass,_Out_opt_ PVOID ObjectInformation,_In_ ULONG ObjectInformationLength,_Out_opt_ PULONG ReturnLength);調用NTQueryObject()函數前,先向第二個參數 OBJECT_INFORMATION_CLASS objectInformationClass賦于某個特定的值,調用API后,包含相關信息的結構指針就被返回到第三個參數PVOID ObjectInformation。
typedef enum _OBJECT_INFORMATION_CLASS {ObjectBasicInformation,ObjectNameInformation,ObjectTypeInformation,ObjectAllInformation,//3ObjectDataInformation } OBJECT_INFORMATION_CLASS, *POBJECT_INFORMATION_CLASS;首先使用ObjectAllTypeInformation值獲取系統所有對象的信息,然后從中檢測是否存在調試對象。NtQueryObject()API使用方法復雜。
1.獲取內核對象信息鏈表的大小
ULONG lSize = 0; pNtQueryObject(NULL, ObjectAllTypesInformation, &lSize, sizeof(lSize), &lSize);2.分配內存
void *pBuf = NULL; pBuf = VirtualAlloc(NULL, lSize, MEM_RESERVE | MEM_COMMIT, PAGE_READWRITE);3.獲取內核對象信息鏈表
typedef struct _OBJECT_TYPE_INFORMATION {UNICODE_STRING TypeName;ULONG TotalNumberOfHandles;ULONG TotalNumberOfObjects;}OBJECT_TYPE_INFORMATION, *POBJECT_TYPE_INFORMATION;typedef struct _OBJECT_ALL_INFORMATION {ULONG NumberOfObjectsTypes;OBJECT_TYPE_INFORMATION ObjectTypeInformation[1];} OBJECT_ALL_INFORMATION, *POBJECT_ALL_INFORMATION;pNtQueryObject((HANDLE)0xFFFFFFFF, ObjectAllTypesInformation, pBuf, lSize, NULL);POBJECT_ALL_INFORMATION pObjectAllInfo = (POBJECT_ALL_INFORMATION)pBuf;調用NtQueryObject()函數后,系統的所有對象的信息代碼就被存入pBuf,然后將pBuf轉換(casing)為POBJECT_ALL_INFORMATION類型。POBJECT_ALL_INFORMATION結構體_OBJECT_TYPE_INFORMATION結構體數組構成。實際內核對象類型的信息就被存儲在_OBJECT_TYPE_INFORMATION結構體數組中,然后通過循環檢索即可查看是否存在“調試對象”對象類型。
完整代碼:
#include "stdio.h" #include "windows.h" #include "tchar.h"typedef enum _OBJECT_INFORMATION_CLASS {ObjectBasicInformation,ObjectNameInformation,ObjectTypeInformation,ObjectAllTypesInformation,ObjectHandleInformation } OBJECT_INFORMATION_CLASS, *POBJECT_INFORMATION_CLASS;void MyNtQueryObject() {//基于檢測調試環境//系統中的某個調試器調試進程時,會創建1個調試對象類型的內核對象。檢測該對象//是否存在即可判斷是否有進程正在被調試(注意不是當前進程)。typedef struct _LSA_UNICODE_STRING {USHORT Length;USHORT MaximumLength;PWSTR Buffer;} LSA_UNICODE_STRING, *PLSA_UNICODE_STRING, UNICODE_STRING, *PUNICODE_STRING;typedef NTSTATUS (WINAPI *NTQUERYOBJECT)(HANDLE Handle,OBJECT_INFORMATION_CLASS ObjectInformationClass,PVOID ObjectInformation,ULONG ObjectInformationLength,PULONG ReturnLength);#pragma pack(1)typedef struct _OBJECT_TYPE_INFORMATION {UNICODE_STRING TypeName;ULONG TotalNumberOfHandles;ULONG TotalNumberOfObjects;}OBJECT_TYPE_INFORMATION, *POBJECT_TYPE_INFORMATION;typedef struct _OBJECT_ALL_INFORMATION {ULONG NumberOfObjectsTypes;OBJECT_TYPE_INFORMATION ObjectTypeInformation[1];} OBJECT_ALL_INFORMATION, *POBJECT_ALL_INFORMATION;#pragma pack()POBJECT_ALL_INFORMATION pObjectAllInfo = NULL;void *pBuf = NULL;ULONG lSize = 0;BOOL bDebugging = FALSE;NTQUERYOBJECT pNtQueryObject = (NTQUERYOBJECT)GetProcAddress(GetModuleHandle(L"ntdll.dll"), "NtQueryObject");// Get the size of the listpNtQueryObject(NULL, ObjectAllTypesInformation, &lSize, sizeof(lSize), &lSize);// Allocate list bufferpBuf = VirtualAlloc(NULL, lSize, MEM_RESERVE | MEM_COMMIT, PAGE_READWRITE);// Get the actual listpNtQueryObject((HANDLE)0xFFFFFFFF, ObjectAllTypesInformation, pBuf, lSize, NULL);pObjectAllInfo = (POBJECT_ALL_INFORMATION)pBuf;UCHAR *pObjInfoLocation = (UCHAR *)pObjectAllInfo->ObjectTypeInformation;POBJECT_TYPE_INFORMATION pObjectTypeInfo = NULL;for( UINT i = 0; i < pObjectAllInfo->NumberOfObjectsTypes; i++ ){pObjectTypeInfo = (POBJECT_TYPE_INFORMATION)pObjInfoLocation;if( wcscmp(L"DebugObject", pObjectTypeInfo->TypeName.Buffer) == 0 ){bDebugging = (pObjectTypeInfo->TotalNumberOfObjects > 0) ? TRUE : FALSE;break;}// calculate next structpObjInfoLocation = (UCHAR*)pObjectTypeInfo->TypeName.Buffer;pObjInfoLocation += pObjectTypeInfo->TypeName.Length;pObjInfoLocation = (UCHAR*)(((ULONG)pObjInfoLocation & 0xFFFFFFFC) + sizeof(ULONG));}if( pBuf )VirtualFree(pBuf, 0, MEM_RELEASE);printf("NtQueryObject(ObjectAllTypesInformation)\n");if( bDebugging ) printf(" => Debugging!!!\n\n");else printf(" => Not debugging...\n\n"); }int _tmain(int argc, TCHAR* argv[]) {MyNtQueryObject();printf("\npress any key to quit...\n");_gettch();return 0; }strongOD->Options中的*KernelMode可以繞過。
破解之法
位于0xCB184A地址處的call dword ptr ss:[ebp-0x3C] 指令是用來調用ntdll.ZwQueryObject()API的,
此時查看棧可以發現,第二個參數值為ObjectAllTypesInformation(3),將該值修改為0后再執行0xCB184A地址處的指令,這樣就無法探測到調試器的存在了
或者直接把這個API給勾取,輸入ObjectAllTypesInformation(3)值或者直接操作結果值,也不會被探測到
反調試技術系列:
靜態反調試技術(1)https://blog.csdn.net/CSNN2019/article/details/113105292
靜態反調試技術(2)https://blog.csdn.net/CSNN2019/article/details/113147820
靜態反調試技術(3)https://blog.csdn.net/CSNN2019/article/details/113178232
動態反調試技術 https://blog.csdn.net/CSNN2019/article/details/113181558
高級反調試技術 https://blog.csdn.net/CSNN2019/article/details/113263215
總結
以上是生活随笔為你收集整理的静态反调试技术(2)的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 静态反调试技术(1)
- 下一篇: PE结构整体叙述