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 找到可銜接的部份。
由 jserv 發表於 May 12, 2012 10:32 PM
迴響
發表迴響









記住我的資訊?