-
-
[原创] 默默无闻·恶意代码分析Lab5
-
2021-4-5 06:44 5515
-
系列往期:
工具说明
参照书籍:《恶意代码分析实战》;
文件来源:官网随书文件、或者附件中(文件密码:
apebro
);;使用工具:WinHex、CFF、StudyPE+、Exeinfo PE、Resource Hacker、Depends Walker、OD、IDA、MSDN;
Lab5-1
只用IDA Pro分析在文件Lab05-01.dll中发现的恶意代码。这个实验的目标是给你的一个用IDA Pro动手的经验。如果你已经用IDA Pro工作过,你可以选择忽略这些问题,而将精力集中在逆向工程恶意代码上。
问题
- 拿到文件先查壳:
很显然,没有壳;
再审题,基本上都是IDA Pro的操作,看来是一次复习IDA Pro使用了。
1、DLLMain的地址是什么?
- 打开IDA导入文件,选择
Graph View
视图(其实进去就是):
- 选择函数右击选择
Text View
视图,看向左边的地址列;
获得DLLMain的位置0x1000D02E
;
这里有一个知识点:就是这里的地址是理论上的VA,注意是理论上的,因为如果理论位置被加载了,就需要让系统重新选择一个位置,然后重定位。所以有效的地址应该是换算成RVA
2、使用Imports窗口并浏览到
gethostbyname
,导入函数定位到什么地址?
- 选择imports窗口,使用
ALT+T
快捷键搜索函数名字符串;
获得定位0x100163CC
;
3、有多少函数调用了
gethostbyname
?
- 双击
0x100163CC
地址,跳转到Text View
;
- 点击函数名,嗯
x
键,跳出xrefs窗口;
共18次引用,Type处p
代表被调用,r
代表[…]
“读取”式引用,所以有9个交叉引用对gethostbyname()
进行调用;
4、将精力集中在位于
0x10001757
处的对gethostbyname
的调用,你能找出哪个DNS请求将被触发吗?
- 在
IDA View-A
窗口,使用g
键,跳转到0x10001757
处;
这里是一个call
操作,所以向上找参数;
1 2 3 | mov eax , off_10019040 ;这里是一个数据地址头 add eax , ODh ;ODh大小 push eax ;把eax压栈 |
所以先要找到数据地址头;
- 双击
off_10019040
,打开数据段看数据;
将鼠标放在黄色字符串上,就能看到完整字符串;或者打开Strings窗口,检索红色框起来的字符串;
这就找到了网址,所以会对该网址发起DNS请求,获得其IP地址;
5、IDA Pro识别了在
0x10001656
处的子过程中的多少个局部变量?
- 在
IDA View-A
窗口,使用g
键,跳转到0x10001656
处;
这个就很显然了,偏移为负的是局部变量 ;
IDA很智能的识别出了23个局部变量;
6、IDA Pro识别了在
0x10001656
处的子过程中的多少个参数?
- 在
IDA View-A
窗口,使用g
键,跳转到0x10001656
处;
偏移为正的是参数;
IDA很智能的识别出了1个参数;
7、使用Strings窗口,来在反汇编中定位字符串
\cmd.exe /c
。它位于哪?
- 打开Strings窗口,快捷键
ALT+T
搜索字符串;
找到字符串地址0x10095B34
;
8、在引用
cmd.exe /c
的代码所在的区域发生了什么?
- 转到IDA View-A窗口,快捷键
g
跳转到0x10095B34
,点击地址,摁快捷键x
;
双击Address
地址,跳转到IDA View-A
代码;
这是个push
操作;
9、在同样的区域,在
0x100101c8
处,看起来好像dword_1008E5C4
是一个全局变量,它帮助决定走哪条路径。那恶意代码是如何设置dword_1008E5C4
的呢?(提示:使用dword_1008E5C4
的交叉引用。)
- 找到全局变量,查找xrefs引用,发现总共三次引用,只有一次有
move
操作;
- 双击
Address
地址,跳转查看对应位置代码;
- 找到对应代码,往上查看,紧接着的是一个函数调用;
- 那
eax
就是上一个函数的返回值,那就双击sub_10003695
,查看对应代码;
- 经过MSDN查看;
1 2 3 4 5 | / / GetVersionEx函数获取当前运行的操作系统版本的扩展信息。 BOOL GetVersionEx( LPOSVERSIONINFO lpVersionInformation / / pointer to version / / information structure ); |
- 通过这段函数的反汇编;
得到:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | bool sub_10003695(){ typedef struct { / / 通过MSDN类型溯源获得结构体完整类型; DWORD dwOSVersionInfoSize; DWORD dwMajorVersion; / / 函数没使用 DWORD dwMinorVersion; / / 函数没使用 DWORD dwBuildNumber; / / 函数没使用 DWORD dwPlatformId; CHAR szCSDVersion[ 128 ]; / / Maintenance string for PSS usage }VersionInformation; VersionInformation.dwOSVersionInfoSize = 0x94h ; GetVersionExA(&VersionInformation); return VersionInformation.dwPlatformId = = 2 ; } |
由代码可知:dwPlatformId
与数字2进行比较,如果相等,al置1,也就是返回true
;
由此可以简单的判断当前操作系统的版本;
只需要查到GetVersionExA()
函数的参数类型,然后一直向上找定义,就能找到结构体类型;
10、在位于
0x1000FF58
处的子过程中的几百行指令中,一系列使用memcmp
来比较字符串的比较。如果对robotwork
的字符串比较是成功的(当memcmp
返回0
),会发生什么?
- 摁
g
跳转到0x1000ff58
,初看有点蒙,哪里来的memcmp()
;
- 一直往下翻;
终于找到了,用Graph View
看更壮观,真的好多memcmp
;
- 然后开始找
robotwork
,终在0x1001044C
处找到了;
指令是个push
,大概率是一个传参操作,继续向下找call
;
注意看call下面那句,明显是平栈操作,很显然memcmp
使用的是__cdecl
调用方式;
- 当然了,我们得先复习一下
memcmp
的功能吧;
1 2 3 4 5 6 7 8 9 10 11 | / / 比较两个缓冲区中的字符。 int memcmp( const void * buf1, const void * buf2, size_t count ); / / Return Value / / 返回值指示缓冲区之间的关系。 / / 返回buf1和buf2的第一个计数字节的值关系 / / < 0 buf1 小于 buf2 / / = 0 buf1 等于 buf2 / / > 0 buf1 大于 buf2 |
根据题意,memcmp
返回0
;
再复习一下
test
指令;- TEST 指令在两个操作数的对应位之间进行 AND 操作,并根据运算结果设置符号标志位、零标志位和奇偶标志位。
- TEST 指令总是先清除溢出和进位标志位;
所以
test eax,eax
等于and eax , eax
也就是and 0 , 0
,结果为0
,ZF位置1
。如果ZF位为
1
,jnz
指令则不跳转;- 顺势执行,调用
sub_100052A2
函数;
- 通过MSDN查询,
RegOpenKeyExA()
函数;
1 2 3 4 5 6 7 8 9 10 11 12 | / / RegOpenKeyEx函数打开指定的注册表键。 LONG RegOpenKeyEx( HKEY hKey, / / handle to open key LPCTSTR lpSubKey, / / address of name of subkey to open DWORD ulOptions, / / reserved REGSAM samDesired, / / security access mask PHKEY phkResult / / address of handle to open key ); / / 返回值 / / 如果函数成功执行,返回值为ERROR_SUCCESS。 / / 如果函数失败,返回值为WINERROR.H中定义的非零错误代码。您可以使用带有 FORMAT_MESSAGE_FROM_SYSTEM标志的FormatMessage函数来获取错误的通用描述。 |
- 如果正确返回,那就应该进入
0x10005309
处函数;
说明它查询了路径为
HKLM\SOFTWARE\Microsoft\Windows\CurrentVersion\
处WorkTime
和WorkTimes
的注册表项;有意思的是看
sub_100038EE
处的函数;
这是一个隐藏的Socket
函数,所以应该是先查询注册表值,然后再发送一个网络信息;
11、
PSLIST
导出函数做了什么?
- 打开
Exports
界面;
- 双击;
最近的跳转是关联sub_100036C3
函数的返回值;
- 进函数瞅瞅;
这函数很眼熟,是获取操作系统版本的;
至于2
和5
是操作系统版本号;
答案给出的是Windows Vista/7
或是Windows XP/2003/2000
;
- 返回
0x10007025
处的函数查看;
- 继续进
loc_1000704E
函数; loc_1000704E
函数;
- 这里看
loc_100066DF
和sub_100038BB
;
- 这是
loc_100066DF
;
依旧是到了sub_100038BB
;
- 这是
sub_100038BB
;
综上所述,这两条代码路径都使用CreateToolHelp32Snapshot
函数,从字符串和API来看,是获取进程列表,通过send
将进程列表通过socket
发送出去;
12、使用图模式来绘制处对
sub_10004E79
的交叉引用图。当进入这个函数时,哪个API函数可能被调用?仅仅基于这些API函数,你会如何重命名这个函数?
- 在函数窗口搜索字符串
sub_10004E79
,双击进函数; - 右击进入
Graph View
视图; - 通过
View -> Graphs -> User Xrefs Chart
步骤,获得交叉引用图;
- 通过下行功能函数名称,可以命名为:
send_languageID
或者sendLanguageID
;
13、DLLMain直接调用了多少个Windows API?多少个在深度为
2
时被调用?
先通过
g
键跳到0x1000D02E
的DLLMain处;右击进入
Graph View
视图;- 通过
View -> Graphs -> User Xrefs Chart
步骤,获得交叉引用图;
将红标处设置为2
;
- 因为太大,只能截取部分图;
14、在
0x10001358
处,有一个对Sleep
(一个使用一个包含要睡眠的毫秒数的参数的API函数)的调用。顺着代码向后看,如果这段代码执行,这个程序会睡眠多久?
先通过
g
键跳到0x10001358
处;找到
Sleep
函数;
- MSDN找函数申明;
1 2 3 4 | / / Sleep函数在指定的时间间隔内挂起当前线程的执行。 VOID Sleep( DWORD dwMilliseconds / / sleep time in milliseconds ); |
- 由函数申明可知,只有一个参数,查看
call
上方的eax
的值; off_10019020
=[This is CTI]30
;eax
=“[T”
;eax
+=0x0Dh
==“30”
;atoi(eax)
==(int)30
;imul eax, 3E8h
==eax
=30
*1000
=30000;Sleep()
参数的单位是毫秒;30000毫秒
=30秒
;- 所以睡眠30秒;
15、在
0x10001701
处是一个对socket
的调用。它的3个参数是什么?
- 直接查MSDN,查看参数类型;
1 2 3 4 5 6 7 | / / Windows Sockets套接字函数创建绑定到特定服务提供程序的套接字。 SOCKET socket ( int af, int type , int protocol ); |
16、使用MSDN页面的
socket
和IDA Pro中的命名符号常量,你能使参数更加有意义吗?在你应用了修改以后,参数是什么?
- 先通过
g
键跳到0x10001701
处;
选中数字,右击选择Use Symbolic Constant
,出现对话框选择OK
就好了;
这就成功了;
- 从参数类型来看,基于IPv4的TCP链接;
17、搜索
in
指令(opcode 0xED
)的使用。这个指令和一个魔术字符串VMXh
用来进行VMware检测。这在这个恶意代码中被使用了吗?使用对执行in
指令函数的交叉引用,能发现进一步检测VMware的证据吗?
有两种方式:
Search->Text
搜in
;Search->Sequence of Bytes
,搜ED
;
选择
Find All Occurrences
,可以列出所有匹配;选中
0x564D5868h
摁r
键可以看出字符串等于VMXh
;看书后答案,这里涉及了反虚拟机技巧,17章的时候会有更详细的介绍;
18、将你的光标跳转到
0x1001D988
处,你发现了什么?
先通过
g
键跳到0x1001D988
处;猜测是一端硬编码;
19、如果你安装了IDA Python插件(包括IDA Pro的商业版本的插件),运行
Lab05-01.py
,一个本书中随恶意代码提供的PDA Pro Python脚本,(确定光标是在0x1001D988
处。)在你运行这个脚本后发生了什么?
根据题意,选择菜单
File -> Script File
,选择随书文件中的Python脚本运行;可惜我的版本并不支持python脚本,掠过掠过;
20、将光标放在同一位置,你如何将这个数据转成一个单一的ASCII字符串?
- 可惜我的版本并不支持python脚本,掠过掠过;
21、使用一个文本编辑器打开这个脚本。它是如何工作的?
- 可惜我的版本并不支持python脚本,掠过掠过;
总结
看似东西不多,做起来也快,就是写笔记写帖子总是很慢很慢;
坚持吧,坚持总会有结果的;
共勉!
[CTF入门培训]顶尖高校博士及硕士团队亲授《30小时教你玩转CTF》,视频+靶场+题目!助力进入CTF世界