mirror of
https://github.com/hakril/PythonForWindows
synced 2026-06-08 14:31:45 +00:00
cosmetic: fucking linter :(
This commit is contained in:
@@ -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
@@ -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
|
||||
|
||||
@@ -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
@@ -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
@@ -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())
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -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()
|
||||
|
||||
Reference in New Issue
Block a user