Big commit <3

This commit is contained in:
Clement Rouault
2015-07-10 16:50:46 +02:00
parent e1bee9f8e8
commit ebb23fd275
16 changed files with 1133 additions and 890 deletions
+1 -1
View File
@@ -1 +1 @@
from native_function import generate_callback_stub, create_function
from .native_function import generate_callback_stub, create_function
+100 -97
View File
@@ -5,6 +5,9 @@ import platform
import windows
import windows.k32testing as k32api
from . import simple_x86 as x86
#from . import simple_x64 as x64
class PyObj(ctypes.Structure):
_fields_ = [("ob_refcnt", ctypes.c_size_t),
("ob_type", ctypes.c_void_p)] #must be cast
@@ -116,7 +119,7 @@ def analyse_callback(callback):
# For windows 32 bits with stdcall
def generate_stub_32(callback):
from simple_x86 import *
allocator = windows.current_process.allocator
obj_id = analyse_callback(callback)
@@ -124,65 +127,64 @@ def generate_stub_32(callback):
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()
save_ebx = x86.create_displacement(disp=allocator.reserve_int())
save_ecx = x86.create_displacement(disp=allocator.reserve_int())
save_edx = x86.create_displacement(disp=allocator.reserve_int())
save_esi = x86.create_displacement(disp=allocator.reserve_int())
save_edi = x86.create_displacement(disp=allocator.reserve_int())
ensure, objcall, release = get_functions()
code = x86.MultipleInstr()
### 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)
code += x86.Mov(save_ebx, 'EBX')
code += x86.Mov(save_ecx, 'ECX')
code += x86.Mov(save_edx, 'EDX')
code += x86.Mov(save_esi, 'ESI')
code += x86.Mov(save_edi, 'EDI')
code += x86.Mov('EAX', ensure)
code += x86.Call('EAX')
code += x86.Mov(gstate_save_addr, 'EAX')
#Save real return addr (for good argument parsing by the callback)
code += Pop_EAX()
code += Mov_DX_EAX(return_addr_save_addr)
code += x86.Pop('EAX')
code += x86.Mov(return_addr_save_addr, 'EAX')
# Set call_real_function to 0 (no call by default)
code += Mov_EAX_X(c_callback)
code += Call_EAX()
code += x86.Mov('EAX', c_callback)
code += x86.Call('EAX')
# Restore real return value
code += Mov_EBX_DX(return_addr_save_addr)
code += Push_EBX()
code += x86.Mov('EBX', return_addr_save_addr)
code += x86.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()
code += x86.Push('EAX')
code += x86.Mov('EBX', gstate_save_addr)
code += x86.Push('EBX')
code += x86.Mov('EAX', release)
code += x86.Call('EAX')
# Discard `release` argument
code += Pop_EAX()
code += x86.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()
code += x86.Pop('EAX')
code += x86.Mov('EBX', save_ebx)
code += x86.Mov('ECX', save_ecx)
code += x86.Mov('EDX', save_edx)
code += x86.Mov('ESI', save_esi)
code += x86.Mov('EDI', save_edi)
code += x86.Ret()
return code
# For windows 32 bits with stdcall
def generate_stub_64(callback):
import simple_x64 as x64
from simple_x64 import *
allocator = windows.current_process.allocator
obj_id = analyse_callback(callback)
@@ -191,8 +193,9 @@ def generate_stub_64(callback):
c_callback = ctypes.c_ulong.from_address(id(callback._objects['0']) + 3 * ctypes.sizeof(ctypes.c_void_p)).value
register_to_save = ("RBX", "RCX", "RDX", "RSI", "RDI", "R8", "R9", "R10", "R11", "R12", "R13", "R14", "R15")
push_all_save_register = MultipleInstr([getattr(x64, "Push_" + reg)() for reg in register_to_save])
pop_all_save_register = MultipleInstr([getattr(x64, "Pop_" + reg)() for reg in reversed(register_to_save)])
push_all_save_register = x64.MultipleInstr([x64.Push(reg) for reg in register_to_save])
pop_all_save_register = x64.MultipleInstr([x64.Pop(reg) for reg in register_to_save])
# Reserve parallel `stack`
save_register_space = allocator.reserve_int(len(register_to_save) + 1)
save_register_space += REG_LEN # The + 1 is for the second-stack xchg
@@ -208,94 +211,94 @@ def generate_stub_64(callback):
save_r9 = save_register_space_end - REG_LEN - (REG_LEN * 6)
gstate_save_addr = allocator.reserve_int()
return_addr_save_addr = allocator.reserve_int()
return_value_save_addr = allocator.reserve_int()
Reserve_space_for_call = MultipleInstr([Push_RDI()] * 4)
Clean_space_for_call = MultipleInstr([Pop_RDI()] * 4)
Do_stack_alignement = MultipleInstr([Push_RDI()] * 1)
Remove_stack_alignement = MultipleInstr([Pop_RDI()] * 1)
gstate_save_addr = create_displacement(disp=allocator.reserve_int())
return_addr_save_addr = create_displacement(disp=allocator.reserve_int())
return_value_save_addr = create_displacement(disp=allocator.reserve_int())
Reserve_space_for_call = x64.MultipleInstr([Push('RDI')] * 4)
Clean_space_for_call = x64.MultipleInstr([Pop('RDI')] * 4)
Do_stack_alignement = MultipleInstr([x64.Push('RDI')] * 1)
Remove_stack_alignement = MultipleInstr([x64.Pop('RDI')] * 1)
ensure, objcall, release = get_functions()
### Shellcode ###
code = MultipleInstr()
code += Mov_RAX_X(save_register_space_end)
code += x64.Mov('RAX', save_register_space_end)
# A lazy working xchg RSP <-> RAX
code += Push_RAX()
code += Push_RSP()
code += Pop_RAX()
code += Pop_RSP()
code += x64.Push('RAX')
code += x64.Push('RSP')
code += x64.Pop('RAX')
code += x64.Pop('RSP')
code += push_all_save_register
# Re-set RSP to its real value
code += Push_RAX()
code += Pop_RSP()
code += x64.Push('RAX')
code += x64.Pop('RSP')
code += Pop_RAX() # Remove the Push_RAX of lazy xchg
code += x64.Pop('RAX') # Remove the Push_RAX of lazy xchg
# GOOO
code += Mov_RAX_X(ensure)
code += x64.Mov('RAX', ensure)
code += Reserve_space_for_call
code += Do_stack_alignement
code += Call_RAX()
code += x64.Call('RAX')
code += Remove_stack_alignement
code += Clean_space_for_call
code += Mov_DX_RAX(gstate_save_addr)
code += x64.Mov(gstate_save_addr, 'RAX')
#Save real return addr (for good argument parsing by the callback)
code += Pop_RAX()
code += Mov_DX_RAX(return_addr_save_addr)
code += x64.Pop('RAX')
code += x64.Mov(return_addr_save_addr, 'RAX')
# Restore parameters for real function call
code += Mov_RAX_X(save_rcx)
code += Mov_RCX_DRAX()
code += Mov_RAX_X(save_rdx)
code += Mov_RDX_DRAX()
code += Mov_RAX_X(save_r8)
code += Mov_R8_DRAX()
code += Mov_RAX_X(save_r9)
code += Mov_R9_DRAX()
code += x64.Mov('RAX', save_rcx)
code += x64.Mov('RCX', x64.create_displacement('RAX'))
code += x64.Mov('RAX', save_rdx)
code += x64.Mov('RDX', x64.create_displacement('RAX'))
code += x64.Mov('RAX', save_r8)
code += x64.Mov('R9', x64.create_displacement('RAX'))
code += x64.Mov('RAX', save_r9)
code += x64.Mov('R8', x64.create_displacement('RAX'))
# Call python code
code += Mov_RAX_X(c_callback)
code += x64.Mov('RAX', c_callback)
code += Reserve_space_for_call
code += Call_RAX() # no need for stack alignement here as we poped the return addr
code += x64.Call('RAX') # no need for stack alignement here as we poped the return addr
code += Clean_space_for_call
# Save return value
code += Mov_DX_RAX(return_value_save_addr)
code += Mov_RAX_DX(return_addr_save_addr)
code += x64.Mov(return_value_save_addr, 'RAX')
# Repush real return value
code += Push_RAX()
code += Mov_RAX_DX(gstate_save_addr)
code += Push_RAX()
code += Pop_RCX()
code += Mov_RAX_X(release)
code += x64.Mov('RAX', return_addr_save_addr)
code += x64.Push('RAX')
# Call release(gstate_save)
code += x64.Mov_RAX_DX('RAX', gstate_save_addr)
code += x64.Push('RAX')
code += x64.Pop('RCX')
code += x64.Mov('RAX', release)
code += Reserve_space_for_call
code += Do_stack_alignement
code += Call_RAX()
code += x64.Call('RAX')
code += Remove_stack_alignement
code += Clean_space_for_call
# Restore registers
code += Mov_RAX_X(save_register_space)
code += x64.Mov('RAX', save_register_space)
# A lazy working xchg RSP <-> RAX
code += Push_RAX()
code += Push_RSP()
code += Pop_RAX()
code += Pop_RSP()
code += x64.Push('RAX')
code += x64.Push('RSP')
code += x64.Pop('RAX')
code += x64.Pop('RSP')
code += pop_all_save_register
# Re-set RSP to its real value
code += Push_RAX()
code += Pop_RSP()
code += Pop_RAX() # Remove the Push_RAX of lazy xchg
code += x64.Push('RAX')
code += x64.Pop('RSP')
code += x64.Pop('RAX') # Remove the Push_RAX of lazy xchg
# Restore return value
code += Mov_RAX_DX(return_value_save_addr)
code += Ret()
code += x64.Mov_RAX_DX('RAX', return_value_save_addr)
code += x64.Ret()
return code
+340 -239
View File
@@ -1,254 +1,355 @@
# You are going to see the most shameful code ever !
# Yes this a a copy of x86 :D
import collections
import struct
import sys
import codecs
from .simple_x86 import MultipleInstr
# This code should really be rewritten..
this_module = sys.modules[__name__]
generated_instruction = []
long = int
def add_instruction(name, instruction):
generated_instruction.append((name, instruction))
setattr(this_module, name, instruction)
def generate_module_doc():
doc_lines = ["Here is the list of instruction in the modules:\n\n"]
for name, instruction in generated_instruction:
doc_lines.append(" | {0} -> <{1}>".format(name, instruction.mnemo))
class BitArray(object):
def __init__(self, size, bits):
self.size = size
if len(bits) > size:
raise ValueError("size > len(bits)")
bits_list = []
for bit in bits:
x = int(bit)
if x not in [0, 1]:
raise ValueError("Not expected bits value {0}".format(x))
bits_list.append(x)
self.array = bits_list
if size > len(self.array):
self.array = ([0] * (size - len(self.array))) + self.array
def dump(self):
res = []
for i in range(self.size // 8):
c = 0
for x in (self.array[i * 8: (i + 1) * 8]):
c = (c << 1) + x
res.append(c)
return bytearray((res))
this_module.__doc__ = "\n".join(doc_lines)
def decode_hex(s):
return codecs.decode(s.encode(), "hex").decode()
def encode_hex(s):
return codecs.encode(s.encode(), "hex").decode()
reg_order = ['EAX', 'ECX', 'EDX', 'EBX', 'ESP', 'EBP', 'ESI', 'EDI']
reg_opcode = {v : format(i, "03b") for i, v in enumerate(reg_order)}
def __getitem__(self, slice):
return self.array[slice]
def __setitem__(self, slice, value):
self.array[slice] = value
return True
def __repr__(self):
return repr(self.array)
def __add__(self, other):
if not isinstance(other, BitArray):
return NotImplemented
return BitArray(self.size + other.size, self.array + other.array)
def __or__(self, other):
if not isinstance(other, BitArray):
return NotImplemented
if self.size != other.size:
raise ValueError("OR ON DIFF SIZE")
new_array = [(x | y) for x,y in zip(self.array, other.array)]
return BitArray(self.size, new_array)
def to_int(self):
return int("".join([str(i) for i in self.array]), 2)
@classmethod
def from_string(cls):
l = []
for c in bytearray(reversed(str_base)):
for i in range(8):
l.append(c & 1)
c = c >> 1
self.array = l
@classmethod
def from_int(cls, size, x):
if x < 0:
x = x & ((2 ** size) - 1)
return cls(size, bin(x)[2:])
# Rules: bytes only !!!!
reg_order = ['RAX', 'RCX', 'RDX', 'RBX', 'RSP', 'RBP', 'RSI', 'RDI']
reg_opcode = {v : format(i, "03b") for i, v in enumerate(reg_order)}
new_reg_order = ['R8', 'R9', 'R10', 'R11', 'R12', 'R13', 'R14', 'R15']
new_reg_opcode = {v : format(i, "03b") for i, v in enumerate(new_reg_order)}
all_regs = dict(reg_opcode)
all_regs.update(new_reg_opcode)
bin_h48 = bin(0x48)[2:]
class X64Instruction(object):
mnemo = ""
code = ""
biding = 0
x64_regs = reg_order + new_reg_order
mem_access = collections.namedtuple('mem_access', ['base', 'index', 'squale', 'disp'])
def create_displacement(base=None, index=None, squale=None, disp=0):
return mem_access(base, index, squale, disp)
class X64RegisterSelector(object):
reg_opcode = {v : BitArray.from_int(size=3, x=i) for i, v in enumerate(reg_order)}
new_reg_opcode = {v : BitArray.from_int(size=3, x=i) for i, v in enumerate(new_reg_order)}
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 <= 0xffffffffffffffff:
raise ValueError("{0} bindings must be between 0 and 0xffffffffffffffff".format(self.__class__.__name__))
def get_unbinded_code(self):
return decode_hex(self.code.replace(" ", ""))
def accept_arg(self, previous, args):
x = args[0]
try:
return (1, self.reg_opcode[x], None)
except KeyError:
pass
try:
return (1, self.new_reg_opcode[x], BitArray.from_int(8, 0x41))
except KeyError:
return (None, None, None)
@classmethod
def get_reg_bits(cls, name):
try:
return cls.reg_opcode[name]
except KeyError:
return cls.new_reg_opcode[name]
class RawBits(BitArray):
def accept_arg(self, previous, args):
return (0, self, None)
class Imm64(object):
def accept_arg(self, previous, args):
try:
x = int(args[0])
return (1, BitArray.from_int(64, X64.to_little_endian(x)), None)
except TypeError:
return (None, None, None)
class Mov_RAX_OFF64(object):
def accept_arg(self, previous, args):
if args[0] != "RAX":
return (None, None, None)
arg2 = args[1]
if not (X64.is_mem_acces(arg2) and X64.mem_access_has_only(arg2, ["disp"])):
return (None, None, None)
return (2, BitArray.from_int(8, 0xa1) + BitArray.from_int(64, X64.to_little_endian(arg2.disp)) , BitArray.from_int(8, 0x48))
class Mov_OFF64_RAX(object):
def accept_arg(self, previous, args):
if args[1] != "RAX":
return (None, None, None)
arg2 = args[0]
if not (X64.is_mem_acces(arg2) and X64.mem_access_has_only(arg2, ["disp"])):
return (None, None, None)
return (2, BitArray.from_int(8, 0xa3) + BitArray.from_int(64, X64.to_little_endian(arg2.disp)) , BitArray.from_int(8, 0x48))
class ModRM(object):
size = 8
def __init__(self, *sub_modrm):
self.sub = sub_modrm
def accept_arg(self, previous, args):
if len(args) < 2:
raise ValueError("Missing arg for modrm")
arg1 = args[0]
arg2 = args[1]
for sub in self.sub:
#import pdb;pdb.set_trace()
if sub.match(arg1, arg2):
d = sub(arg1, arg2, 0)
previous[0][-2] = d.direction
rex = d.rex if d.is_rex_needed else None
return (2, d.mod + d.reg + d.rm + d.after, rex)
elif (not hasattr(sub, "refuse_reverse")) and sub.match(arg2, arg1):
d = sub(arg2, arg1, 1)
previous[0][-2] = d.direction
rex = d.rex if d.is_rex_needed else None
return (2, d.mod + d.reg + d.rm + d.after, rex)
return (None, None, None)
class RexByte(BitArray):
def __init__(self):
super(RexByte, self).__init__(8, "")
self.is_needed = False
class X64(object):
@staticmethod
def is_reg(name):
return name in x64_regs
@staticmethod
def is_new_reg(name):
return name in new_reg_order
@staticmethod
def is_mem_acces(data):
return isinstance(data, mem_access)
@staticmethod
def mem_access_has_only(mem_access, names):
if not X64.is_mem_acces(mem_access):
raise ValueError("mem_access_has_only")
for f in mem_access._fields:
if getattr(mem_access, f) and f not in names:
return False
if "base" in names and mem_access.base is None:
return False
return True
@staticmethod
def to_little_endian(i):
i = i & 0xffffffffffffffff
return struct.unpack("<Q", struct.pack(">Q", i))[0]
# Sub ModRM encoding
class RexByte(object):
def __init__(self):
self.is_needed = False
self.pattern = BitArray(4, "0100")
self.w = BitArray(1, "0")
self.r = BitArray(1, "0")
self.x = BitArray(1, "0")
self.b = BitArray(1, "0")
class SubModRM(object):
def __init__(self):
self.mod = BitArray(2, "")
self.reg = BitArray(3, "")
self.rm = BitArray(3, "")
self.after = BitArray(0, "")
self.rex = BitArray(8, "01000000")
self.is_rex_needed = False
self.direction = 0
def setup_reg_as_register(self, name):
self.reg = X64RegisterSelector.get_reg_bits(name)
if X64.is_new_reg(name):
self.is_rex_needed = True
self.rex[5] = 1
def setup_rm_as_register(self, name):
self.rm = X64RegisterSelector.get_reg_bits(name)
if X64.is_new_reg(name):
self.is_rex_needed = True
self.rex[7] = 1
class ModRM_REG64__REG64(SubModRM):
@classmethod
def match(cls, arg1, arg2):
return X64.is_reg(arg1) and X64.is_reg(arg2)
def __init__(self, arg1, arg2, reversed):
super(ModRM_REG64__REG64, self).__init__()
self.mod = BitArray(2, "11")
self.is_rex_needed = True
self.rex[4] = 1
self.setup_reg_as_register(arg2)
self.setup_rm_as_register(arg1)
self.direction = 0
#class ModRM_REG__DEREF_IMM(SubModRM):
# @classmethod
# def match(cls, arg1, arg2):
# return X64.is_reg(arg1) and X64.is_mem_acces(arg2) and X64.mem_access_has_only(arg2, ["disp"])
#
# def __init__(self, arg1, arg2, reversed):
# super(ModRM_REG__DEREF_IMM, self).__init__()
# self.mod = BitArray(2, "00")
# self.setup_reg_as_register(arg1)
# self.rm = BitArray(3, "101")
# self.after = BitArray.from_int(64, X64.to_little_endian(arg2.disp))
# self.direction = not reversed
class ModRM_REG__DEREF_REG(SubModRM):
@classmethod
def match(cls, arg1, arg2):
return X64.is_reg(arg1) and X64.is_mem_acces(arg2) and X64.mem_access_has_only(arg2, ["base"]) and arg2.base not in ["RSP", "RBP"]
def __init__(self, arg1, arg2, reversed):
super(ModRM_REG__DEREF_REG, self).__init__()
self.mod = BitArray(2, "00")
self.is_rex_needed = True
self.rex[4] = 1
self.setup_reg_as_register(arg1)
self.setup_rm_as_register(arg2.base)
self.after = BitArray(0, "")
self.direction = not reversed
#
#class ModRM_REG__DEREF_REG_IMM(object):
# @classmethod
# def match(cls, arg1, arg2):
# return X86.is_reg(arg1) and X86.is_mem_acces(arg2) and X86.mem_access_has_only(arg2, ["base", "disp"])
#
# def __init__(self, arg1, arg2, reversed):
# self.mod = BitArray(2, "10")
# self.reg = X86RegisterSelector.get_reg_bits(arg1)
# self.rm = X86RegisterSelector.get_reg_bits(arg2.base)
# self.after = BitArray.from_int(32, X86.to_little_endian(arg2.disp))
# self.direction = not reversed
#
class Instruction(object):
encoding = []
def __init__(self, *initial_args):
for type_encoding in self.encoding:
args = list(initial_args)
res = []
full_rex = BitArray(8, "")
if hasattr(self, "default_32_bits") and self.default_32_bits:
full_rex = BitArray.from_int(8, 0x48)
for element in type_encoding:
arg_consum, value, rex = element.accept_arg(res, args)
if arg_consum is None:
break
res.append(value)
del args[:arg_consum]
if rex is not None:
full_rex = full_rex | rex
else: # if no break
if args: # if still args: fail
continue
self.value = sum(res, BitArray(0, ""))
if any(full_rex.array):
self.value = full_rex + self.value
return
raise ValueError("Cannot encode :(")
#
#
class Push(Instruction):
encoding = [(RawBits.from_int(5, 0x50 >> 3), X64RegisterSelector()),]
# (RawBits.from_int(8, 0x68), Imm32())]
class Pop(Instruction):
encoding = [(RawBits.from_int(5, 0x58 >> 3), X64RegisterSelector())]
class Call(Instruction):
encoding = [(RawBits.from_int(13, 0xffd0 >> 3), X64RegisterSelector())]
class Ret(Instruction):
encoding = [(RawBits.from_int(8, 0xc3),)]
class Mov(Instruction):
default_32_bits = True
encoding = [(RawBits.from_int(8, 0x89), ModRM(ModRM_REG64__REG64, ModRM_REG__DEREF_REG)), (RawBits.from_int(5, 0xb8 >> 3), X64RegisterSelector(), Imm64()),
(Mov_RAX_OFF64(),), (Mov_OFF64_RAX(),)]
class MultipleInstr(object):
def __init__(self, instrs=()):
self.instrs = list(instrs)
def __iadd__(self, value):
if type(value) == MultipleInstr:
self.instrs.extend(value.instrs)
return self
self.instrs.append(value)
return self
def get_code(self):
code = self.get_unbinded_code()
for i in range(self.biding):
to_search = codecs.decode(str(i + 1) * 16, 'hex')
import pdb;pdb.set_trace()
code = code.replace(to_search, struct.pack("<Q", self.bind_values[i]))
return code
def get_mnemo(self):
return self.mnemo.format(*(hex(v) for v in self.bind_values))
class Ret(X64Instruction):
mnemo = "ret"
code = "C3"
generated_instruction.append(("Ret", Ret))
class Int3(X64Instruction):
mnemo = "int3"
code = "CC"
generated_instruction.append(("Int3", Int3))
class Retf(X64Instruction):
mnemo = "retf"
code = "CB"
generated_instruction.append(("Retf", Retf))
class SimpleRegInstructionGenerator(object):
name = ""
instruction_bits = ''
class OneBindX64Instruction(X64Instruction):
biding = 1
class Mov_RAX_DX(OneBindX64Instruction):
name = 'Mov_RAX_DX'
mnemo = 'mov rax, [{0}]'
code = "48 a1 11 11 11 11 11 11 11 11"
generated_instruction.append(("Mov_RAX_DX", Mov_RAX_DX))
class Mov_DX_RAX(OneBindX64Instruction):
name = 'Mov_DX_RAX'
mnemo = 'mov [{0}], rax'
code = "48 a3 11 11 11 11 11 11 11 11"
generated_instruction.append(("Mov_DX_RAX", Mov_DX_RAX))
def generate_simple_reg_instruction(instr_cls, include_new_reg=False):
for reg_name, reg_bits in reg_opcode.items():
class SimpleRegInstruction(X64Instruction):
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)
if sys.version_info.major == 3:
return b"".join([x.value.dump() for x in self.instrs])
return "".join([str(x.value.dump()) for x in self.instrs])
if not include_new_reg:
return None
for reg_name, reg_bits in new_reg_opcode.items():
class SimpleRegInstruction(X64Instruction):
mnemo = "{0} {1}".format(instr_cls.mnemo, reg_name)
code = instr_cls.new_reg_prefix + 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'
new_reg_prefix = "41"
generate_simple_reg_instruction(Push_Reg, True)
class Pop_Reg(object):
name = 'Pop'
mnemo = "pop"
instruction_bits = '01011'
new_reg_prefix = "41"
generate_simple_reg_instruction(Pop_Reg, True)
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(OneBindX64Instruction):
mnemo = instr_cls.mnemo.format(reg_name)
code = encode_hex(chr(int(instr_cls.instruction_bits + reg_bits, 2))) + '11 11 11 11 11 11 11 11' # the biding
if instr_cls.prefix_bin_h48:
code = "48" + code
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'
prefix_bin_h48 = True
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 encode_hex(chr(int(str_bits, 2)))
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 encode_hex(chr(int(str_bits, 2)))
#def generate_reg_indirect_modr_byte(reg_dst_bits, reg_src_bits):
# # reg, [reg] or [reg], reg
# return "00{0}{1}".format(reg_dst_bits, reg_src_bits)
#
#def generate_reg_reg_deref(instr_cls, src_first=True):
# "generate the Mov_Reg_DReg and Mov_DReg_Reg"
# for reg_src_name, reg_src_bits in reg_opcode.items():
# for reg_dst_name, reg_dst_bits in reg_opcode.items():
# if reg_dst_name in ("RBP", "RSP") or reg_src_name in ("RBP", "RSP"):
# # Not same encoding -> Not implemented
# continue
# class Reg_DReg_instruction(X64Instruction):
# mnemo = instr_cls.mnemo.format(reg_dst_name, reg_src_name)
# name = instr_cls.name.format(reg_dst_name, reg_src_name)
# if src_first:
# modr_code = generate_reg_indirect_modr_byte(reg_src_bits, reg_dst_bits)
# else:
# modr_code = generate_reg_indirect_modr_byte(reg_dst_bits, reg_src_bits)
# code = instr_cls.instruction_bits + chr(int(modr_code, 2)).encode("hex")
# Reg_DReg_instruction.__name__ = Reg_DReg_instruction.name
# add_instruction(Reg_DReg_instruction.__name__, Reg_DReg_instruction)
def generate_reg_indirect_modr_byte(reg_dst_bits, reg_src_bits, deref_first):
# reg, [reg] or [reg], reg
if deref_first:
return encode_hex(chr(int("00{0}{1}".format(reg_dst_bits, reg_src_bits), 2)))
else:
return encode_hex(chr(int("00{0}{1}".format(reg_src_bits, reg_dst_bits), 2)))
def generate_reg_reg_deref():
for reg1_name, reg1_bits in all_regs.items():
for reg2_name, reg2_bits in all_regs.items():
if reg1_name in ("RBP", "RSP", "R12", "R13") or reg2_name in ("RBP", "RSP", "R12", "R13"):
continue
is_reg1_new = reg1_name in new_reg_opcode
is_reg2_new = reg2_name in new_reg_opcode
first_byte = encode_hex(chr(int("1001{0}0{1}".format(int(is_reg1_new), int(is_reg2_new)), 2)))
class DReg_Reg_instruction(X64Instruction):
mnemo = "mov [{0}], {1}".format(reg1_name, reg2_name)
name = 'Mov_D{0}_{1}'.format(reg1_name, reg2_name)
modr_code = generate_reg_indirect_modr_byte(reg1_bits, reg2_bits, False)
code = first_byte + "89" + modr_code
DReg_Reg_instruction.__name__ = DReg_Reg_instruction.name
add_instruction(DReg_Reg_instruction.__name__, DReg_Reg_instruction)
class Reg_DReg_instruction(X64Instruction):
mnemo = "mov {0}, [{1}]".format(reg1_name, reg2_name)
name = 'Mov_{0}_D{1}'.format(reg1_name, reg2_name)
modr_code = generate_reg_indirect_modr_byte(reg1_bits, reg2_bits, True)
code = first_byte + "8B" + modr_code
Reg_DReg_instruction.__name__ = Reg_DReg_instruction.name
add_instruction(Reg_DReg_instruction.__name__, Reg_DReg_instruction)
generate_reg_reg_deref()
generate_module_doc()
+218 -275
View File
@@ -1,290 +1,238 @@
import collections
import struct
import sys
import codecs
# This code should really be rewritten..
this_module = sys.modules[__name__]
generated_instruction = []
long = int
def add_instruction(name, instruction):
generated_instruction.append((name, instruction))
setattr(this_module, name, instruction)
def generate_module_doc():
doc_lines = ["Here is the list of instruction in the modules:\n\n"]
for name, instruction in generated_instruction:
doc_lines.append(" | {0} -> <{1}>".format(name, instruction.mnemo))
this_module.__doc__ = "\n".join(doc_lines)
def decode_hex(s):
return codecs.decode(s.encode(), "hex").decode()
def encode_hex(s):
return codecs.encode(s.encode(), "hex").decode()
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):
print(self.code)
return codecs.decode(self.code.replace(" ", ""), 'hex')
def get_code(self):
code = self.get_unbinded_code()
for i in range(self.biding):
to_search = codecs.decode(str(i + 1) * 8, 'hex')
code = code.replace(to_search, 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"
generated_instruction.append(("Ret", Ret))
class Int3(X86Instruction):
mnemo = "int3"
code = "CC"
generated_instruction.append(("Int3", Int3))
class BitArray(object):
def __init__(self, size, bits):
self.size = size
if len(bits) > size:
raise ValueError("size > len(bits)")
class SimpleRegInstructionGenerator(object):
name = ""
instruction_bits = ''
class OneBindX86Instruction(X86Instruction):
biding = 1
class Push_X(OneBindX86Instruction):
mnemo = "push {0}"
code = "68 11 11 11 11"
generated_instruction.append(("Push_X", Push_X))
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')
bits_list = []
for bit in bits:
x = int(bit)
if x not in [0, 1]:
raise ValueError("Not expected bits value {0}".format(x))
bits_list.append(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)
self.array = bits_list
if size > len(self.array):
self.array = ([0] * (size - len(self.array))) + self.array
i = int(instr_cls.instruction_bits + reg_bits, 2)
code = bytes([i]) + b'11 11 11 11' # the biding
def dump(self):
res = []
for i in range(self.size // 8):
c = 0
for x in (self.array[i * 8: (i + 1) * 8]):
c = (c << 1) + x
res.append(c)
return bytearray((res))
def __getitem__(self, slice):
return self.array[slice]
def __setitem__(self, slice, value):
self.array[slice] = value
return True
def __repr__(self):
return repr(self.array)
def __add__(self, other):
if not isinstance(other, BitArray):
return NotImplemented
return BitArray(self.size + other.size, self.array + other.array)
def to_int(self):
return int("".join([str(i) for i in self.array]), 2)
@classmethod
def from_string(cls):
l = []
for c in bytearray(reversed(str_base)):
for i in range(8):
l.append(c & 1)
c = c >> 1
self.array = l
@classmethod
def from_int(cls, size, x):
if x < 0:
x = x & ((2 ** size) - 1)
return cls(size, bin(x)[2:])
# Rules: bytes only !!!!
mem_access = collections.namedtuple('mem_access', ['base', 'index', 'squale', 'disp'])
x86_regs = ['EAX', 'ECX', 'EDX', 'EBX', 'ESP', 'EBP', 'ESI', 'EDI']
def create_displacement(base=None, index=None, squale=None, disp=0):
return mem_access(base, index, squale, disp)
class X86RegisterSelector(object):
size = 3 # bits
reg_order = ['EAX', 'ECX', 'EDX', 'EBX', 'ESP', 'EBP', 'ESI', 'EDI']
reg_opcode = {v : BitArray.from_int(size=3, x=i) for i, v in enumerate(reg_order)}
def accept_arg(self, previous, args):
x = args[0]
try:
return (1, self.reg_opcode[x])
except KeyError:
return (None, None)
@classmethod
def get_reg_bits(cls, name):
return cls.reg_opcode[name]
class RawBits(BitArray):
def accept_arg(self, previous, args):
return (0, self)
class Imm32(object):
def accept_arg(self, previous, args):
x = int(args[0])
return (1, BitArray.from_int(32, X86.to_little_endian(x)))
class ModRM(object):
size = 8
def __init__(self, *sub_modrm):
self.sub = sub_modrm
def accept_arg(self, previous, args):
if len(args) < 2:
raise ValueError("Missing arg for modrm")
arg1 = args[0]
arg2 = args[1]
for sub in self.sub:
#import pdb;pdb.set_trace()
#code = encode_hex(chr(int(instr_cls.instruction_bits + reg_bits, 2))) + b'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 encode_hex(chr(int(str_bits, 2)))
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)
if sub.match(arg1, arg2):
d = sub(arg1, arg2, 0)
previous[0][-2] = d.direction
return (2, d.mod + d.reg + d.rm + d.after)
elif sub.match(arg2, arg1):
d = sub(arg2, arg1, 1)
previous[0][-2] = d.direction
return (2, d.mod + d.reg + d.rm + d.after)
return (None, None)
class Add_Reg_X(object):
name = 'Add_{0}_X'
mnemo = 'add {0}, {{0}}'
instruction_bits = '81'
class X86(object):
@staticmethod
def is_reg(name):
return name in x86_regs
@staticmethod
def is_mem_acces(data):
return isinstance(data, mem_access)
@staticmethod
def mem_access_has_only(mem_access, names):
if not X86.is_mem_acces(mem_access):
raise ValueError("mem_access_has_only")
for f in mem_access._fields:
if getattr(mem_access, f) and f not in names:
return False
return True
@staticmethod
def to_little_endian(i):
i = i & 0xffffffff
return struct.unpack("<I", struct.pack(">I", i))[0]
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 encode_hex(chr(int(str_bits, 2)))
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
class ModRM_REG__REG(object):
@classmethod
def match(cls, arg1, arg2):
return X86.is_reg(arg1) and X86.is_reg(arg2)
def __init__(self, arg1, arg2, reversed):
self.mod = BitArray(2, "11")
self.reg = X86RegisterSelector.get_reg_bits(arg2)
self.rm = X86RegisterSelector.get_reg_bits(arg1)
self.after = BitArray(0, "")
self.direction = 0
class ModRM_REG__DEREF_REG(object):
@classmethod
def match(cls, arg1, arg2):
return X86.is_reg(arg1) and arg1 not in ["ESP", "EBP"] and X86.is_mem_acces(arg2) and X86.mem_access_has_only(arg2, ["base"])
def __init__(self, arg1, arg2, reversed):
self.mod = BitArray(2, "00")
self.reg = X86RegisterSelector.get_reg_bits(arg1)
self.rm = X86RegisterSelector.get_reg_bits(arg2.base)
self.after = BitArray(0, "")
self.direction = not reversed
class ModRM_REG__DEREF_REG_IMM(object):
@classmethod
def match(cls, arg1, arg2):
return X86.is_reg(arg1) and X86.is_mem_acces(arg2) and X86.mem_access_has_only(arg2, ["base", "disp"])
def __init__(self, arg1, arg2, reversed):
self.mod = BitArray(2, "10")
self.reg = X86RegisterSelector.get_reg_bits(arg1)
self.rm = X86RegisterSelector.get_reg_bits(arg2.base)
self.after = BitArray.from_int(32, X86.to_little_endian(arg2.disp))
self.direction = not reversed
class ModRM_REG_IMM(object):
@classmethod
def match(cls, arg1, arg2):
return arg1 in x86_regs and arg2 in x86_regs
def __init__(self, arg1, arg2):
self.mod = BitArray(2, "11")
self.reg = X86RegisterSelector.get_reg_bits(arg2)
self.rm = X86RegisterSelector.get_reg_bits(arg1)
self.direction = 0
class Instruction(object):
encoding = []
def __init__(self, *initial_args):
for type_encoding in self.encoding:
args = list(initial_args)
res = []
for element in type_encoding:
arg_consum, value = element.accept_arg(res, args)
if arg_consum is None:
break
res.append(value)
del args[:arg_consum]
else: # if no break
if args: # if still args: fail
continue
self.value = sum(res, BitArray(0, ""))
return
raise ValueError("Cannot encode :(")
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_indirect_modr_byte(reg_dst_bits, reg_src_bits):
# reg, [reg] or [reg], reg
return "00{0}{1}".format(reg_dst_bits, reg_src_bits)
class Push(Instruction):
encoding = [(RawBits.from_int(5, 0x50 >> 3), X86RegisterSelector()),
(RawBits.from_int(8, 0x68), Imm32())]
def generate_reg_reg_deref(instr_cls, src_first=True):
"generate the Mov_Reg_DReg and Mov_DReg_Reg"
for reg_src_name, reg_src_bits in reg_opcode.items():
for reg_dst_name, reg_dst_bits in reg_opcode.items():
if reg_dst_name in ("EBP", "ESP") or reg_src_name in ("EBP", "ESP"):
# Not same encoding -> Not implemented
continue
class Reg_DReg_instruction(X86Instruction):
mnemo = instr_cls.mnemo.format(reg_dst_name, reg_src_name)
name = instr_cls.name.format(reg_dst_name, reg_src_name)
if src_first:
modr_code = generate_reg_indirect_modr_byte(reg_src_bits, reg_dst_bits)
else:
modr_code = generate_reg_indirect_modr_byte(reg_dst_bits, reg_src_bits)
code = encode_hex(instr_cls.instruction_bits + chr(int(modr_code, 2)))
Reg_DReg_instruction.__name__ = Reg_DReg_instruction.name
add_instruction(Reg_DReg_instruction.__name__, Reg_DReg_instruction)
class Pop(Instruction):
encoding = [(RawBits.from_int(5, 0x58 >> 3), X86RegisterSelector())]
class Mov(Instruction):
encoding = [(RawBits.from_int(8, 0x89), ModRM(ModRM_REG__REG, ModRM_REG__DEREF_REG, ModRM_REG__DEREF_REG_IMM)),
(RawBits.from_int(5, 0xb8 >> 3), X86RegisterSelector(), Imm32())]
class Mov_Reg_DReg(object):
name = 'Mov_{0}_D{1}'
mnemo = 'mov [{0}], {1}'
instruction_bits = '8B'
class Call(Instruction):
encoding = [(RawBits.from_int(13, 0xffd0 >> 3), X86RegisterSelector())]
generate_reg_reg_deref(Mov_Reg_DReg, False)
class Mov_DReg_Reg(object):
name = 'Mov_D{0}_{1}'
mnemo = 'mov {0}, [{1}]'
instruction_bits = '89'
generate_reg_reg_deref(Mov_DReg_Reg, True)
def generate_reg_reg_modr_byte(reg_dst_bits, reg_src_bits):
# reg, reg
return "11{0}{1}".format(reg_src_bits, reg_dst_bits)
class Ret(Instruction):
encoding = [(RawBits.from_int(8, 0xc3),)]
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, init_instrs=()):
self.instrs = list(init_instrs)
def __init__(self):
self.instrs = []
def __iadd__(self, value):
if type(value) == MultipleInstr:
@@ -294,12 +242,7 @@ class MultipleInstr(object):
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)
if sys.version_info.major == 3:
return b"".join([x.value.dump() for x in self.instrs])
return "".join([str(x.value.dump()) for x in self.instrs])
generate_module_doc()