反调试技术

反调试技术

反调试技术用于防止程序被调试

一般分为利用函数进行检测或者直接将调试权限清0

IsDebuggerPresent

原理

IsDebuggerPresent是Windows API提供的函数,用于确定调用进程是否由用户模式调试器进行调试。

当调试器存在时, IsDebuggerPresent( )函数返回的是一个非0值.

API获取PEB.BeingDebugged的值来判断是否处于被调试状态。进程处于被调试状态时,PEB.BeingDebugged的值被设置为1,反之为0.

1
BOOL WINAPI IsDebuggerPresent(void);

样例代码

1
2
3
if(IsDebuggerPresent()){
exit(0);
}
1
2
3
4
call IsDebuggerPresent
test al al
jz
call exit

绕过

image-20220710150918570

如图,为一个IsDebuggerPresent()函数,如果正在被调试就会返回非零值

查看汇编

image-20220710151048558

若返回值为非零值

方法一:

修改jnz 指令为jz指令

image-20220710152444126

image-20220710152514507

绕过成功

方法二:

利用OD修改PEB.BeingDebugged 改为0.

PEB的结构指针存储在TEB中,fs:[0x30]是PEB fs:[0x18]是TEB。

CheckRemoteDebuggerPresent

原理

CheckRemoteDebuggerPresent是Windows API提供的函数,用于检测是否正在调试指定的进程

于IsDebuggerPresent不同,它可以在多线程的情况下进行使用

NtQueryInformationProcess

原理

NtQueryInformationProcess( )是一个接受一个信息类的参数用于查询的函数。

kernel32的CheckRemoteDebuggerPresent()函数内部通过调用NtQueryInformationProcess()

检测调试, 而NtQueryInformationProcess内部则是查询EPROCESS结构体的DebugPort字段, 当进

程正在被调试时, 返回值为0xffffffff.

TLS反调试

TLS原本是用于多线程情况下的数据同步,分动态绑定和静态绑定两种用法

而其中的静态绑定可用于反调试

TLS变量

同一个线程里面调用的各个函数都可以访问、但其他线程无法访问的变量(被称为static memory local to a thread 线程局部静态变量)

因此,如果将

TLS回调函数

静态绑定反调试主要体现在回调函数中

程序可以提供一个或多个TLS回调函数,以支持TLS数据对象的附加初始化和终止

尽管通常只有一个回调函数,但回调函数是作为数组实现的,以便在需要时可以添加额外的回调函数

如果有多个回调函数,则按其地址在数组中出现的顺序调用每个函数。空指针终止数组。空列表是完全有效的(不支持回调),在这种情况下,回调数组只有一个成员—— null ptr(空指针)

线程结束时会调用回调函数

1
2
3
4
5
6
7
void NTAPI tls_callback(PVOID Dllhandle, DWORD Reason, PVOID Reserved) {
BOOL ret;
CheckRemoteDebuggerPresent(GetCurrentProcess(), &ret);
if (ret) {
ExitProcess(0);
}
}

绕过

修改ret的值

如果回调函数里面的不是强制退出的话就直接静态分析

花指令

所谓花指令,是指在程序中完全冗余,不影响程序功能却会对逆向工程产生干扰的指令。

不可执行花指令

例如

image-20220704201213135

1
2
3
4
.text:0040102C                 jnz     short near ptr loc_40102E+1
.text:0040102E
.text:0040102E loc_40102E: ; CODE XREF: .text:0040102C↑j
.text:0040102E call near ptr 0EC85D78Bh

这段汇编用jnz指令往后面跳了一行,由于IDA用的是递归下降反汇编算法,会错误识别然后导致无法反汇编,并且后面的代码以数据块的方式展现出来

image-20220704201425907

只能将花指令nop掉,让IDA从数据块开头开始执行递归下降反汇编

image-20220704201525487

再把反汇编出来的指令定义为函数,即可正常的反汇编

image-20220704201701582

当然也可以用OD进行修改

可执行花指令

SMC(self-Modifying Code)

SMC(self-Modifying Code),就是在真正执行某一段代码时,程序会对自身的该段代码进行自修改,只有在修改后的代码才是可汇编,可执行的。在程序未对该段代码进行修改之前,在静态分析状态下,均是不可读的字节码,IDA之类的反汇编器无法识别程序的正常逻辑。是一种反调试代码技术。

示例

image-20220902151957943

image-20220902151933185

程序执行的时候先将数据块解码再执行

选择使用idc脚本进行处理

1
2
3
4
5
6
7
8
9
10
#include <idc.idc>
static main()
{
auto addr = 0x402219;
auto i = 0;
for(i = 0; i <= 223; i++)
{
PatchByte(addr+i,Byte(addr+i)^0x99);
}
}

解码后再手动定义函数进行反汇编