Wasabi
는 WebAssembly 파일의 동적 분석을 위한 프레임워크로, 동작 원리는 아래 그림과 같다.
먼저, Wasabi
의 Javascript API를 이용하여 분석을 위한 javascript 파일을 작성하고, 분석하고 싶은 .wasm
파일에 대해 Wasabi
를 실행하면, 정적으로 binary instrumentation을 해서 Wasabi
의 analysis hook가 추가된 새로운 .wasm
파일과 프로그램에서 정적으로 추출된 정보와, 프로그램과 analysis 파일을 연결하기 위한 glue code
가 있는 새로운 javascript 파일이 생성된다. 그리고, 기존에 .wasm
파일이 실행되던 html
파일의 script
로 분석을 위해 작성한 javascript 파일과 Wasabi
를 통해 생성된 javascript 파일을 추가하면, 원하는 내용을 런타임에 동적으로 분석할 수 있게 된다. 더 자세한 내용은 Wasabi 논문 및 Wasabi Documentation을 참고하면 알 수 있다.
설치 과정은 Wasabi github를 참고하였다.
Wasabi
설치에 앞서, 다음과 같은 라이브러리와 tool들이 설치되어 있어야 한다고 한다.
모두 설치하였다면, 아래의 command line을 실행하여 wasabi를 설치해본다.
$ git clone https://github.com/danleh/wasabi.git
$ cd wasabi/crates/wasabi
# Rust package manage인 cargo를 이용하여 wasabi를 설치한다.
$ cargo install --path .
그리고, wasabi
커맨드를 입력해서 다음과 같은 메시지가 뜨는지 확인한다.
$ wasabi
Error: expected at least one argument
Usage: wasabi <input_wasm_file> [<output_dir>]
만약, wasabi: Command not found
의 에러가 뜬다면 아래 명령어를 통하여 ~/.bashrc
에 wasabi
경로를 저장한 후, 적용한다.
$ echo 'export PATH=$PATH:/home/directory/.cargo/bin' >> ~/.bashrc
$ source ~/.bashrc
wasabi
가 정상 동작하는지 확인하기 위해서 튜토리얼을 진행해보았는데, 직접 .wat
파일을 작성하여 .wasm
파일로 변환하는 방법도 있지만, 나는 emscripten
을 이용하여 C 파일을 .wasm
파일로 컴파일하여 실행하는 방법을 사용하였다.
먼저, 아래와 같은 hello.c
파일을 작성한다.
#include <stdio.h>
int main(int argc, char const *argv[]) {
printf("Hello, world!\n");
return 0;
}
이후, emscripten
을 이용해 hello.c
를 .wasm
파일로 컴파일하고, 웹 서버를 8080 포트에서 실행해본다.
$ emcc hello.c -o hello.html
# wasm 파일과 함께 js파일과 html 파일이 생성되었는지 확인한다.
$ ls
hello.c hello.html hello.js hello.wasm
$ emrun --no_browser --port 8080 .
이후, 브라우저에서 http://localhost:8080/hello.html
주소에 접속하여 화면에 Hello, world!가 출력되는지 확인한다. 만약 출력되고 있다면, 정상적으로 wasm
파일로 컴파일되었다는 것을 알 수 있다.
그리고, wasabi
를 통한 동적 분석이 정상적으로 작동되는 지 확인해보았다.
$ wasabi hello.wasm
inserted 129 low-level hooks
# binary instrumentation 전의 hello.wasm 파일의 이름을 바꾸고, wasabi를 통해 생성된 hello.wasabi.js와 hello.wasm을 현재 디렉토리에 복사한다.
$ mv hello.wasm hello.orig.wasm
$ cp out/* .
$ ls
hello.html hello.orig.wasm hello.wasm hello.c hello.js hello.wasabi.js
# analysis 파일은 wasabi repository에 있는 example analysis 파일 중 하나를 사용하였다.
$ cp wasabi/examples/analyses/log-all.js .
# 이후, html 파일에 wasabi를 통해 생성된 js 파일과 analysis js 파일을 script 태그로 추가한다.
$ sed -i '/<script async type="text\/javascript" src="hello.js"><\/script>/a <script src="hello.wasabi.js"></script>' hello.html
$ sed -i '/<script src="hello.wasabi.js"><\/script>/a <script src="log-all.js"></script>' hello.html
# 다시 한번 웹 서버를 실행해본다.
emrun --no_browser --port 8080 .
웹 서버 실행 이후, 다시 http://localhost:8080/hello.html
주소에 접속해보고, javascript 콘솔의 출력을 확인해본다.
그러면, 다음과 같이 분석 결과가 콘솔에 정상적으로 출력되는 것을 확인할 수 있었다. 따라서, wasbi
가 정상적으로 설치되었고, 실행된다는 것을 알 수 있었다.
이번에는 wasabi
repository에서 제공하는 예제 analysis 파일과 C 파일을 통하여 taint analysis를 해보고자 한다.
먼저, wasabi/examples/taint/c
디렉토리를 복사한다.
$ cp -r wasabi/examples/taint/c .
그리고, wasabi/examples/analyses
디렉토리의 taint.js
파일을 c 디렉토리에 복사해준다. 이 js 파일은 taint analysis를 수행하는 내용이 담긴 analysis 파일이다.
$ cp wasabi/examples/analyses/taint.js c/
그리고, c 디렉토리의 buildBrowserTest.sh
파일의 인자로 C 파일과 analysis 파일을 넘겨주면, 튜토리얼에서의 컴파일, wasabi
실행, html 파일에 script 태그 추가 등의 과정을 모두 자동으로 처리해준다. C 파일은 디렉토리에 있는 C 파일들 중 하나를 선택하였다.
$ cd c
# buildBrowserTest.sh를 실행 가능하게 만든다.
$ chmod +x buildBrowserTest.sh
$ ./buildBrowserTest.sh test_propagate_via_call.c taint.js
그리고, emrun --no_browser --port 8080 .
을 통해 웹 서버를 실행하고, http://localhost:8080/test_propagate_via_call.html
에 접속하여 콘솔의 출력을 확인해본다.
그랬더니, source function과 sink function을 찾지 못했다는 에러가 뜨는 것을 확인할 수 있다. 그 이유를 찾기 위해 taint.js
파일을 확인해보았다.
/*
* Taint policy: sources and sink
*/
function findSourceSinkFcts() {
let sourceFctIdx = -1;
let sinkFctIdx = -1;
for (let i = 0; i < Wasabi.module.info.functions.length; i++) {
const fct = Wasabi.module.info.functions[i];
if (fct.export === "taint_source" || fct.export === "_markSource") {
sourceFctIdx = i;
}
if (fct.export === "taint_sink" || fct.export === "_sink") {
sinkFctIdx = i;
}
}
if (sourceFctIdx === -1) console.log("Warning: No exported source function found.");
if (sinkFctIdx === -1) console.log("Warning: No exported sink function found.");
return {sourceFctIdx:sourceFctIdx, sinkFctIdx:sinkFctIdx};
}
보다시피 taint.js
파일에서는 source function과 sink function을 export된 function name을 가지고 찾는데, 현재 function name과 if문의 function name이 일치하지 않아 발생한 문제로 보였다. 이를 디버깅해보니, fct.export
는 array object이고, 실제 function name은 fct.export[0]
에 담겨있어 생긴 문제였다. 또, C파일에서의 sink function 이름과 source function 이름이 각각 markSource
와 sink
로 export 되는데, 현재 taint.js
에서는 다른 이름을 사용하고 있었다. 따라서, 나는 taint.js
파일의 findSourceSinkFcts()
를 아래와 같이 수정하였다.
/*
* Taint policy: sources and sink
*/
function findSourceSinkFcts() {
let sourceFctIdx = -1;
let sinkFctIdx = -1;
for (let i = 0; i < Wasabi.module.info.functions.length; i++) {
const fct = Wasabi.module.info.functions[i];
if (fct.export[0] === "taint_source" || fct.export[0] === "markSource") {
sourceFctIdx = i;
}
if (fct.export[0] === "taint_sink" || fct.export[0] === "sink") {
sinkFctIdx = i;
}
}
if (sourceFctIdx === -1) console.log("Warning: No exported source function found.");
if (sinkFctIdx === -1) console.log("Warning: No exported sink function found.");
return {sourceFctIdx:sourceFctIdx, sinkFctIdx:sinkFctIdx};
}
그리고, 다시 buildBrowserTest.sh
파일을 실행시켜 컴파일과 wasabi 실행을 다시 진행한 이후, 웹 서버를 실행시켜 콘솔의 출력 결과를 재확인해보았다.
$ ./buildBrowserTest.sh test_propagate_via_call.c taint.js
$ emrun --no_browser --port 8080 .
그랬더니, 위 그림과 같이 tainted된 value가 어느 위치에 도달하는지 확인할 수 있었다.
이렇듯 wasabi
를 이용하면 taint analysis를 런타임에 동적으로 수행할 수 있었는데, 다만 이번에 이용한 예제 analysis 파일은 function name을 이용하여 taint analysis를 수행하여 우리가 원본 소스 파일을 알 수 없고, function name을 알 수 없는 .wasm
파일에 대해서는 taint analysis를 수행할 수 없을 것이라고 생각된다.