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
+4 -6
View File
@@ -11,7 +11,7 @@ Exported:
current_thread : :class:`windows.winobject.CurrentThread`
"""
import windows.winproxy
from . import winproxy
from .utils import VirtualProtected
from .winobject import System, CurrentProcess, CurrentThread
@@ -19,9 +19,7 @@ system = System()
current_process = CurrentProcess()
current_thread = CurrentThread()
__all__ = ["system", "VirtualProtected", 'current_process', 'current_thread']
# Late import
# Late import: other imports should go here
# Do not move it: risk of circular import
import windows.vectored_exception
__all__ = ["system", "VirtualProtected", 'current_process', 'current_thread', 'winproxy']
+10 -7
View File
@@ -3,32 +3,35 @@ import sys
import logging
import inspect
options = {'active':False, 'cats':None}
options = {'active': False, 'cats': None}
def get_stack_func_name(lvl):
info = inspect.stack()[lvl]
return info[0], info[3]
def do_dbgprint(msg, type=None):
if (options['cats'] is None) or type.upper() in options['cats']:
frame, func = get_stack_func_name(2)
logger = logging.getLogger(frame.f_globals['__name__'] + ":" + func)
logger.debug(msg)
def do_nothing(*args, **kwargs):
return None
def parse_option(s):
if s[0] == "=":
s = s[1:]
if s:
cats = [x.upper() for x in s.split('-')]
options['cats'] = cats
formt='DBG|%(name)s|%(message)s'
formt = 'DBG|%(name)s|%(message)s'
logging.basicConfig(format=formt, level=logging.DEBUG)
try:
if 'DBGPRINT' in os.environ:
parse_option(os.environ['DBGPRINT'])
@@ -42,4 +45,4 @@ try:
except Exception as e:
dbgprint = do_nothing
print("dbgprint Error: {0}({1})".format(type(e), e))
x = type(e), e
x = type(e), e
+8 -7
View File
@@ -3,7 +3,8 @@ import ctypes
import windows.utils as utils
from . import native_exec
from .generated_def import winfuncs, windef
from .generated_def import winfuncs
from .generated_def.windef import PAGE_EXECUTE_READWRITE
from .generated_def.winstructs import *
@@ -15,6 +16,7 @@ class Callback(object):
func._types_info = self.types
return func
class KnownCallback(object):
types = ()
@@ -22,6 +24,7 @@ class KnownCallback(object):
func._types_info = self.types
return func
def add_callback_to_module(callback):
setattr(sys.modules[__name__], type(callback).__name__, callback)
@@ -31,13 +34,12 @@ for func in winfuncs.functions:
callback_name = func + "Callback"
class CallBackDeclaration(KnownCallback):
types = (prototype._restype_,) + prototype._argtypes_
types = (prototype._restype_,) + prototype._argtypes_
CallBackDeclaration.__name__ = callback_name
add_callback_to_module(CallBackDeclaration())
class IATHook(object):
"""Look at my hook <3"""
@@ -64,17 +66,16 @@ class IATHook(object):
return res
def enable(self):
with utils.VirtualProtected(self.entry.addr, ctypes.sizeof(PVOID), windef.PAGE_EXECUTE_READWRITE):
with utils.VirtualProtected(self.entry.addr, ctypes.sizeof(PVOID), PAGE_EXECUTE_READWRITE):
self.entry.value = self.stub
self.is_enable = True
def disable(self):
with utils.VirtualProtected(self.entry.addr, ctypes.sizeof(PVOID), windef.PAGE_EXECUTE_READWRITE):
with utils.VirtualProtected(self.entry.addr, ctypes.sizeof(PVOID), PAGE_EXECUTE_READWRITE):
self.entry.value = self.entry.nonhookvalue
self.is_enable = False
def hook_callback(self, *args):
original_args = args
adapted_args = []
for value, type in zip(args, self.original_types[1:]):
if type == ctypes.c_wchar_p:
@@ -88,4 +89,4 @@ class IATHook(object):
if args == ():
args = adapted_args
return self.realfunction(*args)
return self.callback(*adapted_args, real_function=real_function)
return self.callback(*adapted_args, real_function=real_function)
+4 -12
View File
@@ -1,6 +1,3 @@
import sys
import os
import windows
import windows.utils as utils
@@ -21,12 +18,7 @@ def get_loadlib_getproc(target):
# 32 to 32 injection
def generate_python_exec_shellcode_32(target, PYDLL_addr, PyInit, PyRun, PYCODE_ADDR):
#LoadLibraryA = utils.get_func_addr('kernel32', 'LoadLibraryA')
#GetProcAddress = utils.get_func_addr('kernel32', 'GetProcAddress')
LoadLibraryA, GetProcAddress = get_loadlib_getproc(target)
print("LoadLibraryA = {0}".format(hex(LoadLibraryA)))
print("LoadLibraryA = {0}".format(hex(GetProcAddress)))
code = x86.MultipleInstr()
# Load python27.dll
code += x86.Push(PYDLL_addr)
@@ -52,6 +44,7 @@ def generate_python_exec_shellcode_32(target, PYDLL_addr, PyInit, PyRun, PYCODE_
code += x86.Ret()
return code.get_code()
# 64 to 64 injection
def generate_python_exec_shellcode_64(target, PYDLL_addr, PyInit, PyRun, PYCODE_ADDR):
@@ -112,7 +105,6 @@ def inject_python_command(process, code_injected, PYDLL="python27.dll\x00"):
process.write_memory(remote_addr, PYDLL)
remote_addr += len(PYDLL)
PyInitT_ADDR = remote_addr
process.write_memory(remote_addr, PyInitT)
remote_addr += len(PyInitT)
@@ -127,14 +119,14 @@ def inject_python_command(process, code_injected, PYDLL="python27.dll\x00"):
SHELLCODE_ADDR = remote_addr
if process.bitness == 32:
shellcode = generate_python_exec_shellcode_32(process, PYDLL_addr, PyInitT_ADDR, Pyrun_ADDR, PYCODE_ADDR)
shellcode_generator = generate_python_exec_shellcode_32
else:
shellcode = generate_python_exec_shellcode_64(process, PYDLL_addr, PyInitT_ADDR, Pyrun_ADDR, PYCODE_ADDR)
shellcode_generator = generate_python_exec_shellcode_64
shellcode = shellcode_generator(process, PYDLL_addr, PyInitT_ADDR, Pyrun_ADDR, PYCODE_ADDR)
process.write_memory(SHELLCODE_ADDR, shellcode)
return SHELLCODE_ADDR
def execute_python_code(process, code):
print("me = {0}".format(windows.current_process.bitness))
print("him = {0}".format(process.bitness))
+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()
+19 -18
View File
@@ -56,7 +56,7 @@ class TCP4Connection(MIB_TCPROW_OWNER_PID):
def __repr__(self):
if not self.established:
return "<TCP IPV4 Listening socket on {0}:{1}>".format(self.local_addr, self.local_port)
return "<TCP IPV4 Connection {0}:{1} -> {2}:{3}>".format(self.local_addr, self.local_port, self.remote_addr, self.remote_port)
return "<TCP IPV4 Connection {s.local_addr}:{s.local_port} -> {s.remote_addr}:{s.remote_port}>".format(s=self)
class TCP6Connection(MIB_TCP6ROW_OWNER_PID):
@@ -107,11 +107,13 @@ def get_MIB_TCPTABLE_OWNER_PID_from_buffer(buffer):
class _GENERATED_MIB_TCPTABLE_OWNER_PID(ctypes.Structure):
_fields_ = [
("dwNumEntries", DWORD),
("table", TCP4Connection * nb_entry),
]
("dwNumEntries", DWORD),
("table", TCP4Connection * nb_entry),
]
return _GENERATED_MIB_TCPTABLE_OWNER_PID.from_buffer(buffer)
def get_MIB_TCP6TABLE_OWNER_PID_from_buffer(buffer):
x = windows.generated_def.winstructs.MIB_TCP6TABLE_OWNER_PID.from_buffer(buffer)
nb_entry = x.dwNumEntries
@@ -119,33 +121,32 @@ def get_MIB_TCP6TABLE_OWNER_PID_from_buffer(buffer):
# Struct _MIB_TCP6TABLE_OWNER_PID definitions
class _GENERATED_MIB_TCP6TABLE_OWNER_PID(Structure):
_fields_ = [
("dwNumEntries", DWORD),
("table", TCP6Connection * nb_entry),
]
return _GENERATED_MIB_TCP6TABLE_OWNER_PID.from_buffer(buffer)
("dwNumEntries", DWORD),
("table", TCP6Connection * nb_entry),
]
return _GENERATED_MIB_TCP6TABLE_OWNER_PID.from_buffer(buffer)
def get_tcp_ipv4_sockets():
size = ctypes.c_uint(0)
try:
windows.winproxy.GetExtendedTcpTable(None, ctypes.byref(size), ulAf=windows.generated_def.windef.AF_INET)
except windows.winproxy.IphlpapiError as e:
pass # Allow us to set size to the needed value
windows.winproxy.GetExtendedTcpTable(None, ctypes.byref(size), ulAf=AF_INET)
except windows.winproxy.IphlpapiError:
pass # Allow us to set size to the needed value
buffer = (ctypes.c_char * size.value)()
windows.winproxy.GetExtendedTcpTable(buffer, ctypes.byref(size), ulAf=windows.generated_def.windef.AF_INET)
windows.winproxy.GetExtendedTcpTable(buffer, ctypes.byref(size), ulAf=AF_INET)
t = get_MIB_TCPTABLE_OWNER_PID_from_buffer(buffer)
return list(t.table)
def get_tcp_ipv6_sockets():
size = ctypes.c_uint(0)
try:
windows.winproxy.GetExtendedTcpTable(None, ctypes.byref(size), ulAf=windows.generated_def.windef.AF_INET6)
except windows.winproxy.IphlpapiError as e:
pass # Allow us to set size to the needed value
windows.winproxy.GetExtendedTcpTable(None, ctypes.byref(size), ulAf=AF_INET6)
except windows.winproxy.IphlpapiError:
pass # Allow us to set size to the needed value
buffer = (ctypes.c_char * size.value)()
windows.winproxy.GetExtendedTcpTable(buffer, ctypes.byref(size), ulAf=windows.generated_def.windef.AF_INET6)
windows.winproxy.GetExtendedTcpTable(buffer, ctypes.byref(size), ulAf=AF_INET6)
t = get_MIB_TCP6TABLE_OWNER_PID_from_buffer(buffer)
return list(t.table)
+21 -31
View File
@@ -1,6 +1,4 @@
import sys
import ctypes
import struct
import windows
import windows.hooks as hooks
import windows.utils as utils
@@ -22,10 +20,12 @@ def RedefineCtypesStruct(struct, replacement):
NewStruct.__name__ = struct.__name__
return NewStruct
# type replacement based on name
def transform_ctypes_fields(struct, replacement):
return [(name, replacement.get(name, type)) for name, type in struct._fields_]
def PEFile(baseaddr, target=None):
# TODO: 32 with target 32
# 64 with target 64
@@ -40,14 +40,12 @@ def PEFile(baseaddr, target=None):
raise NotImplementedError("Parse 32bits PE with 64bits current_process")
elif targetedbitness == 64 and proc_bitness == 32:
ctypes_structure_transformer = rctypes.transform_type_to_remote64bits
def create_structure_at(structcls, addr):
# try:
return rctypes.transform_type_to_remote64bits(structcls)(addr, target)
#except:
#import pdb;pdb.set_trace()
#return rctypes.transform_type_to_remote64bits(structcls)(addr, target)
elif targetedbitness == proc_bitness: # Does not handle remote of same bitness..
elif targetedbitness == proc_bitness: # Does not handle remote of same bitness..
ctypes_structure_transformer = lambda x: x
def create_structure_at(structcls, addr):
return structcls.from_address(addr)
else:
@@ -123,15 +121,13 @@ def PEFile(baseaddr, target=None):
self.hook.disable()
self.hook = None
return True
class PEFile(object):
def __init__(self):
self.baseaddr = baseaddr
def get_DOS_HEADER(self):
return create_structure_at(IMAGE_DOS_HEADER, baseaddr)
return create_structure_at(IMAGE_DOS_HEADER, baseaddr)
def get_NT_HEADER(self):
return self.get_DOS_HEADER().get_NT_HEADER()
@@ -148,13 +144,11 @@ def PEFile(baseaddr, target=None):
return []
import_descriptor_addr = RVA(import_datadir.VirtualAddress).addr
current_import_descriptor = create_structure_at(self.IMAGE_IMPORT_DESCRIPTOR, import_descriptor_addr)
#current_import_descriptor = self.IMAGE_IMPORT_DESCRIPTOR.from_address(import_descriptor_addr)
res = []
while current_import_descriptor.FirstThunk:
res.append(current_import_descriptor)
import_descriptor_addr += ctypes.sizeof(self.IMAGE_IMPORT_DESCRIPTOR)
#current_import_descriptor = self.IMAGE_IMPORT_DESCRIPTOR.from_address(import_descriptor_addr)
current_import_descriptor = create_structure_at(self.IMAGE_IMPORT_DESCRIPTOR, import_descriptor_addr)
res.append(current_import_descriptor)
import_descriptor_addr += ctypes.sizeof(self.IMAGE_IMPORT_DESCRIPTOR)
current_import_descriptor = create_structure_at(self.IMAGE_IMPORT_DESCRIPTOR, import_descriptor_addr)
return res
def get_EXPORT_DIRECTORY(self):
@@ -163,22 +157,20 @@ def PEFile(baseaddr, target=None):
return None
export_directory_addr = baseaddr + export_directory_rva
return create_structure_at(self._IMAGE_EXPORT_DIRECTORY, export_directory_addr)
#return self._IMAGE_EXPORT_DIRECTORY.from_address(export_directory_addr)
class PESection(ctypes_structure_transformer(IMAGE_SECTION_HEADER)):
@utils.fixedpropety
def name(self):
return ctypes.c_char_p(ctypes.addressof(self.Name)).value
def __repr__(self):
return "<PESection \"{0}\">".format(self.name)
@utils.fixedpropety
def sections(self):
nt_header = self.get_NT_HEADER()
nb_section = nt_header.FileHeader.NumberOfSections
base_section = ctypes.addressof(nt_header) + ctypes.sizeof(nt_header)
#IMAGE_SECTION_H = ctypes_structure_transformer(IMAGE_SECTION_HEADER)
sections_array = create_structure_at(self.PESection * nb_section, base_section)
return list(sections_array)
@@ -195,7 +187,6 @@ def PEFile(baseaddr, target=None):
res[rva_name.str] = rva_addr.addr
return res
# TODO: get imports by parsing other modules exports if no INT
@utils.fixedpropety
def imports(self):
@@ -208,19 +199,18 @@ def PEFile(baseaddr, target=None):
# str(name.decode()) -> python2 and python3 compatible for str result
iat_entry.ord = ord
iat_entry.name = str(name.decode()) if name else ""
res.setdefault(import_descriptor.Name.str.lower(),[]).extend(IAT)
res.setdefault(import_descriptor.Name.str.lower(), []).extend(IAT)
return res
# Will be usable as `self.IMAGE_IMPORT_DESCRIPTOR`
class IMAGE_IMPORT_DESCRIPTOR(ctypes.Structure):
_fields_ = transform_ctypes_fields(IMAGE_IMPORT_DESCRIPTOR, {"Name" : StringRVa, "OriginalFirstThunk" : RVA, "FirstThunk" : RVA})
_fields_ = transform_ctypes_fields(IMAGE_IMPORT_DESCRIPTOR, {"Name": StringRVa, "OriginalFirstThunk": RVA, "FirstThunk": RVA})
def get_INT(self):
if not self.OriginalFirstThunk.value:
return None
int_addr = self.OriginalFirstThunk.addr
int_entry = create_structure_at(THUNK_DATA, int_addr)
int_entry = create_structure_at(THUNK_DATA, int_addr)
res = []
while int_entry.Ordinal:
if int_entry.Ordinal & IMAGE_ORDINAL_FLAG:
@@ -234,30 +224,30 @@ def PEFile(baseaddr, target=None):
name = ctypes.c_char_p(name_address).value
res.append((import_by_name.Hint, name))
int_addr += ctypes.sizeof(type(int_entry))
int_entry = create_structure_at(THUNK_DATA, int_addr)
int_entry = create_structure_at(THUNK_DATA, int_addr)
return res
def get_IAT(self):
iat_addr = self.FirstThunk.addr
iat_entry = create_structure_at(THUNK_DATA, iat_addr)
iat_entry = create_structure_at(THUNK_DATA, iat_addr)
res = []
while iat_entry.Ordinal:
res.append(IATEntry.create(iat_addr, -1, "??"))
iat_addr += ctypes.sizeof(type(iat_entry))
iat_entry = create_structure_at(THUNK_DATA, iat_addr)
iat_entry = create_structure_at(THUNK_DATA, iat_addr)
return res
# Will be usable as `self._IMAGE_EXPORT_DIRECTORY`
class _IMAGE_EXPORT_DIRECTORY(ctypes.Structure):
_fields_ = transform_ctypes_fields(IMAGE_EXPORT_DIRECTORY, {"Name" : StringRVa, "AddressOfFunctions" : RVA, "AddressOfNames" : RVA, "AddressOfNameOrdinals": RVA})
_fields_ = transform_ctypes_fields(IMAGE_EXPORT_DIRECTORY, {"Name": StringRVa, "AddressOfFunctions": RVA, "AddressOfNames": RVA, "AddressOfNameOrdinals": RVA})
def get_exports(self):
NameOrdinals = create_structure_at((WORD * self.NumberOfNames), self.AddressOfNameOrdinals.addr)
NameOrdinals = create_structure_at((WORD * self.NumberOfNames), self.AddressOfNameOrdinals.addr)
NameOrdinals = list(NameOrdinals)
Functions = create_structure_at((RVA * self.NumberOfFunctions), self.AddressOfFunctions.addr)
Names = create_structure_at((StringRVa * self.NumberOfNames), self.AddressOfNames.addr)
res = []
for nb,func in enumerate(Functions):
for nb, func in enumerate(Functions):
if nb in NameOrdinals:
name = Names[NameOrdinals.index(nb)]
else:
@@ -297,4 +287,4 @@ def PEFile(baseaddr, target=None):
return current_pe
tst = PEFile.__code__.co_consts[13]
tst = PEFile.__code__.co_consts[13]
+50 -37
View File
@@ -4,41 +4,48 @@ import ctypes.wintypes
import itertools
from _ctypes import _SimpleCData
# 64bits pointeurs and long
### Utils ####
# ## Utils ### #
def is_pointer(x):
return isinstance(x, _ctypes._Pointer)
def is_pointer_type(x):
return issubclass(x, _ctypes._Pointer)
def is_array(x):
return isinstance(x, _ctypes.Array)
def is_array_type(x):
return issubclass(x, _ctypes.Array)
def is_structure_type(x):
return issubclass(x, ctypes.Structure)
def is_union_type(x):
return issubclass(x, ctypes.Union)
#### My types ####
# ### My types ### #
## 64bits pointer types ##
# # 64bits pointer types # #
# I know direct inheritance from _SimpleCData seems bad
# But it seems to be the only way to have the normal
# ctypes.Structure way of working (need to investigate)
class c_void_p64(_SimpleCData):
_type_ = "Q"
class c_char_p64(_SimpleCData):
_type_ = "Q"
class c_wchar_p64(_SimpleCData):
_type_ = "Q"
@@ -47,11 +54,9 @@ class c_wchar_p64(_SimpleCData):
# maybe force import before ctypes and modif stuff into ctypes ?
## Remote Value
# # Remote Value
# Used by the RemoteStructure to access the target memory
class RemoteValue(object):
@classmethod
def from_buffer_with_target(cls, buffer, offset=0, target=None):
@@ -59,11 +64,13 @@ class RemoteValue(object):
x.target = target
return x
class RemotePtr(RemoteValue):
@property
def raw_value(self):
return ctypes.cast(self, ctypes.c_void_p).value
class RemoteCCharP(RemotePtr, ctypes.c_char_p):
@property
def value(self):
@@ -77,6 +84,7 @@ class RemoteCCharP(RemotePtr, ctypes.c_char_p):
res.append(x)
return "".join(res)
class RemoteWCharP(RemotePtr, ctypes.c_char_p):
@property
def value(self):
@@ -91,6 +99,7 @@ class RemoteWCharP(RemotePtr, ctypes.c_char_p):
res.extend(x)
return "".join(res).decode('utf16')
class RemoteStructurePointer(RemotePtr, ctypes.c_void_p):
@classmethod
def from_buffer_with_target_and_ptr_type(cls, buffer, offset=0, target=None, ptr_type=None):
@@ -101,7 +110,6 @@ class RemoteStructurePointer(RemotePtr, ctypes.c_void_p):
@property
def contents(self):
#print(self.real_pointer_type)
remote_pointed_type = RemoteStructure.from_structure(self.real_pointer_type._type_)
return remote_pointed_type(self.raw_value, self.target)
@@ -110,9 +118,11 @@ class RemoteStructurePointer(RemotePtr, ctypes.c_void_p):
def create_remote_array(subtype, len):
class RemoteArray(_ctypes.Array):
_length_ = len
_type_ = subtype
def __init__(self, addr, target):
self._base_addr = addr
self.target = target
@@ -123,16 +133,15 @@ def create_remote_array(subtype, len):
if slice >= len:
raise IndexError("Access to {0} for a RemoteArray of size {1}".format(slice, len))
item_addr = self._base_addr + (ctypes.sizeof(subtype) * slice)
#TODO: do better ?
# TODO: do better ?
class TST(ctypes.Structure):
_fields_ = [("TST", subtype)]
return RemoteStructure.from_structure(TST)(item_addr, target=self.target).TST
#return subtype(item_addr, target=self.target)
return RemoteArray
# 64bits pointers
class RemotePtr64(RemoteValue):
def __init__(self, value, target):
self.target = target
@@ -144,12 +153,12 @@ class RemotePtr64(RemoteValue):
# Even if we are a subclass of c_ulonglong
my_addr = ctypes.addressof(self)
return ctypes.c_ulonglong.from_address(my_addr).value
return ctypes.cast(self, ctypes.c_ulonglong).value
class Remote_c_void_p64(RemotePtr64, c_void_p64):
pass
# base explanation:
# RemotePtr64 for the good `raw_value` implem
# RemoteCCharP for the good `value` implem
@@ -158,10 +167,12 @@ class Remote_c_char_p64(c_char_p64, RemotePtr64, RemoteCCharP):
def __repr__(self):
return "<Remote_c_char_p64({0})>".format(self.raw_value)
class Remote_w_char_p64(c_wchar_p64, RemotePtr64, RemoteWCharP):
def __repr__(self):
return "<Remote_c_char_p64({0})>".format(self.raw_value)
class RemoteStructurePointer64(Remote_c_void_p64):
@property
def raw_value(self):
@@ -181,27 +192,27 @@ class RemoteStructurePointer64(Remote_c_void_p64):
type_32_64_translation_table = {
ctypes.c_void_p : Remote_c_void_p64,
ctypes.c_char_p : Remote_c_char_p64,
ctypes.c_void_p: Remote_c_void_p64,
ctypes.c_char_p: Remote_c_char_p64,
ctypes.c_wchar_p: Remote_w_char_p64,
}
}
class RemoteStructureUnion(object):
"""Target is a process object"""
_reserved_name = ["_target", "_fields_", "_fields_dict_", "_base_addr", "_get_field_by_name",
"_get_field_descrptor_by_name", "_handle_field_getattr", "_field_type_to_remote_type", "__getattribute__", "_fields_"]
"_get_field_descrptor_by_name", "_handle_field_getattr", "_field_type_to_remote_type",
"__getattribute__", "_fields_"]
_field_type_to_remote_type = {
ctypes.c_char_p : RemoteCCharP,
ctypes.c_wchar_p : RemoteWCharP,
Remote_c_void_p64 : Remote_c_void_p64,
Remote_c_char_p64 : Remote_c_char_p64,
Remote_w_char_p64 : Remote_w_char_p64}
ctypes.c_char_p: RemoteCCharP,
ctypes.c_wchar_p: RemoteWCharP,
Remote_c_void_p64: Remote_c_void_p64,
Remote_c_char_p64: Remote_c_char_p64,
Remote_w_char_p64: Remote_w_char_p64
}
def __init__(self, base_addr, target):
if type(base_addr) not in (int, long):
import pdb;pdb.set_trace()
self._target = target
self._base_addr = base_addr
self._fields_dict_ = dict(self._fields_)
@@ -213,25 +224,24 @@ class RemoteStructureUnion(object):
raise AttributeError(fieldname + "is not a field of {0}".format(type(self)))
def _get_field_descrptor_by_name(self, fieldname):
return getattr(type(self), fieldname) # ctypes metaclass fill this for us
return getattr(type(self), fieldname) # ctypes metaclass fill this for us
def _handle_field_getattr(self, ftype, fosset, fsize):
s = self._target.read_memory(self._base_addr + fosset, fsize)
if ftype in self._field_type_to_remote_type:
return self._field_type_to_remote_type[ftype].from_buffer_with_target(bytearray(s), target=self._target).value
if issubclass(ftype, _ctypes._Pointer): # Pointer
if issubclass(ftype, _ctypes._Pointer): # Pointer
return RemoteStructurePointer.from_buffer_with_target_and_ptr_type(bytearray(s), target=self._target, ptr_type=ftype)
if issubclass(ftype, RemotePtr64): # Pointer to remote64 bits process
if issubclass(ftype, RemotePtr64): # Pointer to remote64 bits process
return RemoteStructurePointer64.from_buffer_with_target_and_ptr_type(bytearray(s), target=self._target, ptr_type=ftype)
if issubclass(ftype, RemoteStructureUnion): # Structure|Union already transfomed in remote
if issubclass(ftype, RemoteStructureUnion): # Structure|Union already transfomed in remote
return ftype(self._base_addr + fosset, self._target)
if issubclass(ftype, ctypes.Structure): # Structure that must be transfomed
if issubclass(ftype, ctypes.Structure): # Structure that must be transfomed
return RemoteStructure.from_structure(ftype)(self._base_addr + fosset, self._target)
if issubclass(ftype, ctypes.Union): # Union that must be transfomed
if issubclass(ftype, ctypes.Union): # Union that must be transfomed
return RemoteUnion.from_structure(ftype)(self._base_addr + fosset, self._target)
if issubclass(ftype, _ctypes.Array): # Arrays
if issubclass(ftype, _ctypes.Array): # Arrays
return create_remote_array(ftype._type_, ftype._length_)(self._base_addr + fosset, self._target)
#return ftype.from_buffer(bytearray(s))
# Normal types
# Follow the ctypes usage: if it's not directly inherited from _SimpleCData
# We do not apply the .value
@@ -241,18 +251,18 @@ class RemoteStructureUnion(object):
return ftype.from_buffer(bytearray(s)).value
def __getattribute__(self, fieldname):
if fieldname in type(self)._reserved_name: #Prevent recursion !
if fieldname in type(self)._reserved_name: # Prevent recursion !
return super(RemoteStructureUnion, self).__getattribute__(fieldname)
try:
t = self._get_field_by_name(fieldname)
except AttributeError as e: # Not a real attribute
except AttributeError: # Not a real attribute
return super(RemoteStructureUnion, self).__getattribute__(fieldname)
descr = self._get_field_descrptor_by_name(fieldname)
return self._handle_field_getattr(t, descr.offset, descr.size)
@classmethod
def from_structure(cls, structcls):
class MyStruct(cls, structcls): # inherit of structcls to keep property (see winobject.LoadedModule)
class MyStruct(cls, structcls): # inherit of structcls to keep property (see winobject.LoadedModule)
_fields_ = structcls._fields_
MyStruct.__name__ = "Remote" + structcls.__name__
@@ -264,17 +274,20 @@ class RemoteStructureUnion(object):
if base_cls:
bases.append(base_cls)
# inherit of structcls to keep property (see winobject.LoadedModule)
RemoteStruct = type("RemoteStruct", tuple(bases), {"_fields_" : fields})
RemoteStruct = type("RemoteStruct", tuple(bases), {"_fields_": fields})
if base_cls:
RemoteStruct.__name__ = "Remote" + base_cls.__name__
return RemoteStruct
class RemoteStructure(RemoteStructureUnion, ctypes.Structure):
pass
class RemoteUnion(RemoteStructureUnion, ctypes.Union):
pass
remote_struct = RemoteStructure.from_structure
if ctypes.sizeof(ctypes.c_void_p) == 4:
@@ -310,4 +323,4 @@ if ctypes.sizeof(ctypes.c_void_p) == 4:
if is_union_type(ftype):
return transform_union_to_remote64bits(ftype)
# Normal types
return type_32_64_translation_table.get(ftype, ftype)
return type_32_64_translation_table.get(ftype, ftype)
+21 -18
View File
@@ -2,7 +2,6 @@ import struct
import ctypes
import codecs
import windows
import windows.winproxy as winproxy
import windows.native_exec.simple_x64 as x64
from generated_def.winstructs import *
@@ -10,16 +9,18 @@ from generated_def.winstructs import *
CS_32bits = 0x23
CS_64bits = 0x33
def genere_return_32bits_stub(ret_addr):
ret_32b = x64.MultipleInstr()
ret_32b += x64.Mov('RCX', (CS_32bits << 32) + ret_addr)
ret_32b += x64.Push('RCX')
ret_32b += x64.Retf32() #32 bits return addr
ret_32b += x64.Retf32() # 32 bits return addr
return ret_32b.get_code()
# The format of a jump to 64bits mode
dummy_jump = "\xea" + struct.pack("<I", 0) + chr(CS_64bits) + "\x00\x00"
def execute_64bits_code_from_syswow(shellcode):
"""shellcode must not end by a ret"""
if not windows.current_process.is_wow_64:
@@ -42,11 +43,11 @@ def execute_64bits_code_from_syswow(shellcode):
exec_stub = ctypes.CFUNCTYPE(HRESULT)(jump_addr)
return exec_stub()
def NtCreateThreadEx_32_to_64(process, addr, param):
NtCreateThreadEx = get_NtCreateThreadEx_syswow_addr()
create_thread = x64.MultipleInstr()
# Save registers
#create_thread += Push('RAX')
create_thread += x64.Push('RBX')
create_thread += x64.Push('RCX')
create_thread += x64.Push('RDX')
@@ -60,20 +61,20 @@ def NtCreateThreadEx_32_to_64(process, addr, param):
create_thread += x64.Push('R13')
# Setup args
create_thread += x64.Push(0)
create_thread += x64.Mov('RCX', 'RSP') #Arg1
create_thread += x64.Mov('RDX', 0x1fffff) #Arg2
create_thread += x64.Mov('R8', 0) #Arg3
create_thread += x64.Mov('R9', process.handle) #Arg4
create_thread += x64.Mov('RCX', 'RSP') # Arg1
create_thread += x64.Mov('RDX', 0x1fffff) # Arg2
create_thread += x64.Mov('R8', 0) # Arg3
create_thread += x64.Mov('R9', process.handle) # Arg4
create_thread += x64.Mov('RAX', 0)
create_thread += x64.Push('RAX') #Arg11
create_thread += x64.Push('RAX') #Arg10
create_thread += x64.Push('RAX') #Arg9
create_thread += x64.Push('RAX') #Arg8
create_thread += x64.Push('RAX') #Arg7
create_thread += x64.Push('RAX') # Arg11
create_thread += x64.Push('RAX') # Arg10
create_thread += x64.Push('RAX') # Arg9
create_thread += x64.Push('RAX') # Arg8
create_thread += x64.Push('RAX') # Arg7
create_thread += x64.Mov('RAX', param)
create_thread += x64.Push('RAX') #Arg6
create_thread += x64.Push('RAX') # Arg6
create_thread += x64.Mov('RAX', addr)
create_thread += x64.Push('RAX') #Arg5
create_thread += x64.Push('RAX') # Arg5
# reserve space for register (calling convention)
create_thread += x64.Push('R9')
create_thread += x64.Push('R8')
@@ -83,7 +84,7 @@ def NtCreateThreadEx_32_to_64(process, addr, param):
create_thread += x64.Mov('R13', NtCreateThreadEx)
create_thread += x64.Call('R13')
# Clean stack
create_thread += x64.Add('RSP' , 12 * 8)
create_thread += x64.Add('RSP', 12 * 8)
create_thread += x64.Pop('R13')
create_thread += x64.Pop('R12')
create_thread += x64.Pop('R11')
@@ -95,7 +96,6 @@ def NtCreateThreadEx_32_to_64(process, addr, param):
create_thread += x64.Pop('RDX')
create_thread += x64.Pop('RCX')
create_thread += x64.Pop('RBX')
return execute_64bits_code_from_syswow(create_thread.get_code())
@@ -114,6 +114,7 @@ def get_NtCreateThreadEx_syswow_addr():
return get_NtCreateThreadEx_syswow_addr.value
get_NtCreateThreadEx_syswow_addr.value = None
def get_current_process_syswow_peb_addr():
current_process = windows.current_process
dest = current_process.virtual_alloc(0x1000)
@@ -126,13 +127,15 @@ def get_current_process_syswow_peb_addr():
peb_addr = struct.unpack("<Q", current_process.read_memory(dest, 8))[0]
return peb_addr
def get_current_process_syswow_peb():
current_process = windows.current_process
class CurrentProcessReadSyswow():
def read_memory(self, addr, size):
buffer_addr = ctypes.create_string_buffer(size)
buffer_addr = ctypes.create_string_buffer(size)
windows.winproxy.NtWow64ReadVirtualMemory64(current_process.handle, addr, buffer_addr, size)
return buffer_addr[:]
bitness = 64
peb_addr = get_current_process_syswow_peb_addr()
return windows.winobject.RemotePEB64(peb_addr, CurrentProcessReadSyswow())
return windows.winobject.RemotePEB64(peb_addr, CurrentProcessReadSyswow())
+3 -1
View File
@@ -1 +1,3 @@
from mytest import WindowsTestCase
from mytest import WindowsTestCase
__all__ = ["WindowsTestCase"]
+11 -10
View File
@@ -3,7 +3,6 @@ import struct
import time
import os
import textwrap
import ctypes
from contextlib import contextmanager
sys.path.append(".")
@@ -12,7 +11,6 @@ import windows
import windows.native_exec.simple_x86 as x86
import windows.native_exec.simple_x64 as x64
is_process_32_bits = windows.current_process.bitness == 32
is_process_64_bits = windows.current_process.bitness == 64
@@ -26,7 +24,6 @@ process_32bit_only = unittest.skipIf(not is_process_32_bits, "Test for 32bits pr
process_64bit_only = unittest.skipIf(not is_process_64_bits, "Test for 64bits process only")
if is_windows_32_bits:
def pop_calc_32():
return windows.utils.create_process(r"C:\Windows\system32\calc.exe", True)
@@ -45,6 +42,7 @@ else:
def pop_calc_64():
return windows.utils.create_process(r"C:\Windows\system32\calc.exe", True)
@contextmanager
def Calc64():
try:
@@ -53,6 +51,7 @@ def Calc64():
finally:
calc.exit()
@contextmanager
def Calc32():
try:
@@ -61,6 +60,7 @@ def Calc32():
finally:
calc.exit()
class WindowsTestCase(unittest.TestCase):
def setUp(self):
@@ -97,7 +97,6 @@ class WindowsTestCase(unittest.TestCase):
k32_base = windows.winproxy.LoadLibraryA("kernel32.dll")
self.assertEqual(windows.winproxy.GetProcAddress(k32_base, "GetCurrentProcessId"), get_current_proc_id)
# Native execution
def test_execute_to_32(self):
with Calc32() as calc:
@@ -161,7 +160,7 @@ class WindowsTestCase(unittest.TestCase):
if is_process_64_bits:
raise NotImplementedError("Python execution 64->32")
data = calc.virtual_alloc(0x1000)
remote_python_code ="""
remote_python_code = """
import ctypes
import windows
# windows.utils.create_console() # remove comment for debug
@@ -182,7 +181,7 @@ class WindowsTestCase(unittest.TestCase):
k32 = mods[0]
get_current_proc_id = k32.pe.exports['GetCurrentProcessId']
data = calc.virtual_alloc(0x1000)
remote_python_code ="""
remote_python_code = """
import ctypes
import windows
# windows.utils.create_console() # remove comment for debug
@@ -200,13 +199,14 @@ class WindowsTestCase(unittest.TestCase):
RegOpenKeyExA = [n for n in pythondll_mod.pe.imports['advapi32.dll'] if n.name == "RegOpenKeyExA"][0]
hook_value = []
@windows.hooks.RegOpenKeyExACallback
def open_reg_hook(hKey, lpSubKey, ulOptions, samDesired, phkResult, real_function):
hook_value.append((hKey, lpSubKey.value))
phkResult[0] = 12345678
return 0
x = RegOpenKeyExA.set_hook(open_reg_hook)
RegOpenKeyExA.set_hook(open_reg_hook)
import _winreg
open_args = (0x12345678, "MY_KEY_VALUE")
k = _winreg.OpenKey(*open_args)
@@ -221,14 +221,15 @@ class WindowsTestCase(unittest.TestCase):
def open_reg_hook_fail(hKey, lpSubKey, ulOptions, samDesired, phkResult, real_function):
return 0x11223344
x = RegOpenKeyExA.set_hook(open_reg_hook_fail)
RegOpenKeyExA.set_hook(open_reg_hook_fail)
import _winreg
open_args = (0x12345678, "MY_KEY_VALUE")
with self.assertRaises(WindowsError) as ar:
k = _winreg.OpenKey(*open_args)
_winreg.OpenKey(*open_args)
self.assertEqual(ar.exception.winerror, 0x11223344)
if __name__ == '__main__':
alltests = unittest.TestSuite()
alltests.addTest(unittest.makeSuite(WindowsTestCase))
unittest.TextTestRunner(verbosity=2).run(alltests)
unittest.TextTestRunner(verbosity=2).run(alltests)
+1 -1
View File
@@ -1,2 +1,2 @@
from pythonutils import *
from winutils import *
from winutils import *
+3 -1
View File
@@ -4,6 +4,7 @@ import ctypes
def fixedpropety(f):
cache_name = "_" + f.__name__
def prop(self):
try:
return getattr(self, cache_name)
@@ -12,7 +13,8 @@ def fixedpropety(f):
return getattr(self, cache_name)
return property(prop)
def swallow_ctypes_copy(ctypes_object):
new_copy = type(ctypes_object)()
ctypes.memmove(ctypes.byref(new_copy), ctypes.byref(ctypes_object), ctypes.sizeof(new_copy))
return new_copy
return new_copy
+28 -16
View File
@@ -1,7 +1,6 @@
import ctypes
import msvcrt
import os
import copy
import sys
import code
@@ -13,13 +12,15 @@ from ..generated_def.winstructs import *
# Function resolution !
def get_func_addr(dll_name, func_name):
dll = ctypes.WinDLL(dll_name)
# Load the DLL
ctypes.WinDLL(dll_name)
modules = windows.current_process.peb.modules
if not dll_name.lower().endswith(".dll"):
dll_name += ".dll"
mod = [x for x in modules if x.name == dll_name][0]
return mod.pe.exports[func_name]
def get_remote_func_addr(target, dll_name, func_name):
name_modules = [m for m in target.peb.modules if m.name == dll_name]
if not len(name_modules):
@@ -27,27 +28,31 @@ def get_remote_func_addr(target, dll_name, func_name):
mod = name_modules[0]
return mod.pe.exports[func_name]
def is_wow_64(hProcess):
try:
fnIsWow64Process = get_func_addr("kernel32.dll", "IsWow64Process")
fnIsWow64Process = get_func_addr("kernel32.dll", "IsWow64Process")
except winproxy.Kernel32Error:
return False
IsWow64Process = ctypes.WINFUNCTYPE(BOOL, HANDLE, ctypes.POINTER(BOOL))(fnIsWow64Process)
IsWow64Process = ctypes.WINFUNCTYPE(BOOL, HANDLE, ctypes.POINTER(BOOL))(fnIsWow64Process)
Wow64Process = BOOL()
res = IsWow64Process(hProcess, ctypes.byref(Wow64Process))
if res:
return bool(Wow64Process)
raise ctypes.WinError()
def create_file_from_handle(handle, mode="r"):
"""Return a Python :class:`file` arround a windows HANDLE"""
fd = msvcrt.open_osfhandle(handle, os.O_TEXT)
return os.fdopen(fd, mode, 0)
def get_handle_from_file(f):
"""Get the windows HANDLE of a python :class:`file`"""
return msvcrt.get_osfhandle(f.fileno())
def create_console():
"""Create a new console displaying STDOUT
Useful in injection of GUI process"""
@@ -64,6 +69,7 @@ def create_console():
console_stderr = create_file_from_handle(stderr_handle, "w")
sys.stderr = console_stderr
def create_process(path, show_windows=False):
proc_info = PROCESS_INFORMATION()
lpStartupInfo = None
@@ -75,7 +81,8 @@ def create_process(path, show_windows=False):
windows.winproxy.CreateProcessA(path, lpProcessInformation=ctypes.byref(proc_info), lpStartupInfo=lpStartupInfo)
proc = [p for p in windows.system.processes if p.pid == proc_info.dwProcessId][0]
return proc
def enable_privilege(lpszPrivilege, bEnablePrivilege):
"""Enable of disable a privilege: enable_privilege(SE_DEBUG_NAME, True)"""
tp = TOKEN_PRIVILEGES()
@@ -94,21 +101,21 @@ def enable_privilege(lpszPrivilege, bEnablePrivilege):
winproxy.CloseHandle(hToken)
if winproxy.GetLastError() == windef.ERROR_NOT_ALL_ASSIGNED:
raise ValueError("Failed to get privilege {0}".format(lpszPrivilege))
return True
return True
def check_is_elevated():
"""Return True if process is Admin"""
tp = TOKEN_PRIVILEGES()
hToken = HANDLE()
elevation = TOKEN_ELEVATION()
cbsize = DWORD()
bcsize = sizeof(elevation)
winproxy.OpenProcessToken(winproxy.GetCurrentProcess(), TOKEN_ALL_ACCESS, byref(hToken))
winproxy.GetTokenInformation(hToken, TokenElevation, byref(elevation), sizeof(elevation), byref(cbsize))
winproxy.CloseHandle(hToken)
return elevation.TokenIsElevated
def check_debug():
"""Check that kernel is in debug mode
beware if NOUMEX (https://msdn.microsoft.com/en-us/library/windows/hardware/ff556253(v=vs.85).aspx#_______noumex______)"""
@@ -119,31 +126,34 @@ def check_debug():
winproxy.RegOpenKeyExA(HKEY_LOCAL_MACHINE, "System\\CurrentControlSet\\Control", 0, KEY_READ, byref(hkresult))
winproxy.RegGetValueA(hkresult, None, "SystemStartOptions", RRF_RT_REG_SZ, None, byref(bufferres), byref(cbsize))
winproxy.RegCloseKey(hkresult)
control = bufferres[:]
if "DEBUG" not in control:
#print "[-] Enable debug boot!"
#print "> bcdedit /debug on"
# print "[-] Enable debug boot!"
# print "> bcdedit /debug on"
return False
if "DEBUG=NOUMEX" not in control:
pass
#print "[*] Warning noumex not set!"
#print "> bcdedit /set noumex on"
# print "[*] Warning noumex not set!"
# print "> bcdedit /set noumex on"
return True
class FixedInteractiveConsole(code.InteractiveConsole):
def raw_input(self, prompt=">>>"):
sys.stdout.write(prompt)
return raw_input("")
def pop_shell():
"""Pop a console with an InterativeConsole"""
create_console()
FixedInteractiveConsole(locals()).interact()
def get_kernel_modules():
cbsize = DWORD()
winproxy.NtQuerySystemInformation(SystemModuleInformation, None, 0, byref(cbsize))
raw_buffer = (cbsize.value * c_char)()
buffer = SYSTEM_MODULE_INFORMATION.from_address(ctypes.addressof(raw_buffer))
@@ -151,6 +161,7 @@ def get_kernel_modules():
modules = (SYSTEM_MODULE * buffer.ModulesCount).from_address(addressof(buffer) + SYSTEM_MODULE_INFORMATION.Modules.offset)
return list(modules)
class VirtualProtected(object):
"""A context manager usable like `VirtualProtect` that will restore the old protection at exit
@@ -175,6 +186,7 @@ class VirtualProtected(object):
winproxy.VirtualProtect(self.addr, self.size, self.old_protect.value, ctypes.byref(self.old_protect))
return False
class DisableWow64FsRedirection(object):
"""A context manager that disable the Wow64 Fs Redirection"""
def __enter__(self):
+69 -67
View File
@@ -1,43 +1,40 @@
import windows
import ctypes
import windows
from windows.generated_def.winstructs import *
import windows.generated_def.windef as windef
import time
EXCEPTION_CONTINUE_SEARCH = (0x0)
EXCEPTION_CONTINUE_EXECUTION = (0xffffffff)
EXCEPTION_CONTINUE_SEARCH = (0x0)
EXCEPTION_CONTINUE_EXECUTION = (0xffffffff)
exception_type = [
"EXCEPTION_ACCESS_VIOLATION",
"EXCEPTION_DATATYPE_MISALIGNMENT",
"EXCEPTION_BREAKPOINT",
"EXCEPTION_SINGLE_STEP",
"EXCEPTION_ARRAY_BOUNDS_EXCEEDED",
"EXCEPTION_FLT_DENORMAL_OPERAND",
"EXCEPTION_FLT_DIVIDE_BY_ZERO",
"EXCEPTION_FLT_INEXACT_RESULT",
"EXCEPTION_FLT_INVALID_OPERATION",
"EXCEPTION_FLT_OVERFLOW",
"EXCEPTION_FLT_STACK_CHECK",
"EXCEPTION_FLT_UNDERFLOW",
"EXCEPTION_INT_DIVIDE_BY_ZERO",
"EXCEPTION_INT_OVERFLOW",
"EXCEPTION_PRIV_INSTRUCTION",
"EXCEPTION_IN_PAGE_ERROR",
"EXCEPTION_ILLEGAL_INSTRUCTION",
"EXCEPTION_NONCONTINUABLE_EXCEPTION",
"EXCEPTION_STACK_OVERFLOW",
"EXCEPTION_INVALID_DISPOSITION",
"EXCEPTION_GUARD_PAGE",
"EXCEPTION_INVALID_HANDLE",
"EXCEPTION_POSSIBLE_DEADLOCK",
"EXCEPTION_ACCESS_VIOLATION",
"EXCEPTION_DATATYPE_MISALIGNMENT",
"EXCEPTION_BREAKPOINT",
"EXCEPTION_SINGLE_STEP",
"EXCEPTION_ARRAY_BOUNDS_EXCEEDED",
"EXCEPTION_FLT_DENORMAL_OPERAND",
"EXCEPTION_FLT_DIVIDE_BY_ZERO",
"EXCEPTION_FLT_INEXACT_RESULT",
"EXCEPTION_FLT_INVALID_OPERATION",
"EXCEPTION_FLT_OVERFLOW",
"EXCEPTION_FLT_STACK_CHECK",
"EXCEPTION_FLT_UNDERFLOW",
"EXCEPTION_INT_DIVIDE_BY_ZERO",
"EXCEPTION_INT_OVERFLOW",
"EXCEPTION_PRIV_INSTRUCTION",
"EXCEPTION_IN_PAGE_ERROR",
"EXCEPTION_ILLEGAL_INSTRUCTION",
"EXCEPTION_NONCONTINUABLE_EXCEPTION",
"EXCEPTION_STACK_OVERFLOW",
"EXCEPTION_INVALID_DISPOSITION",
"EXCEPTION_GUARD_PAGE",
"EXCEPTION_INVALID_HANDLE",
"EXCEPTION_POSSIBLE_DEADLOCK",
]
# x -> x dict may seems strange but useful to get the Flags (with name) from the int
# exception_name_by_value[0x80000001] -> EXCEPTION_GUARD_PAGE(0x80000001L)
exception_name_by_value = dict([(x,x) for x in [getattr(windows.generated_def.windef, name) for name in exception_type]])
exception_name_by_value = dict([(x, x) for x in [getattr(windows.generated_def.windef, name) for name in exception_type]])
class EnhancedEXCEPTION_RECORD(EXCEPTION_RECORD):
@@ -52,39 +49,40 @@ class EnhancedEXCEPTION_RECORD(EXCEPTION_RECORD):
if x is None:
return 0x0
return x
class Eflags(int):
_flags_ = [("CF", 1),
("RES_1", 1),
("PF", 1),
("RES_3", 1),
("AF", 1),
("RES_5", 1),
("ZF", 1),
("SF", 1),
("TF", 1),
("IF", 1),
("DF", 1),
("OF", 1),
("IOPL_1", 1),
("IOPL_2", 1),
("NT", 1),
("RES_15",1),
("RF", 1),
("VM", 1),
("AC", 1),
("VIF", 1),
("VIP", 1),
("ID", 1),
]
_flag_mask_ = dict([(name, 1<< i) for i,(name, size) in enumerate(_flags_)])
("RES_1", 1),
("PF", 1),
("RES_3", 1),
("AF", 1),
("RES_5", 1),
("ZF", 1),
("SF", 1),
("TF", 1),
("IF", 1),
("DF", 1),
("OF", 1),
("IOPL_1", 1),
("IOPL_2", 1),
("NT", 1),
("RES_15", 1),
("RF", 1),
("VM", 1),
("AC", 1),
("VIF", 1),
("VIP", 1),
("ID", 1),
]
_flag_mask_ = dict([(name, 1 << i) for i, (name, size) in enumerate(_flags_)])
def __getattr__(self, name):
if name in self._flag_mask_:
return bool(self & self._flag_mask_[name])
return super(Eflags, self).__getattr_(name)
def dump(self):
res = []
for name in self._flag_mask_:
@@ -93,15 +91,16 @@ class Eflags(int):
if getattr(self, name):
res.append(name)
return "|".join(res)
def __repr__(self):
return "{0}({1})".format(type(self).__name__, self.dump())
__str__ = __repr__
def __hex__(self):
return "{0}({1}:{2})".format(type(self).__name__, int.__hex__(self), self.dump())
class EnhancedCONTEXTBase(CONTEXT):
default_dump = ()
pc_reg = ''
@@ -112,7 +111,7 @@ class EnhancedCONTEXTBase(CONTEXT):
if to_dump is None:
to_dump = self.default_dump
for name in to_dump:
value = getattr(self, name)
value = getattr(self, name)
if name in self.special_reg_type:
value = self.special_reg_type[name](value)
res.append((name, value))
@@ -123,31 +122,34 @@ class EnhancedCONTEXTBase(CONTEXT):
for name, value in regs:
print("{0} -> {1}".format(name, hex(value)))
return None
def get_pc(self):
return getattr(self, self.pc_reg)
def set_pc(self, value):
return setattr(self, self.pc_reg, value)
pc = property(get_pc, set_pc, None, "Program Counter register (EIP or RIP)")
class EnhancedCONTEXT32(EnhancedCONTEXTBase):
default_dump = ('Eip', 'Esp', 'Eax', 'Ebx', 'Ecx', 'Edx', 'Ebp', 'Edi', 'Esi', 'EFlags')
pc_reg = 'Eip'
special_reg_type = {'EFlags' : Eflags}
special_reg_type = {'EFlags': Eflags}
class EnhancedCONTEXT64(EnhancedCONTEXTBase):
default_dump = ('Rip', 'Rsp', 'Rax', 'Rbx', 'Rcx', 'Rdx', 'Rbp', 'Rdi', 'Rsi',
'R9', 'R10', 'R11', 'R12', 'R13', 'R14', 'R15', 'EFlags')
pc_reg = 'Rip'
special_reg_type = {'EFlags' : Eflags}
special_reg_type = {'EFlags': Eflags}
if windows.current_process.bitness == 32:
EnhancedCONTEXT = EnhancedCONTEXT32
else:
EnhancedCONTEXT = EnhancedCONTEXT64
class EnhancedEXCEPTION_POINTERS(ctypes.Structure):
_fields_ = [
("ExceptionRecord", ctypes.POINTER(EnhancedEXCEPTION_RECORD)),
@@ -177,7 +179,7 @@ class VectoredException(object):
except BaseException as e:
print("Ignored Python Exception in Vectored Exception: {0}".format(e))
return windef.EXCEPTION_CONTINUE_SEARCH
class WithExceptionHandler(object):
def __init__(self, handler):
@@ -189,4 +191,4 @@ class WithExceptionHandler(object):
def __exit__(self, exc_type, exc_value, traceback):
windows.winproxy.RemoveVectoredExceptionHandler(self.value)
return False
return False
+44 -41
View File
@@ -1,6 +1,5 @@
import ctypes
import os
import codecs
import copy
import time
import struct
@@ -42,9 +41,10 @@ class AutoHandle(object):
return self._handle
def __del__(self):
if hasattr(self, "_handle") and self._handle:
if hasattr(self, "_handle") and self._handle:
winproxy.CloseHandle(self._handle)
class System(object):
"""Represent the current windows system python is running on"""
@property
@@ -127,7 +127,7 @@ class WinThread(THREADENTRY32, AutoHandle):
@property
def context(self):
x = windows.vectored_exception.EnhancedCONTEXT()
x = windows.vectored_exception.EnhancedCONTEXT()
x.ContextFlags = CONTEXT_FULL
winproxy.GetThreadContext(self.handle, x)
return x
@@ -164,6 +164,7 @@ class WinThread(THREADENTRY32, AutoHandle):
except IndexError:
return (tid, handle)
class Process(AutoHandle):
@utils.fixedpropety
def is_wow_64(self):
@@ -231,20 +232,19 @@ class CurrentThread(AutoHandle):
return winproxy.ExitThread(code)
class CurrentProcess(Process):
"""The current process"""
get_peb = None
get_peb_32_code = x86.MultipleInstr()
get_peb_32_code = x86.MultipleInstr()
get_peb_32_code += x86.Mov('EAX', x86.mem('fs:[0x30]'))
get_peb_32_code += x86.Ret()
get_peb_32_code = get_peb_32_code.get_code()
get_peb_32_code = get_peb_32_code.get_code()
get_peb_64_code = x64.MultipleInstr()
get_peb_64_code = x64.MultipleInstr()
get_peb_64_code += x64.Mov('RAX', x64.mem('gs:[0x60]'))
get_peb_64_code += x64.Ret()
get_peb_64_code = get_peb_64_code.get_code()
get_peb_64_code = get_peb_64_code.get_code()
allocator = native_exec.native_function.allocator
@@ -330,6 +330,7 @@ class CurrentProcess(Process):
"""Exit the process"""
return winproxy.ExitProcess(code)
class WinProcess(PROCESSENTRY32, Process):
"""A Process on the system"""
is_pythondll_injected = 0
@@ -378,7 +379,7 @@ class WinProcess(PROCESSENTRY32, Process):
def low_read_memory(self, addr, buffer_addr, size):
if windows.current_process.bitness == 32 and self.bitness == 64:
#OptionalExport can be None (see winproxy.py)
# OptionalExport can be None (see winproxy.py)
if winproxy.NtWow64ReadVirtualMemory64 is None:
raise ValueError("NtWow64ReadVirtualMemory64 non available in ntdll: cannot write into 64bits processus")
return winproxy.NtWow64ReadVirtualMemory64(self.handle, addr, buffer_addr, size)
@@ -386,27 +387,27 @@ class WinProcess(PROCESSENTRY32, Process):
def read_memory(self, addr, size):
"""Read `size` from `addr`"""
buffer = ctypes.create_string_buffer(size)
buffer = ctypes.create_string_buffer(size)
self.low_read_memory(addr, ctypes.byref(buffer), size)
return buffer[:]
##Simple cache test
#real_read = read_memory
# Simple cache test
# real_read = read_memory
#
#def read_memory(self, addr, size):
# """Cached version for test"""
# dbgprint('Read remote Memory of {0}'.format(self), 'READMEM')
# if not hasattr(self, "_cache_cache"):
# self._cache_cache = {}
# page_addr = addr & 0xfffffffffffff000
# if page_addr in self._cache_cache:
# #print("CACHED Read on page {0}".format(hex(page_addr)))
# page_data = self._cache_cache[page_addr]
# return page_data[addr & 0xfff: (addr & 0xfff) + size]
# else:
# page_data = self.real_read(page_addr, 0x1000)
# self._cache_cache[page_addr] = page_data
# return page_data[addr & 0xfff: (addr & 0xfff) + size]
# def read_memory(self, addr, size):
# """Cached version for test"""
# dbgprint('Read remote Memory of {0}'.format(self), 'READMEM')
# if not hasattr(self, "_cache_cache"):
# self._cache_cache = {}
# page_addr = addr & 0xfffffffffffff000
# if page_addr in self._cache_cache:
# #print("CACHED Read on page {0}".format(hex(page_addr)))
# page_data = self._cache_cache[page_addr]
# return page_data[addr & 0xfff: (addr & 0xfff) + size]
# else:
# page_data = self.real_read(page_addr, 0x1000)
# self._cache_cache[page_addr] = page_data
# return page_data[addr & 0xfff: (addr & 0xfff) + size]
def read_memory_into(self, addr, struct):
"""Read a :mod:`ctypes` struct from `addr`"""
@@ -417,7 +418,7 @@ class WinProcess(PROCESSENTRY32, Process):
"""Create a remote thread"""
if windows.current_process.bitness == 32 and self.bitness == 64:
return windows.syswow64.NtCreateThreadEx_32_to_64(self, addr, param)
return WinThread._from_handle(winproxy.CreateRemoteThread(hProcess=self.handle, lpStartAddress=addr, lpParameter=param))
return WinThread._from_handle(winproxy.CreateRemoteThread(hProcess=self.handle, lpStartAddress=addr, lpParameter=param))
def load_library(self, dll_path):
"""Load the library in remote process"""
@@ -433,7 +434,6 @@ class WinProcess(PROCESSENTRY32, Process):
def get_peb_addr(self):
dest = self.virtual_alloc(0x1000)
if self.bitness == 32:
#get_peb_code = get_peb_32_code
store_peb = x86.MultipleInstr()
store_peb += x86.Mov('EAX', x86.mem('fs:[0x30]'))
store_peb += x86.Mov(x86.create_displacement(disp=dest), 'EAX')
@@ -506,6 +506,7 @@ class LoadedModule(LDR_DATA_TABLE_ENTRY):
"""
return pe_parse.PEFile(self.baseaddr)
class WinUnicodeString(LSA_UNICODE_STRING):
"""LSA_UNICODE_STRING with a nice `__repr__`"""
def __repr__(self):
@@ -514,20 +515,25 @@ class WinUnicodeString(LSA_UNICODE_STRING):
class LIST_ENTRY_PTR(PVOID):
def TO_LDR_ENTRY(self):
return LDR_DATA_TABLE_ENTRY.from_address(self.value - sizeof(PVOID) * 2)
return LDR_DATA_TABLE_ENTRY.from_address(self.value - sizeof(PVOID) * 2)
def transform_ctypes_fields(struct, replacement):
return [(name, replacement.get(name, type)) for name, type in struct._fields_]
class RTL_USER_PROCESS_PARAMETERS(Structure):
_fields_ = transform_ctypes_fields(RTL_USER_PROCESS_PARAMETERS, # The one in generated_def
{"ImagePathName" : WinUnicodeString,
"CommandLine" : WinUnicodeString})
_fields_ = transform_ctypes_fields(RTL_USER_PROCESS_PARAMETERS, # The one in generated_def
{"ImagePathName": WinUnicodeString,
"CommandLine": WinUnicodeString}
)
class PEB(Structure):
"""The PEB (Process Environment Block) of the current process"""
_fields_ = transform_ctypes_fields(PEB, # The one in generated_def
{"ProcessParameters" : POINTER(RTL_USER_PROCESS_PARAMETERS)})
_fields_ = transform_ctypes_fields(PEB, # The one in generated_def
{"ProcessParameters": POINTER(RTL_USER_PROCESS_PARAMETERS)}
)
@property
def imagepath(self):
@@ -564,14 +570,11 @@ class PEB(Structure):
import windows.remotectypes as rctypes
class RemotePEB(rctypes.RemoteStructure.from_structure(PEB)):
RemoteLoadedModule = rctypes.RemoteStructure.from_structure(LoadedModule)
def ptr_flink_to_remote_module(self, ptr_value):
return self.RemoteLoadedModule(ptr_value - ctypes.sizeof(ctypes.c_void_p) * 2, self._target)
return self.RemoteLoadedModule(ptr_value - ctypes.sizeof(ctypes.c_void_p) * 2, self._target)
@property
def modules(self):
@@ -589,6 +592,7 @@ class RemotePEB(rctypes.RemoteStructure.from_structure(PEB)):
current_dll = self.ptr_flink_to_remote_module(list_entry_ptr)
return res
if CurrentProcess().bitness == 32:
class RemoteLoadedModule64(rctypes.transform_type_to_remote64bits(LoadedModule)):
@property
@@ -600,10 +604,9 @@ if CurrentProcess().bitness == 32:
return pe_parse.PEFile(self.baseaddr, target=self._target)
class RemotePEB64(rctypes.transform_type_to_remote64bits(PEB)):
#RemoteLoadedModule64 = rctypes.transform_type_to_remote64bits(LoadedModule)
def ptr_flink_to_remote_module(self, ptr_value):
return RemoteLoadedModule64(ptr_value - ctypes.sizeof(rctypes.c_void_p64) * 2, self._target)
return RemoteLoadedModule64(ptr_value - ctypes.sizeof(rctypes.c_void_p64) * 2, self._target)
@property
def modules(self):
@@ -619,4 +622,4 @@ if CurrentProcess().bitness == 32:
res.append(current_dll)
list_entry_ptr = current_dll.InMemoryOrderLinks.Flink.raw_value
current_dll = self.ptr_flink_to_remote_module(list_entry_ptr)
return res
return res
+79 -25
View File
@@ -14,13 +14,14 @@ advapi32 = ctypes.windll.Advapi32
iphlpapi = ctypes.windll.iphlpapi
ntdll = ctypes.windll.ntdll
class Kernel32Error(WindowsError):
def __new__(cls, func_name):
win_error = ctypes.WinError()
api_error = super(Kernel32Error, cls).__new__(cls)
api_error.api_name = func_name
api_error.winerror = win_error.winerror
api_error.strerror = win_error.strerror
api_error.strerror = win_error.strerror
api_error.args = (func_name, win_error.winerror, win_error.strerror)
return api_error
@@ -30,6 +31,7 @@ class Kernel32Error(WindowsError):
def __str__(self):
return "{0}: {1}".format(self.api_name, super(Kernel32Error, self).__str__())
class IphlpapiError(Kernel32Error):
def __new__(cls, func_name, code):
@@ -37,18 +39,20 @@ class IphlpapiError(Kernel32Error):
api_error = super(Kernel32Error, cls).__new__(cls)
api_error.api_name = func_name
api_error.winerror = win_error.winerror
api_error.strerror = win_error.strerror
api_error.strerror = win_error.strerror
api_error.args = (func_name, win_error.winerror, win_error.strerror)
return api_error
def __init__(self, func_name, code):
pass
# Error check method
def no_error_check(func_name, result, func, args):
"""Nothing special"""
return args
def minus_one_error_check(func_name, result, func, args):
if result == -1:
raise Kernel32Error(func_name)
@@ -61,30 +65,34 @@ def kernel32_error_check(func_name, result, func, args):
raise Kernel32Error(func_name)
return args
def kernel32_zero_check(func_name, result, func, args):
"""raise Kernel32Error if result is NOT 0"""
if result:
raise Kernel32Error(func_name)
return args
def iphlpapi_error_check(func_name, result, func, args):
"""raise IphlpapiError if result is NOT 0"""
if result:
raise IphlpapiError(func_name, result)
return args
def error_ntstatus(func_name, result, func, args):
if result:
raise NtStatusException(result & 0xffffffff)
return args
class ExportNotFound(AttributeError):
def __init__(self, func_name, api_name):
self.func_name = func_name
self.api_name = api_name
super(ExportNotFound, self).__init__("Function {0} not found into {1}".format(func_name, api_name))
# Design 1
class ApiProxy(object):
APIDLL = None
"""Create a python wrapper around a kernel32 function"""
@@ -107,6 +115,7 @@ class ApiProxy(object):
doc = doc if doc else ""
python_proxy.__doc__ = doc + "\nErrcheck:\n " + self.error_check.__doc__
params_name = [param[1] for param in params]
def perform_call(*args):
if len(params_name) != len(args):
print("ERROR:")
@@ -120,22 +129,27 @@ class ApiProxy(object):
setattr(python_proxy, "ctypes_function", perform_call)
return python_proxy
class Kernel32Proxy(ApiProxy):
APIDLL = kernel32
default_error_check = staticmethod(kernel32_error_check)
class Advapi32Proxy(ApiProxy):
APIDLL = advapi32
default_error_check = staticmethod(kernel32_error_check)
class IphlpapiProxy(ApiProxy):
APIDLL = iphlpapi
default_error_check = staticmethod(iphlpapi_error_check)
class NtdllProxy(ApiProxy):
APIDLL = ntdll
default_error_check = staticmethod(kernel32_zero_check)
class OptionalExport(object):
"""used 'around' a Proxy decorator
Should be used for export that are not available everywhere (ntdll internals | 32/64 bits stuff)
@@ -156,6 +170,7 @@ class OptionalExport(object):
dbgprint("Export <{e.func_name}> not found in <{e.api_name}>".format(e=e), "EXPORTNOTFOUND")
return None
def TransparentApiProxy(APIDLL, func_name, error_check):
"""Create a ctypes function for 'func_name' with no python arg pre-check"""
@@ -169,12 +184,14 @@ def TransparentApiProxy(APIDLL, func_name, error_check):
return c_prototyped
TransparentKernel32Proxy = lambda func_name, error_check=kernel32_error_check : TransparentApiProxy(kernel32, func_name, error_check)
TransparentAdvapi32Proxy = lambda func_name, error_check=kernel32_error_check : TransparentApiProxy(advapi32, func_name, error_check)
TransparentIphlpapiProxy = lambda func_name, error_check=iphlpapi_error_check : TransparentApiProxy(iphlpapi, func_name, error_check)
TransparentKernel32Proxy = lambda func_name, error_check=kernel32_error_check: TransparentApiProxy(kernel32, func_name, error_check)
TransparentAdvapi32Proxy = lambda func_name, error_check=kernel32_error_check: TransparentApiProxy(advapi32, func_name, error_check)
TransparentIphlpapiProxy = lambda func_name, error_check=iphlpapi_error_check: TransparentApiProxy(iphlpapi, func_name, error_check)
class NeededParameterType(object):
_inst = None
def __new__(cls):
if cls._inst is None:
cls._inst = super(NeededParameterType, cls).__new__(cls)
@@ -182,8 +199,6 @@ class NeededParameterType(object):
def __repr__(self):
return "NeededParameter"
NeededParameter = NeededParameterType()
ExitProcess = TransparentKernel32Proxy("ExitProcess")
@@ -201,7 +216,6 @@ FreeConsole = TransparentKernel32Proxy("FreeConsole")
GetStdHandle = TransparentKernel32Proxy("GetStdHandle")
SetStdHandle = TransparentKernel32Proxy("SetStdHandle")
GetCurrentThreadId = TransparentKernel32Proxy("GetCurrentThreadId")
TerminateThread = TransparentKernel32Proxy("TerminateThread")
ExitThread = TransparentKernel32Proxy("ExitThread")
SuspendThread = TransparentKernel32Proxy("SuspendThread", minus_one_error_check)
@@ -209,45 +223,54 @@ ResumeThread = TransparentKernel32Proxy("ResumeThread", minus_one_error_check)
GetThreadId = TransparentKernel32Proxy("GetThreadId")
Wow64DisableWow64FsRedirection = OptionalExport(TransparentKernel32Proxy)("Wow64DisableWow64FsRedirection")
Wow64RevertWow64FsRedirection = OptionalExport(TransparentKernel32Proxy)("Wow64RevertWow64FsRedirection")
Wow64EnableWow64FsRedirection = OptionalExport(TransparentKernel32Proxy)("Wow64EnableWow64FsRedirection")
Wow64RevertWow64FsRedirection = OptionalExport(TransparentKernel32Proxy)("Wow64RevertWow64FsRedirection")
Wow64EnableWow64FsRedirection = OptionalExport(TransparentKernel32Proxy)("Wow64EnableWow64FsRedirection")
@Kernel32Proxy("CreateFileA")
def CreateFileA(lpFileName, dwDesiredAccess, dwShareMode=0, lpSecurityAttributes=None, dwCreationDisposition=OPEN_EXISTING, dwFlagsAndAttributes=FILE_ATTRIBUTE_NORMAL, hTemplateFile=None):
return CreateFileA.ctypes_function(lpFileName, dwDesiredAccess, dwShareMode, lpSecurityAttributes, dwCreationDisposition, dwFlagsAndAttributes, hTemplateFile)
@Kernel32Proxy("VirtualAlloc")
def VirtualAlloc(lpAddress=0, dwSize=NeededParameter, flAllocationType=MEM_COMMIT, flProtect=PAGE_EXECUTE_READWRITE):
def VirtualAlloc(lpAddress=0, dwSize=NeededParameter, flAllocationType=MEM_COMMIT, flProtect=PAGE_EXECUTE_READWRITE):
return VirtualAlloc.ctypes_function(lpAddress, dwSize, flAllocationType, flProtect)
@Kernel32Proxy("VirtualFree")
def VirtualFree(lpAddress, dwSize=0, dwFreeType=MEM_RELEASE):
return VirtualFree.ctypes_function(lpAddress, dwSize, dwFreeType)
@Kernel32Proxy("VirtualAllocEx")
def VirtualAllocEx(hProcess, lpAddress=0, dwSize=NeededParameter, flAllocationType=MEM_COMMIT, flProtect=PAGE_EXECUTE_READWRITE):
def VirtualAllocEx(hProcess, lpAddress=0, dwSize=NeededParameter, flAllocationType=MEM_COMMIT, flProtect=PAGE_EXECUTE_READWRITE):
return VirtualAllocEx.ctypes_function(hProcess, lpAddress, dwSize, flAllocationType, flProtect)
@Kernel32Proxy("VirtualFreeEx")
def VirtualFreeEx(hProcess, lpAddress, dwSize=0, dwFreeType=MEM_RELEASE):
return VirtualFreeEx.ctypes_function(hProcess, lpAddress, dwSize, dwFreeType)
@Kernel32Proxy("CreateThread")
def CreateThread(lpThreadAttributes=None, dwStackSize=0, lpStartAddress=NeededParameter, lpParameter=NeededParameter, dwCreationFlags=0, lpThreadId=None):
return CreateThread.ctypes_function(lpThreadAttributes, dwStackSize, lpStartAddress, lpParameter, dwCreationFlags, lpThreadId)
@Kernel32Proxy("CreateRemoteThread")
def CreateRemoteThread(hProcess=NeededParameter, lpThreadAttributes=None, dwStackSize=0,
lpStartAddress=NeededParameter, lpParameter=NeededParameter, dwCreationFlags=0, lpThreadId=None):
lpStartAddress=NeededParameter, lpParameter=NeededParameter, dwCreationFlags=0, lpThreadId=None):
return CreateRemoteThread.ctypes_function(hProcess, lpThreadAttributes, dwStackSize, lpStartAddress, lpParameter, dwCreationFlags, lpThreadId)
@Kernel32Proxy("VirtualProtect")
def VirtualProtect(lpAddress, dwSize, flNewProtect, lpflOldProtect=0):
return VirtualProtect.ctypes_function(lpAddress, dwSize, flNewProtect, lpflOldProtect)
@Kernel32Proxy("CreateProcessA")
def CreateProcessA(lpApplicationName, lpCommandLine=None, lpProcessAttributes=None, lpThreadAttributes=None, bInheritHandles=False,
dwCreationFlags=0, lpEnvironment=None, lpCurrentDirectory=None, lpStartupInfo=None, lpProcessInformation=None):
dwCreationFlags=0, lpEnvironment=None, lpCurrentDirectory=None, lpStartupInfo=None, lpProcessInformation=None):
if lpStartupInfo is None:
StartupInfo = STARTUPINFOA()
StartupInfo.cb = ctypes.sizeof(StartupInfo)
@@ -258,9 +281,10 @@ def CreateProcessA(lpApplicationName, lpCommandLine=None, lpProcessAttributes=No
lpProcessInformation = ctypes.byref(PROCESS_INFORMATION())
return CreateProcessA.ctypes_function(lpApplicationName, lpCommandLine, lpProcessAttributes, lpThreadAttributes, bInheritHandles, dwCreationFlags, lpEnvironment, lpCurrentDirectory, lpStartupInfo, lpProcessInformation)
@Kernel32Proxy("CreateProcessW")
def CreateProcessW(lpApplicationName, lpCommandLine=None, lpProcessAttributes=None, lpThreadAttributes=None, bInheritHandles=False,
dwCreationFlags=0, lpEnvironment=None, lpCurrentDirectory=None, lpStartupInfo=None, lpProcessInformation=None):
dwCreationFlags=0, lpEnvironment=None, lpCurrentDirectory=None, lpStartupInfo=None, lpProcessInformation=None):
if lpStartupInfo is None:
StartupInfo = STARTUPINFOW()
StartupInfo.cb = ctypes.sizeof(StartupInfo)
@@ -271,6 +295,7 @@ def CreateProcessW(lpApplicationName, lpCommandLine=None, lpProcessAttributes=No
lpProcessInformation = ctypes.byref(PROCESS_INFORMATION())
return CreateProcessW.ctypes_function(lpApplicationName, lpCommandLine, lpProcessAttributes, lpThreadAttributes, bInheritHandles, dwCreationFlags, lpEnvironment, lpCurrentDirectory, lpStartupInfo, lpProcessInformation)
@Kernel32Proxy("GetThreadContext")
def GetThreadContext(hThread, lpContext=None):
if lpContext is None:
@@ -279,6 +304,7 @@ def GetThreadContext(hThread, lpContext=None):
lpContext = ctypes.byref(Context)
return GetThreadContext.ctypes_function(hThread, lpContext)
@Kernel32Proxy("SetThreadContext")
def SetThreadContext(hThread, lpContext):
""" Allows to directly pass a CONTEXT and will call with byref(CONTEXT) by itself"""
@@ -286,18 +312,22 @@ def SetThreadContext(hThread, lpContext):
lpContext = ctypes.byref(lpContext)
return SetThreadContext.ctypes_function(hThread, lpContext)
@Kernel32Proxy("OpenThread")
def OpenThread(dwDesiredAccess=THREAD_ALL_ACCESS, bInheritHandle=0, dwThreadId=NeededParameter):
return OpenThread.ctypes_function(dwDesiredAccess, bInheritHandle, dwThreadId)
@Kernel32Proxy("OpenProcess")
def OpenProcess(dwDesiredAccess=PROCESS_ALL_ACCESS, bInheritHandle=0, dwProcessId=NeededParameter):
return OpenProcess.ctypes_function(dwDesiredAccess, bInheritHandle, dwProcessId)
@Kernel32Proxy("ReadProcessMemory")
def ReadProcessMemory(hProcess, lpBaseAddress, lpBuffer, nSize, lpNumberOfBytesRead=None):
return ReadProcessMemory.ctypes_function(hProcess, lpBaseAddress, lpBuffer, nSize, lpNumberOfBytesRead)
@Kernel32Proxy("WriteProcessMemory")
def WriteProcessMemory(hProcess, lpBaseAddress, lpBuffer, nSize=None, lpNumberOfBytesWritten=None):
"""Computer nSize with len(lpBuffer) if not given"""
@@ -305,6 +335,7 @@ def WriteProcessMemory(hProcess, lpBaseAddress, lpBuffer, nSize=None, lpNumberOf
nSize = len(lpBuffer)
return WriteProcessMemory.ctypes_function(hProcess, lpBaseAddress, lpBuffer, nSize, lpNumberOfBytesWritten)
@Kernel32Proxy('SetThreadAffinityMask')
def SetThreadAffinityMask(hThread=None, dwThreadAffinityMask=NeededParameter):
"""If hThread is not given, it will be the current thread"""
@@ -312,37 +343,43 @@ def SetThreadAffinityMask(hThread=None, dwThreadAffinityMask=NeededParameter):
hThread = GetCurrentThread()
return SetThreadAffinityMask.ctypes_function(hThread, dwThreadAffinityMask)
@Kernel32Proxy("CreateToolhelp32Snapshot")
def CreateToolhelp32Snapshot(dwFlags, th32ProcessID=0):
return CreateToolhelp32Snapshot.ctypes_function(dwFlags, th32ProcessID)
@Kernel32Proxy("Thread32First", no_error_check)
def Thread32First(hSnapshot, lpte):
"""Set byref(lpte) if needed"""
if type(lpte) == THREADENTRY32:
lpte = ctypes.byref(lpte)
return Thread32First.ctypes_function(hSnapshot, lpte)
return Thread32First.ctypes_function(hSnapshot, lpte)
@Kernel32Proxy("Thread32Next", no_error_check)
def Thread32Next(hSnapshot, lpte):
"""Set byref(lpte) if needed"""
if type(lpte) == THREADENTRY32:
lpte = ctypes.byref(lpte)
return Thread32Next.ctypes_function(hSnapshot, lpte)
return Thread32Next.ctypes_function(hSnapshot, lpte)
@Kernel32Proxy("Process32First", no_error_check)
def Process32First(hSnapshot, lpte):
"""Set byref(lpte) if needed"""
if type(lpte) == THREADENTRY32:
lpte = ctypes.byref(lpte)
return Process32First.ctypes_function(hSnapshot, lpte)
return Process32First.ctypes_function(hSnapshot, lpte)
@Kernel32Proxy("Process32Next", no_error_check)
def Process32Next(hSnapshot, lpte):
"""Set byref(lpte) if needed"""
if type(lpte) == THREADENTRY32:
lpte = ctypes.byref(lpte)
return Process32Next.ctypes_function(hSnapshot, lpte)
return Process32Next.ctypes_function(hSnapshot, lpte)
# File stuff
@Kernel32Proxy("WriteFile")
@@ -353,23 +390,28 @@ def WriteFile(hFile, lpBuffer, nNumberOfBytesToWrite=None, lpNumberOfBytesWritte
lpNumberOfBytesWritten = ctypes.byref(DWORD())
return WriteFile.ctypes_function(hFile, lpBuffer, nNumberOfBytesToWrite, lpNumberOfBytesWritten, lpOverlapped)
# Exception stuff
@Kernel32Proxy("AddVectoredContinueHandler")
def AddVectoredContinueHandler(FirstHandler=1, VectoredHandler=NeededParameter):
return AddVectoredContinueHandler.ctypes_function(FirstHandler, VectoredHandler)
@Kernel32Proxy("AddVectoredExceptionHandler")
def AddVectoredExceptionHandler(FirstHandler=1, VectoredHandler=NeededParameter):
return AddVectoredExceptionHandler.ctypes_function(FirstHandler, VectoredHandler)
@Kernel32Proxy("RemoveVectoredExceptionHandler")
def RemoveVectoredExceptionHandler(Handler):
return RemoveVectoredExceptionHandler.ctypes_function(Handler)
@Kernel32Proxy("WaitForSingleObject", kernel32_zero_check)
def WaitForSingleObject(hHandle, dwMilliseconds=INFINITE):
return WaitForSingleObject.ctypes_function(hHandle, dwMilliseconds)
@Kernel32Proxy("DeviceIoControl")
def DeviceIoControl(hDevice, dwIoControlCode, lpInBuffer, nInBufferSize=None, lpOutBuffer=NeededParameter, nOutBufferSize=None, lpBytesReturned=None, lpOverlapped=None):
if nInBufferSize is None:
@@ -380,14 +422,15 @@ def DeviceIoControl(hDevice, dwIoControlCode, lpInBuffer, nInBufferSize=None, lp
# Some windows check 0 / others does not
lpBytesReturned = ctypes.byref(DWORD())
return DeviceIoControl.ctypes_function(hDevice, dwIoControlCode, lpInBuffer, nInBufferSize, lpOutBuffer, nOutBufferSize, lpBytesReturned, lpOverlapped)
#### NTDLL #####
# ### NTDLL #### #
@OptionalExport(NtdllProxy('NtWow64ReadVirtualMemory64', error_ntstatus))
def NtWow64ReadVirtualMemory64(hProcess, lpBaseAddress, lpBuffer, nSize, lpNumberOfBytesRead=None):
return NtWow64ReadVirtualMemory64.ctypes_function(hProcess, lpBaseAddress, lpBuffer, nSize, lpNumberOfBytesRead)
def ntquerysysteminformation_error_check(func_name, result, func, args):
if result == 0:
return args
@@ -396,13 +439,15 @@ def ntquerysysteminformation_error_check(func_name, result, func, args):
return args
raise Kernel32Error("{0} failed with NTStatus {1}".format(func_name, hex(result)))
@NtdllProxy('NtQuerySystemInformation', ntquerysysteminformation_error_check)
def NtQuerySystemInformation(SystemInformationClass, SystemInformation=None, SystemInformationLength=0, ReturnLength=NeededParameter):
if SystemInformation is not None and SystemInformation == 0:
SystemInformationLength = ctypes.sizeof(SystemInformation)
return NtQuerySystemInformation.ctypes_function(SystemInformationClass, SystemInformation, SystemInformationLength, ReturnLength)
###### ADVAPI32 ########
# ##### ADVAPI32 ####### #
@Advapi32Proxy('OpenProcessToken')
def OpenProcessToken(ProcessHandle=None, DesiredAccess=NeededParameter, TokenHandle=NeededParameter):
@@ -411,20 +456,24 @@ def OpenProcessToken(ProcessHandle=None, DesiredAccess=NeededParameter, TokenHan
ProcessHandle = GetCurrentProcess()
return OpenProcessToken.ctypes_function(ProcessHandle, DesiredAccess, TokenHandle)
@Advapi32Proxy('LookupPrivilegeValueA')
def LookupPrivilegeValueA(lpSystemName=None, lpName=NeededParameter, lpLuid=NeededParameter):
return LookupPrivilegeValueA.ctypes_function(lpSystemName, lpName, lpLuid)
@Advapi32Proxy('LookupPrivilegeValueW')
def LookupPrivilegeValueW(lpSystemName=None, lpName=NeededParameter, lpLuid=NeededParameter):
return LookupPrivilegeValueW.ctypes_function(lpSystemName, lpName, lpLuid)
@Advapi32Proxy('AdjustTokenPrivileges')
def AdjustTokenPrivileges(TokenHandle, DisableAllPrivileges=False, NewState=NeededParameter, BufferLength=None, PreviousState=None, ReturnLength=None):
if BufferLength is None:
BufferLength = ctypes.sizeof(NewState)
return AdjustTokenPrivileges.ctypes_function(TokenHandle, DisableAllPrivileges, NewState, BufferLength, PreviousState, ReturnLength)
# Registry stuff
@Advapi32Proxy('GetTokenInformation')
@@ -433,33 +482,38 @@ def GetTokenInformation(TokenHandle=NeededParameter, TokenInformationClass=Neede
ReturnLength = ctypes.byref(DWORD())
return GetTokenInformation.ctypes_function(TokenHandle, TokenInformationClass, TokenInformation, TokenInformationLength, ReturnLength)
@Advapi32Proxy('RegOpenKeyExA', kernel32_zero_check)
def RegOpenKeyExA(hKey, lpSubKey, ulOptions, samDesired, phkResult):
return RegOpenKeyExA.ctypes_function(hKey, lpSubKey, ulOptions, samDesired, phkResult)
# TODO: default values? which ones ?
@Advapi32Proxy('RegOpenKeyExW', kernel32_zero_check)
def RegOpenKeyExW(hKey, lpSubKey, ulOptions, samDesired, phkResult):
return RegOpenKeyExW.ctypes_function(hKey, lpSubKey, ulOptions, samDesired, phkResult)
@Advapi32Proxy('RegGetValueA', kernel32_zero_check)
def RegGetValueA(hkey, lpSubKey, lpValue, dwFlags, pdwType, pvData, pcbData):
return RegGetValueA.ctypes_function(hkey, lpSubKey, lpValue, dwFlags, pdwType, pvData, pcbData)
@Advapi32Proxy('RegGetValueW', kernel32_zero_check)
def RegGetValueW(hkey, lpSubKey, lpValue, dwFlags, pdwType, pvData, pcbData):
return RegGetValueW.ctypes_function(hkey, lpSubKey, lpValue, dwFlags, pdwType, pvData, pcbData)
@Advapi32Proxy('RegCloseKey', kernel32_zero_check)
def RegCloseKey(hKey):
return RegCloseKey.ctypes_function(hKey)
###### Iphlpapi (network list and stuff) #######
# ##### Iphlpapi (network list and stuff) ###### #
SetTcpEntry = TransparentIphlpapiProxy('SetTcpEntry')
@OptionalExport(IphlpapiProxy('GetExtendedTcpTable'))
def GetExtendedTcpTable(pTcpTable, pdwSize=None, bOrder=True, ulAf=NeededParameter, TableClass=TCP_TABLE_OWNER_PID_ALL, Reserved=0):
if pdwSize is None: