2010-07-20 2 views
1

У меня есть простой массив байтов, который я заполнил x86 -программой. Который мне нужно выполнить во время выполнения.позиция содержимого array.array в кучу

""" 
    Produces a simple callable procedure which returns a constant. 
""" 
from array import array 

simple = array('B') 

# mov rax, 0x10 
simple.extend((0x81, 0xc0, 0x10, 0x0, 0x0, 0x0)) 
# ret 
simple.append(0xc3) 

Теперь, чтобы получить этот ход, я должен разгрузить его в область памяти, в моем процессе, который имеет PROT_EXEC флаги. Также нужно знать адрес этой области памяти, чтобы я мог ее назвать. Как я мог сделать то, что я только что описал?

from ctypes import CFUNCTYPE, c_int 

procedure = CFUNCTYPE(c_int)(program.address) 
print "result correct: %r" % (procedure() == 0x10) 
print "result: %r" % procedure() 

Кроме того, это может быть полезно, чтобы сделать это:

program[2] = 15 

print "result correct: %r" % (procedure() == 15) 
print "result: %r" % procedure() 

ответ

1

Я решил эту проблему самостоятельно. Может быть, в любом случае не так много сказать.

Я сделал для этого библиотеку. Это небольшая упаковка вокруг Linux mmap -команда.

Модуль mmap, предоставляемый python, недостаточен. Я не мог получить адрес из объекта. Вместо этого я должен был предоставить свой собственный модуль для этого.

# -*- coding: utf-8 -*- 
from ctypes import (
    pythonapi, c_void_p, c_size_t, c_int, c_uint64, 
    c_byte, cast, POINTER, memmove, string_at, 
) 
import errno 

mmap = pythonapi.mmap 
mmap.restype = c_void_p 
mmap.argtypes = [c_void_p, c_size_t, c_int, c_int, c_int, c_uint64] 

munmap = pythonapi.munmap 
munmap.restype = c_int 
munmap.argtypes = [c_void_p, c_size_t] 

errno_location = pythonapi.__errno_location 
errno_location.restype = POINTER(c_int) 

errormessage = lambda: errno.errorcode[errno_location()[0]] 

PROT_NONE = 0 
PROT_READ = 1 
PROT_WRITE = 2 
PROT_EXEC = 4 

MAP_SHARED = 1 
MAP_PRIVATE = 2 
MAP_ANONYMOUS = 0x20 

class RawData(object): 
    "Allocated with mmap -call, no file handles." 
    def __init__(self, length, prot): 
     flags = MAP_PRIVATE | MAP_ANONYMOUS 
     self.address = mmap(None, length, prot, flags, -1, 0) 
     if 0 == self.address: 
      raise Exception(errormessage()) 
     self.__length = length 
     self.__accessor = cast(self.address, POINTER(c_byte)) 

    def __len__(self): 
     return self.__length 

    def __getitem__(self, key): 
     assert key < len(self) 
     return self.__accessor[key] 

    def __setitem__(self, key, value): 
     assert key < len(self) 
     self.__accessor[key] = value 

    def close(self): 
     "the mapped memory must be freed manually" 
     if 0 != munmap(self.address, len(self)): 
      raise Exception(errormessage()) 

    def poke(self, offset, data): 
     "poke data (from a tuple) into requested offset" 
     for i, byte in enumerate(data): 
      self[offset+i] = byte 

    def upload(self, data, offset=0): 
     "upload the data from a string" 
     data = data.tostring() 
     assert offset+len(data) <= len(self) 
     memmove(self.address+offset, data, len(data)) 

    def tostring(self): 
     return string_at(self.address, len(self)) 

__all__ = [ 
    'PROT_NONE', 
    'PROT_READ', 
    'PROT_WRITE', 
    'PROT_EXEC', 
    'RawData', 
] 

Я также написал библиотеку утилита для прямой порядок байтов целых чисел, дополняющий набор инструментов:

# -*- coding: utf-8 -*- 

TWOPOWER32 = 1 << 32 
TWOPOWER64 = 1 << 64 

TWOPOWER31 = TWOPOWER32 >> 1 
TWOPOWER63 = TWOPOWER64 >> 1 

def uint32(value): 
    assert 0 <= value < TWOPOWER32 
    return (
     value >> 0 & 255, 
     value >> 8 & 255, 
     value >> 16 & 255, 
     value >> 24 & 255 
    ) 

def uint64(value): 
    assert 0 <= value < TWOPOWER64 
    return (
     value >> 0 & 255, 
     value >> 8 & 255, 
     value >> 16 & 255, 
     value >> 24 & 255, 
     value >> 32 & 255, 
     value >> 40 & 255, 
     value >> 48 & 255, 
     value >> 56 & 255 
    ) 

def int32(value): 
    assert -TWOPOWER31 <= value < TWOPOWER31 
    return uint32((TWOPOWER32 + value) & (TWOPOWER32-1)) 

def int64(value): 
    assert -TWOPOWER63 <= value < TWOPOWER63 
    return uint64((TWOPOWER64 + value) & (TWOPOWER64-1)) 

__all__ = ['uint32', 'int32', 'uint64', 'int64'] 

Это просто вещи. Вот пример использования:

from ctypes import CFUNCTYPE, c_int 
from array import array 
#... bunch of imports 

simple = array('B') 

# x86 and x64 machine code (MOV eax, 0x10; RET) 
simple.extend((0x81, 0xc0) + int32(0x10)) 
simple.append(0xc3) 

program = RawData(len(simple), PROT_READ|PROT_WRITE|PROT_EXEC) 
program.upload(simple) 

procedure = CFUNCTYPE(c_int)(program.address) 
print "result:", procedure() 

# alters the first instruction 
program.poke(2, int32(123)) 
print "result:", procedure() 

# transforms that first instruction into (NOP,NOP,NOP,NOP,NOP,NOP) 
program.poke(0, [0x90]*6) 
print "result:", procedure() 

Я думаю, мне понравится. http://hg.boxbase.org/ в конечном итоге будет размещать этот модуль.

Я использую ссылки на команду opcode и инструкции для выбора инструкций. Вот несколько таких ссылок:

Смежные вопросы