在 Python 中執行 shellcode
其實這不是什麼新技巧,只是剛好最近研究 [
PyPy],思考到若干低階處理的細節,就順道把這個在 Python 中執行 shellcode 的技巧分享出來,本文的實驗平台為 GNU/Linux x86/32-bit,採用 CPython 2.7.3,目的為實踐前文 [
SM 版 Hello World] 中具備自我修改能力的程式 (self-modifying code)。
借助 [
ctypes] 套件,我們很容易就能打造以下骨幹程式碼:
from ctypes import *
libc = CDLL('libc.so.6')
mprotect = libc.mprotect
getpagesize = libc.getpagesize
codes = (c_ubyte * 32)(
)
p = addressof(codes) & ~(getpagesize() - 1)
mprotect(p, getpagesize(), PROT_READ | PROT_WRITE | PROT_EXEC)
my_func = CFUNCTYPE(c_int, c_int, c_int)(addressof(codes))
在此,我們呼叫了 POSIX mprotect(),使得目標機械碼所在的 page 標注為可讀 + 可寫 + 可執行。接著,就來填補上方機械碼即可。以簡單的 C 語言加法處理函式為例,觀察其機械碼:
$ echo "int add(int a, int b) { return a + b; }" > add.c
$ gcc -Os -c add.c
$ objdump -d add.o
其中 .text 為:
00000000 <add>:
0: 55 push %ebp
1: 89 e5 mov %esp,%ebp
3: 8b 45 0c mov 0xc(%ebp),%eax
6: 03 45 08 add 0x8(%ebp),%eax
9: 5d pop %ebp
a: c3 ret
只差臨門一腳了,以下是整合好的 Python 程式碼:(smc.py)
from ctypes import *
libc = CDLL('libc.so.6')
mprotect = libc.mprotect
getpagesize = libc.getpagesize
PROT_READ = 0x1
PROT_WRITE = 0x2
PROT_EXEC = 0x4
codes = (c_ubyte * 32)(
0x55, 0x89, 0xe5, 0x8b, 0x45, 0x0c, 0x8b, 0x55, 0x08, 0x01, 0xd0, 0x5d, 0xc3 )
p = addressof(codes) & ~(getpagesize() - 1)
mprotect(p, getpagesize(), PROT_READ | PROT_WRITE | PROT_EXEC)
add_func = CFUNCTYPE(c_int, c_int, c_int)(addressof(codes))
print "add(99, 1) = %d" % add_func(99, 1)
咱們來測試看看:
$ python smc.py
add(99, 1) = 100
果然符合預期,呼叫了 add_func 所指向的 shellcode,得到 99 + 1 = 100 的執行結果。不過, 這雖然驗證了概念,但距離完整的 shellcode 還有些一段路要走,得借助於 [
qsort 與 shellcode] 一文提到的技巧,並在 CPython 找到可銜接的部份。
由 jserv 發表於 May 12, 2012 10:32 PM