0x00.BeingDebugged
1. PEB(Process Environment Block,进程环境块):
(1). 有种保护技术会检测是否有调试器正在调试保护软件,然后需要获取是否被调试的消息,这个消息存储在PEB结构中,PEB结构:
typedef struct _PEB {
BYTE Reserved1[2];
BYTE BeingDebugged; //被调试状态
BYTE Reserved2[1];
PVOID Reserved3[2];
PPEB_LDR_DATA Ldr;
PRTL_USER_PROCESS_PARAMETERS ProcessParameters;
BYTE Reserved4[104];
PVOID Reserved5[52];
PPS_POST_PROCESS_INIT_ROUTINE PostProcessInitRoutine;
BYTE Reserved6[128];
PVOID Reserved7[1];
ULONG SessionId;
} PEB, *PPEB;
(2).
BYTE BeingDebugged 这里就是记录程序的调试状态的,(1代表被调试,0代表没有被调试)我们可以使用 BOOL WINAPI IsDebuggerPresent(void);这个函数来检测是否有调试器存在,返回非0值代表被调试,如果返回0代表没有被调试
FS:[0x30] | 4 | NT | Linear address of Process Environment Block (PEB) |
mov eax,dword ptr fs:[00000030h] //获取PEB结构基地址
movzx eax,byte ptr [eax+2] //根据PEB结构,BeingDebugged
ret
(
所以利用以上远离OD可以清除掉BeingDebugged位来隐藏调试器)
(4). 然而!!!在WINDOWS XP SP2后的系统有了一个特性,PEB地址随机化!所以换一些套路来得到PEB -。-
(5). 下面两个函数就可以找到PEB,具体细节可以到对应网站查看。
//https://msdn.microsoft.com/en-us/library/windows/desktop/ms679363(v=vs.85).aspx
BOOL WINAPI GetThreadSelectorEntry(
_In_ HANDLE hThread,
_In_ DWORD dwSelector,
_Out_ LPLDT_ENTRY lpSelectorEntry
);
//https://msdn.microsoft.com/en-us/library/ms684280(VS.85).aspx
NTSTATUS WINAPI NtQueryInformationProcess(
_In_ HANDLE ProcessHandle,
_In_ PROCESSINFOCLASS ProcessInformationClass,
_Out_ PVOID ProcessInformation,
_In_ ULONG ProcessInformationLength,
_Out_opt_ PULONG ReturnLength
);
(6). 然而,这个BeingDebugged的问题并没有那么简单,通过查看源码发现(虽然我看不太懂源码),如果BeingDebugged的值改成TRUE,那么NtGlobalFlag也会被设置为:
FLG_HEAP_ENABLE_TAIL_CHECK(0X10)
FLG_HEAP_ENABLE_FREE_CHECK(0X20)
FLG_HEAP_VALIDATE_PARAMETERS(0X40)
这些标志是在ntdll!LdrpInitializeExecutionOptions()里设置的。请注意NtGlobalFlag的默认值可以通过gflags.exe或者在注册表以下位置创建条目来修改:
HKLM\Software\Microsoft\Windows Nt\CurrentVersion\Image File Execution Options
(7). Heap Magic(看不懂系列):因为FLG_HEAP_VALIDATE_PARAMETERS被设置了,因此RtlCreateHeap选择用RtlDebugCreateHeap创建调试堆
PVOID RtlCreateHeap(
_In_ ULONG Flags,
_In_opt_ PVOID HeapBase,
_In_opt_ SIZE_T ReserveSize,
_In_opt_ SIZE_T CommitSize,
_In_opt_ PVOID Lock,
_In_opt_ PRTL_HEAP_PARAMETERS Parameters
);
总之是因为创建调试堆的时候堆中的一些Flag位被设置了一些标记。堆中的内存也被填充了很多特定字节,这样就会被检测到调试器(这个坑以后再填...)
(8). 彻底干掉BeingDebugged:在编写调试器的时候,创建进程并调用WaitForDebugEvent后,在第一次LOAD_DLL_DEBUG_EVENT时置BeingDebugged为FALSE,第二次LOAD_DLL_DEBUG_EVENT时置BeingDebugged为TRUE,这样就会停在系统断点,安全的清除掉BeingDebugged了。
0x01.Native:
1. CheckRemoteDebuggerPresent:
//https://msdn.microsoft.com/en-us/library/ms679280(VS.85).aspx BOOL WINAPI CheckRemoteDebuggerPresent( _In_ HANDLE hProcess, _Inout_ PBOOL pbDebuggerPresent );不受BeingDebugged位的影响
2. ProcessDebugPort:
CheckRemoteDebuggerPresent的过程中对调用NtQueryInformationProcess(Native API)
//https://msdn.microsoft.com/en-us/library/ms687420(VS.85).aspx
NTSTATUS WINAPI ZwQueryInformationProcess(
_In_ HANDLE ProcessHandle,
_In_ PROCESSINFOCLASS ProcessInformationClass,
_Out_ PVOID ProcessInformation,
_In_ ULONG ProcessInformationLength,
_Out_opt_ PULONG ReturnLength
);
//根据不同的ProcessInformationClass查询关于一个进程对象的信息。
Value | Meaning |
---|---|
|
Retrieves a pointer to a PEB structure that can be used to determine whether the specified process is being debugged, and a unique value used by the system to identify the specified process. It is best to use the CheckRemoteDebuggerPresent and GetProcessId functions to obtain this information. |
|
Retrieves a DWORD_PTR value that is the port number of the debugger for the process. A nonzero value indicates that the process is being run under the control of a ring 3 debugger. It is best to use the CheckRemoteDebuggerPresent or IsDebuggerPresent function. |
|
Determines whether the process is running in the WOW64 environment (WOW64 is the x86 emulator that allows Win32-based applications to run on 64-bit Windows). It is best to use the IsWow64Process function to obtain this information. |
|
Retrieves a UNICODE_STRING value containing the name of the image file for the process. |
|
Retrieves a ULONG value indicating whether the process is considered critical.
Note This value can be used starting in Windows XP with SP3. Starting in Windows 8.1,
IsProcessCritical should be used instead.
|
CheckRemoteDebuggerPresent的过程中对调用NtQueryInformationProcess,查询了某个进程的ProcessDebugPort(7),这个值是系统与调试器通信的端口句柄。BeingDebugged被清除不会影响调试,但是若将调试端口设置为0,系统就不会向用户态调试器发送调试事件通知,调试器就无法正常工作。
3. ThreadHideFromDebugger:
//https://docs.microsoft.com/en-us/windows-hardware/drivers/ddi/content/ntddk/nf-ntddk-zwsetinformationthread
NTSTATUS ZwSetInformationThread(
_In_ HANDLE ThreadHandle,
_In_ THREADINFOCLASS ThreadInformationClass,
_In_ PVOID ThreadInformation,
_In_ ULONG ThreadInformationLength
);
//http://undocumented.ntinternals.net/index.html?page=UserMode%2FUndocumented%20Functions%2FNT%20Objects%2FThread%2FTHREAD_INFORMATION_CLASS.html typedef enum _THREAD_INFORMATION_CLASS { ThreadBasicInformation, ThreadTimes, ThreadPriority, ThreadBasePriority, ThreadAffinityMask, ThreadImpersonationToken, ThreadDescriptorTableEntry, ThreadEnableAlignmentFaultFixup, ThreadEventPair, ThreadQuerySetWin32StartAddress, ThreadZeroTlsCell, ThreadPerformanceCount, ThreadAmILastThread, ThreadIdealProcessor, ThreadPriorityBoost, ThreadSetTlsArrayAddress, ThreadIsIoPending, ThreadHideFromDebugger } THREAD_INFORMATION_CLASS, *PTHREAD_INFORMATION_CLASS;为进程设置ThreadHideFromDebugger可以禁止某一个线程产生调试事件。
如果进程成功建立了一个映射,就会调用这个函数,如果进程和一个调试端口连接,就会通知调试器发生了LOAD_DLL_DEBUG_EVENT事件。若线程的HideFromDebugger为TRUE,代码在中间返回,调试器就什么都不知道了。
4. Debug Object:
(http://www.mengwuji.net/thread-628-1-1.html 这篇大神写的吊吊吊!)
调试器与被调试程序建立关系有两种途径:在创建进程的时候设置DEBUG_PROCESS,或者调用DebugActiveProcess附加到某个已经运行的进程上,我们从后者入手。
//https://msdn.microsoft.com/en-us/library/windows/desktop/ms679295(v=vs.85).aspx BOOL WINAPI DebugActiveProcess( _In_ DWORD dwProcessId );DebugActiveProcess函数很简单,可以总结它只干了三件事情,按顺序分别是:
(3). 附加指定的进程。
创建调试对象是调用了DbgUiConnectToDbg函数,这个函数没有参数,调用了ZwCreateDebugObject,返回值是NTSTATUS
ZwCreateDebugObject函数也甚是简单,填充好一些参数直接调用ZwCreateDebugObject就完事儿了,注意这里创建调试对象成功后,输出的句柄是保存在TEB->DbgSsReserved[1]中。普通的进程中TEB->DbgSsReserved[1]为NULL。可以通过DbgUiGetThreadDebugObject来得到这个量的准确偏移量。
ZwCreateDebugObject实际上调用了ObCreateObject创建对象,因此可以用ZwQueryObject查询所有对象的类型,若发现名为DebugObject的数目不为0,说明系统中存在调试器
5. Native API:
(1). Native API 是指以二进制方式,函式库 (DLL) 直接开放的应用程式开发接口 (Application Programming Interface),可以直接由 C/C++ 来呼叫存取使用。
参考这两篇别人的博客
(https://www.cnblogs.com/findumars/p/5931684.html)
(http://blog.csdn.net/kingswb/article/details/51873233)
自己理解起来很吃力。跪了跪了
6. SDT:
系统描述表(System Descriptor Table,SDT)中有系统服务表(System Sevice Table)表项
7. Nt和Zw:
(1). ntdll.dll中Zw.... 和Nt....函数都是导向ntoskrnl.exe的Stub函数。
(2). ntoskrnl.exe中所有Zw....函数也是Stub函数,导向ntoskrnl中的Nt....函数。
(3). ntoskrnl.exe中的Nt....函数所有Stub函数的目的地,是真正在做事情的函数。
0x02.Hook和AntiHook
1. OD中的HideOD插件可以自动挂钩Native API,让这些教科书式的反调试无效(GET了,轮子我还时会用的!)
2. HideOD插件只是简单的利用了Ring3层的简单钩子,滤掉了ThreadHideFromDebugger这个动作。(然而我还没学过钩子,所以觉得没那么简单,先挖坑,再补!)
3. 等学会了钩子再把坑填上。。。