Committing all the stuff

This commit is contained in:
Clement Rouault
2015-03-11 15:51:00 +01:00
commit 9c80593a97
15 changed files with 2431 additions and 0 deletions
+2
View File
@@ -0,0 +1,2 @@
from native_function import generate_callback_stub, create_function
+200
View File
@@ -0,0 +1,200 @@
import ctypes
import mmap
import platform
class PyObj(ctypes.Structure):
_fields_ = [("ob_refcnt", ctypes.c_size_t),
("ob_type", ctypes.c_void_p)] #must be cast
class PyMmap(PyObj):
_fields_ = [("ob_addr", ctypes.c_size_t), ("ob_size", ctypes.c_size_t)]
# Specific mmap class for code injection
class MyMap(mmap.mmap):
""" A mmap that is never unmapped and that contains the page address """
def __init__(self, *args, **kwarg):
#Get the page address by 'introspection' of the C struct
m = PyMmap.from_address(id(self))
self.addr = m.ob_addr
#Prevent garbage collection (so unmaping) of the page
m.ob_refcnt += 1
@classmethod
def get_map(cls, size):
""" Dispatch to the good mmap implem depending on the current system """
systems = {'windows' : Win32MyMap,
'linux' : UnixMyMap }
x = platform.system().lower()
if x not in systems:
raise ValueError("Unknow system {0}".format(x))
return systems[x].get_map(size)
class Win32MyMap(MyMap):
@classmethod
def get_map(cls, size):
#access = mmap.ACCESS_READ | mmap.ACCESS_WRITE
#return cls(-1, size, access=access)
access = mmap.ACCESS_READ | mmap.ACCESS_WRITE
addr = ctypes.windll.kernel32.VirtualAlloc(0, size, 0x1000, 0x40)
new_map = (ctypes.c_char * size).from_address(addr)
new_map.addr = addr
if new_map.addr == 0:
raise ctypes.WinError()
return new_map
class UnixMyMap(MyMap):
@classmethod
def get_map(cls, size):
prot = mmap.PROT_EXEC | mmap.PROT_WRITE | mmap.PROT_READ
return cls(-1, size, prot=prot)
class CustomAllocator(object):
int_size = {'32bit' : 4, '64bit' : 8}
def __init__(self):
self.maps = []
self.get_new_page(0x1000)
self.names = []
@classmethod
def get_int_size(cls):
bits = platform.architecture()[0]
if bits not in cls.int_size:
raise ValueError("Unknow platform bits <{0}>".format(bits))
return cls.int_size[bits]
def get_new_page(self, size):
self.maps.append(MyMap.get_map(size))
self.cur_offset = 0
self.cur_page_size = size
def reserve_size(self, size):
if size + self.cur_offset > self.cur_page_size:
self.get_new_page((payload_size + 0x1000) & ~0xfff)
addr = self.maps[-1].addr + self.cur_offset
self.cur_offset += size
return addr
def reserve_int(self):
int_size = self.get_int_size()
return self.reserve_size(int_size)
def write_code(self, code):
size = len(code)
if size + self.cur_offset > self.cur_page_size:
self.get_new_page((payload_size + 0x1000) & ~0xfff)
self.maps[-1][self.cur_offset: self.cur_offset + size] = code
addr = self.maps[-1].addr + self.cur_offset
self.cur_offset += size
return addr
allocator = CustomAllocator()
def get_functions():
# Windows only with python27.dll | improve this
import sys
sys.path.append(r"C:\Users\hakril\Documents\Work\Python_Injection")
import windows
PyGILState_Ensure = windows.get_func_addr('python27', 'PyGILState_Ensure')
PyObject_CallObject = windows.get_func_addr('python27', 'PyObject_CallObject')
PyGILState_Release = windows.get_func_addr('python27', 'PyGILState_Release')
return [PyGILState_Ensure, PyObject_CallObject, PyGILState_Release]
def analyse_callback(callback):
if not callable(callback):
raise ValueError("Need a callable object :)")
obj_id = id(callback)
if not hasattr(callback, '_objects'):
raise ValueError("Need a ctypes PyCFuncPtr")
return obj_id
from simple_x86 import *
# For windows 32 bits with stdcall
def generate_stub(callback):
obj_id = analyse_callback(callback)
c_callback = ctypes.c_ulong.from_address(id(callback._objects['0']) + 0xc).value
gstate_save_addr = allocator.reserve_int()
return_addr_save_addr = allocator.reserve_int()
save_ebx = allocator.reserve_int()
save_ecx = allocator.reserve_int()
save_edx = allocator.reserve_int()
save_esi = allocator.reserve_int()
save_edi = allocator.reserve_int()
ensure, objcall, release = get_functions()
### Shellcode ###
code = MultipleInstr()
code += Mov_DX_EBX(save_ebx)
code += Mov_DX_ECX(save_ecx)
code += Mov_DX_EDX(save_edx)
code += Mov_DX_ESI(save_esi)
code += Mov_DX_EDI(save_edi)
code += Mov_EAX_X(ensure)
code += Call_EAX()
code += Mov_DX_EAX(gstate_save_addr)
#Save real return addr (for good argument parsing by the callback)
code += Pop_EAX()
code += Mov_DX_EAX(return_addr_save_addr)
# Set call_real_function to 0 (no call by default)
code += Mov_EAX_X(c_callback)
code += Call_EAX()
for i in range(len(callback.argtypes)):
code += Pop_EBX()
# Restore real return value
code += Mov_EBX_DX(return_addr_save_addr)
code += Push_EBX()
# Save return value
code += Push_EAX()
code += Mov_EBX_DX(gstate_save_addr)
code += Push_EBX()
code += Mov_EAX_X(release)
code += Call_EAX()
# Discard `release` argument
code += Pop_EAX()
# Restore return value
code += Pop_EAX()
code += Mov_EBX_DX(save_ebx)
code += Mov_ECX_DX(save_ecx)
code += Mov_EDX_DX(save_edx)
code += Mov_ESI_DX(save_esi)
code += Mov_EDI_DX(save_edi)
code += Ret()
return code
def generate_callback_stub(callback, types):
func_type = ctypes.CFUNCTYPE(*types)
c_callable = func_type(callback)
stub = generate_stub(c_callable)
stub_addr = allocator.write_code(stub.get_code())
generate_callback_stub.l.append((stub, c_callable))
return stub_addr
generate_callback_stub.l = []
def create_function(code, types):
func_type = ctypes.CFUNCTYPE(*types)
addr = allocator.write_code(code)
return func_type(addr)
+227
View File
@@ -0,0 +1,227 @@
import struct
import sys
this_module = sys.modules[__name__]
def add_instruction(name, instruction):
setattr(this_module, name, instruction)
reg_order = ['EAX', 'ECX', 'EDX', 'EBX', 'ESP', 'EBP', 'ESI', 'EDI']
reg_opcode = {v : format(i, "03b") for i, v in enumerate(reg_order)}
class X86Instruction(object):
mnemo = ""
code = ""
biding = 0
def __init__(self, *bind_values):
if len(bind_values) != self.biding:
raise ValueError("{0} expect {1} values got {2}".format(self.__class__.__name__, self.biding, len(bind_values)))
self.bind_values = bind_values
for i, v in enumerate(bind_values):
if not isinstance(v, (int, long)):
raise ValueError("{0} bindings must be 'int' got '{1}' instead".format(self.__class__.__name__, type(v).__name__))
if not 0 <= v <= 0xffffffff:
raise ValueError("{0} bindings must be between 0 and 0xffffffff".format(self.__class__.__name__))
def get_unbinded_code(self):
return self.code.replace(" ", "").decode('hex')
def get_code(self):
code = self.get_unbinded_code()
for i in range(self.biding):
code = code.replace((str(i + 1) * 8).decode('hex'), struct.pack("<I", self.bind_values[i]))
return code
def get_mnemo(self):
return self.mnemo.format(*(hex(v) for v in self.bind_values))
class Ret(X86Instruction):
mnemo = "ret"
code = "C3"
class Int3(X86Instruction):
mnemo = "int3"
code = "CC"
class SimpleRegInstructionGenerator(object):
name = ""
instruction_bits = ''
class OneBindX86Instruction(X86Instruction):
biding = 1
class Push_X(OneBindX86Instruction):
mnemo = "push {0}"
code = "68 11 11 11 11"
def generate_simple_reg_instruction(instr_cls):
for reg_name, reg_bits in reg_opcode.items():
class SimpleRegInstruction(X86Instruction):
mnemo = "{0} {1}".format(instr_cls.mnemo, reg_name)
code = format(int(instr_cls.instruction_bits + reg_bits, 2), 'x')
SimpleRegInstruction.__name__ = "{0}_{1}".format(instr_cls.name, reg_name)
add_instruction(SimpleRegInstruction.__name__, SimpleRegInstruction)
class Push_Reg(object):
name = 'Push'
mnemo = "push"
instruction_bits = '01010'
generate_simple_reg_instruction(Push_Reg)
class Pop_Reg(object):
name = 'Pop'
mnemo = "pop"
instruction_bits = '01011'
generate_simple_reg_instruction(Pop_Reg)
class Call_Reg(object):
name = 'Call'
mnemo = "call"
instruction_bits = '1111111111010'
generate_simple_reg_instruction(Call_Reg)
def generate_reg_instruction_onebind(instr_cls):
for reg_name, reg_bits in reg_opcode.items():
class OneBindRegInstruction(OneBindX86Instruction):
mnemo = instr_cls.mnemo.format(reg_name)
code = chr(int(instr_cls.instruction_bits + reg_bits, 2)).encode('hex') + '11 11 11 11' # the biding
OneBindRegInstruction.__name__ = instr_cls.name.format(reg_name)
add_instruction(OneBindRegInstruction.__name__, OneBindRegInstruction)
class Mov_Reg_X(object):
name = 'Mov_{0}_X'
mnemo = 'mov {0}, {{0}}'
instruction_bits = '10111'
generate_reg_instruction_onebind(Mov_Reg_X)
def get_immediat_modr_byte(register_bits):
"Generate a modr-reg-r/m indicating a register and an immediat"
str_bits = "11000{0}".format(register_bits)
return chr(int(str_bits, 2)).encode('hex')
def generate_reg_immediat_modr(instr_cls):
for reg_name, reg_bits in reg_opcode.items():
class Reg_MEM_Instruction(OneBindX86Instruction):
mnemo = instr_cls.mnemo.format(reg_name)
code = instr_cls.instruction_bits + get_immediat_modr_byte(reg_bits) + '11 11 11 11' # the biding
Reg_MEM_Instruction.__name__ = instr_cls.name.format(reg_name)
add_instruction(Reg_MEM_Instruction.__name__, Reg_MEM_Instruction)
class Add_Reg_X(object):
name = 'Add_{0}_X'
mnemo = 'add {0}, {{0}}'
instruction_bits = '81'
generate_reg_immediat_modr(Add_Reg_X)
def get_simple_modr_byte(register_bits):
"Generate a simple modr-reg-r/m for a displacement only mode"
str_bits = "00{0}101".format(register_bits)
return chr(int(str_bits, 2)).encode('hex')
def generate_reg_modr(instr_cls):
for reg_name, reg_bits in reg_opcode.items():
class Reg_MEM_Instruction(OneBindX86Instruction):
mnemo = instr_cls.mnemo.format(reg_name)
code = instr_cls.instruction_bits + get_simple_modr_byte(reg_bits) + '11 11 11 11' # the biding
Reg_MEM_Instruction.__name__ = instr_cls.name.format(reg_name)
add_instruction(Reg_MEM_Instruction.__name__, Reg_MEM_Instruction)
class Mov_Reg_DX(object):
name = 'Mov_{0}_DX'
mnemo = 'mov {0}, [{{0}}]'
instruction_bits = '8B'
generate_reg_modr(Mov_Reg_DX)
class Mov_DX_Reg(object):
name = 'Mov_DX_{0}'
mnemo = 'mov [{{0}}], {0}'
instruction_bits = '89'
generate_reg_modr(Mov_DX_Reg)
def generate_reg_reg_modr_byte(reg_dst_bits, reg_src_bits):
return "11{0}{1}".format(reg_src_bits, reg_dst_bits)
def generate_reg_reg_modr(instr_cls):
for reg_src_name, reg_src_bits in reg_opcode.items():
for reg_dst_name, reg_dst_bits in reg_opcode.items():
class Reg_Reg_instruction(X86Instruction):
mnemo = "{0} {1},{2}".format(instr_cls.mnemo, reg_dst_name, reg_src_name)
modr_code = format(int(generate_reg_reg_modr_byte(reg_dst_bits, reg_src_bits) , 2), 'x')
code = instr_cls.instruction_bits + modr_code
Reg_Reg_instruction.__name__ = "{0}_{1}_{2}".format(instr_cls.name, reg_dst_name, reg_src_name)
add_instruction(Reg_Reg_instruction.__name__, Reg_Reg_instruction)
class Test_Reg_Reg(object):
mnemo = "tst"
name = "Tst"
instruction_bits = "85"
generate_reg_reg_modr(Test_Reg_Reg)
#### JUMP ####
class JZ(OneBindX86Instruction):
code = "0F 84 11 11 11 11"
def __init__(self, instr_block):
self.instr_block = instr_block
instr_block_size = len(instr_block.get_code())
super(JZ, self).__init__(instr_block_size)
def get_code(self):
return super(JZ, self).get_code() + self.instr_block.get_code()
class JNZ(OneBindX86Instruction):
code = "0F 85 11 11 11 11"
def __init__(self, instr_block):
self.instr_block = instr_block
instr_block_size = len(instr_block.get_code())
super(JNZ, self).__init__(instr_block_size)
def get_code(self):
return super(JNZ, self).get_code() + self.instr_block.get_code()
class MultipleInstr(object):
def __init__(self):
self.instrs = []
def __iadd__(self, value):
self.instrs.append(value)
return self
def get_code(self):
return "".join(i.get_code() for i in self.instrs)
def get_mnemo(self):
return "\n".join(i.get_mnemo() for i in self.instrs)