恶意代码分析实战 IDA lab 6 c2程序分析很有用!
第6章 识别汇编中的C代码结构(实验)
Lab 6-1:在这个实验中,你将分析在文件Lab06-01.exe中发现的恶意代码
1.1 由main函数调用的唯一子过程中发现的主要代码结构是什么?
1.2 位于0x40105F的子过程是什么?
1.3 这个程序的目的是什么?
Lab 6-2:分析在文件Lab06-02.exe中发现的恶意代码。
2.1 main函数调用的第一个子过程执行了什么操作?
2.2 位于0x40117F的子过程是什么?
2.3 被main函数调用的第二个子过程做了什么?
2.4 在这个子过程中使用了什么类型的代码结构?
2.5 在这个程序中有任何基于网络的指示吗?
2.6 这个恶意代码的目的是什么?
Lab 6-3:在这个实验中,我们会分析在文件Lab06-03.exe中发现的恶意代码。
3.1 比较在main函数与实验6-2的main函数的调用。从main中调用的新的函数是什么?
3.2 这个新的函数使用的参数是什么?
3.3 这个函数包含的主要代码结构是什么?
3.4 这个函数能够做什么?
3.5 在这个恶意代码中有什么本地特征吗?
3.6 这个恶意代码的目的是什么?
Lab 6-4:在这个实验中,我们会分析在文件Lab06-04.exe中发现的恶意代码。
4.1 在实验6-3和6-4的main函数中的调用之间的区别是什么?
4.2 什么新的代码结构已经被添加到main中?
4.3 这个实验的解析HTML的函数和前面实验中的哪些有什么区别?
4.4 这个程序会运行多久?(假设它已经连接到互联网。)
4.5 在这个恶意代码中有什么新的基于网络的迹象吗?
4.6 这个恶意代码的目的是什么?
在这个实验中,你将分析在文件Lab06-01.exe中发现的恶意代码;
- 习惯吧,拿到文件先用
ExeInfo PE
查壳;
结果无壳;
问题:
1、由
main
函数调用的唯一子过程中发现的主要代码结构是什么?
- 使
IDA
打开程序;
从main
函数开始找第一个调用sub_401000
,双击进函数一探究竟;
- 进入
Graph View
宏观查看,很显然是一个if结构;
- 仔细查看一下具体结构,使用了
IneternetGetConnectedState
函数以及两个成功与失败的字符串;
-
如果
IneternetGetConnectedState
函数如果有连接,返回1,否则返回0; -
返回值与0比较:
- 如果函数返回为
1
,ZF
置0
,CF置0
,PF置1
,OF置0;
- 运行
00401017
处,显示Scuess
字符串的程序块;
- 运行
- 如果函数返回为
0
,ZF
置1
,CF置0
,PF置0
,OF置0;
- 跳转到
loc_40102B
,显示Error
字符串的程序块;
- 跳转到
- 如果函数返回为
2、位于
0x40105F
的子过程是什么?
- 双击跳转到
0x40105F
处;
看这个函数传递的参数是一个字符串;
这里我盲猜是一个printf
函数,为了验证,我们可以写一个例程进行反汇编;
1
2
3
4
5
6
|
#include <stdio.h> int main() { printf( "hello World\n" ); return 0 ; } |
再使用IDA
反汇编找到printf
函数一直看下去,找到如下页面;
看眼熟不。所以基本上可以判断,是一个printf
函数。
要说一句的是,我是编译成Realeass版本,而不是Debug版本,后者调试信息太多了,不适用对比
3、这个程序的目的是什么?
- 综上所述,这个程序是检查是否有一个可用Internet连接;
- 如果找到,就打印
Success:……
; - 否则打印
Error 1.1: ……
;- ==》其实从整个调用图看,如果成功以后,感觉是做了进程注入。。。
-
- 如果找到,就打印
Lab6-2
分析在文件Lab06-02.exe中发现的恶意代码;
- 同上,拿到文件先用
ExeInfo PE
查壳;
结果无壳;
问题:
1、
main
函数调用的第一个子过程执行了什么操作?
- 使用
IDA
打开程序;
- 双击进程序;
- 依旧是一个
if
语句,而且仍然使用的是InternetGetConnectedState
函数,进入Graph View
视图看的更清晰;
返回值与0比较:
- 如果函数返回为
1
,ZF
置0
,CF置0
,PF置1
,OF置0;
- 运行
00401017
处,显示Scuess
字符串的程序块;
- 运行
- 如果函数返回为
0
,ZF
置1
,CF置0
,PF置0
,OF置0;
- 跳转到
loc_40102B
,显示Error
字符串的程序块;
- 跳转到
2、位于
0x40117F
的子过程是什么?
- 双击
sub_40117F
进入函数;
- 又看到一个亲切的
不解释了,又是一个printf
函数(如有问题看Lab06-1);
3、被
main
函数调用的第二个子过程做了什么?
- 回到
main
函数;
- 双击第二个函数,
sub_401040
;
- 查询MSDN看看这些函数的功能;
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
|
/ / 初始化应用程序对Win32 Internet函数的使用。 HINTERNET InternetOpen( IN LPCSTR lpszAgent, IN DWORD dwAccessType, IN LPCSTR lpszProxyName, IN LPCSTR lpszProxyBypass, IN DWORD dwFlags ); / / 开始读取完整的FTP、Gopher或HTTP URL。如果使用的URL包含由空格分隔的相对URL和基URL,则首先使用InternetCanonicalizeUrl。 HINTERNET InternetOpenUrl( IN HINTERNET hInternetSession, IN LPCSTR lpszUrl, IN LPCSTR lpszHeaders, IN DWORD dwHeadersLength, IN DWORD dwFlags, IN DWORD dwContext ); / / 从InternetOpenUrl、FtpOpenFile、GopherOpenFile或HttpOpenRequest函数打开的句柄读取数据。 BOOL InternetReadFile( IN HINTERNET hFile, IN LPVOID lpBuffer, IN DWORD dwNumberOfBytesToRead, OUT LPDWORD lpNumberOfBytesRead ); / / internetCloseHeadle很显然就是关闭句柄,我就不再赘述了,有兴趣可以查一下MSDN; |
- 理一下程序过程;
-
- 先打开网络;
- 然后通过URL下载文件;
- 再对下载的文件通过
loc_4010E5
处的函数进行解析;
我们重点来看看loc_4010E5
处的解析规则;
选定字符使用r
键进行转换后;
<!--
是标准的HTML的注释开始的部分;(在网页中,注释只有在源码处能看到);- 在这个子程序中有一个贯彻始终的
Buffer
的数组,选定后,摁ctl+k
,查看定义大小;
选择拉取后发现,共0x200
也就是512个字节;
==》这个代码就是if c=='<' { if c == '!' .. {if c == '-' { if ... }}}
回到这个函数,我们发现,如果前面四个字符都匹配的话,会将第五个字符传给al
,也就是函数的返回值;
-
所以我们可以判断此函数的功能就是找到注释正文开始的地方将字符串首地址返回;
-
由标注出的被调用函数来看,这个函数的功能是从
www.practicalmalwarenalysis.com
下载了网页,并且解析;
看这个调用图,还是能够发现问题的!
2.4,2.5,2.6的具体分析过程上面已经体现
2.4 使用了if结构 多重比较
2.5 该恶意代码使用Internet Explorer 7.5/pma
作为代理(作为HTTP的User-Agent字段)、对http://www.practicalmalwareanalysis.com/cc.htm进行了数据访问(下载了该页面)。
2.6、访问指定url的页面,访问注释中的内容(<!-*-)从注释中获取命令,属于一种C2的隐藏手段。然后调用休眠指令休眠60s。
后面这本书会不会继续深入吧。见下!
有一个细节:见:https://www.jianshu.com/p/5e5a80cbcc45 里的说明!
这里IDA识别有些混乱,按照书上介绍的逻辑,应该是比较buf的前四个字节是不是`<!-*- 但是IDA好像都识别成了局部变量,我们需要修改这个函数的栈。
8.png
其实这里将这几个局部变量换成buf的偏移看起来就比较明确了,实际上就是比较前四个字符,全部匹配之后将第五个字符放在al,并返回;执行失败返回0.
9.png
下面是修改过的版本,看起来更舒服
10.png
修改方式是定义一个512大小的Array,每个元素1字节宽。
11.png
12.png
4、在这个子过程中使用了什么类型的代码结构?
很显然,这是一个多层if
的结构;
5、在这个程序中有任何基于网络的指示吗?
- 根据查看字符串窗口;
6、这个恶意代码的目的是什么?
综上所述:
- 首先判断是否有可用的Internet连接:
- 如果不存在就停止运行;
- 否则程序会使用一个用户代理下载网页:
- 该网页包含了
<!--
开始的注释; - 程序解析其后的字符;
- 将解析出来的字符串格式输出到屏幕,格式为
Success : Parsed command is X
其中
X`就是解析出来的字符;
- 如果解析成功,程序会休眠60秒,然后终止运行;
- 将解析出来的字符串格式输出到屏幕,格式为
- 该网页包含了
Lab6-3
在这个实验中,我们会分析在文件Lab06-03.exe中发现的恶意代码;
问题:
1、比较在
main
函数与实验6-2的main
函数的调用。从main
中调用的新的函数是什么?
- 经过对比,
sub_401130
处的函数是新出现的;
1
2
3
|
0x401000 (); / / 检查Internet连接; 0x401040 (); / / 下载网页并解析HTML注释; 0x401271 (); / / printf; |
2、这个新的函数使用的参数是什么?
-
在
call
上方是两个push
,所以是有两个参数; -
因为函数传参是从右往左进行压栈;
-
所以函数可以看作
sub_401130(ecx,eax)
;
-
1
2
|
+ `ecx`中存放的是`byte`的`var_8(是IDA给命名的)`; + `eax`中存放的是dword的`argv`; |
仅仅到这里就够了吗?真的是逼死强迫症,我们还是试着找一下var_8
和argv[0]
两个具体是啥;
jnz
指令如果是ZF=0
,就进行跳转;- 继续向上看,
test
指令;- 两个操作数按位
and
操作;- 结果为0,
ZF=1
;- 结果不为0,
ZF=0
;
- 结果不为0,
SF
为结果的最高位;- 结果的低八位中,1的个数是偶数,
PF=1
;- 否则,
PF=0
;
- 否则,
- 结果为0,
- 两个操作数按位
- 可以看见,
eax
是符号位扩展后的sub_401040()
的返回值;
还记得var_20C
是啥么?没错,就是从HTML注释中解析出的字符串首字符;
所以var_8
就是HTML注释中解析出的字符串首字符
再看argv
是入口函数的参数,所以应该是用户输入;
- 上面没有,那我们就往下找找,双击进入
sub_401130
;
- 点击
lpExistingFileName
查看引用关系;
又传给了CopyFileA()
函数,上神器MSDN瞅瞅;
1
2
3
4
5
6
7
8
|
CopyFile函数的作用是将一个现有文件复制到一个新的文件中。 BOOL CopyFile( LPCTSTR lpExistingFileName, / / pointer to name of an existing file LPCTSTR lpNewFileName, / / pointer to filename to copy to BOOL bFailIfExists / / flag for operation if file exists ); |
- 看传参是(从左往右数第一个)
lpExistingFileName
; - 要不咱再看看这个是啥类型吧;
原来是个指向字符串的指针;
至于书上答案说:argv就是Argv[0],是一个对当前运行程序名字,也就是Lab06-03.exe
的字符串引用。可惜的是,我并没有搜到Lab06-03.exe
这个字符串。所以我还是坚信是入口函数人为输入的字符串。如果有哪个大佬能指出问题所在,感激不尽;
3、这个函数包含的主要代码结构是什么?
- 很显然,这是一个典型的
switch
结构;
4、这个函数能够做什么?
看到一个很亲切的数字61h
,这就是ASCll码中的a
;
- 再点进跳转表
0ff_4011F2
看看情况;
紧凑的,由此可见,switch
判断的就是指令字符arg_0
是否为a
、b
、c
、d
、e
;
然后根据选择的不同,执行不同的功能;
switch功能列表如下所示(除了b的参数字符串没找到)
(a):调用CreateDirectory
,参数是C:\\Temp
,如果目录不存在,就创建一个;
(b):调用CopyFile
,两个参数,一个源文件,一个目的文件。此处功能将Lab06-03.exe
(没找到这个字符串)复制为C:\Temp\cc.exe
;
(c):调用DeleteFile
,参数是C:\\Temp\\cc.exe
,如果该文件存在就删除;
(d):在windows注册表中设置一个值,获得持久性运行。此处是将注册表键Software\Microsoft\Windows\CurrentVersion\Run\Malware
的值设置为C:\Temp\cc.exe
;
(e):休眠100秒;
(default):打印Error 3.2:Not a valid command provided
;
5、在这个恶意代码中有什么本地特征吗?
可以将这些作为本地特征;
6、这个恶意代码的目的是什么?
- 使用
if
结构,检查是否是存在一个可用的Internet连接。- 不存在,程序终止;
- 下载一个网页,解析出注释后面的第一个字符,根据这个字符操作;
- (a);
- (b);
- (c);
- (d);
- (e);
-
Lab6-4
在这个实验中,我们会分析在文件Lab06-04.exe中发现的恶意代码;
问题:
1、在实验6-3和6-4的
main
函数中的调用之间的区别是什么?
很明显看流程图可以看出结构成了循环了不再是之前的if了!
之前的:
新的:
2、什么新的代码结构已经被添加到
main
中?==》就是循环1440次,每次sleep 60s,也就是程序会执行一天!之前是一次性退出。
加了循环操作;
- 书上说是
for
循环,这个我个人觉得并不绝对。众所周知,for
、while
、do{}while
以及goto
结构都是互通的。因为编译器会经过优化,将原本的for
语句换成上面的任何一个形式;==》NO!就是一个for循环!见下图: -
- for varc 0~1440 而已!
3、这个实验的解析HTML的函数和前面实验中的那些有什么区别?
- 找到解析HTML函数
0x401040
;
这里出现了一个之前没用遇见的字符串Internet Explorer 7.50/pma%d
;==》for循环里的计数值写到这里面而已!下图:
var_c 就是传进去打印的!
调用了下方是_sprintf
函数;
sprintf
返回存储在缓冲区中的字节数,不包括结束的null
字符。swprintf
返回存储在缓冲区中的宽字符数,不包括结束的null宽字符;
传入字符串参数经过sprintf
–>InternetOpenA
最终赋值给了InternetOpenUrlA
的hInternet
字段;
4、这个程序会运行多久?(假设它已经链接到互联网)
- 回到
main
循环,进行查看;
- 首先
sub_401000
是个联网检测,既然假设连接上了,那就直接向下跳转;
- 判断通过后,开始了循环;
jge
是带符号内容前者大于后者,则跳转结束,否则向下执行;5A0h
等于十进制的1440
;- 继续向下走,就是
sub_401040
函数,解析HTML函数,没啥好说的,继续向下;
- 因为
sub_4012B5
是printf
函数、sub_401150
是switch
,都可以直接跳过,直到我们要找的Sleep
时间函数; - 看一看
call
上面的值,0EA60h
也就是十进制60000,单位毫秒,等于1分钟; - 所以每次循环是睡眠一分钟,共循环
1440
次,共计24小时;
5、在这个恶意代码中有什么新的基于网络的迹象吗?
此字符串是一个User-Agent
,包含了一个计数器,记录了该程序运行了多久;
6、这个恶意代码的目的是什么?
- 首先判断是否有可用的Internet连接:
- 如果不存在就停止运行;
- 设置循环终点,
1440
;- 否则程序会使用一个独特用户代理下载网页:
- 该网页包含了
<!--
开始的注释;- 程序解析其后的字符;
- 将解析出来的字符根据switch进行操作:
- (a);
- (b);
- (c);
- (d);
- (e)
- 该网页包含了
- 否则程序会使用一个独特用户代理下载网页: