Help us understand the problem. What is going on with this article?

Dynamic shellcode analysis with miasm2

More than 1 year has passed since last update.

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

miasm2 介紹

本文修改自 miasm 官方部落格上的 Dynamic Shellcode Analysis。因為官方部落格的 API 版本有點舊,直接拿起來跑會有很多 error ,所以在把文件用繁體中文重寫的同時,把內容也更新成最新的 miasm2 也可以跑的格式。

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

這篇主要是介紹怎麼使用 miasm 分析一個 shellcode, 在分析的過程中,你會學習到 miasm 內部的架構,和使用的小技巧。Shellcode 請從這裡下載 dyn_s_shellcodes.zip 解壓密碼是 infected 。

在繼續下去之前,請先在 Linux (我用的是 Ubuntu 16.04.3 LTS) 安裝好 Python 2.7, Z3, miasm2 安裝的過程還需要其它套件,請參考 這裡 的說明。這裡列出的步驟僅供參考...

安裝 miasm2 和 z3

# 安裝 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 檔就可以看到內容了。

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

它看起來會像這樣:
graph_execflow.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 改來的。

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

從這邊可以看得出來,這段 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:

我們先拿原版來跑一次:

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 開始:

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 到檔案上:

# 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:

完整的程式如下:

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

嗯,看起來好像在呼叫什麼,對不對?

>>> "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 檔出來:

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 目錄下!)

$ 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 的檔名...

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 掉原來的函式。比方說:

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?

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

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() 前面加上這行,讓它看起來漂亮一點:

# 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 。那我們就來模擬一下...

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

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

# 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'

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

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 有一點點不一樣?) 我們會看到最後這個錯誤訊息:

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

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

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

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

結語

這篇並不是 官網原文 的完整翻譯,建議大家有空還是去看一下原文。如果對 miasm2 有興趣的話,也可以參考他們在今年 BlackHat US 的投影片喲!

Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
Comments
Sign up for free and join this conversation.
If you already have a Qiita account
Why do not you register as a user and use Qiita more conveniently?
You need to log in to use this function. Qiita can be used more conveniently after logging in.
You seem to be reading articles frequently this month. Qiita can be used more conveniently after logging in.
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away