二进制代码保护和混淆

多摘自NU1L的《0到1》

抵御静态分析

花指令:

指在程序中完全冗余,不会影响程序功能,但会干扰逆向的指令。没有固定形式。

脏字节:

会导致反汇编或反编译解析错误的多余字节(或指令)

反正就是很奇怪的指令多半就有问题

遇到花指令或者脏字节需要仔细分析上下文,找到有毛病的指令或字节进行patch

指令替换(变形)

还有一些题目为达成混淆效果,会故意将一些指令用其他指令来实现相同或类似的功能。如

1
2
3
4
5
6
7
8
9
10
11
call addr  
; 可替换成
push addr
ret
-----------
ret
;可替换成
push ecx
mov ecx,[esp+4]
add esp,8
jmp ecx ;这样的替换需保证ecx未被使用

代码自修改(SMC)

原理就是在算法脚本中认为地写一段代码将可执行代码修改,让 真正执行的代码不在静态分析中出现。

一般,SMC的代码在ida工具中会被识别为数据,但会将该数据的地址当作函数指针并进行调用。

解决方案:

  • 静态分析SMC代码的自修改过程并复现出真正执行的代码
  • 动态分析至SMC代码解密完毕出并设置断点,然后往下接着分析

加密

主要是指对数据,代码,指令等进行加密保护,VMProtect是一种很强的加密壳。(这个就先记到这个程度)

反调试

概念:指在程序代码中运用若干种反调试技术,干扰对某个进程进行动态调试、逆向分析的手段。

简单来说就是:检测到你正在动态调试程序,然后执行干扰代码。

如:

  • Windows下正在被调试的进程的PEB中的BeingDebugged字段会被置为true,由此诞生了IsDebuggerPresent()API,用来检测当前进程是否正在被调试。
  • Linux下调用的ptrace也是类似的道理

Windows API:

  1. IsDebuggerPresent()
  2. CheckRemoteDebuggerPresent()
  3. NtQueryInformationProcess()

绕过方法:

  • 手动:找到反调试语句所在,进行相应修改绕过
  • ScyllaHide

时间间隔检测

单步跟踪的时候指令运行所耗费的时间长得多,基于此来检测

如:x86cpu中存在一个TSC(时间戳计数器)的64位寄存器RDTSC指令会将TSC的值读入EDX:EAX寄存器中。一般实现这种反调试只需探测TSC的低32位变化量(即EAX的变化量)。绕过方法:在没有变化量下界的情况下,将所有相关RDTSC(0F 32)指令替换成xor eax(33 C0)

基于异常的反调试

这个还没见过,所以先不细学,记个大概

绕过方法一般是对所使用的调试器进行配置,使之忽略程序产生的特定异常。

对于x64dbg而言:顶部菜单->选项->选项->异常->添加上次忽略上一个产生的异常类型。其他调试器同理。

TLS反调试

**Thread local Storage(TLS)**,即线程本地存储,是为解决一个进程中多个线程同时访问全局变量而提供的机制。Windows提供了TLS回调函数机制,借此实现反调试。

解决方案:IDA静态分析下可以很好地识别TLS回调函数。动态调试以x64dbg为例,顶部菜单->选项->选项->事件中勾选“TLS回调函数”项,再调试程序,会停在TLS回调函数被调用前,然后再跟踪分析

特定调试器检测

就是检测所用调试器

断点检测

这个大概看了一遍,没太搞懂,先放着(也还没见过)

Comments