TLS(Thread Local Storage)回调函数
首先需要注意的一点就是,TLS回调函数是在程序进入OEP之前调用的,因此利用这一特性可以在此设置反调试。
简要说明TLS定义:TLS是各线程的独立的数据存储空间,使用TLS技术可在线程内部独立使用或修改进程的全局数据或静态数据,就像对待自身的局部变量一样。
那么TLS回调函数是指
值得一提的是,TLS表是需要在编程中开启TLS功能才会被设置在PE头文件中
IMAGE_TLS_DIRECTORY结构体有两种版本,32位和64位。
AddressOfCallBacks: 指向TLS注册的回调函数的函数指针(地址)数组(这个逆向时就很重要了)
通过TLS回调函数和DLLmain的定义比较可以发现,二者是类似的
上述图一中,reason是指调用TLS函数的原因
如图所示
分别是DLL进程/线程附加;DLL进程/线程分离
关于调试TLS函数和手动添加TLS函数此处不在描述,过程中涉及对PE文件结构知识的回顾,重在实践。
TEB
贴上书中的定义
丛书中的描述可知,TEB结构体的成员多而复杂,但在用户模式调试中起重要作用的成员有两个
先看ProcessEnvironmentBlock,它是指向PEB—进程环境块结构体的指针.每个进程对应一个PEB结构体
再看NtTib成员,TEB结构体的第一个成员为_NT_TIB(Thread Information Block—线程信息块)结构体
下图为其结构体定义
self成员为该结构体的自引用指针,同时因为该结构体为TEB的第一个结构体成员,所以它同时也是指向TEB结构体的指针。
TEB访问方法(用户模式下)
这里对FS段寄存器做了说明
原文解释的很清楚
由上可知,FS实际存储的是SDT的索引
PEB(进程环境块)
PEB是存放进程信息的结构体
根据PEB结构体地址我们可以用如下方式访问PEB结构体
PEB的重要成员
先看第一个成员,顾名思义,用来检测当前进程是否处于调试状态,是则返回1,否则返回0.
可以看到该API就是通过调用该成员实现检测当前进程是否处于调试状态。
接下来是第二个成员
该进程用来获取进程的ImageBase
上图的API用来获取ImageBase,实现方法与上一个API类似。
再下一个Ldr,该成员是指向PEB_LDR_DATA结构体的指针,结构体成员如下图所示
该成员可直接获取加载到进程的DLL模块的加载基地址
可以看到结构体中有三个_LIST_ENTRY类型的成员。,该结构体定义如下图
最后两个成员都应用于反调试技术。进程处于调试状态时,有特定值。
SEH
SEH机制在程序源代码中使用try,except,finally关键字来实现。
书中第一个例子的异常触发是由于访问未分配的内存。
操作系统的异常处理方法大致可根据程序运行情况分为两大类
- 正常运行时处理
- 调试运行时处理
第一种操作系统会将异常交给进程处理,若进程代码有相应的解决代码,则顺利处理,否则终止进程运行。
第二种则是交给调试器处理。调试器的权限很多,除了与被调试的进程有几乎一样的权限外,还有被调试者的虚拟内存,寄存器的读写权限。在遇到异常时,调试器会暂停运行,异常处理完后继续调试
下图为几种处理方法
一下是操作系统所定义的—异常
书中着重讨论了5种
EXCEPTION_ACCESS_VIOLATION(C0000005)
这就是访问不存在或不具访问权限的内存区域是所触发的异常—非法访问异常(最常见)
EXCEPTION_BREAKPOINT(80000003)
这就是调试器实现断点的原理,CPU遇到被设置了断点的代码会暂停运行。
实现方法:
INT3为设置断点命令的汇编语言,所对应的机器码为0xCC。CPU若遇到了该指令,即触发该异常
EXCEPTION_ILLEGAL_INSTRUCTION(C000001D)
CPU遇到无法解析(在该CPU架构下未定义)的指令时触发异常
EXCEPTION_INT_DIVIDE_BY_ZERO(C0000094)
小学数学经典错误,整数除法当中,分母为0.当然,若分母为某个变量,变量在某个时刻为0,也会触发该异常
。比如一个变量自己跟自己异或,返回0
EXCEPTION_SINGLE_STEP
单步过后程序暂停,程序进入单步模式后,每执行一次单步,就触发一次该异常。
SEH链
原图简洁明了
异常处理函数的定义
该函数传入四个参数,返回一个枚举类型.四个参数都保存异常的相关信息,第三个参数是指向CONTEXT结构体的指针.
该结构体的定义如下
该结构体用来备份CPU寄存器的值(多线程环境下需要).每个线程内部都拥有一个CONTEXT结构体,当CPU暂时离开当前线程去执行其他线程时,CPU寄存器的值就会保存到当前的CONTEXT结构体中,再次运行该线程时,就会用保存的值覆盖寄存器的值,从之前暂停的代码处继续执行.正是因此,OS才可以在多线程下安全运行各线程.
所返回的枚举类型如下
在异常发生时执行异常代码的线程会被中断运行,转而运行SEH,OS会把线程的CONTEXT结构体指针传递给异常处理函数的相应参数.
第一个成员是一个结构体,其定义如下
其中,第一个成员用来指出异常类型;第四个则用来表示发生异常的代码地址.
TEB结构体的第一个成员是TEB.NtTib.ExceptionList,通过它可以很容易地访问进程的SEH链.
SEH安装方法
后面的调试建议动手调,没有什么需要特别说明的。
关于IA32指令解析,就像是在教你如何查阅程序员的字典一样,跟着阅读即可。