Implem remote ctypes + Injection 32->64

This commit is contained in:
Clement Rouault
2015-09-10 16:55:02 +02:00
parent 7ddcb492ed
commit e69480364d
7 changed files with 215 additions and 316 deletions
-1
View File
@@ -16,7 +16,6 @@ MY_FUNC PROC
push r11
push r12
push r13
;TODO save the QUEEN (and the registers !)
mov rax, 0h
push rax
mov rax, 7845646165726854h
+26 -11
View File
@@ -7,13 +7,28 @@ import windows.utils as utils
from .native_exec import simple_x86 as x86
from .native_exec import simple_x64 as x64
def get_loadlib_getproc(target):
if windows.current_process.bitness == target.bitness:
LoadLibraryA = utils.get_func_addr('kernel32', 'LoadLibraryA')
GetProcAddress = utils.get_func_addr('kernel32', 'GetProcAddress')
return LoadLibraryA, GetProcAddress
else:
k32 = [x for x in target.peb.modules if x.name == "kernel32.dll"][0]
exp = k32.pe.exports
return exp['LoadLibraryA'], exp['GetProcAddress']
# 32 to 32 injection
def generate_python_exec_shellcode_32(PYDLL_addr, PyInit, PyRun, PYCODE_ADDR):
LoadLibraryA = utils.get_func_addr('kernel32', 'LoadLibraryA')
GetProcAddress = utils.get_func_addr('kernel32', 'GetProcAddress')
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)
@@ -40,10 +55,9 @@ def generate_python_exec_shellcode_32(PYDLL_addr, PyInit, PyRun, PYCODE_ADDR):
return code.get_code()
# 64 to 64 injection
def generate_python_exec_shellcode_64(PYDLL_addr, PyInit, PyRun, PYCODE_ADDR):
LoadLibraryA = utils.get_func_addr('kernel32', 'LoadLibraryA')
GetProcAddress = utils.get_func_addr('kernel32', 'GetProcAddress')
def generate_python_exec_shellcode_64(target, PYDLL_addr, PyInit, PyRun, PYCODE_ADDR):
LoadLibraryA, GetProcAddress = get_loadlib_getproc(target)
Reserve_space_for_call = x64.MultipleInstr([x64.Push('RDI')] * 4)
Clean_space_for_call = x64.MultipleInstr([x64.Pop('RDI')] * 4)
@@ -115,9 +129,9 @@ 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(PYDLL_addr, PyInitT_ADDR, Pyrun_ADDR, PYCODE_ADDR)
shellcode = generate_python_exec_shellcode_32(process, PYDLL_addr, PyInitT_ADDR, Pyrun_ADDR, PYCODE_ADDR)
else:
shellcode = generate_python_exec_shellcode_64(PYDLL_addr, PyInitT_ADDR, Pyrun_ADDR, PYCODE_ADDR)
shellcode = generate_python_exec_shellcode_64(process, PYDLL_addr, PyInitT_ADDR, Pyrun_ADDR, PYCODE_ADDR)
process.write_memory(SHELLCODE_ADDR, shellcode)
return SHELLCODE_ADDR
@@ -127,6 +141,7 @@ def execute_python_code(process, code):
print("me = {0}".format(windows.current_process.bitness))
print("him = {0}".format(process.bitness))
if windows.current_process.bitness != process.bitness:
raise NotImplementedError("Cannot perform 32 <-> 64 injection")
if windows.current_process.bitness == 64 and process.bitness == 32:
raise NotImplementedError("Cannot perform 64 -> 32 injection")
shellcode_remote_addr = inject_python_command(process, code)
return process.create_thread(shellcode_remote_addr, 0)
+35 -47
View File
@@ -3,6 +3,7 @@ import ctypes
import struct
import windows
import windows.hooks as hooks
import windows.utils as utils
from windows.generated_def.winstructs import *
import windows.remotectypes as rctypes
@@ -26,25 +27,32 @@ 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
# For now you can do it by injecting a remote python..
proc_bitness = windows.current_process.bitness
if target is None:
targetedbitness = proc_bitness
else:
targetedbitness = target.bitness
if targetedbitness == 32 and proc_bitness == 64:
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):
return rctypes.transform_type_to_remote64bits(structcls)(addr, target)
# 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..
ctypes_structure_transformer = lambda x: x
def create_structure_at(structcls, addr):
return structcls.from_address(addr)
else:
raise NotImplementedError("Parsing {0} PE from {1} Process".format(targetedbitness, proc_bitness))
if targetedbitness == 32:
IMAGE_ORDINAL_FLAG = IMAGE_ORDINAL_FLAG32
else:
@@ -54,30 +62,19 @@ def PEFile(baseaddr, target=None):
@property
def addr(self):
return baseaddr + self.value
def __repr__(self):
return "<DWORD {0} (RVA to '{1}')>".format(self.value, hex(self.addr))
#class RVA(ctypes.Structure):
# _fields_ = [("Whatever", DWORD)]
# @property
# def addr(self):
# return baseaddr + self.Whatever
#
# def __repr__(self):
# return "<DWORD {0} (RVA to '{1}')>".format(self.Whatever, hex(self.addr))
class StringRVa(RVA):
if proc_bitness == 32 and ctypes_structure_transformer == rctypes.transform_type_to_remote64bits:
if proc_bitness == 32 and targetedbitness == 64:
@property
def str(self):
#return rctypes.rctypes.Remote_c_char_p64.from_buffer_with_target(struct.pack("<Q", self.addr), target)
return rctypes.Remote_c_char_p64(self.addr, target=target).value
#return ctypes_structure_transformer(ctypes.c_char_p)(self.addr, target).value.decode()
else:
@property
def str(self):
return ctypes.c_char_p(self.addr).value.decode()
return ctypes.c_char_p(self.addr).value.decode()
def __repr__(self):
return "<DWORD {0} (String RVA to '{1}')>".format(self.value, self.str)
@@ -103,7 +100,7 @@ def PEFile(baseaddr, target=None):
@classmethod
def create(cls, addr, ord, name):
self = create_structure_at(cls, addr)
self = create_structure_at(cls, addr)
self.addr = addr
self.ord = ord
self.name = name
@@ -158,7 +155,7 @@ def PEFile(baseaddr, target=None):
#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)
return res
def get_EXPORT_DIRECTORY(self):
export_directory_rva = self.get_DataDirectory()[IMAGE_DIRECTORY_ENTRY_EXPORT].VirtualAddress
if export_directory_rva == 0:
@@ -168,19 +165,24 @@ def PEFile(baseaddr, target=None):
#return self._IMAGE_EXPORT_DIRECTORY.from_address(export_directory_addr)
@property
@utils.fixedpropety
def exports(self):
print("ASK FOR EXPORTS {0}".format(target))
res = {}
raw_exports = self.get_EXPORT_DIRECTORY().get_exports()
exp_dir = self.get_EXPORT_DIRECTORY()
if exp_dir is None:
return res
raw_exports = exp_dir.get_exports()
for id, rva_addr, rva_name in raw_exports:
res[id] = rva_addr.addr
if rva_name is not None:
res[rva_name.str] = rva_addr.addr
return res
# TODO: get imports by parsing other modules exports if no INT
@property
@utils.fixedpropety
def imports(self):
res = {}
for import_descriptor in self.get_IMPORT_DESCRIPTORS():
@@ -189,8 +191,8 @@ def PEFile(baseaddr, target=None):
if INT is not None:
for iat_entry, (ord, name) in zip(IAT, INT):
# str(name.decode()) -> python2 and python3 compatible for str result
iat_entry.name = str(name.decode()) if name else name
iat_entry.ord = ord
iat_entry.name = str(name.decode()) if name else ""
res.setdefault(import_descriptor.Name.str.lower(),[]).extend(IAT)
return res
@@ -199,57 +201,45 @@ def PEFile(baseaddr, target=None):
_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 = THUNK_DATA.from_address(int_addr)
int_entry = create_structure_at(THUNK_DATA, int_addr)
res = []
while int_entry.Ordinal:
if int_entry.Ordinal & IMAGE_ORDINAL_FLAG:
res += [(int_entry.Ordinal & 0x7fffffff, None)]
else:
#import_by_name = IMPORT_BY_NAME.from_address(baseaddr + int_entry.AddressOfData)
import_by_name = create_structure_at(IMPORT_BY_NAME, baseaddr + int_entry.AddressOfData)
if proc_bitness == 32 and ctypes_structure_transformer == rctypes.transform_type_to_remote64bits:
name = rctypes.Remote_c_char_p64(baseaddr + int_entry.AddressOfData + type(import_by_name).Name.offset, target=target).value
#name = rctypes.Remote_c_char_p64.from_buffer_with_target(bytearray(struct.pack("<Q", baseaddr + int_entry.AddressOfData + type(import_by_name).Name.offset)), target=target).value
name_address = baseaddr + int_entry.AddressOfData + type(import_by_name).Name.offset
if proc_bitness == 32 and targetedbitness == 64:
name = rctypes.Remote_c_char_p64(name_address, target=target).value
else:
name = ctypes.c_char_p(ctypes.addressof(import_by_name) + IMPORT_BY_NAME.Name.offset).value # FIXIT
name = ctypes.c_char_p(name_address).value
res.append((import_by_name.Hint, name))
int_addr += ctypes.sizeof(type(int_entry))
#int_entry = THUNK_DATA.from_address(int_addr)
int_entry = create_structure_at(THUNK_DATA, int_addr)
return res
def get_IAT(self):
iat_addr = self.FirstThunk.addr
#iat_entry = THUNK_DATA.from_address(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 = THUNK_DATA.from_address(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})
def get_exports(self):
#NameOrdinals = (WORD * self.NumberOfNames).from_address(self.AddressOfNameOrdinals.addr)
NameOrdinals = create_structure_at((WORD * self.NumberOfNames), self.AddressOfNameOrdinals.addr)
NameOrdinals = list(NameOrdinals)
#Functions = (RVA * self.NumberOfFunctions).from_address(self.AddressOfFunctions.addr)
Functions = create_structure_at((RVA * self.NumberOfFunctions), self.AddressOfFunctions.addr)
#Names = (StringRVa * self.NumberOfNames).from_address(self.AddressOfNames.addr)
Names = create_structure_at((StringRVa * self.NumberOfNames), self.AddressOfNames.addr)
res = []
for nb,func in enumerate(Functions):
@@ -259,7 +249,7 @@ def PEFile(baseaddr, target=None):
name = None
res.append((nb, func, name))
return res
current_pe = PEFile()
class IMAGE_DOS_HEADER(ctypes.Structure):
@@ -286,12 +276,10 @@ def PEFile(baseaddr, target=None):
]
def get_NT_HEADER(self):
if targetedbitness == 32:
return create_structure_at(IMAGE_NT_HEADERS32, baseaddr + self.e_lfanew)
return create_structure_at(IMAGE_NT_HEADERS64, baseaddr + self.e_lfanew)
#return IMAGE_NT_HEADERS64.from_address(baseaddr + self.e_lfanew)
return current_pe
tst = PEFile.__code__.co_consts[13]
+65 -61
View File
@@ -4,32 +4,27 @@ import ctypes.wintypes
import itertools
from _ctypes import _SimpleCData
class DummyTarget(object):
def read_memory(self, addr, size):
#print("read_memory at {0} size {1}".format(addr, size))
return (ctypes.c_char * size).from_address(addr)[:]
# 64bits pointeurs and long
### 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 ####
## 64bits pointer types ##
@@ -43,10 +38,10 @@ class c_void_p64(_SimpleCData):
class c_char_p64(_SimpleCData):
_type_ = "Q"
class c_wchar_p64(_SimpleCData):
_type_ = "Q"
# standard type translation
# don't know how to handle size_t since it's non-distinguable from c_ulong
# maybe force import before ctypes and modif stuff into ctypes ?
@@ -56,7 +51,7 @@ class c_wchar_p64(_SimpleCData):
# Used by the RemoteStructure to access the target memory
class RemoteValue(object):
@classmethod
def from_buffer_with_target(cls, buffer, offset=0, target=None):
@@ -69,7 +64,7 @@ class RemotePtr(RemoteValue):
def raw_value(self):
return ctypes.cast(self, ctypes.c_void_p).value
class RemoteCCharP(RemotePtr, ctypes.c_char_p):
class RemoteCCharP(RemotePtr, ctypes.c_char_p):
@property
def value(self):
base = self.raw_value
@@ -82,7 +77,7 @@ class RemoteCCharP(RemotePtr, ctypes.c_char_p):
res.append(x)
return "".join(res)
class RemoteWCharP(RemotePtr, ctypes.c_char_p):
class RemoteWCharP(RemotePtr, ctypes.c_char_p):
@property
def value(self):
base = self.raw_value
@@ -95,7 +90,7 @@ class RemoteWCharP(RemotePtr, ctypes.c_char_p):
break
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):
@@ -109,11 +104,34 @@ class RemoteStructurePointer(RemotePtr, ctypes.c_void_p):
#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)
def __repr__(self):
return "<RemoteStructurePointer to {0}>".format(self.real_pointer_type._type_.__name__)
# 64bits pointers
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
def __getitem__(self, slice):
if not isinstance(slice, (int, long)):
raise NotImplementedError("RemoteArray slice __getitem__")
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 ?
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):
@@ -127,13 +145,11 @@ class RemotePtr64(RemoteValue):
my_addr = ctypes.addressof(self)
return ctypes.c_ulonglong.from_address(my_addr).value
return ctypes.cast(self, ctypes.c_ulonglong).value
#def from_value(self):
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
@@ -141,16 +157,16 @@ class Remote_c_void_p64(RemotePtr64, c_void_p64):
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):
return self.value
@classmethod
def from_buffer_with_target_and_ptr_type(cls, buffer, offset=0, target=None, ptr_type=None):
x = cls.from_buffer(buffer)
@@ -163,29 +179,25 @@ class RemoteStructurePointer64(Remote_c_void_p64):
remote_pointed_type = transform_type_to_remote64bits(self.real_pointer_type._sub_ctypes_)
return remote_pointed_type(self.raw_value, self.target)
type_32_64_translation_table = {
ctypes.c_void_p : Remote_c_void_p64,
ctypes.c_char_p : Remote_c_char_p64,
ctypes.c_wchar_p: Remote_w_char_p64,
#ctypes.wintypes.WPARAM : ctypes.c_ulonglong,
#ctypes.wintypes.LPARAM : ctypes.c_longlong,
#ctypes.c_size_t : c_size_t64, # don't know how to handle size_t size it's non-distinguable from c_ulong
#ctypes.c_ssize_t: c_ssize_t64
}
}
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}
def __init__(self, base_addr, target):
if type(base_addr) not in (int, long):
@@ -211,24 +223,24 @@ class RemoteStructureUnion(object):
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
return RemoteStructurePointer64.from_buffer_with_target_and_ptr_type(bytearray(s), target=self._target, ptr_type=ftype)
if issubclass(ftype, RemoteStructureUnion): # Structure 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
return RemoteStructure.from_structure(ftype)(self._base_addr + fosset, self._target)
if issubclass(ftype, ctypes.Union): # Structure that must be transfomed
return RemoteUnion.from_structure(ftype)(self._base_addr + fosset, self._target)
if issubclass(ftype, _ctypes.Array):
return ftype.from_buffer(bytearray(s))
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
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
# Seems weird but it's mandatory AND useful :D (in pe_parse)
if _SimpleCData not in ftype.__bases__:
return ftype.from_buffer(bytearray(s))
return ftype.from_buffer(bytearray(s)).value
def __getattribute__(self, fieldname):
def __getattribute__(self, fieldname):
if fieldname in type(self)._reserved_name: #Prevent recursion !
return super(RemoteStructureUnion, self).__getattribute__(fieldname)
try:
@@ -242,11 +254,11 @@ class RemoteStructureUnion(object):
def from_structure(cls, structcls):
class MyStruct(cls, structcls): # inherit of structcls to keep property (see winobject.LoadedModule)
_fields_ = structcls._fields_
MyStruct.__name__ = "Remote" + structcls.__name__
return MyStruct
@classmethod
@classmethod
def from_fields(cls, fields, base_cls=None):
bases = [cls]
if base_cls:
@@ -256,15 +268,13 @@ class RemoteStructureUnion(object):
if base_cls:
RemoteStruct.__name__ = "Remote" + base_cls.__name__
return RemoteStruct
class RemoteStructure(RemoteStructureUnion, ctypes.Structure):
pass
class RemoteUnion(RemoteStructureUnion, ctypes.Union):
pass
pass
remote_struct = RemoteStructure.from_structure
if ctypes.sizeof(ctypes.c_void_p) == 4:
@@ -281,7 +291,7 @@ if ctypes.sizeof(ctypes.c_void_p) == 4:
ftype = transform_type_to_remote64bits(ftype)
new_fields.append((fname, ftype))
return RemoteStructure.from_fields(new_fields, base_cls=structcls)
def transform_union_to_remote64bits(structcls):
"""Create a remote structure for a 64bits target process"""
new_fields = []
@@ -289,21 +299,15 @@ if ctypes.sizeof(ctypes.c_void_p) == 4:
ftype = transform_type_to_remote64bits(ftype)
new_fields.append((fname, ftype))
return RemoteUnion.from_fields(new_fields, base_cls=structcls)
def transform_type_to_remote64bits(ftype):
if is_pointer_type(ftype):
return MakePtr(ftype._type_)
if is_array_type(ftype):
return (transform_type_to_remote64bits(ftype._type_) * ftype._length_)
return create_remote_array(transform_type_to_remote64bits(ftype._type_), ftype._length_)
if is_structure_type(ftype):
return transform_structure_to_remote64bits(ftype)
if is_union_type(ftype):
return transform_union_to_remote64bits(ftype)
# Normal types
return type_32_64_translation_table.get(ftype, ftype)
def dump_type(t):
fields_name = t._fields_
for fn in fields_name:
print getattr(t, fn[0])
return type_32_64_translation_table.get(ftype, ftype)
+1 -130
View File
@@ -5,7 +5,6 @@ import windows.native_exec.simple_x64 as x64
from generated_def.winstructs import *
# Special code for syswow64 process
CS_32bits = 0x23
CS_64bits = 0x33
@@ -71,132 +70,4 @@ def NtCreateThreadEx_32_to_64(process, addr, param):
shellcode = NtCreateThreadStub.replace("\x40" * 8, struct.pack("<Q", process.handle))
shellcode = shellcode.replace("\x41" * 8, struct.pack("<Q", addr))
shellcode = shellcode.replace("\x42" * 8, struct.pack("<Q", param))
return execute_64bits_code_from_syswow(shellcode)
# TODO : implem remote PEB parsing
class RemotePointerBase(ULONG64):
pass
class Remote_wchar_p(RemotePointerBase):
def __init__(self, *args):
self.proc = None
super(RemotePointerImp, self).__init__(*args)
def str(self):
addr = self.value
buffer = (ctypes.c_wchar * 255)()
self.proc.read_memory_into(self.value, buffer)
return str(ctypes.c_wchar_p(buffer[:]).value)
class Remote_LIST_ENTRY_PTR(RemotePointerBase):
def __init__(self, *args):
self.proc = None
super(RemotePointerImp, self).__init__(*args)
def TO_LDR_ENTRY(self):
v = RemotePointer(LDR_DATA_TABLE_ENTRY64)(self.value - sizeof(ULONG64) * 2)
v.proc = self.proc
return v
def RemotePointer(struct):
class RemotePointerImp(RemotePointerBase):
def __init__(self, *args):
self.proc = None
super(RemotePointerImp, self).__init__(*args)
def contents(self):
if self.proc is None:
raise ValueError("Non binded :(")
s = struct(self.proc)
self.proc.read_memory_into(self.value, s)
print("set proc via contents for {0}".format(s))
return s
return RemotePointerImp
class RemoteStructure(Structure):
def __init__(self, proc):
self.proc = proc
def __getattribute__(self, value):
print(value)
if value in ["_fields_", "proc"]:
return super(RemoteStructure, self).__getattribute__(value)
d = dict(self._fields_)
t = d.get(value, type(None))
field = super(RemoteStructure, self).__getattribute__(value)
if isinstance(field, (RemoteStructure, RemotePointerBase)):
print("Set proc for {0}".format(field))
field.proc = self.proc
return field
# Struct _LSA_UNICODE_STRING definitions
class _LSA_UNICODE_STRING(RemoteStructure):
_fields_ = [
("Length", USHORT),
("MaximumLength", USHORT),
("Buffer", Remote_wchar_p),
]
PUNICODE_STRING = POINTER(_LSA_UNICODE_STRING)
UNICODE_STRING = _LSA_UNICODE_STRING
LSA_UNICODE_STRING = _LSA_UNICODE_STRING
PLSA_UNICODE_STRING = POINTER(_LSA_UNICODE_STRING)
class _LIST_ENTRY64(RemoteStructure): pass
_LIST_ENTRY64._fields_ = [
("Flink", Remote_LIST_ENTRY_PTR),
("Blink", Remote_LIST_ENTRY_PTR),
]
LIST_ENTRY64 = _LIST_ENTRY64
# Struct _LDR_DATA_TABLE_ENTRY definitions
class _LDR_DATA_TABLE_ENTRY64(RemoteStructure):
_fields_ = [
("Reserved1", ULONG64 * 2),
("InMemoryOrderLinks", _LIST_ENTRY64),
("Reserved2", ULONG64 * 2),
("DllBase", ULONG64),
("EntryPoint", ULONG64),
("Reserved3", ULONG64),
("FullDllName", UNICODE_STRING),
("BaseDllName", UNICODE_STRING),
("Reserved5", ULONG64 * 3),
("CheckSum", ULONG),
("TimeDateStamp", ULONG),
]
LDR_DATA_TABLE_ENTRY64 = _LDR_DATA_TABLE_ENTRY64
class _PEB_LDR_DATA64(RemoteStructure):
_fields_ = [
("Reserved1", BYTE * 8),
("Reserved2", ULONG64 * 3),
("InMemoryOrderModuleList", LIST_ENTRY64),
]
class _PEB64(RemoteStructure):
_fields_ = [
("Reserved1", BYTE * 2),
("BeingDebugged", BYTE),
("Reserved2", BYTE * 1),
("Reserved3", ULONG64 * 2),
("Ldr", RemotePointer(_PEB_LDR_DATA64)),
("ProcessParameters", ULONG64),
("Reserved4", BYTE * 104),
("Reserved5", ULONG64 * 52),
("PostProcessInitRoutine", ULONG64),
("Reserved6", BYTE * 128),
("Reserved7", ULONG64 * 1),
("SessionId", ULONG),
]
PEB64 = _PEB64
return execute_64bits_code_from_syswow(shellcode)
+26 -7
View File
@@ -10,13 +10,25 @@ from . import k32testing as kernel32proxy
from .generated_def import windef
from .generated_def.winstructs import *
# Function resolution !
def fixedpropety(f):
cache_name = "_" + f.__name__
def prop(self):
try:
return getattr(self, cache_name)
except AttributeError:
setattr(self, cache_name, f(self))
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
# Function resolution !
def get_func_addr(dll_name, func_name):
dll = ctypes.WinDLL(dll_name)
modules = windows.current_process.peb.modules
@@ -24,7 +36,14 @@ def get_func_addr(dll_name, func_name):
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):
raise ValueError("Module <{0}> not loaded in target <{1}>".format(dll_name, target))
mod = name_modules[0]
return mod.pe.exports[func_name]
def is_wow_64(hProcess):
try:
fnIsWow64Process = get_func_addr("kernel32.dll", "IsWow64Process")
@@ -63,19 +82,19 @@ def create_console():
console_stderr = create_file_from_handle(stderr_handle, "w")
#print(stderr_handle, console_stderr)
import os
#os.dup2(console_stderr.fileno(), 2)
#os.dup2(console_stderr.fileno(), 2)
sys.stderr = console_stderr
class FixedInteractiveConsole(code.InteractiveConsole):
def raw_input(self, prompt=">>>"):
sys.stdout.write(prompt)
return raw_input("")
def pop_shell():
create_console()
FixedInteractiveConsole(locals()).interact()
class VirtualProtected(object):
"""A context manager usable like `VirtualProtect` that will restore the old protection at exit
+62 -59
View File
@@ -14,14 +14,13 @@ import windows.native_exec.simple_x86 as x86
import windows.native_exec.simple_x64 as x64
from . import utils
from .dbgprint import dbgprint
from windows.generated_def.winstructs import *
from .generated_def import windef
import windows.pe_parse as pe_parse
class AutoHandle(object):
"""An abstract class that allow easy handle creation/destruction"""
def _get_handle(self):
@@ -66,7 +65,7 @@ class System(object):
"""
return self.enumerate_threads()
@property
@utils.fixedpropety
def bitness(self):
"""The bitness of the system
@@ -78,7 +77,7 @@ class System(object):
if "PROCESSOR_ARCHITEW6432" in os.environ:
return 64
return 32
@staticmethod
def enumerate_processes():
process_entry = WinProcess()
@@ -90,7 +89,7 @@ class System(object):
while kernel32proxy.Process32Next(snap, process_entry):
res.append(utils.swallow_ctypes_copy(process_entry))
return res
@staticmethod
def enumerate_threads():
thread_entry = WinThread()
@@ -106,12 +105,12 @@ class System(object):
class WinThread(THREADENTRY32, AutoHandle):
"""Represent a thread """
@property
@utils.fixedpropety
def tid(self):
"""Thread ID"""
return self.th32ThreadID
@property
@utils.fixedpropety
def owner(self):
"""The Process owning the thread
@@ -125,23 +124,23 @@ class WinThread(THREADENTRY32, AutoHandle):
except IndexError:
return None
return self._owner
@property
def context(self):
x = windows.vectored_exception.EnhancedCONTEXT()
x.ContextFlags = CONTEXT_FULL
kernel32proxy.GetThreadContext(self.handle, x)
return x
def set_context(self, context):
return kernel32proxy.SetThreadContext(self.handle, context)
def exit(self, code=0):
return kernel32proxy.TerminateThread(self.handle, code)
def resume(self):
return kernel32proxy.ResumeThread(self.handle)
def suspend(self):
return kernel32proxy.SuspendThread(self.handle)
@@ -155,8 +154,8 @@ class WinThread(THREADENTRY32, AutoHandle):
else:
owner_name = owner.name
return '<{0} {1} owner "{2}" at {3}>'.format(self.__class__.__name__, self.tid, owner_name, hex(id(self)))
@staticmethod
@staticmethod
def _from_handle(handle):
tid = kernel32proxy.GetThreadId(handle)
print(tid)
@@ -164,9 +163,9 @@ class WinThread(THREADENTRY32, AutoHandle):
return [t for t in System().threads if t.tid == tid][0]
except IndexError:
return (tid, handle)
class Process(AutoHandle):
@property
@utils.fixedpropety
def is_wow_64(self):
"""Is True if the process is a SysWow64 process
@@ -176,7 +175,7 @@ class Process(AutoHandle):
"""
return utils.is_wow_64(self.handle)
@property
@utils.fixedpropety
def bitness(self):
"""The bitness of the process
@@ -208,12 +207,12 @@ class Process(AutoHandle):
class CurrentThread(AutoHandle):
"""The current thread"""
@property
@utils.fixedpropety
def tid(self):
"""Thread ID"""
return kernel32proxy.GetCurrentThreadId()
@property
@utils.fixedpropety
def owner(self):
"""The current process
@@ -226,12 +225,12 @@ class CurrentThread(AutoHandle):
def __del__(self):
pass
def exit(self, code=0):
"""Exit the thread"""
return kernel32proxy.ExitThread(code)
class CurrentProcess(Process):
"""The current process"""
@@ -256,7 +255,7 @@ class CurrentProcess(Process):
def _get_handle(self):
return kernel32proxy.GetCurrentProcess()
def __del__(self):
pass
@@ -269,7 +268,7 @@ class CurrentProcess(Process):
return os.getpid()
# Is there a better way ?
@property
@utils.fixedpropety
def ppid(self):
"""Parent Process ID
@@ -277,7 +276,7 @@ class CurrentProcess(Process):
"""
return [p for p in windows.system.processes if p.pid == self.pid][0].ppid
@property
@utils.fixedpropety
def peb(self):
"""The Process Environment Block of the current process
@@ -285,11 +284,12 @@ class CurrentProcess(Process):
"""
return PEB.from_address(self.get_peb_builtin()())
@property
@utils.fixedpropety
def bitness(self):
"""The bitness of the process
:returns: int -- 32 or 64"""
print("FAIL")
import platform
bits = platform.architecture()[0]
return int(bits[:2])
@@ -309,6 +309,7 @@ class CurrentProcess(Process):
def read_memory(self, addr, size):
"""Read size from adddr"""
dbgprint('Read CurrentProcess Memory', 'READMEM')
buffer = (c_char * size).from_address(addr)
return buffer[:]
@@ -330,7 +331,7 @@ class WinProcess(PROCESSENTRY32, Process):
is_pythondll_injected = 0
is_remote_slave_running = False
@property
@utils.fixedpropety
def name(self):
"""Name of the process
@@ -338,7 +339,7 @@ class WinProcess(PROCESSENTRY32, Process):
"""
return self.szExeFile[:].decode()
@property
@utils.fixedpropety
def pid(self):
"""Process ID
@@ -346,7 +347,7 @@ class WinProcess(PROCESSENTRY32, Process):
"""
return self.th32ProcessID
@property
@utils.fixedpropety
def ppid(self):
"""Parent Process ID
@@ -373,28 +374,29 @@ class WinProcess(PROCESSENTRY32, Process):
def low_read_memory(self, addr, buffer_addr, size):
if windows.current_process.bitness == 32 and self.bitness == 64:
NtWow64ReadVirtualMemory64Addr = windows.utils.get_func_addr("ntdll.dll", "NtWow64ReadVirtualMemory64")
NtWow64ReadVirtualMemory64 = WINFUNCTYPE(HRESULT, HANDLE, ULONG64, PVOID, ULONG64, PULONG64)(NtWow64ReadVirtualMemory64Addr)
return NtWow64ReadVirtualMemory64(self.handle, addr, buffer_addr, size, None)
if not hasattr(self, "NtWow64ReadVirtualMemory64"): # TODO: better stuff : (in k32testing?)
NtWow64ReadVirtualMemory64Addr = windows.utils.get_func_addr("ntdll.dll", "NtWow64ReadVirtualMemory64")
self.NtWow64ReadVirtualMemory64 = WINFUNCTYPE(HRESULT, HANDLE, ULONG64, PVOID, ULONG64, PULONG64)(NtWow64ReadVirtualMemory64Addr)
return self.NtWow64ReadVirtualMemory64(self.handle, addr, buffer_addr, size, None)
return kernel32proxy.ReadProcessMemory(self.handle, addr, lpBuffer=buffer_addr, nSize=size)
def read_memory(self, addr, size):
"""Read `size` from `addr`"""
print("Read on page {0}".format(hex(addr & 0xfffffffffffff000)))
buffer = ctypes.create_string_buffer(size)
self.low_read_memory(addr, ctypes.byref(buffer), size)
return buffer[:]
#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)))
#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:
@@ -423,7 +425,7 @@ class WinProcess(PROCESSENTRY32, Process):
def execute_python(self, pycode):
"""Execute Python code into the remote process"""
return injection.execute_python_code(self, pycode)
def get_peb_addr(self):
get_peb_32_code = codecs.decode(b'64a130000000', 'hex')
get_peb_64_code = codecs.decode(b"65488B042560000000", 'hex')
@@ -452,13 +454,14 @@ class WinProcess(PROCESSENTRY32, Process):
time.sleep(0.01)
peb_addr = struct.unpack("<Q", self.read_memory(dest, 8))[0]
return peb_addr
@utils.fixedpropety
def peb(self):
if windows.current_process.bitness == 32 and self.bitness == 64:
return RemotePEB64(self.get_peb_addr(), self)
return RemotePEB(self.get_peb_addr(), self)
class LoadedModule(LDR_DATA_TABLE_ENTRY):
"""An entry in the PEB Ldr list"""
@property
@@ -508,17 +511,17 @@ class LIST_ENTRY_PTR(PVOID):
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})
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)})
@property
def imagepath(self):
"""The ImagePathName of the PEB
@@ -550,19 +553,19 @@ class PEB(Structure):
list_entry_ptr = ctypes.cast(current_dll.InMemoryOrderLinks.Flink, LIST_ENTRY_PTR)
current_dll = list_entry_ptr.TO_LDR_ENTRY()
return [LoadedModule.from_address(addressof(LDR)) for LDR in res]
import windows.remotectypes as rctypes
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)
@property
def modules(self):
"""The loaded modules present in the PEB
@@ -571,39 +574,39 @@ class RemotePEB(rctypes.RemoteStructure.from_structure(PEB)):
"""
res = []
list_entry_ptr = self.Ldr.contents.InMemoryOrderModuleList.Flink.raw_value
current_dll = self.ptr_flink_to_remote_module(list_entry_ptr)
while current_dll.DllBase:
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
if CurrentProcess().bitness == 32:
class RemoteLoadedModule64(rctypes.transform_type_to_remote64bits(LoadedModule)):
@property
def pe(self):
"""A PE representation of the module
:type: :class:`windows.pe_parse.PEFile`
"""
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)
@property
def modules(self):
"""The loaded modules present in the PEB
:type: [:class:`LoadedModule`] -- List of loaded modules
"""
res = []
list_entry_ptr = self.Ldr.contents.InMemoryOrderModuleList.Flink.raw_value
current_dll = self.ptr_flink_to_remote_module(list_entry_ptr)
while current_dll.DllBase:
res.append(current_dll)