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