WebAssembly逆向
Posted eliwang
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了WebAssembly逆向相关的知识,希望对你有一定的参考价值。
一、WebAssembly
-
简介
- WebAssembly是一种可以使用非JavaScript编程语言编写代码,并且能在浏览器上运行的技术方案。借助Emscripten编译工具,能将C/C++文件转成wasm格式,JavaScript可以直接调用该文件并执行其中的方法。
-
好处
- 可以隐藏核心逻辑,增大逆向难度
- 提高执行效率(基于C/C++编写)
二、解决方案(模拟执行)
-
python模拟执行wasm库
- 一、pywasm:使用简单
- 二、wasmer:功能更强大
-
pywasm使用
-
安装
pip3 install pywasm
-
使用
import pywasm runtime = pywasm.load(\'./Wasm.wasm\') # 加载wasm文件 result = runtime.exec(\'encrypt\', [10,200]) # 通过runtime.exec()执行wasm文件中的方法,参数1:方法名,参数2:传入该方法的参数列表 print(result)
示例中Wasm.wasm文件下载地址:https://spa14.scrape.center/js/Wasm.wasm
-
-
wasmer使用
-
安装
pip3 install wasmer pip3 install wasmer-compiler-cranelift
-
使用
from wasmer import engine,Store,Module,Instance from wasmer_compiler_cranelift import Compiler store = Store(engine.JIT(Compiler)) # 声明一个Store对象 module = Module(store, open(\'./Wasm.wasm\', \'rb\').read()) # 将wasm转化为Module对象 instance = Instance(module) # 将其转化为Instance对象 result = instance.exports.encrypt(10, 200) # 调用encrypt方法,并传入参数 print(result)
-
更多API:
- 参考:https://wasmerio.github.io/wasmer-python/api/wasmer
-
如何逆向分析WebAssembly二进制代码
最近,我们发表过一篇关于WebAssembly(Wasm)基本概念及其安全问题的文章。作为后续内容,本文将为读者介绍Wasm应用程序的逆向工程方法。当我们遇到一个未知源码的Wasm应用程序,并且想要弄清楚其内部机制的时候,该如何进行分析呢?关于这个主题,目前几乎找不到任何有用的文档,所以,我们决定抛砖引玉。
对于Wasm应用程序,我们可以使用不同的方式进行分析。在本文中,我们将通过一个非常简单的应用程序,来为读者详细介绍Chrome内置的Wasm调试功能。在此过程中,我们将因地制宜地引入一些相关概念。
对于急着了解相关技术内容的读者,如果按耐不住的话,可以先从附录部分获取HTML文件test.html,然后直接跳转到“调试示例应用程序”部分。
为什么要逆向分析Wasm呢?
我们为什么对分析Wasm应用程序如此饶有兴趣呢?在深入学习逆向分析的细节知识之前,让我们先来回答这个问题。
对于安全分析人员来说,我们最感兴趣的就是了解恶意软件作者是如何利用新兴技术的。每当出现新威胁时,例如新的勒索软件家族、物联网蠕虫或更不寻常的东西,安全研究人员都希望深入分析该恶意代码的所有功能。当我们知道了恶意软件是如何工作的,而且了解了它们的特性后,我们就可以编写签名来提供相应的安全保护了。
在分析传统恶意软件的时候,有许多分析工具可选,无论对于混淆过的JavaScript、恶意Flash对象、可移植可执行文件(PE)还是其他软件,都是如此。并且,在分析这些恶意软件的时候,总能找到一种行之有效的方法。
正如我们在本系列的第一篇文章中提到的,在安全分析工具与分析方法上面,Wasm的情况有所不同。关于如何分析Wasm应用程序方面,几乎没有任何文档可用,而且大多数常见的逆向工程工具,当前也不适用于Wasm。因此,我们才决心撰写本文,为读者深入揭示如何逆向分析Wasm二进制文件。
创建Wasm示例应用程序:“Hello World”
首先,让我们来创建一个简单的Wasm应用程序,以便稍后对其进行逆向分析。我们将在浏览器中运行该应用程序,并使用Chrome的开发人员工具对其进行逆向分析。
要在浏览器中运行Wasm应用程序,我们需要使用一个HTML文件来加载和执行Wasm二进制文件。下面,我们开始介绍如何创建这个HTML文件。(如前所述,完整的文件可以在附录中找到。)
首先,建立一个框架(我们将进一步对其进行修改),并将其保存到test.html文件:
<html>
<script>
function test() {
}
</script>
<body onLoad="test()">
</body>
</html>
为了便于配置并避免安装任何工具,我们这里使用名为WasmFiddler的在线Web应用程序来生成Wasm。在WasmFiddler中,输入以下内容:
void hello() {
printf("Hello World\n");
}
然后点击“Build”按钮:
图1:使用WasmFiddler编译Wasm应用程序
在上图的右侧,我们可以看到一个名为utf8ToString()的函数。我们需要将该函数复制并粘贴到HTML页面的JavaScript部分,并将其放在test()函数的上方。
在截图的右侧,我们可以在函数utf8ToString()后面看到如下所示的几行JavaScript代码:
let m = new WebAssembly.Instance(new WebAssembly.Module(buffer));
let h = new Uint8Array(m.exports.memory.buffer);
let p = m.exports.hello();
复制这些代码,并将其粘贴到test()函数中。这些代码的作用,就是根据定义在buffer数组中的代码来实例化我们的Wasm,然后执行hello()函数。
那么我们如何定义这个缓冲区的内容(Wasm代码)呢? 在WasmFiddler中,单击源代码下面的下拉菜单(图1中的“Text Format”),然后选择“Code Buffer”即可。
图2:在WasmFiddler中查看代码缓冲区。
这样,WasmFiddler就会生成二进制Wasm代码,并将其放入JavaScript缓冲区中。这时,我们会看到下列内容(有所删减):
var wasmCode = new Uint8Array([0,97,115,109,1,0,0,0,...,108,100,0]);
注意:如果得到的只是一个空数组(“var wasmCode = new Uint8Array([null]);”),说明忘了先编译源代码。在这种情况下,可以点击Build按钮,然后重试。
复制这个缓冲区的内容,并将其粘贴到test()函数的开头部分。然后,将数组从wasmCode重命名为buffer,以便与WasmFiddler生成的其他代码的命名相匹配。
如果您读过本系列的第一篇文章的话,就知道Wasm应用程序本身无法将文本打印到屏幕上。所以,需要定义一个JavaScript函数,在我们的Wasm代码中调用printf()函数。在WasmFiddler中,选择下拉菜单中的Text Format选项,以查看编译后的Wasm应用程序的文本表示形式:
图4:puts()函数的Imports模板。
复制上面wasmImports的定义,并将其粘贴到test()函数的开头部分。然后,我们还需要将Imports的定义提供给Wasm的实例,具体如下所示:
var m = new WebAssembly.Instance(new WebAssembly.Module(buffer),wasmImports);
最后,让我们来定义puts()函数在被调用时应该做些什么。具体来说,就是将其改为下面的样子:
puts: function puts (index) {
alert(utf8ToString(h, index));
}
现在,我们已经完成了构建演示程序所需的全部步骤。接下来,请在Chrome中加载test.html文件,这时会看到:
图5:Chrome中的通知。
我们可以看到,Wasm代码成功的调用了我们的外部函数。
注意:如果您没有看到弹出窗口,可能是您的浏览器不支持Wasm所致。在这种情况下,请尝试更新浏览器,因为目前所有主流浏览器的最新版本都支持Wasm。
调试示例应用程序
现在,我们终于可以通过Chrome开发者工具来调试示例应用程序了。
利用Chrome打开test.html文件后,启动Chrome开发人员工具(按F12键),并选择顶部的Sources选项卡。然后,按Ctrl+R组合键重新加载页面。现在,应该出现一个带有文字“wasm”的小云图标。接下来,请展开它及其下面的项目,选择wasm子树下的叶子项目,具体如下图所示:
图6:Chrome开发人员工具
让我们单步执行这个函数,以便更好的理解其功能。为此,请点击左边以“i32”开头的那行代码,为其设置断点。这时会显示一个蓝条,表明已设置好断点。接下来,按Ctrl+R组合键重新加载页面。现在,将在断点处停下来。这时候,Wasm堆栈是空的。然后,单击调试器中的Step Over按钮(或点击F10键或带有弯曲箭头的图标)以执行指令“i32.const 16”,该指令会将16的值压入堆栈:
图7:将值16压入堆栈。
Wasm中的所有函数都具有对应的编号,编号为0的函数对应于Wasm从JavaScript导入的puts函数(函数编号1对应于hello函数)。因此,下一条调用0的指令实际上就是调用printf/puts函数,并且堆栈中的值“16”是其参数。
图8:考察Wasm应用程序的内存。
下面让我们来看看内存中位置编号为16处的内容:
图9:Wasm应用程序内存中的“Hello World”。
运行状态下的Wasm应用程序的内存空间实际上是作为JavaScript数组实现的。该数组的定义位于负责加载Wasm应用程序的HTML文件中。在上面的例子中,变量“h”的定义如下所示;该变量用于保存应用程序的内存空间:
let h = new Uint8Array(m.exports.memory.buffer);
现在,请重新点击Step Over按钮来执行该调用。这样,就能看到相应的JavaScript警报了。
结束语
现在,我们已经对一个简单的Wasm程序成功地进行了逆向分析。虽然这个例子非常简单,但请不要忘记,千里之行始于足下。
在逆向过程中,我们是通过调用JavaScript声明的导入函数来了解Wasm是如何与外部环境进行交互的。此外,我们还介绍了如何在JavaScript和Wasm之间共享内存。
参考文献
WasmFiddle,在线编译Wasm: https://wasdk.github.io/WasmFiddle/?wvzhb
关于如何在浏览器调试器中调试Wasm的视频: https://www.youtube.com/watch?v=R1WtBkMeGds
在JavaScript和Wasm之间传递值: https://hacks.mozilla.org/2017/07/memory-in-webassembly-and-why-its-safer-than-you-think/
附录: test.html
为了便于参考,以下是我们创建并分析的整个test.html文件:
<html>
<script>
function utf8ToString(h, p) {
let s = "";
for (i = p; h[i]; i++) {
s += String.fromCharCode(h[i]);
}
return s;
}
function test() {
var wasmImports = {
env: {
puts: function puts (index) {
alert(utf8ToString(h, index));
}
}
};
var buffer = new Uint8Array([0,97,115,109,1,0,0,0,1,137,128,128,128,0,2,
96,1,127,1,127,96,0,0,2,140,128,128,128,0,1,3,101,110,118,4,112,117,
116,115,0,0,3,130,128,128,128,0,1,1,4,132,128,128,128,0,1,112,0,0,5,
131,128,128,128,0,1,0,1,6,129,128,128,128,0,0,7,146,128,128,128,0,2,6,
109,101,109,111,114,121,2,0,5,104,101,108,108,111,0,1,10,141,128,128,
128,0,1,135,128,128,128,0,0,65,16,16,0,26,11,11,146,128,128,128,0,1,0,
65,16,11,12,72,101,108,108,111,32,87,111,114,108,100,0]);
let m = new WebAssembly.Instance(new WebAssembly.Module(buffer),wasmImports);
let h = new Uint8Array(m.exports.memory.buffer);
m.exports.hello();
}
</script>
<body onLoad="test()">
</body>
</html>
以上是关于WebAssembly逆向的主要内容,如果未能解决你的问题,请参考以下文章
FLARE脚本系列:使用idawasm IDA Pro插件逆向WebAssembly(Wasm)模块
《WebAssembly 权威指南》WebAssembly 模块
《WebAssembly 权威指南》WebAssembly 入门
WebAssembly 系列WebAssembly 工作原理