May 12, 2012
在 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)( # (omitted) machine code ) 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, # push %ebp 0x89, 0xe5, # mov %esp,%ebp 0x8b, 0x45, 0x0c, # mov 0xc(%ebp),%eax 0x8b, 0x55, 0x08, # mov 0x8(%ebp),%edx 0x01, 0xd0, # add %edx,%eax 0x5d, # pop %ebp 0xc3 # ret ) 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 找到可銜接的部份。
Posted by jserv at 10:32 PM
| Comments (0)