cosmetic: fucking linter :(

This commit is contained in:
Clement Rouault
2015-09-23 14:32:27 +02:00
parent 663de130b5
commit f5b0c0294f
23 changed files with 654 additions and 453 deletions
+3 -1
View File
@@ -1 +1,3 @@
from .native_function import generate_callback_stub, create_function
from .native_function import generate_callback_stub, create_function
__all__ = ["generate_callback_stub", "create_function"]
+20 -7
View File
@@ -6,24 +6,28 @@ import simple_x86 as x86
import simple_x64 as x64
from windows.generated_def.winstructs import *
def bitness():
"""Return 32 or 64"""
import platform
bits = platform.architecture()[0]
return int(bits[:2])
class X86CpuidResult(ctypes.Structure):
_fields_ = [("EAX", DWORD),
("EBX", DWORD),
("ECX", DWORD),
("EDX", DWORD)]
class X64CpuidResult(ctypes.Structure):
_fields_ = [("RAX", ULONG64),
("RBX", ULONG64),
("RCX", ULONG64),
("RDX", ULONG64)]
class X86IntelCpuidFamilly(ctypes.Structure):
_fields_ = [("SteppingID", DWORD, 4),
("ModelID", DWORD, 4),
@@ -34,6 +38,7 @@ class X86IntelCpuidFamilly(ctypes.Structure):
("ExtendedFamily", DWORD, 8),
("Reserved", DWORD, 2)]
class X86AmdCpuidFamilly(ctypes.Structure):
_fields_ = [("SteppingID", DWORD, 4),
("ModelID", DWORD, 4),
@@ -43,6 +48,7 @@ class X86AmdCpuidFamilly(ctypes.Structure):
("ExtendedFamily", DWORD, 8),
("Reserved", DWORD, 2)]
cpuid32_code = x86.MultipleInstr()
cpuid32_code += x86.Push('EDI')
cpuid32_code += x86.Mov('EAX', x86.mem('[ESP + 0x8]'))
@@ -56,6 +62,7 @@ cpuid32_code += x86.Pop('EDI')
cpuid32_code += x86.Ret()
do_cpuid32 = native_function.create_function(cpuid32_code.get_code(), [DWORD, DWORD, PVOID])
cpuid64_code = x64.MultipleInstr()
cpuid64_code += x64.Mov('RAX', 'RCX')
cpuid64_code += x64.Mov('R10', 'RDX')
@@ -68,34 +75,40 @@ cpuid64_code += x64.Mov(x64.mem('[R10 + 0x18]'), 'RDX')
cpuid64_code += x64.Ret()
do_cpuid64 = native_function.create_function(cpuid64_code.get_code(), [DWORD, DWORD, PVOID])
def x86_cpuid(req):
cpuid_res = X86CpuidResult()
do_cpuid32(req, ctypes.addressof(cpuid_res))
return cpuid_res
def x64_cpuid(req):
cpuid_res = X64CpuidResult()
do_cpuid64(req, ctypes.addressof(cpuid_res))
# For now assembler cannot do 32bits register in x64
return X86CpuidResult(cpuid_res.RAX, cpuid_res.RBX, cpuid_res.RCX, cpuid_res.RDX)
if bitness() == 32:
do_cpuid = x86_cpuid
else:
do_cpuid = x64_cpuid
def get_vendor_id():
cpuid_res = do_cpuid(0)
return struct.pack("<III", cpuid_res.EBX , cpuid_res.EDX, cpuid_res.ECX)
return struct.pack("<III", cpuid_res.EBX, cpuid_res.EDX, cpuid_res.ECX)
# platform.processor() could do the trick
def is_intel_proc():
return get_vendor_id() == "GenuineIntel"
def is_amd_proc():
return get_vendor_id() == "AuthenticAMD"
def get_proc_family_model():
cpuid_res = do_cpuid(1)
if is_intel_proc():
@@ -112,5 +125,5 @@ def get_proc_family_model():
if infos.FamilyID == 0x0F:
ComputedFamily = infos.FamilyID + infos.ExtendedFamily
else:
ComputedFamily = infos.FamilyID;
return ComputedFamily, ComputedModel
ComputedFamily = infos.FamilyID
return ComputedFamily, ComputedModel
+30 -23
View File
@@ -9,40 +9,40 @@ import windows.winproxy
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
("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
# 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
# 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
# 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 }
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 = windows.winproxy.VirtualAlloc(0, size, 0x1000, 0x40)
new_map = (ctypes.c_char * size).from_address(addr)
new_map.addr = addr
@@ -50,6 +50,7 @@ class Win32MyMap(MyMap):
raise ctypes.WinError()
return new_map
class UnixMyMap(MyMap):
@classmethod
def get_map(cls, size):
@@ -58,7 +59,7 @@ class UnixMyMap(MyMap):
class CustomAllocator(object):
int_size = {'32bit' : 4, '64bit' : 8}
int_size = {'32bit': 4, '64bit': 8}
def __init__(self):
self.maps = []
@@ -99,6 +100,7 @@ class CustomAllocator(object):
allocator = CustomAllocator()
def get_functions():
version = sys.version_info
python_dll = "python" + str(version.major) + str(version.minor)
@@ -108,6 +110,7 @@ def get_functions():
PyGILState_Release = windows.utils.get_func_addr(python_dll, 'PyGILState_Release'.encode())
return [PyGILState_Ensure, PyObject_CallObject, PyGILState_Release]
def analyse_callback(callback):
if not callable(callback):
raise ValueError("Need a callable object :)")
@@ -119,10 +122,9 @@ def analyse_callback(callback):
# For windows 32 bits with stdcall
def generate_stub_32(callback):
obj_id = analyse_callback(callback)
c_callback = get_callback_address_32(callback)
gstate_save_addr = x86.create_displacement(disp=allocator.reserve_int())
gstate_save_addr = x86.create_displacement(disp=allocator.reserve_int())
return_addr_save_addr = x86.create_displacement(disp=allocator.reserve_int())
save_ebx = x86.create_displacement(disp=allocator.reserve_int())
save_ecx = x86.create_displacement(disp=allocator.reserve_int())
@@ -133,7 +135,7 @@ def generate_stub_32(callback):
ensure, objcall, release = get_functions()
code = x86.MultipleInstr()
### Shellcode ###
# ## Shellcode ## #
code += x86.Mov(save_ebx, 'EBX')
code += x86.Mov(save_ecx, 'ECX')
code += x86.Mov(save_edx, 'EDX')
@@ -144,7 +146,7 @@ def generate_stub_32(callback):
code += x86.Call('EAX')
code += x86.Mov(gstate_save_addr, 'EAX')
#Save real return addr (for good argument parsing by the callback)
# Save real return addr (for good argument parsing by the callback)
code += x86.Pop('EAX')
code += x86.Mov(return_addr_save_addr, 'EAX')
@@ -177,7 +179,6 @@ def generate_stub_32(callback):
def generate_stub_64(callback):
obj_id = analyse_callback(callback)
c_callback = get_callback_address_64(callback)
REG_LEN = ctypes.sizeof(ctypes.c_void_p)
register_to_save = ("RBX", "RCX", "RDX", "RSI", "RDI", "R8", "R9", "R10", "R11", "R12", "R13", "R14", "R15")
@@ -185,15 +186,18 @@ def generate_stub_64(callback):
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 reversed(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
save_register_space_end = save_register_space + (ctypes.sizeof(ctypes.c_void_p) * (len(register_to_save) ))
save_register_space = allocator.reserve_int(len(register_to_save))
save_register_space += REG_LEN
save_register_space_end = save_register_space + (ctypes.sizeof(ctypes.c_void_p) * (len(register_to_save)))
save_rbx = save_register_space_end - REG_LEN
save_rbx # Fuck the linter :D
save_rcx = save_register_space_end - REG_LEN - REG_LEN
save_rdx = save_register_space_end - REG_LEN - (REG_LEN * 2)
save_rsi = save_register_space_end - REG_LEN - (REG_LEN * 3)
save_rsi # Fuck the linter :D
save_rdi = save_register_space_end - REG_LEN - (REG_LEN * 4)
save_rdi # Fuck the linter :D
save_r8 = save_register_space_end - REG_LEN - (REG_LEN * 5)
save_r9 = save_register_space_end - REG_LEN - (REG_LEN * 6)
@@ -208,7 +212,7 @@ def generate_stub_64(callback):
ensure, objcall, release = get_functions()
### Shellcode ###
# ## Shellcode ## #
code = x64.MultipleInstr()
# Save all registers
code += x64.Mov('RAX', save_register_space_end)
@@ -223,7 +227,7 @@ def generate_stub_64(callback):
code += Remove_stack_alignement
code += Clean_space_for_call
code += x64.Mov(gstate_save_addr, 'RAX')
#Save real return addr (for good argument parsing by the callback)
# Save real return addr (for good argument parsing by the callback)
code += x64.Pop('RAX')
code += x64.Mov(return_addr_save_addr, 'RAX')
# Restore parameters for real function call
@@ -237,9 +241,9 @@ def generate_stub_64(callback):
code += x64.Mov('R8', x64.mem('[RAX]'))
# Call python code
code += x64.Mov('RAX', c_callback)
code += x64.Call('RAX') # no need for stack alignement here as we poped the return addr
# no need for Reserve_space_for_call as we must use the previous one for
# correct argument parsing
# no need for stack alignement here as we poped the return addr
# no need for Reserve_space_for_call as we must use the previous one for correct argument parsing
code += x64.Call('RAX')
# Save return value
code += x64.Mov(return_value_save_addr, 'RAX')
# Repush real return value
@@ -278,6 +282,7 @@ def generate_callback_stub(callback, types):
generate_callback_stub.l = []
def create_function(code, types):
"""Create a python function that call raw machine code
@@ -290,12 +295,14 @@ def create_function(code, types):
addr = allocator.write_code(code)
return func_type(addr)
# Return First argument for 32 bits code
raw_code = x86.MultipleInstr()
raw_code += x86.Mov('EAX', x86.mem('[ESP + 4]'))
raw_code += x86.Ret()
get_callback_address_32 = create_function(raw_code.get_code(), [ctypes.c_void_p])
# Return First argument for 64 bits code
raw_code = x64.MultipleInstr()
raw_code += x64.Mov('RAX', 'RCX')
+116 -63
View File
@@ -1,8 +1,6 @@
import collections
import struct
import sys
# TODO: fix immediat signed/unsigned assembly
class BitArray(object):
def __init__(self, size, bits):
@@ -50,7 +48,7 @@ class BitArray(object):
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)]
new_array = [(x | y) for x, y in zip(self.array, other.array)]
return BitArray(self.size, new_array)
def to_int(self):
@@ -74,9 +72,11 @@ class BitArray(object):
def copy(self):
return type(self)(self.size, self.array)
# Prefix
class Prefix(object):
PREFIX_VALUE = None
def __init__(self, next=None):
self.next = next
@@ -86,21 +86,22 @@ class Prefix(object):
def get_code(self):
return chr(self.PREFIX_VALUE) + self.next.get_code()
def create_prefix(name, value):
prefix_type = type(name + "Type", (Prefix,), {'PREFIX_VALUE' : value})
setattr(sys.modules[__name__], name, prefix_type())
create_prefix('LockPrefix', 0xf0)
create_prefix('Repne', 0xf2)
create_prefix('Rep', 0xf3)
create_prefix('SSPrefix', 0x36)
create_prefix('CSPrefix', 0x2e)
create_prefix('DSPrefix', 0x3e)
create_prefix('ESPrefix', 0x26)
create_prefix('FSPrefix', 0x64)
create_prefix('GSPrefix', 0x65)
create_prefix('OperandSizeOverride', 0x66)
create_prefix('AddressSizeOverride', 0x67)
def create_prefix(name, value):
prefix_type = type(name + "Type", (Prefix,), {'PREFIX_VALUE': value})
return prefix_type()
LockPrefix = create_prefix('LockPrefix', 0xf0)
Repne = create_prefix('Repne', 0xf2)
Rep = create_prefix('Rep', 0xf3)
SSPrefix = create_prefix('SSPrefix', 0x36)
CSPrefix = create_prefix('CSPrefix', 0x2e)
DSPrefix = create_prefix('DSPrefix', 0x3e)
ESPrefix = create_prefix('ESPrefix', 0x26)
FSPrefix = create_prefix('FSPrefix', 0x64)
GSPrefix = create_prefix('GSPrefix', 0x65)
OperandSizeOverride = create_prefix('OperandSizeOverride', 0x66)
AddressSizeOverride = create_prefix('AddressSizeOverride', 0x67)
mem_access = collections.namedtuple('mem_access', ['base', 'index', 'scale', 'disp', 'prefix'])
@@ -108,22 +109,23 @@ reg_order = ['RAX', 'RCX', 'RDX', 'RBX', 'RSP', 'RBP', 'RSI', 'RDI']
new_reg_order = ['R8', 'R9', 'R10', 'R11', 'R12', 'R13', 'R14', 'R15']
x64_regs = reg_order + new_reg_order
x64_segment_selectors = {'CS' : CSPrefix, 'DS' : DSPrefix, 'ES' : ESPrefix, 'SS' : SSPrefix,
'FS': FSPrefix, 'GS' : GSPrefix}
x64_segment_selectors = {'CS': CSPrefix, 'DS': DSPrefix, 'ES': ESPrefix, 'SS': SSPrefix,
'FS': FSPrefix, 'GS': GSPrefix}
class X64(object):
@staticmethod
def is_reg(name):
try:
return (name.upper() in reg_order) or X64.is_new_reg(name)
except AttributeError: # Not a string
except AttributeError: # Not a string
return False
@staticmethod
def is_new_reg(name):
try:
return name.upper() in new_reg_order
except AttributeError: # Not a string
except AttributeError: # Not a string
return False
@staticmethod
@@ -143,7 +145,7 @@ class X64(object):
@staticmethod
def to_little_endian(i, size=64):
pack = {8: 'B', 16 : 'H', 32 : 'I', 64 : 'Q'}
pack = {8: 'B', 16: 'H', 32: 'I', 64: 'Q'}
s = pack[size]
mask = (1 << size) - 1
i = i & mask
@@ -159,9 +161,11 @@ def create_displacement(base=None, index=None, scale=None, disp=0, prefix=None):
raise ValueError("Cannot create displacement with index == RSP")
return mem_access(base, index, scale, disp, prefix)
def deref(disp):
return create_displacement(disp=disp)
def mem(data):
"""Parse a memory access string of format [EXPR] or seg:[EXPR]
EXPR may describe: BASE | INDEX * SCALE | DISPLACEMENT or any combinaison (in this order)
@@ -183,7 +187,7 @@ def mem(data):
# A l'arrache.. j'aime pas le parsing de trucs
data = data[1:-1]
items = data.split("+")
parsed_items = {'prefix' : prefix}
parsed_items = {'prefix': prefix}
for item in items:
item = item.strip()
# Index * scale
@@ -199,14 +203,14 @@ def mem(data):
raise ValueError("Invalid index <{0}> in mem access".format(index))
try:
scale = int(scale, 0)
except ValueError as e:
except ValueError:
raise ValueError("Invalid scale <{0}> in mem access".format(scale))
parsed_items['scale'] = scale
parsed_items['index'] = index
else:
# displacement / base / index alone
if X64.is_reg(item):
if not 'base' in parsed_items:
if 'base' not in parsed_items:
parsed_items['base'] = item
continue
# Already have base + index -> cannot avec another register in expression
@@ -216,7 +220,7 @@ def mem(data):
continue
try:
disp = int(item, 0)
except ValueError as e:
except ValueError:
raise ValueError("Invalid base/index or displacement <{0}> in mem access".format(item))
if 'disp' in parsed_items:
raise ValueError("Multiple displacement in mem expression <{0}>".format(data))
@@ -224,11 +228,10 @@ def mem(data):
return create_displacement(**parsed_items)
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)}
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 accept_arg(self, args, instr_state):
x = args[0]
@@ -248,6 +251,7 @@ class X64RegisterSelector(object):
except KeyError:
return cls.new_reg_opcode[name.upper()]
class FixedRegister(object):
def __init__(self, register):
self.reg = register.upper()
@@ -260,31 +264,37 @@ class FixedRegister(object):
RegisterRax = lambda: FixedRegister('RAX')
class RawBits(BitArray):
def accept_arg(self, args, instr_state):
return (0, self.copy(), None)
class ImmediatOverflow(ValueError):
pass
def accept_as_8immediat(x):
try:
return struct.pack("<b", x)
except struct.error:
raise ImmediatOverflow("8bits signed Immediat overflow")
def accept_as_16immediat(x):
try:
return struct.pack("<h", x)
except struct.error:
raise ImmediatOverflow("16bits signed Immediat overflow")
def accept_as_32immediat(x):
try:
return struct.pack("<i", x)
except struct.error:
raise ImmediatOverflow("32bits signed Immediat overflow")
def accept_as_64immediat(x):
try:
return struct.pack("<q", x)
@@ -295,6 +305,7 @@ def accept_as_64immediat(x):
except struct.error:
raise ImmediatOverflow("64bits signed Immediat overflow")
class Imm8(object):
def accept_arg(self, args, instr_state):
try:
@@ -307,6 +318,7 @@ class Imm8(object):
return None, None, None
return (1, BitArray.from_string(imm8), None)
class Imm16(object):
def accept_arg(self, args, instr_state):
try:
@@ -319,6 +331,7 @@ class Imm16(object):
return None, None
return (1, BitArray.from_string(imm16), None)
class Imm32(object):
def accept_arg(self, args, instr_state):
try:
@@ -331,6 +344,7 @@ class Imm32(object):
return None, None, None
return (1, BitArray.from_string(imm32), None)
class Imm64(object):
def accept_arg(self, args, instr_state):
try:
@@ -343,6 +357,7 @@ class Imm64(object):
return None, None, None
return (1, BitArray.from_string(imm64), None)
class Mov_RAX_OFF64(object):
def accept_arg(self, args, instr_state):
if RegisterRax().accept_arg(args, instr_state) == (None, None, None):
@@ -353,7 +368,8 @@ class Mov_RAX_OFF64(object):
# Migth Raise an ImmediatOverflow bu no other encoding for this so precise error is cool
if arg2.prefix is not None:
instr_state.prefixes.append(x64_segment_selectors[arg2.prefix])
return (2, BitArray.from_int(8, 0xa1) + BitArray.from_string(accept_as_64immediat(arg2.disp)) , BitArray.from_int(8, 0x48))
return (2, BitArray.from_int(8, 0xa1) + BitArray.from_string(accept_as_64immediat(arg2.disp)), BitArray.from_int(8, 0x48))
class Mov_OFF64_RAX(object):
def accept_arg(self, args, instr_state):
@@ -364,7 +380,8 @@ class Mov_OFF64_RAX(object):
return (None, None, None)
if arg2.prefix is not None:
instr_state.prefixes.append(x64_segment_selectors[arg2.prefix])
return (2, BitArray.from_int(8, 0xa3) + BitArray.from_string(accept_as_64immediat(arg2.disp)) , BitArray.from_int(8, 0x48))
return (2, BitArray.from_int(8, 0xa3) + BitArray.from_string(accept_as_64immediat(arg2.disp)), BitArray.from_int(8, 0x48))
class ModRM(object):
size = 8
@@ -394,8 +411,8 @@ class ModRM(object):
return (2, d.mod + d.reg + d.rm + d.after, rex)
return (None, None, None)
# Sub ModRM encoding
# Sub ModRM encoding
class SubModRM(object):
def __init__(self):
self.mod = BitArray(2, "")
@@ -430,6 +447,7 @@ class SubModRM(object):
self.rex[6] = 1
return X64RegisterSelector.get_reg_bits(indexregister)
class ModRM_REG64__REG64(SubModRM):
@classmethod
def match(cls, arg1, arg2):
@@ -444,6 +462,7 @@ class ModRM_REG64__REG64(SubModRM):
self.setup_rm_as_register(arg1)
self.direction = 0
class ModRM_REG64__MEM(SubModRM):
@classmethod
def match(cls, arg1, arg2):
@@ -453,20 +472,20 @@ class ModRM_REG64__MEM(SubModRM):
super(ModRM_REG64__MEM, self).__init__()
if arg2.prefix is not None:
instr_state.prefixes.append(x64_segment_selectors[arg2.prefix])
# ARG1 : REG
# ARG2 : [MEM]
# this encode [rip + disp]
# TODO :)
#if X64.mem_access_has_only(arg2, ["disp"]):
# self.mod = BitArray(2, "00")
# self.setup_reg_as_register(arg1)
# self.rm = BitArray(3, "101")
# try:
# self.after = BitArray.from_string(accept_as_32immediat(arg2.disp))
# except ImmediatOverflow:
# raise ImmediatOverflow("Interger32 overflow for displacement {0}".format(hex(arg2.disp)))
# self.direction = not reversed
# return
# # ARG1 : REG
# # ARG2 : [MEM]
# # this encode [rip + disp]
# # TODO :)
# if X64.mem_access_has_only(arg2, ["disp"]):
# self.mod = BitArray(2, "00")
# self.setup_reg_as_register(arg1)
# self.rm = BitArray(3, "101")
# try:
# self.after = BitArray.from_string(accept_as_32immediat(arg2.disp))
# except ImmediatOverflow:
# raise ImmediatOverflow("Interger32 overflow for displacement {0}".format(hex(arg2.disp)))
# self.direction = not reversed
# return
# Those registers cannot be addressed without SIB
FIRE_UP_SIB = not arg2.base or arg2.base.upper() in ["RSP", "RBP"] or arg2.index
@@ -522,7 +541,7 @@ class ModRM_REG64__MEM(SubModRM):
raise ValueError("Displacement {0} is too big".format(hex(displacement)))
def compute_sib(self, mem_access):
scale = {1: 0, 2 : 1, 4: 2, 8 : 3}
scale = {1: 0, 2: 1, 4: 2, 8: 3}
if mem_access.index is None and mem_access.base is None:
return BitArray(2, "00") + BitArray(3, "100") + BitArray(3, "101")
if mem_access.index is None:
@@ -549,10 +568,11 @@ class Slash(object):
arg_consum, value, rex = ModRM([ModRM_REG64__REG64, ModRM_REG64__MEM], has_direction_bit=False).accept_arg(args[:1] + [self.reg] + args[1:], instr_state)
if value is None:
return arg_consum, value, rex
return arg_consum-1, value, rex
return arg_consum - 1, value, rex
instr_state = collections.namedtuple('instr_state', ['previous', 'prefixes'])
class Instruction(object):
encoding = []
default_rex = BitArray(8, "")
@@ -573,8 +593,8 @@ class Instruction(object):
del args[:arg_consum]
if rex is not None:
full_rex = full_rex | rex
else: # if no break
if args: # if still args: fail
else: # if no break
if args: # if still args: fail
continue
self.prefix = prefix
self.value = sum(res, BitArray(0, ""))
@@ -585,13 +605,15 @@ class Instruction(object):
def get_code(self):
prefix_opcode = b"".join(chr(p.PREFIX_VALUE) for p in self.prefix)
return prefix_opcode + bytes(self.value.dump())
return prefix_opcode + bytes(self.value.dump())
class DelayedJump(object):
def __init__(self, type, label):
self.type = type
self.label = label
class JmpType(Instruction):
def __new__(cls, *initial_args):
if len(initial_args) == 1:
@@ -600,41 +622,49 @@ class JmpType(Instruction):
return DelayedJump(cls, arg)
return super(JmpType, cls).__new__(cls, *initial_args)
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(8, 0xff), Slash(2))]
class Xchg(Instruction):
default_32_bits = True
encoding = [(RawBits.from_int(5, 0x90 >> 3), RegisterRax(), X64RegisterSelector()),
(RawBits.from_int(5, 0x90 >> 3), X64RegisterSelector(), RegisterRax())]
class Ret(Instruction):
encoding = [(RawBits.from_int(8, 0xc3),)]
class Int3(Instruction):
encoding = [(RawBits.from_int(8, 0xcc),)]
class Dec(Instruction):
default_32_bits = True
encoding = [(RawBits.from_int(8, 0xff), Slash(1))]
class Inc(Instruction):
default_32_bits = True
encoding = [(RawBits.from_int(8, 0xff), Slash(0))]
class Add(Instruction):
default_32_bits = True
encoding = [(RawBits.from_int(8, 0x05), RegisterRax(), Imm32()),
(RawBits.from_int(8, 0x81), Slash(0), Imm32()),
#(RawBits.from_int(8, 0x01), ModRM(ModRM_REG64__REG64, ModRM_REG__DEREF_REG, ModRM_REG__DEREF_REG_IMM, ModRM_REG__DEREF_BASE_INDEX, )),]
(RawBits.from_int(8, 0x01), ModRM([ModRM_REG64__REG64, ModRM_REG64__MEM])),]
(RawBits.from_int(8, 0x01), ModRM([ModRM_REG64__REG64, ModRM_REG64__MEM]))]
class Sub(Instruction):
@@ -642,21 +672,26 @@ class Sub(Instruction):
encoding = [(RawBits.from_int(8, 0x2D), RegisterRax(), Imm32()),
(RawBits.from_int(8, 0x81), Slash(5), Imm32())]
class Out(Instruction):
encoding = [(RawBits.from_int(8, 0xee), FixedRegister('DX'), FixedRegister('AL')),
(RawBits.from_int(16, 0x66ef), FixedRegister('DX'), FixedRegister('AX')), # Fuck-it hardcoded prefix for now
(RawBits.from_int(16, 0x66ef), FixedRegister('DX'), FixedRegister('AX')), # Fuck-it hardcoded prefix for now
(RawBits.from_int(8, 0xef), FixedRegister('DX'), FixedRegister('EAX'))]
class In(Instruction):
encoding = [(RawBits.from_int(8, 0xec), FixedRegister('AL'), FixedRegister('DX')),
(RawBits.from_int(16, 0x66ed), FixedRegister('AX'), FixedRegister('DX')), # Fuck-it hardcoded prefix for now
(RawBits.from_int(16, 0x66ed), FixedRegister('AX'), FixedRegister('DX')), # Fuck-it hardcoded prefix for now
(RawBits.from_int(8, 0xed), FixedRegister('EAX'), FixedRegister('DX'))]
class Cpuid(Instruction):
encoding = [(RawBits.from_int(16, 0x0fa2),)]
class JmpImm(object):
accept_as_Ximmediat = (None)
def __init__(self, sub):
self.sub = sub
@@ -672,33 +707,41 @@ class JmpImm(object):
return (None, None, None)
return (1, BitArray.from_string(jmp_imm), None)
class JmpImm8(JmpImm):
accept_as_Ximmediat = staticmethod(accept_as_8immediat)
class JmpImm32(JmpImm):
accept_as_Ximmediat = staticmethod(accept_as_32immediat)
class Jmp(JmpType):
encoding = [(RawBits.from_int(8, 0xeb), JmpImm8(2)),
(RawBits.from_int(8, 0xe9), JmpImm32(5)),
(RawBits.from_int(13, 0xffe0 >> 3), X64RegisterSelector())]
class Jz(JmpType):
encoding = [(RawBits.from_int(8, 0x74), JmpImm8(2)),
(RawBits.from_int(16, 0x0f84), JmpImm32(6))]
class Jnz(JmpType):
encoding = [(RawBits.from_int(8, 0x75), JmpImm8(2)),
(RawBits.from_int(16, 0x0f85), JmpImm32(6))]
class Jb(JmpType):
encoding = [(RawBits.from_int(8, 0x72), JmpImm8(2)),
(RawBits.from_int(16, 0x0f82), JmpImm32(6))]
class Jbe(JmpType):
encoding = [(RawBits.from_int(8, 0x76), JmpImm8(2)),
(RawBits.from_int(16, 0x0f86), JmpImm32(6))]
class Jnb(JmpType):
encoding = [(RawBits.from_int(8, 0x73), JmpImm8(2)),
(RawBits.from_int(16, 0x0f83), JmpImm32(6))]
@@ -708,46 +751,57 @@ class Lea(Instruction):
refuse_reverse = True
encoding = [(RawBits.from_int(8, 0x8d), ModRM([ModRM_REG64__MEM], accept_reverse=False, has_direction_bit=False))]
class Mov(Instruction):
default_32_bits = True
encoding = [(Mov_RAX_OFF64(),), (Mov_OFF64_RAX(),), (RawBits.from_int(8, 0x89), ModRM([ModRM_REG64__REG64, ModRM_REG64__MEM])),
default_32_bits = True
encoding = [(Mov_RAX_OFF64(),), (Mov_OFF64_RAX(),), (RawBits.from_int(8, 0x89), ModRM([ModRM_REG64__REG64, ModRM_REG64__MEM])),
(RawBits.from_int(5, 0xb8 >> 3), X64RegisterSelector(), Imm64())]
class Cmp(Instruction):
default_32_bits = True
encoding = [(RawBits.from_int(8, 0x3d), RegisterRax(), Imm32()),
(RawBits.from_int(8, 0x81), Slash(7), Imm32()),
(RawBits.from_int(8, 0x3b), ModRM([ModRM_REG64__REG64, ModRM_REG64__MEM])),]
(RawBits.from_int(8, 0x3b), ModRM([ModRM_REG64__REG64, ModRM_REG64__MEM]))]
class Xor(Instruction):
default_32_bits = True
encoding = [(RawBits.from_int(8, 0x31), ModRM([ModRM_REG64__REG64, ModRM_REG64__MEM]))]
class Nop(Instruction):
encoding = [(RawBits.from_int(8, 0x90),)]
class Retf(Instruction):
default_32_bits = True
encoding = [(RawBits.from_int(8, 0xcb),)]
class Retf32(Instruction):
encoding = [(RawBits.from_int(8, 0xcb),)]
class _NopArtifact(Nop):
pass
def JmpAt(addr):
code = MultipleInstr()
code += Mov('RAX', addr)
code += Jmp('RAX')
return code
class Label(object):
def __init__(self, name):
self.name = name
class MultipleInstr(object):
JUMP_SIZE = 6
def __init__(self, init_instrs=()):
self.instrs = {}
self.labels = {}
@@ -822,12 +876,12 @@ class MultipleInstr(object):
return
def _reduce_shellcode(self):
to_remove = [offset for offset,instr in self.instrs.items() if type(instr) == _NopArtifact]
to_remove = [offset for offset, instr in self.instrs.items() if type(instr) == _NopArtifact]
while to_remove:
self._remove_nop_artifact(to_remove[0])
# _remove_nop_artifact will change the offsets of the nop
# Need to refresh these offset
to_remove = [offset for offset,instr in self.instrs.items() if type(instr) == _NopArtifact]
to_remove = [offset for offset, instr in self.instrs.items() if type(instr) == _NopArtifact]
def _remove_nop_artifact(self, offset):
"""Remove a NOP from the shellcode, adjust jump and labels"""
@@ -851,7 +905,7 @@ class MultipleInstr(object):
# dec offset of all Label after the NOP
for name, labeloffset in self.labels.items():
if labeloffset > offset:
self.labels[name] = labeloffset - 1
self.labels[name] = labeloffset - 1
# dec offset of all instr after the NOP
new_instr = {}
@@ -925,5 +979,4 @@ if in_IDA:
midap.here(idc.MinEA()).write(s.get_code())
idc.MakeFunction(idc.MinEA())
#tst()
# tst()
+101 -43
View File
@@ -1,6 +1,6 @@
import collections
import struct
import sys
class BitArray(object):
def __init__(self, size, bits):
@@ -61,9 +61,11 @@ class BitArray(object):
x = x & ((2 ** size) - 1)
return cls(size, bin(x)[2:])
# Prefix
class Prefix(object):
PREFIX_VALUE = None
def __init__(self, next=None):
self.next = next
@@ -73,36 +75,38 @@ class Prefix(object):
def get_code(self):
return chr(self.PREFIX_VALUE) + self.next.get_code()
def create_prefix(name, value):
prefix_type = type(name + "Type", (Prefix,), {'PREFIX_VALUE' : value})
setattr(sys.modules[__name__], name, prefix_type())
create_prefix('LockPrefix', 0xf0)
create_prefix('Repne', 0xf2)
create_prefix('Rep', 0xf3)
create_prefix('SSPrefix', 0x36)
create_prefix('CSPrefix', 0x2e)
create_prefix('DSPrefix', 0x3e)
create_prefix('ESPrefix', 0x26)
create_prefix('FSPrefix', 0x64)
create_prefix('GSPrefix', 0x65)
create_prefix('OperandSizeOverride', 0x66)
create_prefix('AddressSizeOverride', 0x67)
def create_prefix(name, value):
prefix_type = type(name + "Type", (Prefix,), {'PREFIX_VALUE': value})
return prefix_type()
LockPrefix = create_prefix('LockPrefix', 0xf0)
Repne = create_prefix('Repne', 0xf2)
Rep = create_prefix('Rep', 0xf3)
SSPrefix = create_prefix('SSPrefix', 0x36)
CSPrefix = create_prefix('CSPrefix', 0x2e)
DSPrefix = create_prefix('DSPrefix', 0x3e)
ESPrefix = create_prefix('ESPrefix', 0x26)
FSPrefix = create_prefix('FSPrefix', 0x64)
GSPrefix = create_prefix('GSPrefix', 0x65)
OperandSizeOverride = create_prefix('OperandSizeOverride', 0x66)
AddressSizeOverride = create_prefix('AddressSizeOverride', 0x67)
# Main informations about X86
mem_access = collections.namedtuple('mem_access', ['base', 'index', 'scale', 'disp', 'prefix'])
x86_regs = ['EAX', 'ECX', 'EDX', 'EBX', 'ESP', 'EBP', 'ESI', 'EDI']
x86_16bits_regs = ['AX', 'CX', 'DX', 'BX', 'SP', 'BP', 'SI', 'DI']
x86_segment_selectors = {'CS' : CSPrefix, 'DS' : DSPrefix, 'ES' : ESPrefix, 'SS' : SSPrefix,
'FS': FSPrefix, 'GS' : GSPrefix}
x86_segment_selectors = {'CS': CSPrefix, 'DS': DSPrefix, 'ES': ESPrefix, 'SS': SSPrefix,
'FS': FSPrefix, 'GS': GSPrefix}
class X86(object):
@staticmethod
def is_reg(name):
try:
return name.upper() in x86_regs + x86_16bits_regs
except AttributeError: # Not a string
except AttributeError: # Not a string
return False
@staticmethod
@@ -141,9 +145,11 @@ def create_displacement(base=None, index=None, scale=None, disp=0, prefix=None):
raise ValueError("Cannot create displacement with index == ESP")
return mem_access(base, index, scale, disp, prefix)
def deref(disp):
return create_displacement(disp=disp)
def mem(data):
"""Parse a memory access string of format [EXPR] or seg:[EXPR]
EXPR may describe: BASE | INDEX * SCALE | DISPLACEMENT or any combinaison (in this order)
@@ -165,7 +171,7 @@ def mem(data):
# A l'arrache.. j'aime pas le parsing de trucs
data = data[1:-1]
items = data.split("+")
parsed_items = {'prefix' : prefix}
parsed_items = {'prefix': prefix}
for item in items:
item = item.strip()
# Index * scale
@@ -183,7 +189,7 @@ def mem(data):
raise NotImplementedError("16bits modrm")
try:
scale = int(scale, 0)
except ValueError as e:
except ValueError:
raise ValueError("Invalid scale <{0}> in mem access".format(scale))
parsed_items['scale'] = scale
parsed_items['index'] = index
@@ -192,7 +198,7 @@ def mem(data):
if X86.is_reg(item):
if X86.reg_size(item) == 16:
raise NotImplementedError("16bits modrm")
if not 'base' in parsed_items:
if 'base' not in parsed_items:
parsed_items['base'] = item
continue
# Already have base + index -> cannot avec another register in expression
@@ -202,19 +208,19 @@ def mem(data):
continue
try:
disp = int(item, 0)
except ValueError as e:
except ValueError:
raise ValueError("Invalid base/index or displacement <{0}> in mem access".format(item))
if 'disp' in parsed_items:
raise ValueError("Multiple displacement in mem expression <{0}>".format(data))
parsed_items['disp'] = disp
return create_displacement(**parsed_items)
# Helper to get the BitArray associated to a register
# Helper to get the BitArray associated to a register
class X86RegisterSelector(object):
size = 3 # bits
reg_opcode = {v : BitArray.from_int(size=3, x=i) for i, v in enumerate(x86_regs)}
reg_opcode.update({v : BitArray.from_int(size=3, x=i) for i, v in enumerate(x86_16bits_regs)})
size = 3 # bits
reg_opcode = {v: BitArray.from_int(size=3, x=i) for i, v in enumerate(x86_regs)}
reg_opcode.update({v: BitArray.from_int(size=3, x=i) for i, v in enumerate(x86_16bits_regs)})
def accept_arg(self, args, instr_state):
x = args[0]
@@ -227,8 +233,8 @@ class X86RegisterSelector(object):
def get_reg_bits(cls, name):
return cls.reg_opcode[name.upper()]
## Instruction Parameters
# Instruction Parameters
class FixedRegister(object):
def __init__(self, register):
self.reg = register.upper()
@@ -241,28 +247,32 @@ class FixedRegister(object):
RegisterEax = lambda: FixedRegister('EAX')
class RawBits(BitArray):
def accept_arg(self, args, instr_state):
return (0, self)
# Immediat value logic
# All 8/16 bits stuff are sign extended
class ImmediatOverflow(ValueError):
pass
def accept_as_8immediat(x):
try:
return struct.pack("<b", x)
except struct.error:
raise ImmediatOverflow("8bits signed Immediat overflow")
def accept_as_16immediat(x):
try:
return struct.pack("<h", x)
except struct.error:
raise ImmediatOverflow("16bits signed Immediat overflow")
def accept_as_32immediat(x):
try:
return struct.pack("<i", x)
@@ -273,6 +283,7 @@ def accept_as_32immediat(x):
except struct.error:
raise ImmediatOverflow("32bits signed Immediat overflow")
class Imm8(object):
def accept_arg(self, args, instr_state):
try:
@@ -285,6 +296,7 @@ class Imm8(object):
return None, None
return (1, BitArray.from_string(imm8))
class Imm16(object):
def accept_arg(self, args, instr_state):
try:
@@ -297,6 +309,7 @@ class Imm16(object):
return None, None
return (1, BitArray.from_string(imm16))
class Imm32(object):
def accept_arg(self, args, instr_state):
try:
@@ -309,6 +322,7 @@ class Imm32(object):
return None, None
return (1, BitArray.from_string(imm32))
class ModRM(object):
def __init__(self, sub_modrm, accept_reverse=True, has_direction_bit=True):
self.accept_reverse = accept_reverse
@@ -336,6 +350,7 @@ class ModRM(object):
class ModRM_REG__REG(object):
@classmethod
def match(cls, arg1, arg2):
return X86.is_reg(arg1) and X86.is_reg(arg2)
@@ -351,7 +366,9 @@ class ModRM_REG__REG(object):
self.after = BitArray(0, "")
self.direction = 0
class ModRM_REG__MEM(object):
@classmethod
def match(cls, arg1, arg2):
return X86.is_reg(arg1) and X86.is_mem_acces(arg2)
@@ -429,7 +446,7 @@ class ModRM_REG__MEM(object):
raise ValueError("Displacement {0} is too big".format(hex(displacement)))
def compute_sib(self, mem_access):
scale = {1: 0, 2 : 1, 4: 2, 8 : 3}
scale = {1: 0, 2: 1, 4: 2, 8: 3}
if mem_access.index is None:
return BitArray(2, "00") + BitArray(3, "100") + X86RegisterSelector.get_reg_bits(mem_access.base)
if mem_access.scale not in scale:
@@ -454,13 +471,15 @@ class Slash(object):
arg_consum, value = ModRM([ModRM_REG__REG, ModRM_REG__MEM], has_direction_bit=False).accept_arg(args[:1] + [self.reg] + args[1:], instr_state)
if value is None:
return arg_consum, value
return arg_consum-1, value
return arg_consum - 1, value
instr_state = collections.namedtuple('instr_state', ['previous', 'prefixes'])
class Instruction(object):
"""Base class of instructions, use `encoding` to find a valid way to assemble the instruction"""
encoding = []
def __init__(self, *initial_args):
for type_encoding in self.encoding:
args = list(initial_args)
@@ -472,8 +491,8 @@ class Instruction(object):
break
res.append(value)
del args[:arg_consum]
else: # if no break
if args: # if still args: fail
else: # if no break
if args: # if still args: fail
continue
self.value = sum(res, BitArray(0, ""))
self.prefix = prefix
@@ -482,17 +501,21 @@ class Instruction(object):
def get_code(self):
prefix_opcode = b"".join(chr(p.PREFIX_VALUE) for p in self.prefix)
return prefix_opcode + bytes(self.value.dump())
return prefix_opcode + bytes(self.value.dump())
# Jump helpers
class DelayedJump(object):
"""A jump to a label :NAME"""
def __init__(self, type, label):
self.type = type
self.label = label
class JmpType(Instruction):
"""Dispatcher between a real jump or DelayedJump if parameters is a label"""
def __new__(cls, *initial_args):
if len(initial_args) == 1:
arg = initial_args[0]
@@ -500,10 +523,12 @@ class JmpType(Instruction):
return DelayedJump(cls, arg)
return super(JmpType, cls).__new__(cls, *initial_args)
class JmpImm(object):
"""Immediat parameters for Jump instruction
Sub a specified size from the size to jump to `emulate` a jump from the begin address of the instruction"""
accept_as_Ximmediat = None
def __init__(self, sub):
self.sub = sub
@@ -519,125 +544,158 @@ class JmpImm(object):
return (None, None)
return (1, BitArray.from_string(jmp_imm))
class JmpImm8(JmpImm):
accept_as_Ximmediat = staticmethod(accept_as_8immediat)
class JmpImm32(JmpImm):
accept_as_Ximmediat = staticmethod(accept_as_32immediat)
## Instructions
# Instructions
class Jmp(JmpType):
encoding = [(RawBits.from_int(8, 0xeb), JmpImm8(2)),
(RawBits.from_int(8, 0xe9), JmpImm32(5))]
class Jz(JmpType):
encoding = [(RawBits.from_int(8, 0x74), JmpImm8(2)),
(RawBits.from_int(16, 0x0f84), JmpImm32(6))]
class Jnz(JmpType):
encoding = [(RawBits.from_int(8, 0x75), JmpImm8(2)),
(RawBits.from_int(16, 0x0f85), JmpImm32(6))]
class Jbe(JmpType):
encoding = [(RawBits.from_int(8, 0x76), JmpImm8(2)),
(RawBits.from_int(16, 0x0f86), JmpImm32(6))]
class Jnb(JmpType):
encoding = [(RawBits.from_int(8, 0x73), JmpImm8(2)),
(RawBits.from_int(16, 0x0f83), JmpImm32(6))]
class Push(Instruction):
encoding = [(RawBits.from_int(5, 0x50 >> 3), X86RegisterSelector()),
(RawBits.from_int(8, 0x68), Imm32())]
class Pop(Instruction):
encoding = [(RawBits.from_int(5, 0x58 >> 3), X86RegisterSelector())]
class Dec(Instruction):
encoding = [(RawBits.from_int(5, 0x48 >> 3), X86RegisterSelector())]
class Inc(Instruction):
encoding = [(RawBits.from_int(5, 0x40 >> 3), X86RegisterSelector()),
(RawBits.from_int(8, 0xff), Slash(0)),]
(RawBits.from_int(8, 0xff), Slash(0))]
class Add(Instruction):
encoding = [(RawBits.from_int(8, 0x05), RegisterEax(), Imm32()),
(RawBits.from_int(8, 0x81), Slash(0), Imm32()),
(RawBits.from_int(8, 0x01), ModRM([ModRM_REG__REG, ModRM_REG__MEM])),]
(RawBits.from_int(8, 0x01), ModRM([ModRM_REG__REG, ModRM_REG__MEM]))]
class Sub(Instruction):
encoding = [(RawBits.from_int(8, 0x2D), RegisterEax(), Imm32()),
(RawBits.from_int(8, 0x81), Slash(5), Imm32())]
class Mov(Instruction):
encoding = [(RawBits.from_int(8, 0x89), ModRM([ModRM_REG__REG, ModRM_REG__MEM])),
(RawBits.from_int(5, 0xb8 >> 3), X86RegisterSelector(), Imm32())]
class Movsb(Instruction):
encoding = [(RawBits.from_int(8, 0xa4),)]
class Movsd(Instruction):
encoding = [(RawBits.from_int(8, 0xa5),)]
class Lea(Instruction):
encoding = [(RawBits.from_int(8, 0x8d), ModRM([ModRM_REG__MEM], accept_reverse=False, has_direction_bit=False))]
class Cmp(Instruction):
encoding = [(RawBits.from_int(8, 0x3d), RegisterEax(), Imm32()),
(RawBits.from_int(8, 0x81), Slash(7), Imm32()),
(RawBits.from_int(8, 0x3b), ModRM([ModRM_REG__REG, ModRM_REG__MEM])),]
(RawBits.from_int(8, 0x3b), ModRM([ModRM_REG__REG, ModRM_REG__MEM]))]
class Out(Instruction):
encoding = [(RawBits.from_int(8, 0xee), FixedRegister('DX'), FixedRegister('AL')),
(RawBits.from_int(16, 0x66ef), FixedRegister('DX'), FixedRegister('AX')), # Fuck-it hardcoded prefix for now
(RawBits.from_int(16, 0x66ef), FixedRegister('DX'), FixedRegister('AX')), # Fuck-it hardcoded prefix for now
(RawBits.from_int(8, 0xef), FixedRegister('DX'), FixedRegister('EAX'))]
class In(Instruction):
encoding = [(RawBits.from_int(8, 0xec), FixedRegister('AL'), FixedRegister('DX')),
(RawBits.from_int(16, 0x66ed), FixedRegister('AX'), FixedRegister('DX')), # Fuck-it hardcoded prefix for now
(RawBits.from_int(16, 0x66ed), FixedRegister('AX'), FixedRegister('DX')), # Fuck-it hardcoded prefix for now
(RawBits.from_int(8, 0xed), FixedRegister('EAX'), FixedRegister('DX'))]
class Xor(Instruction):
encoding = [(RawBits.from_int(8, 0x31), ModRM([ModRM_REG__REG]))]
class Xchg(Instruction):
encoding = [(RawBits.from_int(5, 0x90 >> 3), RegisterEax(), X86RegisterSelector()), (RawBits.from_int(5, 0x90 >> 3), X86RegisterSelector(), RegisterEax())]
class Call(Instruction):
encoding = [(RawBits.from_int(8, 0xff), Slash(2))]
class Cpuid(Instruction):
encoding = [(RawBits.from_int(16, 0x0fa2),)]
class Ret(Instruction):
encoding = [(RawBits.from_int(8, 0xc3),)]
class Nop(Instruction):
encoding = [(RawBits.from_int(8, 0x90),)]
class Retf(Instruction):
encoding = [(RawBits.from_int(8, 0xcb),)]
class Int3(Instruction):
encoding = [(RawBits.from_int(8, 0xcc),)]
class _NopArtifact(Nop):
"""Special NOP used in shellcode reduction"""
pass
class Label(object):
def __init__(self, name):
self.name = name
def JmpAt(addr):
code = MultipleInstr()
code += Push(addr)
code += Ret()
return code
class MultipleInstr(object):
JUMP_SIZE = 6
def __init__(self, init_instrs=()):
self.instrs = {}
self.labels = {}
@@ -712,12 +770,12 @@ class MultipleInstr(object):
return
def _reduce_shellcode(self):
to_remove = [offset for offset,instr in self.instrs.items() if type(instr) == _NopArtifact]
to_remove = [offset for offset, instr in self.instrs.items() if type(instr) == _NopArtifact]
while to_remove:
self._remove_nop_artifact(to_remove[0])
# _remove_nop_artifact will change the offsets of the nop
# Need to refresh these offset
to_remove = [offset for offset,instr in self.instrs.items() if type(instr) == _NopArtifact]
to_remove = [offset for offset, instr in self.instrs.items() if type(instr) == _NopArtifact]
def _remove_nop_artifact(self, offset):
# Remove a NOP from the shellcode
@@ -741,7 +799,7 @@ class MultipleInstr(object):
# dec offset of all Label after the NOP
for name, labeloffset in self.labels.items():
if labeloffset > offset:
self.labels[name] = labeloffset - 1
self.labels[name] = labeloffset - 1
# dec offset of all instr after the NOP
new_instr = {}
@@ -805,4 +863,4 @@ if in_IDA:
def tst():
reset()
midap.here(idc.MinEA()).write(s.get_code())
idc.MakeFunction(idc.MinEA())
idc.MakeFunction(idc.MinEA())
+6 -6
View File
@@ -4,10 +4,12 @@ from simple_x64 import *
disassembleur = capstone.Cs(capstone.CS_ARCH_X86, capstone.CS_MODE_64)
disassembleur.detail = True
def disas(x):
return list(disassembleur.disasm(x, 0))
mnemonic_name_exception = {'movabs' : 'mov'}
mnemonic_name_exception = {'movabs': 'mov'}
class TestInstr(object):
def __init__(self, instr_to_test, immediat_accepted=None):
@@ -38,7 +40,7 @@ class TestInstr(object):
if len(args) != len(capres_op):
raise AssertionError("Expected {0} operands got {1}".format(len(args), len(capres_op)))
for op_args, cap_op in zip(args, capres_op):
if isinstance(op_args, str): # Register
if isinstance(op_args, str): # Register
if cap_op.type != capstone.x86.X86_OP_REG:
raise AssertionError("Expected args {0} operands got {1}".format(op_args, capres_op))
if op_args.lower() != capres.reg_name(cap_op.reg).lower():
@@ -56,7 +58,7 @@ class TestInstr(object):
raise AssertionError("Expected Memaccess <{0}> got {1}".format(memaccess, cap_op))
if memaccess.prefix is not None and capres.prefix[1] != x64_segment_selectors[memaccess.prefix].PREFIX_VALUE:
try:
get_prefix = [n for n,x in x64_segment_selectors.items() if x.PREFIX_VALUE == capres.prefix[1]][0]
get_prefix = [n for n, x in x64_segment_selectors.items() if x.PREFIX_VALUE == capres.prefix[1]][0]
except IndexError:
get_prefix = None
raise AssertionError("Expected Segment overide <{0}> got {1}".format(memaccess.prefix, get_prefix))
@@ -108,8 +110,6 @@ TestInstr(Push)(-1)
TestInstr(Call)('RAX')
TestInstr(Call)(mem('[RAX + RCX * 8]'))
TestInstr(Cpuid)()
TestInstr(Xchg)('RAX', 'RSP')
assert Xchg('RAX', 'RCX').get_code() == Xchg('RCX', 'RAX').get_code()
@@ -118,4 +118,4 @@ code += Nop()
code += Rep + Nop()
code += Ret()
print(repr(code.get_code()))
assert code.get_code() == "\x90\xf3\x90\xc3"
assert code.get_code() == "\x90\xf3\x90\xc3"
+3 -12
View File
@@ -4,6 +4,7 @@ from simple_x86 import *
disassembleur = capstone.Cs(capstone.CS_ARCH_X86, capstone.CS_MODE_32)
disassembleur.detail = True
def disas(x):
return list(disassembleur.disasm(x, 0))
@@ -42,7 +43,7 @@ class TestInstr(object):
if len(args) != len(capres_op):
raise AssertionError("Expected {0} operands got {1}".format(len(args), len(capres_op)))
for op_args, cap_op in zip(args, capres_op):
if isinstance(op_args, str): # Register
if isinstance(op_args, str): # Register
if cap_op.type != capstone.x86.X86_OP_REG:
raise AssertionError("Expected args {0} operands got {1}".format(op_args, capres_op))
if op_args.lower() != capres.reg_name(cap_op.reg).lower():
@@ -60,7 +61,7 @@ class TestInstr(object):
raise AssertionError("Expected Memaccess <{0}> got {1}".format(memaccess, cap_op))
if memaccess.prefix is not None and capres.prefix[1] != x86_segment_selectors[memaccess.prefix].PREFIX_VALUE:
try:
get_prefix = [n for n,x in x86_segment_selectors.items() if x.PREFIX_VALUE == capres.prefix[1]][0]
get_prefix = [n for n, x in x86_segment_selectors.items() if x.PREFIX_VALUE == capres.prefix[1]][0]
except IndexError:
get_prefix = None
raise AssertionError("Expected Segment overide <{0}> got {1}".format(memaccess.prefix, get_prefix))
@@ -87,38 +88,28 @@ TestInstr(Mov)('EDX', mem('[0x11223344]'))
TestInstr(Mov)('EDX', mem('[ESP + EBP * 2 + 0x223344]'))
TestInstr(Mov)(mem('[EBP + EBP * 2 + 0x223344]'), 'ESP')
TestInstr(Mov)('ESI', mem('[ESI + EDI * 1]'))
TestInstr(Mov)('EAX', mem('fs:[0x30]'))
TestInstr(Mov)('EDI', mem('gs:[EAX + ECX * 4]'))
TestInstr(Mov)('AX', 'AX')
TestInstr(Mov)('SI', 'DI')
TestInstr(Mov)('AX', 'AX')
TestInstr(Mov)('AX', mem('fs:[0x30]'))
TestInstr(Mov)('AX', mem('fs:[EAX + 0x30]'))
TestInstr(Mov)('AX', mem('fs:[EAX + ECX * 4+0x30]'))
TestInstr(Add)('EAX', 8)
TestInstr(Add)('EAX', 0xffffffff)
TestInstr(Inc)('EAX')
TestInstr(Inc)(mem('[0x42424242]'))
TestInstr(Lea)('EAX', mem('[EAX + 1]'))
TestInstr(Lea)('ECX', mem('[EDI + -0xff]'))
TestInstr(Call)('EAX')
TestInstr(Call)(mem('[EAX + ECX * 8]'))
TestInstr(Cpuid)()
TestInstr(Movsb, expected_result='movsb byte ptr es:[edi], byte ptr [esi]')()
TestInstr(Movsd, expected_result='movsd dword ptr es:[edi], dword ptr [esi]')()
TestInstr(Xchg)('EAX', 'ESP')
assert Xchg('EAX', 'ECX').get_code() == Xchg('ECX', 'EAX').get_code()
code = MultipleInstr()
code += Nop()
code += Rep + Nop()