【恶意软件分析】使用 S2E 分析 Wannacry

上篇对测试程序进行了分析,挖坑到现在一直都没有填。这次尝试对 wannacry 使用 S2E 进行分析。

准备工作

软件版本

S2E 镜像:windows-7sp1pro-i386,使用 en_windows_7_professional_with_sp1_x86_dvd_u_677056.iso 制作。(SHA-1:d89937df3a9bc2ec1a1486195fd308cd3dade928)

Visual Studio 2017 Community:记得安装 “对 C++ 的 Windows XP 支持”。

wannacry:我没找到原版的 DB349B97C37D22F5EA1D1841E3C89EB4,只能使用其他版本的 41B5BA4BF74E65845FA8C9861CA34508 (~~~~下载链接~~~~)原版下载链接https://malshare.com/sample.php?action=detail&hash=db349b97c37d22f5ea1d1841e3c89eb4。这个网站免费注册。后面再有找病毒的需求可以去 https://cyberlab.pacific.edu/resources/malware-samples-for-students ,整理了很多网站的链接。

S2E.sln

我们需要通过 API hook 的方式向 wannacry 中注入符号变量。之前一样,找到 $S2EDIR/source/s2e/guest/windows/s2e.sln 工程,并使用 VS 打开。

这里先把之前 malware-inject 项目重写出来,顺便检查环境是否有问题。

如果编译时报错和“静态分析”、“代码分析”有关,那么可以去 项目→属性→代码分析→常规,取消选中“生成时启用代码分析”。

Untitled

同时,把平台工具集改成 Windows XP (v141_xp):

Untitled

Wannacry 简单分析

参考链接:

这里不会详细分析 wannacry 的动作,具体完整的分析报告见参考链接。

Untitled

所以这里需要 hook InternetOpenUrlA 函数,让它返回符号变量,进而可以探索这两个不同的分支。

使用 S2E 分析 wannacry

编写 Hook 注入符号变量

在 VS2017 中新建 Wannacry-hook 项目,创建 malware-hook.c ,直接使用作者写好的代码即可。

在这个 malware-hook.c 中主要看 InternetOpenUrlAHook:

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
static HINTERNET WINAPI InternetOpenUrlAHook(
HINTERNET hInternet,
LPCSTR lpszUrl,
LPCSTR lpszHeaders,
DWORD dwHeadersLength,
DWORD dwFlags,
DWORD_PTR dwContext
) {
Message("Intercepted InternetOpenUrlA(%p, %s, %s, 0x%x, 0x%x, %p)\n",
hInternet,lpszUrl, lpszHeaders, dwHeadersLength, dwFlags, dwContext);

// Force a fork via a symbolic variable. Since both branches are feasible,
// both paths are taken
UINT8 returnResource = S2ESymbolicChar("hInternet", 1);
if (returnResource) {
// Explore the program when InternetOpenUrlA "succeeds" by returning a
// dummy resource handle. Because we know that the resource handle is
// never used, we don't have to do anything fancy to create it.
// However, we will need to keep track of it so we can free it when the
// handle is closed.
HINTERNET resourceHandle = (HINTERNET) malloc(sizeof(HINTERNET));

// Record the dummy handle so we can clean up afterwards
dummyHandles.insert(resourceHandle);

return resourceHandle;
} else {
// Explore the program when InternetOpenUrlA "fails"
return NULL;
}
}

调用 S2ESymbolicChar 创建了 1 字节的符号化变量(returnResource)。随后立刻进入 if 语句,判断 returnResource 的真假。显然对于符号化变量遇到分支会 fork。此时强行要求程序分出两个状态来分别探索。这里由于 wannacry 对返回的 HINTERNET 结构体不感兴趣(没有使用),所以只是返回了一个 malloc 分配的空间,本质上它不是一个合法的 HINTERNET 结构体。假如有恶意程序使用了某个结构体,我们也可以向其写入符号变量返回给它。

说白了就是在这里插入符号变量,迫使 state fork 进而执行两种不同的路径。

此外,可以看出来符号变量并没有传递给 wannacry,所以其内部也不会有别的 fork。

接下来编译这个 dll。仍然需要用 nuget 安装 EasyHookNativePackage,并引入 s2e/s2e.h 文件。

编译时报错:

Untitled

项目→ 属性 → C/C++ → 语言 → 符合模式 设置为 “否” 即可。

Untitled

完成编译后,目录下会有两个 PE 文件:

  • malware-inject.exe:用于启动目标程序,注入 dll

  • wannacry-hook.dll:被注入的 dll

为了能够正常运行,需要把 EasyHook32.dll 找出来,一起放到 S2E 里去。

运行 S2E

创建 S2E 工程:

1
s2e new_project -i windows-7sp1pro-i386 /home/ubuntu/workspace/wannacry/wcry.exe

然后修改 bootstrap.sh ,主要是用 malware-inject 来启动 wcry.exe 并把这几个文件拷贝到 VM 中:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
# This function executes the target program.
# You can customize it if your program needs special invocation,
# custom symbolic arguments, etc.
function execute_target {
local TARGET
TARGET="$1"

run_cmd "malware-inject.exe --dll wannacry-hook.dll --app ${TARGET}" > /dev/null 2> /dev/null
}

... ...

# Download the target file to analyze
${S2EGET} "wcry.exe"
${S2EGET} "easyhook32.dll"
${S2EGET} "wannacry-hook.dll"
${S2EGET} "malware-inject.exe"

然后在 s2e-config.lua 最后添加 add_plugin("LibraryCallMonitor")

这里有一个坑:EasyHook32.dll 拷贝到 S2E 环境下的时候,一定一定一定要重命名为 easyhook32.dll,然后在 bootstrap.sh 里也是用小写,否则不能正常运行!!!

在启动 S2E 时,可以用 GUI=1 ./launch-s2e.sh ,这样会弹出 QEMU 窗口,可以实时看到效果

Hook 运行相关的部分日志如下,可以看出 API Hook 正常:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
$ cat s2e-last/info.txt | grep -E  "malware-inject\]|malware-hook\]"
133 [State 0] BaseInstructions: Message from guest (0x12f254): [malware-inject] Successfully injected c:\s2e\wannacry-hook.dll into c:\s2e\wcry.exe (PID=0x494)
# 成功将 wannacry-hook.dll 注入到 wcry.exe 中
133 [State 0] BaseInstructions: Message from guest (0x197faf4): [0x494|malware-hook] Successfully hooked wininet.InternetOpenUrlA
133 [State 0] BaseInstructions: Message from guest (0x197faf4): [0x494|malware-hook] Successfully hooked wininet.InternetCloseHandle
133 [State 0] BaseInstructions: Message from guest (0x197faf4): [0x494|malware-hook] Successfully hooked kernel32.CreateProcessA
# 对 wininet.InternetOpenUrlA, wininet.InternetCloseHandle, kernel32.CreateProcessA 进行 hook
142 [State 0] BaseInstructions: Message from guest (0x12fa28): [0x494|malware-hook] Intercepted InternetOpenUrlA(00CC0004, http://www.iuqerfsodp9ifjaposdfjhgosurijfaewrwergwea.com, (null), 0x0, 0x84000000, 00000000)
142 [State 0] BaseInstructions: Message from guest (0x12fa48): [0x494|malware-hook] Intercepted InternetCloseHandle(00CC0004)
142 [State 0] BaseInstructions: Message from guest (0x12fa48): [0x494|malware-hook] Intercepted InternetCloseHandle(00B9F2C0)
146 [State 1] BaseInstructions: Message from guest (0x12fa48): [0x494|malware-hook] Intercepted InternetCloseHandle(00CC0004)
147 [State 1] BaseInstructions: Message from guest (0x12fa48): [0x494|malware-hook] Intercepted InternetCloseHandle(00000000)
208 [State 1] BaseInstructions: Message from guest (0x12f548): [0x494|malware-hook] Intercepted CreateProcessA((null), C:\WINDOWS\tasksche.exe /i, 00000000, 00000000, 0, 134217728, 00000000, (null), 0012FC30, 0012FC20)
# 拦截到新进程的创建
220 [State 1] BaseInstructions: Message from guest (0x12f56c): [0x494|malware-hook] Failed to create suspended process: 0x36B1

由于使用了 LibraryCallMonitor 插件,info.txt 中的无用信息很多。我们只关心 wcry.exe 本体调用的外部函数。先看 State 0 调用的函数(State 0 就是 InternetOpenUrlA 返回了非空,wcry.exe 没有正常启动的状态 ):

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
$ cat s2e-last/info.txt | grep "LibraryCallMonitor: wcry.exe:" | grep "State 0"
133 [State 0] LibraryCallMonitor: wcry.exe:0x409a43 (0x409a43) called msvcrt.dll!__set_app_type:0x6ff62804 (0x76762804) (pid=0x494)
133 [State 0] LibraryCallMonitor: wcry.exe:0x409a58 (0x409a58) called msvcrt.dll!__p__fmode:0x6ff627ce (0x767627ce) (pid=0x494)
133 [State 0] LibraryCallMonitor: wcry.exe:0x409a66 (0x409a66) called msvcrt.dll!__p__commode:0x6ff627c3 (0x767627c3) (pid=0x494)
133 [State 0] LibraryCallMonitor: wcry.exe:0x409acb (0x409acb) called msvcrt.dll!__getmainargs:0x6ff62bc0 (0x76762bc0) (pid=0x494)
133 [State 0] LibraryCallMonitor: wcry.exe:0x408238 (0x408238) called msvcp60.dll!??0_Lockit@std@@QAE@XZ:0x6e6b16c0 (0x6ed416c0) (pid=0x494)
133 [State 0] LibraryCallMonitor: wcry.exe:0x40826a (0x40826a) called msvcp60.dll!??1_Lockit@std@@QAE@XZ:0x6e6ddf70 (0x6ed6df70) (pid=0x494)
133 [State 0] LibraryCallMonitor: wcry.exe:0x409823 (0x409823) called msvcrt.dll!_onexit:0x6ff6112d (0x7676112d) (pid=0x494)
133 [State 0] LibraryCallMonitor: wcry.exe:0x409b1a (0x409b1a) called kernel32.dll!GetStartupInfoA:0x77de1e10 (0x769d1e10) (pid=0x494)
133 [State 0] LibraryCallMonitor: wcry.exe:0x409b3e (0x409b3e) called kernel32.dll!GetModuleHandleA:0x77e2dac3 (0x76a1dac3) (pid=0x494)
133 [State 0] LibraryCallMonitor: wcry.exe:0x40817b (0x40817b) called wininet.dll!InternetOpenA:0x6302abf0 (0x76d9abf0) (pid=0x494)
142 [State 0] LibraryCallMonitor: wcry.exe:0x408194 (0x408194) called wininet.dll!InternetOpenUrlA:0x630f6610 (0x76e66610) (pid=0x494)
# 从这里开始发生 fork
142 [State 0] LibraryCallMonitor: wcry.exe:0x4081bc (0x4081bc) called wininet.dll!InternetCloseHandle:0x630120c0 (0x76d820c0) (pid=0x494)
142 [State 0] LibraryCallMonitor: wcry.exe:0x4081bf (0x4081bf) called wininet.dll!InternetCloseHandle:0x630120c0 (0x76d820c0) (pid=0x494)
142 [State 0] LibraryCallMonitor: wcry.exe:0x409b4e (0x409b4e) called msvcrt.dll!exit:0x6ff636aa (0x767636aa) (pid=0x494)
142 [State 0] LibraryCallMonitor: wcry.exe:0x4010fe (0x4010fe) called msvcp60.dll!??0_Lockit@std@@QAE@XZ:0x6e6b16c0 (0x6ed416c0) (pid=0x494)
142 [State 0] LibraryCallMonitor: wcry.exe:0x401121 (0x401121) called msvcp60.dll!??1_Lockit@std@@QAE@XZ:0x6e6ddf70 (0x6ed6df70) (pid=0x494)

fork 后, State 1 调用的库函数如下:

1
2
3
4
5
6
7
8
9
146 [State 1] LibraryCallMonitor: wcry.exe:0x4081a7 (0x4081a7)  called wininet.dll!InternetCloseHandle:0x630120c0 (0x76d820c0) (pid=0x494)
147 [State 1] LibraryCallMonitor: wcry.exe:0x4081ab (0x4081ab) called wininet.dll!InternetCloseHandle:0x630120c0 (0x76d820c0) (pid=0x494)
147 [State 1] LibraryCallMonitor: wcry.exe:0x40809f (0x40809f) called kernel32.dll!GetModuleFileNameA:0x77e2d92a (0x76a1d92a) (pid=0x494)
147 [State 1] LibraryCallMonitor: wcry.exe:0x4080a5 (0x4080a5) called msvcrt.dll!__p___argc:0x6ffa3ac9 (0x767a3ac9) (pid=0x494)
147 [State 1] LibraryCallMonitor: wcry.exe:0x407c56 (0x407c56) called msvcrt.dll!sprintf:0x6ff6d35c (0x7676d35c) (pid=0x494)
147 [State 1] LibraryCallMonitor: wcry.exe:0x407c68 (0x407c68) called advapi32.dll!OpenSCManagerA:0x77c72b08 (0x752c2b08) (pid=0x494)
147 [State 1] LibraryCallMonitor: wcry.exe:0x407c9b (0x407c9b) called advapi32.dll!CreateServiceA:0x77ca3434 (0x752f3434) (pid=0x494)
148 [State 1] LibraryCallMonitor: wcry.exe:0x407cb2 (0x407cb2) called advapi32.dll!StartServiceA:0x77ca381f (0x752f381f) (pid=0x494)
... ...

太多了,可以看到启动了服务,但是没有给出启动参数。看来想实现完整的监控要写很多 hook?

中间跑了很多次,都是还没有反应 bootstrap.sh 先中止了。分析了一下确实是因为 WannaCry 创建了子进程之后自己停止,所以 bootstrap.sh 退出导致 S2E 退出。我在 execute 函数中加了 sleep 3000,并且在 wannacry-hook.dll 中把等待子进程的时间改成 1000 秒。最后截图留念,wannacry 启动成功:

Untitled

screenshot1