代码插桩(Code Instrumentation) 是一种在程序代码中插入额外指令或代码片段的技术。这通常是为了收集执行时的信息、监控程序行为、调试代码或进行性能分析。
⼆进制程序代码插桩(DBI)技术通常采⽤ JIT 编译器,实现运⾏
反代码插桩,是重要的恶意软件分析与检测⼿段,但恶意软件也可以通过比较插桩前后差异(如特定寄存器)探测到执⾏环境异常,实现⾃⾝恶意⾏为隐藏或导致插桩执⾏失败,从⽽规避分析检测
使用加密和压缩技术对恶意代码进行加密和压缩,使代码难以解析和识别,从而阻止了动态检测
恶意软件检查程序的代码部分是否已被修改。如果修改,则更改程序的执行并异常退出
“Resist debugging”(抵抗调试)是一种在软件中采取措施以防止被调试的技术。当程序被调试时,调试器(debugger)可以访问程序的内部状态、内存内容和执行路径,这为逆向工程和分析提供了便利。因此,一些软件开发者或软件保护方案可能会采取措施,以抵抗调试,增加对逆向工程的难度。
这些技术的目标是增加逆向工程的难度,以保护软件免受未经授权的分析和修改。然而,反调试技术并不是绝对的,有经验的逆向工程师通常能够找到方法来绕过这些保护机制
真实的恶意软件示例Mirai:
sigemptyset(&sigs);
sigaddset($set, SIGINT);
sigprocmask(SIG_BLOCK, &sigs, NULL);
signal(SIGCHLD, SIG_IGN);
signal(SIGTRAP, &anti_gdb_entry);
使用sigemptyset()和sigaddset()分别清除和添加信号集,并将SIGINT信号添加到信号集。使用sigprocmask()函数来阻止信号集,以防止程序运行时其他进程或信号处理程序将其发送到此进程。使用signal()函数设置信号处理程序,设置要忽略的SIGCHLD信号,并设置要由反gdb-entry()函数处理的SIGTRAP信号。CPU将在硬件断点处触发中断信号(SIGTRAP),并暂停程序的执行。
Mirai使用了一系列技术,通过信号攻击来防止被调试器检测和分析
Dynamic Binary Instrumentation (DBI) 工具用于在运行时分析和修改二进制程序的行为。这类工具常用于调试、性能分析、安全研究等领域。然而,一些恶意软件或反分析技术可能会尝试检测 DBI 工具的存在,以防止被分析。
许多恶意代码使用反调试技术来识别它们是否正在被调试或禁用调试器。恶意代码编写者意识到,分析人员经常使用调试器来观察恶意代码的操作,因此他们使用反调试技术来尽可能延长恶意代码的分析时间。为了防止调试器进行分析,当恶意代码意识到它正在被调试时,它可能会更改正常执行路径或修改自己的程序以崩溃,从而增加调试时间和复杂性。
"int3"是一种用于在x86架构上触发调试中断的汇编指令。它实际上是一条中断指令,它引发的是中断号为3的中断。在Intel x86体系结构中,中断号3通常用于调试目的。
int3 指令的作用是在程序执行过程中插入一个软中断,这可以被调试器(如GDB)捕获。一旦中断被触发,操作系统或调试器会停止程序的执行,并将控制权传递给调试器,以便进行相应的调试操作。
当调试器想要在某一点停止,即设置断点或断点时,它将使用“int3”(0xcc)。在旧版本的gdb中,我们可以通过识别这个字符来判断它是否处于调试状态,但在新版本中它无法工作。但是我们可以尝试在代码中插入“int3”字符来破坏程序。
在一些恶意软件分析和逆向工程的情境中,int3 指令有时被用来干扰调试工具的运行,例如通过检测调试器的存在或动态修改代码来绕过调试。
在这个例子中,我们捕获SigTrap信号,这样程序在正常操作过程中就不会崩溃,并启动一个“int3”,它以类似于断点的方式在调试器中触发SigTrap
如
// Insert ’int3 ’
#include
#include
#include
// 信号处理器,对 SIGTRAP 信号的处理函数
void handler(int signo) {}
// 在函数中插入 int3 指令的函数
void PassByInt3() {
// 设置对 SIGTRAP 信号的处理函数为 handler
signal(SIGTRAP, handler);
// 在汇编中插入 nop 和 int3 指令
asm("nop\n\t" "int3\n\t");
// 这条语句将在 int3 执行成功后被打印
printf("Code executes successfully\n");
}
int main(int argc, char *argv[]) {
// 调用函数插入 int3 指令
PassByInt3();
return 0;
}
ptrace()系统调用用于进程跟踪。它为父进程提供了观察和控制其子进程执行的能力,并允许父进程检查和替换子进程内核映像(包括寄存器)的值。许多调试工具都是基于此功能进行调试的。但是,每个进程只能调用一个ptrace()。只要我们试图在代码中跟踪自己,就会出现错误
交互进程有一个标识符,称为会话的“Leader”进程,即SID。我们可以通过父进程(PPID)和引导进程(SID)之间的差异来判断执行应用程序时是否存在中间程序,也就是我们的调试工具。通过关注getsid()、getppid()、getpgid()和getpgrp()的值,我们可以检测调试器当前是否正在运行。
从bash-shell解释文档中,我们可以看到环境变量“-”的值将包含已运行的每个应用程序的全名。在正常环境中,此变量的值应等于/path/argv[0]。但是,在调试器中,这个变量的值经常被修改,因此我们可以通过比较这两个值来知道它当前是否处于调试环境中
当程序处于调试状态时,由于调试期间的断点、内存检查和其他操作,运行时间通常比正常时间长得多。因此,如果程序运行时间过长,可能是由于正在调试。具体来说,当程序启动时,通过警报设置计时器,当计时器到达时,程序终止。但是,对于一些调试器(如gdb),我们可以设置gdb处理信号的方式。如果我们选择忽略SIGALRM而不是将其传递给程序,则不会执行alarmHandler。我们需要考虑其他实时检测运行时的方法。
Ptrace是Unix系列系统的系统调用之一。它的主要功能是跟踪过程。对于目标进程,执行流量控制,读取和写入用户寄存器值,以及读取和修改内存。这个特性非常适合编写实现和远程代码注入。大多数病毒都会使用它来实现自用空间注入、rip位置直接注入以及文本和数据段之间的间隙注入。
LD PRELOAD是Linux/Unix系统的一个环境变量。它会影响程序的运行时链接器。它允许在程序运行之前定义要首先加载的动态链接库。该函数主要用于在不同的动态链接库中选择性地加载相同的函数。通过这个环境变量,我们可以在主程序与其动态链接库之间加载其他动态链接库,甚至可以覆盖正常的函数库
Proc是一个虚拟文件系统,安装在Linux系统中的/Proc目录上。Proc具有多种功能,包括允许用户访问内核信息或使用它进行故障排除。其中一个非常有用的功能是能够以文本流的形式访问进程信息,这在Linux中变得更加独特。在Proc中,有两个伪文件,即Proc/pid/maps和Proc/pid/mem。
proc/pid/mem提供了进程使用的完整内存空间的稀疏架构,并结合从maps文件获得的偏移量,mem文件可以用于读取和写入进程的内存空间。如果偏移量不正确,或者从起始位置按顺序读取文件,则会返回读写错误,因为这相当于读取无法访问的未分配内存。
通过这两个文件,可以定位目标进程中的函数。使用这些函数地址可以替换当前堆栈上的正常返回地址,并完成进程注入。
在这里,不可忽略的必须与分配给程序自动分析的总时间联系起来
恶意软件的运行时度量是指在程序运行时收集信息的行为。这些信息可能包括系统配置、进程信息、文件系统结构、网络连接等。这些信息可以帮助恶意软件适应不同的环境,或者帮助它们在感染计算机后进行更多攻击。通过编译语言插入,我们可以获得CPU品牌、供应商、版本和Hypervisor模式状态等信息。
许多恶意代码使用反仿真技术来阻止研究人员在沙盒中检查程序。反仿真技术包括硬件检查、环境检查和时间分析三种方法
从字面上看,硬件检查就是检查模拟器不支持的硬件命令和行为。这是因为虚拟机/模拟器对某些指令的实现与物理机不同。首先,官方arm手册对某些指令的返回值有所保留,未定义的返回值由处理器制造商自行返回。第二种是,qemu等模拟器在执行特殊指令时可能与arm手册不一致,包括arm自己的fvp试剂盒,该试剂盒也与手册不一致。因此,通过检查指令的特性,它不仅有助于识别它是否在模拟器中运行,而且有助于确定它在哪个模拟器中运行。
环境检查是恶意软件检测系统环境中是否存在模拟器相关内容的地方。通常模拟器中的环境变量会与物理机器不同,例如硬盘、NIC信息和制造商。
时序分析是检测程序是否在模拟器中执行的常用方法。在物理机器上,一些与加密、解密、媒体编码和解码相关的指令由硬件加速。然而,在模拟器中,指令不是由硬件加速的,而是由CPU计算的,这导致了执行时间的指数差。通过这种方式,恶意软件可以很容易地检测它是否在模拟器中执行。
“反内存转储”(Anti-Memory Dumping)是指一类技术和手段,旨在防止或干扰对程序内存的转储操作。在计算机安全领域,内存转储是指将程序的内存内容写入文件,通常是为了进行分析、调试或逆向工程。反内存转储技术旨在对抗这种操作,使得攻击者难以获取敏感信息或分析程序的内部结构
反内存转储是用于防止恶意代码分析的常用方法。通常,恶意软件实际上无法阻止我们转储物理内存,但恶意软件可以对代码进行模糊处理并加密,只有在代码执行时才能解密。恶意软件通常会虚拟化指令并使用虚拟指令解释器。重新配置寄存器和指令,以重新模拟CPU的本机指令,并形成一个虚拟指令集,该虚拟指令集依赖解释器将它们解释为本机指令以供执行,解释器只处理当前获取的指令。