记一次样本分析
样本的文件来源: https://bbs.360.cn/thread-16185961-1-1.html白加黑:白进程 360speedld.exe,黑 DLL somkernl.dll。黑 DLL 内部名是 proram_c_nocrt_agent_failover_dll.dll,构建标记 clash-silent-speedld-dll。导出故意对齐系统库 msimg32.dll(Alpha
样本的文件
来源: https://bbs.360.cn/thread-16185961-1-1.html
白加黑:白进程 360speedld.exe,黑 DLL somkernl.dll。
黑 DLL 内部名是 proram_c_nocrt_agent_failover_dll.dll,构建标记 clash-silent-speedld-dll。导出故意对齐系统库 msimg32.dll(AlphaBlend / GradientFill / TransparentBlt / vSetDdrawflag),真正干活的是多出来的 SomPlugin。五个绘图导出会 LoadLibraryA("C:\\Windows\\SysWOW64\\msimg32.dll") 再 jmp 过去,白程序界面不崩。
也可以改名叫 msimg32.dll 放白进程旁边做侧加载,或者保持 somkernl.dll 当 360 插件被调 SomPlugin。
下面先看黑 DLL。
行为分析
目标是一套 C2 Agent(PRO_RAM C NoCRT Agent/0.2),进白进程后上线。
DllMain / SomPlugin
|- 宿主校验 sub_1001A7D4
|- 通过:sub_1001A712 心跳线程 + start_agent_thread
|- 不通过:挂起拉起白进程,注入自己,设 PRORAM_SL_KEEP=1
|- start_agent_thread // CAS 只起一次
|- agent_main_thread
|- agent_main // 等价 main
|- 解析命令行
|- create_instance_mutex -> build_signed_hello
|- agent_connect_loop // WSS / DNS 上线SomPlugin 做完同样启动后,如果宿主是 360speedld.exe,会 Sleep(60000) 死循环,插件调用不返回,DLL 不容易被卸。
DllEntryPoint
加载 DLL 时先把函数名改清楚再往下看。DllMain 在 DLL_PROCESS_ATTACH 里大致是:
DisableThreadLibraryCallssub_1001A7D4:当前进程能不能直接上线- 能:
sub_1001A712+start_agent_thread(原来的sub_1001A5E8) - 不能:记下自身路径,挂起创建白进程再注入,并设置
PRORAM_SL_KEEP=1,避免递归
sub_1001A7D4 不是反沙箱,是宿主白名单。满足任一条件就返回 1:
int sub_1001A7D4()
{
if (sub_1001A7F4()) // PRORAM_SL_KEEP == "1"
return 1;
if (sub_1001A75E()) // 进程名是 360speedld.exe
return 1;
return sub_1001A752(); // 进程名是 fixmbr.exe
}sub_1001A712 也不是藏窗口。只有宿主已经是 360speedld.exe 或 fixmbr.exe 时,CAS 一次,起心跳线程 sub_1001AAA1。
HANDLE sub_1001A712()
{
HANDLE result;
if (sub_1001A752() || (result = (HANDLE)sub_1001A75E()) != 0)
{
result = (HANDLE)_InterlockedCompareExchange((volatile signed __int32 *)&unk_10021028, 1, 0);
if (!result)
{
result = CreateThread(0, 0, sub_1001AAA1, 0, 0, 0);
if (result)
return (HANDLE)CloseHandle(result);
}
}
return result;
}sub_1001A5E8 才是起 Agent 主线程(start_agent_thread),不是 agent_main 本身:
HANDLE start_agent_thread() // sub_1001A5E8
{
HANDLE result;
result = (HANDLE)_InterlockedCompareExchange(&dword_10021020, 1, 0);
if (!result)
{
result = CreateThread(0, 0, agent_main_thread, 0, 0, 0);
if (result)
return (HANDLE)CloseHandle(result);
}
return result;
}agent_main_thread 进去直接调 agent_main,最后 exit,不回到 DLL。
agent_main
这是 C2 Agent 的 main。按顺序:
准备内存
一块命令行 0x800,一块运行时配置 cfg 0xC00。失败则 exit(2)。
灌默认配置
load_embedded_config 把 .prcfg 里的 C2、build、版本填进 cfg+0x400。
读命令行
用的是 cmdline_has_flag,不是整串 strcmp。
--print-capabilities:打印能力列表后 exit(0)--once:只连一次--insecure-tls:不校 TLS--server URL:覆盖 C2,命令行地址顶掉内嵌地址,后面主循环连这个
没有 --server 时:先环境变量 PRORAM_SERVER_URL,再没有才用内嵌 URL。
单实例
create_instance_mutex(sub_10015B32)。互斥量已存在打 duplicate_instance,exit(0x11)。
进入主循环
agent_connect_loop 走 WSS / DNS failover。循环返回后把返回码交给 exit。
--print-capabilities 打出来的能力:
void print_capabilities_and_exit()
{
print_stdout(
"core.transport.failover,core.transport.websocket,core.transport.dns,"
"core.identity.signed_hello,core.heartbeat.ping_pong,core.protocol.msgpack,"
"core.transfer.file_upload_v1,system_info.get,diagnostics.get,shell.exec,"
"file_manager.list_dir,file_manager.stat,file_manager.read_small_file,"
"file_manager.download_file,file_manager.upload,file_manager.create_file,"
"file_manager.mkdir,file_manager.rename,file_manager.delete,"
"persistence.check,persistence.install,persistence.uninstall");
exit(0);
}对应:WSS/DNS 双通道、signed hello、心跳、msgpack、shell、文件管理、HKCU Run 持久化(值名 ProRAMAgent)。导入里还能看到远程线程、写内存、reverse socks / 截屏相关痕迹。
create_instance_mutex(sub_10015B32)
这里会生成签名,但本函数不验签。
流程:
- 调
build_signed_hello("PRORAM_INSTANCE_LOCK_NONCE", build, hello),算出整份 0x180 字节 HELLO(里面带着 Ed25519 签名)。 - 从 HELLO 的 fingerprint 里取出
"sha256:"后面前 16 个 hex(公钥指纹,不是 signature 字段)。 - 拼名字:
Global\ProRAM-Agent-+ 这 16 hex。 CreateMutexA。GetLastError()==0xB7(ERROR_ALREADY_EXISTS)就认为已经有实例。
同一份 identity.bin → 同一把锁,只能跑一个 Agent。签名字段 hello+0x120 这里看都不看。
build_signed_hello
真正生成签名的地方。不联网。ecx=nonce,edx=build,输出 0x180 字节:
偏移 | 字段 | 内容 |
+0x00 | agent_id | agent_c_ + 公钥 SHA256 前 16 hex(可用 PRORAM_AGENT_ID 覆盖) |
+0x60 | public_key | Ed25519 公钥 Base64 |
+0x90 | fingerprint | "sha256:" + 公钥哈希 |
+0xD8 | host_fingerprint | MachineGuid / 计算机名 / USERDOMAIN / 卷序列号 |
+0x120 | signature | 对 hello 明文的签名再 Base64 |
算法:
- 32 字节身份种子
PRORAM_AGENT_SEED_HEX/PRORAM_AGENT_IDENTITY_SEED_HEX(64 hex),否则读/创建identity.bin(CryptGenRandom后落盘)。build 只参与目录名,例如%LOCALAPPDATA%\ProRAM\instances\portable-<16hex>\identity.bin。 - 种子 → Ed25519 公钥(clamp 后乘基点)。
- 拼明文并签名
"PRO_RAM_AGENT_HELLO_V1:" + nonce,私钥签 64 字节,再 Base64 写入+0x120。
WSS 上线时把整份 HELLO(公钥 + signature + agent_id 等)当 signed_hello 发给 C2,对端用公钥验签。本 DLL 里只负责生成和组包,没有本地验签。
C2 和落地
- 传输:WSS 主通道 + DNS failover,协议 msgpack,心跳 ping/pong。
- 持久化:
HKCU\Software\Microsoft\Windows\CurrentVersion\Run\ProRAMAgent。 - 身份目录:
%LOCALAPPDATA%\ProRAM\instances\或%ProgramData%\ProRAM\instances\,文件identity.bin(32 字节)。 - 互斥量:
Global\ProRAM-Agent-*。 - 环境变量前缀
PRORAM_:PRORAM_SERVER_URL、PRORAM_SL_KEEP、PRORAM_AGENT_ID、PRORAM_C_AGENT_LOG等。
检测可以看:360speedld.exe 从非官方目录加载 msimg32.dll / somkernl.dll;导出同时有 AlphaBlend 和 SomPlugin;节名 .prcfg 以 PRORAMCFGv2 开头。