Dynamic shellcode analysis with miasm2

(台湾の中国語で書いたから、申し上げありません)

# miasm2 介紹
本文修改自 miasm 官方部落格上的 [Dynamic Shellcode Analysis](http://www.miasm.re/blog/2016/02/12/dynamic_shellcode_analysis.html)。因為官方部落格的 API 版本有點舊，直接拿起來跑會有很多 error ，所以在把文件用繁體中文重寫的同時，把內容也更新成最新的 miasm2 也可以跑的格式。

miasm 是一個可以做動態及靜態分析的框架，支援許多處理器之外，也可以 load Windows DLL 和使用 Python override 指定的 DLL，讓分析二進位可執行檔、病毒、Shellcode、甚至打 CTF 都方便許多。相關的介紹請參考官網。

這篇主要是介紹怎麼使用 miasm 分析一個 shellcode, 在分析的過程中，你會學習到 miasm 內部的架構，和使用的小技巧。Shellcode 請從這裡下載 [dyn_s_shellcodes.zip](http://www.miasm.re/blog/_downloads/dyn_sc_shellcodes.zip) 解壓密碼是 infected 。

在繼續下去之前，請先在 Linux (我用的是 Ubuntu 16.04.3 LTS) 安裝好 Python 2.7, [Z3](https://github.com/Z3Prover/z3), [miasm2](https://github.com/cea-sec/miasm) 安裝的過程還需要其它套件，請參考 [這裡](https://github.com/cea-sec/miasm#user-content-obtaining-miasm) 的說明。這裡列出的步驟僅供參考...

## 安裝 miasm2 和 z3

```bash
# 安裝 z3
git clone https://github.com/Z3Prover/z3.git
cd z3/
python scripts/mk_make.py --python
cd build/
make
sudo -H make install
cd ~
# 安裝 elfesteem
git clone https://github.com/serpilliere/elfesteem.git elfesteem
cd elfesteem/
python setup.py build
sudo -H python setup.py install
cd ~
# 安裝相依套件
sudo -H pip install llvmlite pyparsing
# 安裝 miasm2
git clone https://github.com/cea-sec/miasm.git
cd miasm/
python setup.py build
sudo -H python setup.py install
# 測試安裝結果
cd test/
python test_all.py 
```

理論上測試應該會 all pass. 
安裝好並下載完 shellcode.zip 後，請解開來，再進入下一節。

# Shellcode

這個 shellcode 是傳說中的「純粹 ASCII shellcode」，也就是它在傳輸的過程中，不太會被認為是惡意軟體。直接 cat 就可以看到它的內容:

```
$ cat shellcode1.bin 
PYIIIIIIIIIIIIIIII7QZjAXP0A0AkAAQ2AB2BB0BBABXP8ABuJIbxjKdXPZk9n6lIKgK0enzIBTFklyzKwswpwpLlfTWlOZ9rkJkOYBZcHhXcYoYoKOzUvwEOglwlCrsyNuzY1dRSsBuLGlrTe90npp2QpH1dnrcbwb8ppt6kKf4wQbhtcxGnuLULqUQU2TpyL3rsVyrlidNleNglULPLCFfzPvELsD7wvzztdQqdKJ5 [... 未完]
```

我們可以用 miasm2 直接反組譯它。執行下面這行，會先得到 graph_execflow.dot 。再用 dot 把它轉成 png 檔就可以看到內容了。

```bash
$ python  ~/miasm/example/disasm/full.py -m x86_32 shellcode1.bin --blockwatchdog 1
$ dot -Tpng graph_execflow.dot > graph_execflow.png
```

它看起來會像這樣:
![graph_execflow.png](https://qiita-image-store.s3.amazonaws.com/0/283618/7fb7f5ff-58e0-f0cf-2410-e7c9fa58dbe9.png)

看起來好像沒什麼問題對不對？除了這行 code

```
00000019 XOR        BYTE PTR [ECX+0x30], AL
```

`[ECX+0x30]` 到底會指向哪裡呢。有兩種方法可以知道，第一個是從頭開始做 symbolic execution 就知道 ECX 的值是多少，第二個是使用 DependencyGraph 。本文要介紹的是第一種做法。

# Symbolic Execution

一段程式要做 symbolic execution 需要這幾個步驟:
1. 反組譯
2. 翻譯成 miasm 的中介表示法 (IR)
3. 初始化執行狀態
4. 開始符號執行

這段 code 可以從 0x00 反組譯到 0x1c 然後執行。
因為 API 更新了的關係，直接跑官網上的程式不會動滴~! 這段 code 是參考 `~/miasm/example/disasm/full.py` 改來的。

```python:symbexec.py
# -*- coding: UTF-8 -*-
import sys

from miasm2.analysis.machine import Machine
from miasm2.core.bin_stream import bin_stream_str
from miasm2.ir.symbexec import SymbolicExecutionEngine
from miasm2.core.locationdb import LocationDB    # 改用 LocationDB 囉

# Create a bin_stream from a Python string
bs = bin_stream_str(open(sys.argv[1]).read())

# Get a Miasm x86 32bit machine
machine = Machine("x86_32")
loc_db = LocationDB()

# link the disasm engine to the bin_stream
mdis = machine.dis_engine(bs, loc_db=loc_db)

# Stop disassembler after the XOR 執行到 0x1C 為止
mdis.dont_dis = [0x1C]
# Disassemble one basic block
block = mdis.dis_block(0)

# instanciate an IR analysis
ira = machine.ira(mdis.loc_db)
# Translate asm basic block to an IR basic block
ircfg = ira.new_ircfg()
ira.add_asmblock_to_ircfg(block, ircfg)

# Store IR graph 順便把 IR graph 存下來
out = ircfg.dot()
open('ir_graph.dot', 'w').write(out)

# Instanciate a Symbolic Execution engine with default value for registers
symb = SymbolicExecutionEngine(ira)
# Start execution at address 0
# IRDst represents the label of the next IR basic block to execute
cur_addr = symb.run_at(ircfg, 0)

# 算出 ECX
print 'ECX =', symb.symbols[ira.arch.regs.ECX]
```

執行的結果，會得到 ECX 的值是 EAX + 0xFFFFFFF0 所以 ECX + 0x30 就是 0x00000020 。此外還有一張 IR Graph :

```
$ python symexec.py shellcode1.bin 
ECX = EAX + 0xFFFFFFF0
```

![ir_graph.png](https://qiita-image-store.s3.amazonaws.com/0/283618/f4a40903-92ab-2189-abcf-f2273974b96f.png)

從這邊可以看得出來，這段 code 根本是 self-modifying code 。如果要繼續追下去，就必須開一個 miasm sandbox 然後把程式放進去跑才行。

BTW, 原文在這邊有介紹 miasm 提供給 IDA Pro 的 plugin, 但是因為我沒有 IDA Pro (請支持開放原始碼的 radare2 :D) 所以就不實作了。

# Miasm 砂箱

為了要知道這段自我修改的 code 葫蘆裡賣什麼膏藥，我們必須把它丟進 miasm 砂箱裡執行。不過官網上的程式實在沒辦法跑，還好， `miasm/example/jitter/x86_32.py` 裡的可以正確執行 :ok_hand: 

我們先拿原版來跑一次:

```python:x86_32.py
from argparse import ArgumentParser
from miasm2.jitter.csts import PAGE_READ, PAGE_WRITE
from miasm2.analysis.machine import Machine

from pdb import pm

parser = ArgumentParser(description="x86 32 basic Jitter")
parser.add_argument("filename", help="x86 32 shellcode filename")
parser.add_argument("-j", "--jitter",
                    help="Jitter engine (default is 'gcc')",
                    default="gcc")
args = parser.parse_args()

def code_sentinelle(jitter):
    jitter.run = False
    jitter.pc = 0
    return True


myjit = Machine("x86_32").jitter(args.jitter)
myjit.init_stack()

data = open(args.filename).read()
run_addr = 0x40000000
myjit.vm.add_memory_page(run_addr, PAGE_READ | PAGE_WRITE, data)

myjit.set_trace_log()
myjit.push_uint32_t(0x1337beef)

myjit.add_breakpoint(0x1337beef, code_sentinelle)

myjit.init_run(run_addr)
myjit.continue_run()
```

用 `python x86_32.py shellcode1.bin` 來執行，可以很快看到一個例外拋出:

```
EAX 00000041 EBX 00000000 ECX FFFFFFF0 EDX FFFFFFF0 ESI 00000000 EDI 00000000 ESP 0123FFFC EBP 00000000 EIP 40000000 zf 0 nf 1 of 0 cf 0
40000018 PUSH       EAX
EAX 00000041 EBX 00000000 ECX FFFFFFF0 EDX FFFFFFF0 ESI 00000000 EDI 00000000 ESP 0123FFF8 EBP 00000000 EIP 40000000 zf 0 nf 1 of 0 cf 0
40000019 XOR        BYTE PTR [ECX + 0x30], AL
WARNING: address 0x20 is not mapped in virtual memory:
```

沒錯，就是我們剛剛計算的 ECX + 0x30 = 0x20 因為這個記憶體位置完全不在 VM 配置的位置上，所以就出錯了。我們在 `init_run()` 前面加這兩行，告訴它記憶體從 0x4000000 開始:

```python
myjit.cpu.EAX = run_addr        # step 2
myjit.jit.log_newbloc = True    # step 2
```

開始執行的時候，前幾行是這樣:

```
XOR        BYTE PTR [ECX+0x30], AL
INC        ECX
IMUL       EAX, DWORD PTR [ECX+0x41], 0x51  # 注意這裡
XOR        AL, BYTE PTR [ECX+0x42]
XOR        AL, BYTE PTR [EDX+0x42]
XOR        BYTE PTR [EDX+0x42], AL
INC        ECX
INC        EDX
POP        EAX
PUSH       EAX
CMP        BYTE PTR [ECX+0x42], AL
JNZ        loc_000000004000007D:0x4000007d
->      c_next:loc_0000000040000033:0x40000033  c_to:loc_000000004000007D:0x4000007d
```

第一輪 XOR 以後，它變成這樣了:

```
loc_000000004000001C:0x4000001c
INC        ECX
IMUL       EAX, DWORD PTR [ECX+0x41], 0x10   # 變 0x10 了!
XOR        AL, BYTE PTR [ECX+0x42]
XOR        AL, BYTE PTR [EDX+0x42]
XOR        BYTE PTR [EDX+0x42], AL
INC        ECX
INC        EDX
POP        EAX
PUSH       EAX
CMP        BYTE PTR [ECX+0x42], AL
JNZ        loc_000000004000007D:0x4000007d
->      c_to:loc_000000004000007D:0x4000007d    c_next:loc_0000000040000033:0x40000033
```

到這邊為止，miasm 做了以下的事情:
1. 把 shellcode1.bin 載入後翻譯成 miasm IR 然後執行
2. 執行到自我修改的程式，改動到目前的 block 
3. miasm 清掉內部的 cache
4. 用新的程式重新執行同一個 block

# 繼續執行 shellcode

我們可以繼續執行下去，直接它停在 0x40000033 為止，然後把記憶體 dump 出來，得到解密後的記憶體。收工！不過，下面這一段也在解密...

```
loc_0000000040000040:0x40000040
MOV        ECX, 0x3EB
LODSB
XOR        AL, 0x1C
STOSB
LOOP       loc_0000000040000045:0x40000045
->      c_next:loc_000000004000004B:0x4000004b  c_to:loc_0000000040000045:0x40000045
```

我們可以在 step2 後面幫 0x4000004b 加一個斷點，然後在斷點執行函式，把記憶體 dump 到檔案上:

```python
# A breakpoint callback takes the jitter as first parameter
def dump(jitter):
    # Dump data ad address run_addr with a length of len(data)
    new_data = jitter.vm.get_mem(run_addr, len(data))
    # Save to disk
    open('/tmp/dump.bin', 'wb').write(new_data)
    # Stop execution
    return False

# Register a callback to the breakpoint
myjit.add_breakpoint(0x4000004b, dump)
```

到這邊為止，你得到的是靜態分析的結果。可以看一下 `/tmp/dump.bin` 看看這個 shellcode 到底想做些什麼 :grin: 

完整的程式如下:

```python:x86_32.py
from argparse import ArgumentParser
from miasm2.jitter.csts import PAGE_READ, PAGE_WRITE
from miasm2.analysis.machine import Machine

from pdb import pm

parser = ArgumentParser(description="x86 32 basic Jitter")
parser.add_argument("filename", help="x86 32 shellcode filename")
parser.add_argument("-j", "--jitter",
                    help="Jitter engine (default is 'gcc')",
                    default="gcc")
args = parser.parse_args()

def code_sentinelle(jitter):
    jitter.run = False
    jitter.pc = 0
    return True


myjit = Machine("x86_32").jitter(args.jitter)
myjit.init_stack()

data = open(args.filename).read()
run_addr = 0x40000000
myjit.vm.add_memory_page(run_addr, PAGE_READ | PAGE_WRITE, data)

myjit.set_trace_log()
myjit.push_uint32_t(0x1337beef)

myjit.add_breakpoint(0x1337beef, code_sentinelle)

myjit.cpu.EAX = run_addr        # step 2
myjit.jit.log_newbloc = True    # step 2

def dump(jitter):               # Step 3
    # Dump data ad address run_addr with a length of len(data)
    new_data = jitter.vm.get_mem(run_addr, len(data))
    # Save to disk
    open('dump.bin', 'wb').write(new_data)
    # Stop execution
    return False

# Register a callback to the breakpoint
myjit.add_breakpoint(0x4000004b, dump)

myjit.init_run(run_addr)
myjit.continue_run()
```

# 動態執行

我們繼續看下一個 block (要先把 `dump` 的斷點拿掉才看得到):

```
loc_0000000040000058:0x40000058
POP        ESI
PUSH       EBP
MOV        EBP, ESP
PUSH       0x6E6F
PUSH       0x6D6C7275
PUSH       ESP
PUSH       0xEC0E4E8E
PUSH       0x6E2BCA17
CALL       loc_00000000400002CA:0x400002ca
->      c_next:loc_0000000040000076:0x40000076
```

嗯，看起來好像在呼叫什麼，對不對？

```python
>>> "6D6C7275".decode('hex')[::-1] + "6E6F".decode('hex')[::-1]
'urlmon'
```

然後 0x400002d9 那一段，有一個 

```
loc_00000000400002D9:0x400002d9
PUSHAD
XOR        EAX, EAX
MOV        EDX, DWORD PTR FS:[EAX+0x30]
```

這邊 [EAX+0x30] 會造成 VM 記憶體錯誤，這時候該怎麼辦呢？因為 miasm 沒有模擬 `FS:` 區段，所以只剩下兩種選擇:
1. 插入假的 `PEB` 宣告 0x30 是合法的記憶體
2. 開啟 miasm 的區段支援，然後給 `FS:` 一塊合法的基底位置，並在那邊放 `PEB`.

很複雜對不對？ miasm 有提供「最基本的 Windows 結構模擬」 `miasm2.os_dep.win_api_x86_32_seh.py` 。因為這邊實在有點複雜，建議大家去看 `miasm/example/jitter/sandbox_pe_x86_32.py` 的原始碼。

一般來說， shellcode 會需要一個宿主 `explorer.exe` ，和一個被它跑起來的程式 `calc.exe` 。如果你沒有這兩個 EXE 檔也沒關係，我們可以用 `elfesteem` 生一個 EXE 檔出來:

```python:gen_exe.py
import sys
from elfesteem import pe_init

# Get the shellcode
data = open(sys.argv[1]).read()
# Generate a PE
pe = pe_init.PE(wsize=32)
# Add a ".text" section containing the shellcode to the PE
s_text = pe.SHList.add_section(name=".text", addr=0x1000, data=data)
# Set the entrypoint to the shellcode's address
pe.Opthdr.AddressOfEntryPoint = s_text.addr
# Write the PE to "sc_pe.py"
open('sc_pe.exe', 'w').write(str(pe))
```

我們後面會使用 miasm 內建的 `box_upx.exe` ，因為如果你在有防毒軟體的電腦裡，執行 `python gen_exe.py shellcode1.bin` 的話，防毒軟體馬上就會叫了 :yum:

我們先把 `miasm/example/jitter/sandbox_pe_x86_32.py` 複製一份，叫它 `run_sc.py` 然後要做很多修改...

# run_sc.py

可以先用 `python run_sc.py -h` 看一下參數說明。原始的 `sandbox_pe_x86_32.py` 只有 17 行，我們需要先幫它加一些要載入的 DLL (請從 WinXP 裡複製這些 DLL 然後放到 `./win_dll` 目錄下!) 

```bash
$ ls win_dll/
advapi32.dll  iertutil.dll  msvcirt.dll  ole32.dll     psapi.dll   shlwapi.dll  user32.dll  ws2help.dll
gdi32.dll     kernel32.dll  ntdll.dll    oleaut32.dll  rpcrt4.dll  urlmon.dll   ws2_32.dll
```

然後指定開始執行的位置，以及加上要載入的 shellcode 的檔名...

```python:run_sc.py
from pdb import pm
from miasm2.jitter.csts import PAGE_READ, PAGE_WRITE
from miasm2.analysis.sandbox import Sandbox_Win_x86_32

# Insert here user defined methods
# Sanbox.ALL_IMP_DLL
ALL_IMP_DLL = ["ntdll.dll", "kernel32.dll", "user32.dll",
               "ole32.dll", "urlmon.dll",
               "ws2_32.dll", 'advapi32.dll', "psapi.dll",
               ]

# Parse arguments
parser = Sandbox_Win_x86_32.parser(description="PE sandboxer")
parser.add_argument("filename", help="PE Filename")

# Get the shellcode from the second argument
parser.add_argument("shellcode", help="shellcode file")

options = parser.parse_args()

# Create sandbox
sb = Sandbox_Win_x86_32(options.filename, options, globals())

# Load the shellcode
data = open(options.shellcode).read()
run_addr = 0x40000000
sb.jitter.vm.add_memory_page(run_addr, PAGE_READ | PAGE_WRITE, data)
sb.jitter.cpu.EAX = run_addr

# Run
sb.run(run_addr)

assert(sb.jitter.run is False)
```

用這行來執行:

```
python -i run_sc.py -b -s -l -y ~/miasm/example/samples/box_upx.exe shellcode1.bin
```

你會看到它載入一堆 DLL (這邊的結果和官網有一點點不同，但是不影響實驗):

```
[INFO]: Loading module 'ntdll.dll'
[INFO]: Loading module 'kernel32.dll'
[INFO]: Loading module 'user32.dll'
[INFO]: Loading module 'ole32.dll'
[INFO]: Loading module 'urlmon.dll'
[INFO]: Loading module 'ws2_32.dll'
[INFO]: Loading module 'advapi32.dll'
[INFO]: Loading module 'psapi.dll'
[WARNING]: Create dummy entry for 'msvcrt.dll'
[WARNING]: Create dummy entry for 'iertutil.dll'
[WARNING]: Create dummy entry for 'oleaut32.dll'
[WARNING]: Create dummy entry for 'rpcrt4.dll'
[WARNING]: Create dummy entry for 'shlwapi.dll'
[WARNING]: Create dummy entry for 'gdi32.dll'
[WARNING]: Create dummy entry for 'ws2help.dll'
INFO : Add module 400000 'box_upx.exe'
INFO : Add module 45180000 'urlmon.dll'
INFO : Add module 7c800000 'kernel32.dll'
INFO : Add module 77da0000 'advapi32.dll'
INFO : Add module 7c910000 'ntdll.dll'
INFO : Add module 774a0000 'ole32.dll'
INFO : Add module 719f0000 'ws2_32.dll'
INFO : Add module 76ba0000 'psapi.dll'
INFO : Add module 7e390000 'user32.dll'
```

miasm 會自動載入放在 `./win_dll` 目錄裡的 DLL, 然後沒有的 DLL 它會建一個 dummy entry, 然後載入 `box_upx.exe` ，把 `shellcode1.bin` 載入 0x40000000 然後執行。

觀察一下 `run_sc.py` 的輸出，我們可以發現 function and DLL hashes 的呼叫慣例:

```
PUSH       EBP
MOV        EBP, ESP
PUSH       0x6E6F
PUSH       0x6D6C7275
PUSH       ESP
PUSH       0xEC0E4E8E
PUSH       0x6E2BCA17
CALL       loc_400002ca
->	c_next:loc_40000076 
loc_400002ca
POP        ECX
CALL       loc_400002d9
->	c_next:loc_400002d0 
loc_400002d9
PUSHAD     
XOR        EAX, EAX
MOV        EDX, DWORD PTR FS:[EAX + 0x30]
MOV        EDX, DWORD PTR [EDX + 0xC]
MOV        EDX, DWORD PTR [EDX + 0x14]
MOV        ESI, DWORD PTR [EDX + 0x28]

```

然後在 0x400002d9 那邊匯入 `PEB` 、搜尋被呼叫的函式等等。
因為 miasm 會在每次載入 DLL 的時候，自動把它的 export table 裡的函式都記錄下載，並加上斷點，所以每次只要呼叫 DLL 的函式， miasm 都會去找找看有沒有 `ModuleName_ModuleFunction` 這種格式的函式，有的話就去執行。這樣我們就可以很方便地用 Python 模擬 Windows 函式、或是 override 掉原來的函式。比方說: 

```python
def msvcrt_rand(jitter):
    ret_ad, _ = jitter.func_args_cdecl(0)
    jitter.func_ret_stdcall(ret_ad, 0x666)
```

這個可以 override 掉原來的 `rand()` 讓每次傳回的「亂數」都是 0x666 。實作的細節請參考 `miasm2.os_dep.win_api_x86_32` 。

# Unknown API?

執行下去，你會發現程式壞在這裡: 

```python
ValueError: ('unknown api', '0x769a42f3L', "'ole32_CoInitializeEx'")
```

往上看一下，我們可以發現 0x400002ca 是負責搜尋 hash 的

```
loc_40000083
PUSH       EAX
PUSH       0x6
PUSH       0x0
PUSH       0xDC8061B
PUSH       0x2E773AE6
CALL       loc_400002ca
->	c_next:loc_40000097 
```

我們可以在 `sb.run()` 前面加上這行，讓它看起來漂亮一點:

```python
# Links address 0x400002ca to the label name resolve_by_hash
sb.jitter.ir_arch.loc_db.add_location('resolve_by_hash', 0x400002ca)
```

以後它的輸出，就會變成

```
PUSH       EAX
PUSH       0x6
PUSH       0x0
PUSH       0xDC8061B
PUSH       0x2E773AE6
CALL       resolve_by_hash
->	c_next:loc_40000097 
```

然後我們要接著定義 `ole32_CoInitializeEx` 函式。查一下 MSDN, 你可以知道這個函式用來啟始 COM 物件，然後 OK 就傳回 1 。那我們就來模擬一下...

```python
def ole32_CoInitializeEx(jitter):
    ret_ad, args = jitter.func_args_stdcall(["pvReserved", "dwCoInit"])
    jitter.func_ret_stdcall(ret_ad, 1)
```

要注意這個要放在全域變數的範圍內，也就是說，要放在下面這行之前:

```python
# Create sandbox
sb = Sandbox_Win_x86_32(options.filename, options, globals())
```

模擬了一個、還有一個... 所以我們要把所有沒定義的函式都手動補上。官網的原文裡，有提到 shellcode 有呼叫 `kernel32_GetVersion` 如果傳回來的版本不同，會有不同的反應，大家可以自己試試。

補齊函式的過程，可以看一下官網原文，或是直接看 code 應該就很清楚了。有趣的是這個 shellcode 會先去這個地方下載 payload:

```
[INFO]: ntdll_swprintf(dst=0x20000000, pfmt=0x13ffc8) ret addr: 0x40000184
FMT: '%S'
OUT: 'hXXp://efyjlXXXXXXXXXXXXXXXXXXin.net/fXXXXXXXXXXXXXXX8867XXXX5'
```

最後我們得到的程式像這樣:

```python:run_sc.py
from pdb import pm
from miasm2.jitter.csts import PAGE_READ, PAGE_WRITE
from miasm2.analysis.sandbox import Sandbox_Win_x86_32

# Insert here user defined methods
# Sanbox.ALL_IMP_DLL
ALL_IMP_DLL = ["ntdll.dll", "kernel32.dll", "user32.dll",
               "ole32.dll", "urlmon.dll",
               "ws2_32.dll", 'advapi32.dll', "psapi.dll",
               ]

def ole32_CoInitializeEx(jitter):
    ret_ad, args = jitter.func_args_stdcall(["pvReserved", "dwCoInit"])
    jitter.func_ret_stdcall(ret_ad, 1)

def ntdll_swprintf(jitter):
    ret_ad, args = jitter.func_args_stdcall(["dst", "pfmt"])
    fmt = jitter.get_str_unic(args.pfmt)
    print "FMT:", repr(fmt)
    if fmt == "%S":
        psrc = jitter.pop_uint32_t()
        src = jitter.get_str_ansi(psrc)
        out = "%s" % src
    else:
        raise RuntimeError("unknown fmt %s" % fmt)
    print "OUT:", repr(out)
    jitter.set_str_unic(args.dst, out)

    # Returns the string len in wchar unit
    jitter.func_ret_stdcall(ret_ad, len(out)/2)

def urlmon_URLDownloadToCacheFileW(jitter):
    ret_ad, args = jitter.func_args_stdcall(["lpunkcaller",
                                             "szurl",
                                             "szfilename",
                                             "ccfilename",
                                             "reserved",
                                             "pbsc"])
    url = jitter.get_str_unic(args.szurl)
    print "URL:", url
    jitter.set_str_unic(args.szfilename, "toto")
    jitter.func_ret_stdcall(ret_ad, 0)

# Parse arguments
parser = Sandbox_Win_x86_32.parser(description="PE sandboxer")
parser.add_argument("filename", help="PE Filename")

# Get the shellcode from the second argument
parser.add_argument("shellcode", help="shellcode file")

options = parser.parse_args()

# Create sandbox
sb = Sandbox_Win_x86_32(options.filename, options, globals())

# Load the shellcode
data = open(options.shellcode).read()
run_addr = 0x40000000
sb.jitter.vm.add_memory_page(run_addr, PAGE_READ | PAGE_WRITE, data)
sb.jitter.cpu.EAX = run_addr

def stop_exec(jitter):
    return False

# sb.jitter.add_breakpoint(0x40000076, stop_exec)

# Links address 0x400002ca to the label name resolve_by_hash
sb.jitter.ir_arch.loc_db.add_location('resolve_by_hash', 0x400002ca)

# Run
sb.run(run_addr)

assert(sb.jitter.run is False)
```

使用 `ipython -i run_sc.py -- -b -s -l -y ~/miasm/example/samples/box_upx.exe shellcode1.bin` 執行之後 (有沒有發現和 `python` 有一點點不一樣?) 我們會看到最後這個錯誤訊息:

```python
ValueError: ('unknown api', '0x7c802332L', "'kernel32_CreateProcessW'")
```

咦，它要執行的是什麼呢? (笑)

```python
In [1]: sb.jitter.get_str_unic(sb.jitter.get_stack_arg(1))
Out[1]: 'toto'
```

它要執行的是剛剛下載下來的 payload 呢!

# 結語

這篇並不是 [官網原文](http://www.miasm.re/blog/2016/02/12/dynamic_shellcode_analysis.html) 的完整翻譯，建議大家有空還是去看一下原文。如果對 miasm2 有興趣的話，也可以參考他們在今年 [BlackHat US](https://www.blackhat.com/us-18/briefings.html#miasm-reverse-engineering-framework) 的投影片喲!