本帖最后由 图钉鱼 于 2025-9-6 20:15 编辑
带毒
原文件结构破坏无法运行需修复后分析,经脱壳处理后是一个的自释放恶意文件,具体恶意行为需搭建特定环境分析,没有条件,不分析;
start入口函数首先调用sub_405000执行反分析检查(反调试、反虚拟化、LZCNT指令支持)。
若检测通过(非调试器/虚拟机,CPU支持所有特性): sub_405000正常返回,ax存储检测结果。
若检测失败(如调试器修改标志位、虚拟机不支持LZCNT): sub_405000通过jz指令提前跳转至结尾(loc_405059),返回错误值(bp=0,即ax=0)。
无论sub_405000返回什么结果,start函数都会立即覆盖返回值,执行后续初始化逻辑:
- 0x40103f: call sub_405000 ; 反分析检查
- 0x401044: mov eax, offset unk_416084 ; 覆盖eax(包括ax),指向一个未初始化的全局变量
- 0x401049: mov dword_41702C, eax ; 将unk_416084的地址写入全局变量dword_41702C
- 0x40104F: call sub_40DB6A ; 调用内存初始化函数
复制代码
覆盖eax(sub_405000的返回值),意味着返回值未参与后续逻辑判断。
调用sub_40DB6A(内存初始化函数),该函数通过HeapAlloc分配内存并初始化unk_416084指向的结构,为后续恶意行为做准备。
虽然sub_405000的返回值未被使用,但检测结果通过“程序状态”隐式控制后续流程:
若检测通过:
sub_405000正常执行,未修改任何全局状态,sub_40DB6A成功初始化内存结构,后续函数(如sub_411744、sub_411B6F)会正常触发恶意行为。
若检测失败:
sub_405000提前终止时,会通过pushf/popf篡改标志寄存器,导致后续sub_40DB6A初始化内存失败(如HeapAlloc返回NULL),或后续函数检测到标志寄存器异常,主动终止恶意行为(避免在分析环境中暴露真实逻辑)。
样本依赖LZCNT指令(CPUID Bit31),若CPU不支持,后续用到该指令的函数(如加密/数据处理函数)会直接崩溃,样本无法执行。
1. 反分析技术
CPU特性验证: 通过CPUID指令查询CPU标准(eax=1)与扩展(eax=0x80000001)特性,不支持SSE/SS/HTT/LZCNT则终止执行;
反调试: 篡改标志寄存器(XOR ax, 0x200000),检测调试器是否干预标志位,若干预则提前返回。
反分析过滤: 通过CPU特性验证与反调试逻辑,拒绝在调试器、虚拟机或老旧CPU环境中执行;
函数 sub_405000(反分析核心:CPU特性验证+反调试)
sub_405000 是样本反分析链条的核心函数,通过CPUID指令验证CPU特性,结合标志位篡改检测调试器,过滤分析环境(如虚拟机、调试器)。
基本块0:初始化与反调试检查
指令序列
- 0x405000: push ebp ; 保存栈帧
- 0x405001: mov ebp, esp ; 初始化栈帧
- 0x405003: sub esp, 0x18 ; 分配栈空间
- 0x405006: mov bp, 0 ; 初始化结果寄存器bp(0=未通过,非0=通过)
- 0x405009: pushf ; 保存标志寄存器
- 0x40500a: pop ax ; 将标志寄存器值存入ax
- 0x40500b: xor ax, 0x200000 ; 篡改标志寄存器(反调试:干扰调试器的TF位监控)
- 0x405011: push ax ; 压入篡改后的标志寄存器
- 0x405012: popf ; 恢复标志寄存器
- 0x405013: pushf ; 再次保存标志寄存器
- 0x405014: pop ax ; 取出篡改后的标志寄存器
- 0x405015: xor ax, cx ; 比较原始值(cx保存原始标志位)
- 0x405017: jz loc_405059 ; 若标志位未被修改(调试器未干预),则继续;否则跳至结尾(反调试触发)
复制代码 功能: 通过篡改标志寄存器检测调试器——若调试器监控标志位,会修改ax的值,导致jz跳转,函数提前返回(未通过检查)。
基本块1:CPU特性验证(CPUID eax=1)
指令序列:
- 0x405019: mov eax, 1 ; CPUID参数:查询标准特性
- 0x40501e: cpuid ; 执行CPUID,结果存入edx(标准特性)
- 0x405020: mov ax, dx ; 将edx的值存入ax
- 0x405022: and ax, 0x800000 ; 检查Bit23(SSE支持)
- 0x405028: jz loc_405059 ; 不支持SSE则跳至结尾(未通过)
- 0x40502a: mov ax, dx ; 重新加载edx的值
- 0x40502c: and ax, 0x2000000 ; 检查Bit25(SS支持)
- 0x405032: jz loc_405046 ; 不支持SS则跳至下一个检查
- 0x405034: or bp, 2 ; 支持SS,bp置位(2)
- 0x405038: mov ax, dx ; 重新加载edx的值
- 0x40503a: and ax, 0x4000000 ; 检查Bit26(HTT支持)
- 0x405040: jz loc_405046 ; 不支持HTT则跳至下一个检查
- 0x405042: or bp, 8 ; 支持HTT,bp置位(8)
复制代码
功能: 验证CPU的标准特性(SSE、SS、HTT),不支持则未通过检查。
基本块2:CPU扩展特性验证(CPUID eax=0x80000001)
指令序列:
- 0x405046: mov eax, 0x80000001 ; CPUID参数:查询扩展特性
- 0x40504b: cpuid ; 执行CPUID,结果存入edx(扩展特性)
- 0x40504d: mov ax, dx ; 将edx的值存入ax
- 0x40504f: and ax, 0x80000000 ; 检查Bit31(LZCNT支持)
- 0x405055: jz loc_405059 ; 不支持LZCNT则跳至结尾(未通过)
- 0x405057: or bp, 4 ; 支持LZCNT,bp置位(4)
复制代码
功能: 验证CPU的扩展特性(LZCNT),不支持则未通过检查。
函数通过两次 CPUID 调用和位掩码检查,验证CPU的4项核心特性,最终将结果编码到 bp 寄存器中返回。以下是分步逻辑:
1. 初始化与前置检查(基本块0)
mov bp, 0 → 初始化 bp 寄存器(用于存储检测结果)。
pushf → 保存标志寄存器。
pop ax → 将标志寄存器值存入 ax。
xor ax, 0x200000 → 篡改标志寄存器(疑似反调试,干扰调试器的标志位监控)。
push ax; popf → 恢复篡改后的标志寄存器。
pushf; pop ax; xor ax, cx; jz loc_405059 → 检查标志位是否被调试器修改,若被修改则跳转至函数结尾(反调试逻辑)。
2. 第一次 CPUID 调用(基本块1-4)
CPUID 指令输入 eax=1,获取CPU的标准特性(存储在 edx 寄存器)。函数检查 edx 的3个位:
Bit 23(SSE 支持): and ax, 0x800000 → 若为0(不支持SSE),跳转至结尾。
Bit 25(SS 支持): and ax, 0x2000000 → 若为0,跳转至 loc_405046。
Bit 26(HTT 支持): and ax, 0x4000000 → 若为0,跳转至 loc_405046。
每通过一项检查,bp 寄存器会被置位(or bp, 2/8/16),记录特性支持情况。
3. 第二次 CPUID 调用(基本块5-6)
CPUID 指令输入 eax=0x80000001,获取CPU的扩展特性(存储在 edx 寄存器)。函数检查:
Bit 31(扩展特性支持): and ax, 0x80000000 → 若为0,跳转至结尾。
通过检查后,bp 寄存器置位(or bp, 4)。
4. 结果返回(基本块7)
指令: mov ax, bp → 将编码后的检测结果存入 ax。
指令: retn → 返回结果(ax 寄存器的值即为检测结果)。
基本块3:结果返回
指令序列:
- 0x405059: mov ax, bp ; 将结果存入ax
- 0x40505b: leave ; 清理栈帧
- 0x40505c: retn ; 返回结果(ax=0=未通过,非0=通过)
复制代码
sub_405000 的核心是过滤“不合法”运行环境:
反调试: 通过篡改标志寄存器检测调试器;
CPU特性验证: 要求CPU支持SSE、SS、HTT、LZCNT(部分虚拟机/老旧CPU不支持,样本拒绝在这类环境运行);
隐式控制: 结果未通过ax传递(被start函数覆盖),但通过函数执行状态(如是否提前返回)影响后续内存初始化(sub_40DB6A)——未通过检查时,sub_40DB6A分配内存失败,样本终止恶意行为。
2. 混淆技术
字符串XOR加密: sub_4092F5用固定密钥0x5A对配置字符串逐字节加密,需动态执行或密钥解密。
sub_4092F5 是样本的字符串混淆函数,被sub_4011DE(配置解析函数)调用,核心功能是通过XOR运算加密配置字符串(如C2服务器IP、端口),防止静态分析时暴露敏感信息。
调用者: sub_4011DE(配置解析函数);
输入参数:
arg_0: 字符串地址(待加密的配置字符串);
arg_4: 密钥(固定值0x5A,反汇编);
输出: 加密后的字符串地址(存入eax返回)。
XOR加密: 对输入字符串的每个字符(除结尾0x00)执行bl XOR cl(cl=0x5A);
原地加密: 加密后的字符直接写回原字符串地址(不分配新内存);
调用场景: sub_4011DE解析配置字符串前,调用sub_4092F5解密(或加密),确保配置信息在静态扫描/分析中不可读。
3. 内存管理
用HeapAlloc分配堆内存(如unk_416084结构),存储解析后的C2配置,避免栈溢出风险;
内存初始化后,后续C2通信函数直接读取堆内存中的参数。
核心逻辑: 调用系统APIHeapAlloc分配堆内存(大小由全局变量dword_41702C控制),并初始化unk_416084指向的全局结构(推测载荷解压缓冲区)。
被start函数直接调用(初始化基础内存);
被sub_411B6F通过全局函数指针dword_417654间接调用(复用内存初始化功能)。
|