Unicorn、usercornを使ってみる
Unicornとは
この記事での Unicorn は、エミュレータの方であり、決してRackサーバの方ではない。
Unicornは軽量でマルチプラットフォーム、マルチアーキテクチャな CPU emulator frameworkである。
UnicornはNguyen Anh Quynh氏が中心となって開発されているが、彼は disassembly/disassembler frameworkであるCapstone、 assembler frameworkであるKeystoneの開発も行っており、すごい!
また、似たものとしてQEMUが思い浮かぶが、UnicornはQEMUをベースに開発されていて、QEMUより優れている点として、frameworkであること、flexibleであること、dynamic instrumententionをサポートしていること、thread-safeであること、いろんな言語によるbindingsがあること、軽量であること、セキュアであることが挙げられている。
Unicornのインストール
基本的に/docs/COMPILE.mdの通りにやればうまくいく。
ただ、macにインストールする場合、Homebrewで入るものは古く、GitHub上のmasterは違うものになっているので、手でインストールするほうが無難。特にHomebrewでは入らないbindingをインストールするとき、手抜きで本体だけHomebrewでいれてbindingだけを手でインストールしても動かないので注意。これでハマった...
各言語によるbindingのインストール方法は/bindings/各言語/にあるREADMEを参照。
例えばPythonだと/bindings/python/README.TXTをみればいい。
動かしてみる
Python bindingを使って動かしてみる。
コード
#!/usr/bin/env python2 # coding: UTF-8 from unicorn import * from unicorn.x86_const import * import sys # memory address where emulation starts ADDRESS = 0x1000000 # initialize unicorn emulator mu = Uc(UC_ARCH_X86, UC_MODE_32) # map 4MB for this emulation mu.mem_map(ADDRESS, 4 * 1024 * 1024) binary = '' if len(sys.argv) == 2: with open(sys.argv[1], 'rb') as f: binary = f.read() else: binary = b"\x41\x4a" # INC ecx; DEC edx mu.mem_write(ADDRESS, binary) # emulate code in infinite time & unlimited instructions mu.emu_start(ADDRESS, ADDRESS + len(binary)) r_eax = mu.reg_read(UC_X86_REG_EAX) r_ebx = mu.reg_read(UC_X86_REG_EBX) r_ecx = mu.reg_read(UC_X86_REG_ECX) r_edx = mu.reg_read(UC_X86_REG_EDX) r_ebp = mu.reg_read(UC_X86_REG_EBP) r_esp = mu.reg_read(UC_X86_REG_ESP) r_edi = mu.reg_read(UC_X86_REG_EDI) r_esi = mu.reg_read(UC_X86_REG_ESI) r_eip = mu.reg_read(UC_X86_REG_EIP) r_eflags = mu.reg_read(UC_X86_REG_EFLAGS) print(">>> EAX = 0x%x" % r_eax) print(">>> EBX = 0x%x" % r_ebx) print(">>> ECX = 0x%x" % r_ecx) print(">>> EDX = 0x%x" % r_edx) print(">>> EBP = 0x%x" % r_ebp) print(">>> ESP = 0x%x" % r_esp) print(">>> EDI = 0x%x" % r_edi) print(">>> ESI = 0x%x" % r_esi) print(">>> EIP = 0x%x" % r_eip) print(">>> EFLAGS = 0x%x" % r_eflags)
実行結果
$ python nishizumi.py >>> EAX = 0x0 >>> EBX = 0x0 >>> ECX = 0x1 >>> EDX = 0xffffffff >>> EBP = 0x0 >>> ESP = 0x0 >>> EDI = 0x0 >>> ESI = 0x0 >>> EIP = 0x1000002 >>> EFLAGS = 0x94
INC ecx; DEC edxを実行した。正しくレジスタが加減されているのが分かる。
Unicornとusercorn
Unicornを試したところで、やはりPEやELF等のバイナリを実行したくなる。
しかし、Unicornはframeworkであり、ツールではないので、ヘッダーをパースして、メモリ上に配置したり、スタックを初期化したりといったことまではできない。
unicorn emulate binary · Issue #227 · unicorn-engine/unicorn · GitHub
そこでusercornを使う。usercornは、Unicornを使った User-space system emulatorでバイナリを実行できる。
GitHub - lunixbochs/usercorn: dynamic binary analysis via platform emulation
usercornのインストール
CapstoneとUnicornを入れて、go getする。
go get -u github.com/lunixbochs/usercorn/go/cmd/usercorn
動かしてみる
サンプルファイルを動かしてみる。
$ usercorn bins/x86_64.linux.elf hello with printf: world args (1): 0 @0x607ffa37 = "bins/x86_64.linux.elf" test: hi ceil(1.0) = 1, ceil(1.1) = 2 strcmp: -1, 1, 0 file test 1: success file test 2: success $ usercorn bins/mipsel.linux.elf hello with printf: world args (1): 0 @0x607ffa3d = "bins/mipsel.linux.elf" test: hi memcmp("aa", "aa") == 0 memcmp("aa", "bb") == -1 memcmp("bb", "aa") == 1 strcmp: -1, 1, 0 file test 1: success file test 2: success
感想
unicorn、usercornを用いてなにか便利ツールを作っていきたい。