注:这里的反调试技术主要针对Windows平台
TEB
TEB指线程环境块,它是一个结构体,包含进程中运行线程的各种信息,每个线程中都有一个对应的TEB结构体。由于这个结构体实在太复杂,这里只说几个跟调试有关的部分。
+0x30 ProcessEnvironmentBlock
它是指向PEB结构体的指针,PEB是进程控制块,每个进程都有一个对应的PEB
+0x00 NtTib
NtTib是TEB结构体的第一个成员,定义如下:
typedef struct __NT_TIB {
struct _EXCEPTION_REGISTRATION_RECORD *ExceptionList;
PVOID StackBase;
PVOID StackLimit;
PVOID SubSystemTib;
union{
PVOID FiberData;
DWORD Version;
};
PVOID ArbitraryUserPointer;
struct _NT_TIB *Self;
} NT_TIB;
访问方式:
Ntdll.NtCurrentTeb(),代码如下
_asm{
mov eax, DWORD PTR FS:[18]
ret
}
所以本质上可以直接通过FS寄存器来访问TEB结构体,如
TEB起始地址,FS:[0x18]
PEB起始地址,FS:[0x30]
SEH起始地址,FS:[0]
PEB
PEB是存放进程信息的结构体,它的结构同样十分复杂,这里也只记录关键部分。
关于调试状态的成员在反调试时再来看,这里只说与进程相关的成员
+0x08 ImageBaseAddress用来表示进程的ImageBase可用API GetModuleHandle()来获取
+0x0c Ldr指向_PEB_LDR_DATA结构体,可以直接获取dll模块加载基地址
访问方式:
FS:[0x30]
TEB.ProcessEnvironmentBlock
mov eax,DWORD PTR FS:[18]
mov eax,DWORD PTR DS:[eax+0x30]
都差不多吧
异常处理
Windows异常处理是操作系统处理程序错误或异常的一系列流程和技术的总称,开发人员主要使用两种异常处理技术,分别是SEH和VEH
SEH
SEH(结构化异常处理),是Windows系统用于自身除错的一种机制,它告诉系统当程序运行出现异常或错误时由谁来处理,从程序设计的角度来说,就是系统在终结程序之前给程序提供的一个执行其预先设定的回调函数的机会。
相关数据结构
1.EXCEPTION_REGISTRATION_RECORD
TIB在上面提到的TEB+0位置的结构,其中字段ExceptionList是一个链表,它是由EXCEPTION_REGISTRATION_RECORD结构作为表项组成的,定义如下:
typedef struct _EXCEPTION_REGISTRATION_RECORD{
struct _EXCEPTION_REGISTRATION_RECORD *Next;
PEXCEPTION_ROUTINE Handler;
} EXCEPTION_REGISTRATION_RECORD;
异常处理回调函数的地址就放在Handler中。
2.EXCEPTION_POINTERS
在程序的执行过程中,当发生一个异常时,若并非是调试状态,则操作系统会将异常信息转交给用户态的异常处理过程。有一个重要的事实是,内核态和用户态使用的堆栈并非同一个,所以在操作系统转交异常信息的方式是将一个EXCEPTION_POINTERS结构放入用户栈中,这个结构定义如下
typedef struct _EXCEPTION_POINTERS {
PEXCEPTION_RECORD ExceptionRecord;
PCONTEXT ContextRecord;//表示CPU当前线程状态
}EXCEPTION_POINTERS
可以看到,它本质上包装了另外两个结构而已。
3.EXCEPTION_RECORD
EXCEPTION_RECORD结构就是异常信息的本体了,定义如下
typedef struct _EXCEPTION_RECORD {
NTSTATUS ExceptionCode;//异常代码,表示异常产生的原因
ULONG ExceptionFlags;
struct _EXCEPTION_RECORD *ExceptionRecord;
PVOID ExceptionAddress;//异常发生地址
ULONG NumberParameters;
ULONG_PTR ExceptionInformation[];
}EXCEPTION_RECORD;
安装SEH
由于TEB+0x00处为ExceptionList结构,所以只要将链表头指针放入该结构中就算是安装完成了,所以,我们经常可以看到在函数开头处有类似这样的代码
就是在给fs:[0]赋值的,就说明是在安装SEH,更标准的写法是这样
push offset SEHandler
push fs:[0]
push fs:[0],esp