mirror of
https://github.com/hakril/PythonForWindows
synced 2026-06-08 14:31:45 +00:00
Fix GetContext 32<->64 + Thread.start_address 32<->64
This commit is contained in:
@@ -1,2 +1,8 @@
|
||||
TODO:
|
||||
- get thread context (clean code + 32<->64 compat)
|
||||
- ProcessMemory object ? (metasm like)
|
||||
|
||||
FIXME:
|
||||
- WMI
|
||||
- COM initialisation when injected in another process
|
||||
- The CoInitialize might be already called
|
||||
- Fix that
|
||||
+19
-11
@@ -54,8 +54,6 @@ def execute_64bits_code_from_syswow(shellcode):
|
||||
def generate_syswow64_call(target):
|
||||
nb_args = len(target.prototype._argtypes_)
|
||||
target_addr = get_syswow_ntdll_exports()[target.__name__]
|
||||
print hex(target_addr)
|
||||
|
||||
argument_buffer_len = (nb_args * 8)
|
||||
argument_buffer = windows.current_process.allocator.reserve_size(argument_buffer_len)
|
||||
alignement_information = windows.current_process.allocator.reserve_size(8)
|
||||
@@ -76,8 +74,8 @@ def generate_syswow64_call(target):
|
||||
code_64b += x64.Push('R11')
|
||||
code_64b += x64.Push('R12')
|
||||
code_64b += x64.Push('R13')
|
||||
# Alignment stuff :)
|
||||
|
||||
# Alignment stuff :)
|
||||
code_64b += x64.Mov('RCX', 'RSP')
|
||||
code_64b += x64.And('RCX', 0x0f)
|
||||
code_64b += x64.Mov(x64.deref(alignement_information), 'RCX')
|
||||
@@ -167,17 +165,15 @@ def try_generate_stub_target(shellcode, argument_buffer, target):
|
||||
|
||||
def get_current_process_syswow_peb_addr():
|
||||
current_process = windows.current_process
|
||||
dest = current_process.virtual_alloc(0x1000)
|
||||
get_peb_64_code = codecs.decode(b"65488B042560000000", 'hex')
|
||||
store_peb = x64.MultipleInstr()
|
||||
store_peb += x64.Mov(x64.create_displacement(disp=dest), 'RAX')
|
||||
get_peb_64_code += store_peb.get_code()
|
||||
dest = current_process.allocator.reserve_size(8)
|
||||
get_peb_64_code = x64.MultipleInstr()
|
||||
get_peb_64_code += x64.Mov('RAX', x64.mem('gs:[0x60]'))
|
||||
get_peb_64_code += x64.Mov(x64.create_displacement(disp=dest), 'RAX')
|
||||
current_process.write_memory(dest, "\x00" * 8)
|
||||
windows.syswow64.execute_64bits_code_from_syswow(get_peb_64_code)
|
||||
execute_64bits_code_from_syswow(get_peb_64_code.get_code())
|
||||
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
|
||||
|
||||
@@ -250,6 +246,16 @@ def NtQueryInformationProcess_32_to_64(ProcessHandle, ProcessInformationClass=Pr
|
||||
return NtQueryInformationProcess_32_to_64.ctypes_function(ProcessHandle, ProcessInformationClass, ProcessInformation, ProcessInformationLength, ReturnLength)
|
||||
|
||||
|
||||
@Syswow64ApiProxy(windows.winproxy.NtQueryInformationThread)
|
||||
def NtQueryInformationThread_32_to_64(ThreadHandle, ThreadInformationClass, ThreadInformation, ThreadInformationLength=0, ReturnLength=None):
|
||||
if ReturnLength is None:
|
||||
ReturnLength = byref(ULONG())
|
||||
if ThreadInformation is not None and ThreadInformationLength == 0:
|
||||
ThreadInformationLength = ctypes.sizeof(ThreadInformation)
|
||||
return NtQueryInformationThread_32_to_64.ctypes_function(ThreadHandle, ThreadInformationClass, ThreadInformation, ThreadInformationLength, ReturnLength)
|
||||
|
||||
|
||||
|
||||
@Syswow64ApiProxy(windows.winproxy.NtQueryVirtualMemory)
|
||||
def NtQueryVirtualMemory_32_to_64(ProcessHandle, BaseAddress, MemoryInformationClass=MemoryBasicInformation, MemoryInformation=NeededParameter, MemoryInformationLength=0, ReturnLength=None):
|
||||
if ReturnLength is None:
|
||||
@@ -265,4 +271,6 @@ def NtQueryVirtualMemory_32_to_64(ProcessHandle, BaseAddress, MemoryInformationC
|
||||
def NtGetContextThread_32_to_64(hThread, lpContext):
|
||||
if type(lpContext) == windows.vectored_exception.EnhancedCONTEXT64:
|
||||
lpContext = byref(lpContext)
|
||||
return NtGetContextThread_32_to_64.ctypes_function(hThread, lpContext)
|
||||
return NtGetContextThread_32_to_64.ctypes_function(hThread, lpContext)
|
||||
|
||||
|
||||
|
||||
@@ -235,6 +235,7 @@ class WindowsTestCase(unittest.TestCase):
|
||||
with self.assertRaises(windows.injection.RemotePythonError) as ar:
|
||||
t = calc.execute_python("import time;time.sleep(0.1); raise ValueError('BYE')")
|
||||
|
||||
@windows_64bit_only
|
||||
def test_thread_exit_value_64(self):
|
||||
with Calc64() as calc:
|
||||
res = calc.execute_python("import time;time.sleep(0.1); 2")
|
||||
@@ -242,6 +243,37 @@ class WindowsTestCase(unittest.TestCase):
|
||||
with self.assertRaises(windows.injection.RemotePythonError) as ar:
|
||||
t = calc.execute_python("import time;time.sleep(0.1); raise ValueError('BYE')")
|
||||
|
||||
def test_thread_start_address_32(self):
|
||||
with Calc32() as calc:
|
||||
t = calc.threads[0]
|
||||
t.start_address # No better idea right now that checking for crash/exception
|
||||
|
||||
@windows_64bit_only
|
||||
def test_thread_start_address_64(self):
|
||||
with Calc64() as calc:
|
||||
t = calc.threads[0]
|
||||
t.start_address # No better idea right now that checking for crash/exception
|
||||
|
||||
def test_get_context_address_32(self):
|
||||
with Calc32() as calc:
|
||||
code = x86.MultipleInstr()
|
||||
code += x86.Mov("EAX", 0x42424242)
|
||||
code += x86.Label(":LOOP")
|
||||
code += x86.Jmp(":LOOP")
|
||||
t = calc.execute(code.get_code())
|
||||
cont = t.context
|
||||
self.assertEqual(cont.Eax, 0x42424242)
|
||||
|
||||
@windows_64bit_only
|
||||
def test_get_context_address_64(self):
|
||||
with Calc64() as calc:
|
||||
code = x64.MultipleInstr()
|
||||
code += x64.Mov("RAX", 0x4242424243434343)
|
||||
code += x64.Label(":LOOP")
|
||||
code += x64.Jmp(":LOOP")
|
||||
t = calc.execute(code.get_code())
|
||||
cont = t.context
|
||||
self.assertEqual(cont.Rax, 0x4242424243434343)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
|
||||
+78
-15
@@ -36,19 +36,39 @@ exception_type = [
|
||||
# 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]])
|
||||
|
||||
def generate_enhanced_exception_record(base, name_suffix=""):
|
||||
class EnhancedEXCEPTION_RECORD(base):
|
||||
@property
|
||||
def ExceptionCode(self):
|
||||
real_code = super(EnhancedEXCEPTION_RECORD, self).ExceptionCode
|
||||
return exception_name_by_value.get(real_code, 'UNKNOW_EXCEPTION({0})'.format(hex(real_code)))
|
||||
|
||||
class EnhancedEXCEPTION_RECORD(EXCEPTION_RECORD):
|
||||
@property
|
||||
def ExceptionCode(self):
|
||||
real_code = super(EnhancedEXCEPTION_RECORD, self).ExceptionCode
|
||||
return exception_name_by_value.get(real_code, 'UNKNOW_EXCEPTION({0})'.format(hex(real_code)))
|
||||
@property
|
||||
def ExceptionAddress(self):
|
||||
x = super(EnhancedEXCEPTION_RECORD, self).ExceptionAddress
|
||||
if x is None:
|
||||
return 0x0
|
||||
return x
|
||||
EnhancedEXCEPTION_RECORD.__name__ += name_suffix
|
||||
return EnhancedEXCEPTION_RECORD
|
||||
|
||||
@property
|
||||
def ExceptionAddress(self):
|
||||
x = super(EnhancedEXCEPTION_RECORD, self).ExceptionAddress
|
||||
if x is None:
|
||||
return 0x0
|
||||
return x
|
||||
EnhancedEXCEPTION_RECORD = generate_enhanced_exception_record(EXCEPTION_RECORD)
|
||||
EnhancedEXCEPTION_RECORD32 = generate_enhanced_exception_record(EXCEPTION_RECORD32, "32")
|
||||
EnhancedEXCEPTION_RECORD64 = generate_enhanced_exception_record(EXCEPTION_RECORD64, "64")
|
||||
|
||||
|
||||
#class EnhancedEXCEPTION_RECORD(EXCEPTION_RECORD):
|
||||
# @property
|
||||
# def ExceptionCode(self):
|
||||
# real_code = super(EnhancedEXCEPTION_RECORD, self).ExceptionCode
|
||||
# return exception_name_by_value.get(real_code, 'UNKNOW_EXCEPTION({0})'.format(hex(real_code)))
|
||||
#
|
||||
# @property
|
||||
# def ExceptionAddress(self):
|
||||
# x = super(EnhancedEXCEPTION_RECORD, self).ExceptionAddress
|
||||
# if x is None:
|
||||
# return 0x0
|
||||
# return x
|
||||
|
||||
|
||||
class Eflags(int):
|
||||
@@ -101,7 +121,7 @@ class Eflags(int):
|
||||
return "{0}({1}:{2})".format(type(self).__name__, int.__hex__(self), self.dump())
|
||||
|
||||
|
||||
class EnhancedCONTEXTBase(CONTEXT):
|
||||
class EnhancedCONTEXTBase():
|
||||
default_dump = ()
|
||||
pc_reg = ''
|
||||
special_reg_type = {}
|
||||
@@ -132,19 +152,48 @@ class EnhancedCONTEXTBase(CONTEXT):
|
||||
pc = property(get_pc, set_pc, None, "Program Counter register (EIP or RIP)")
|
||||
|
||||
|
||||
class EnhancedCONTEXT32(EnhancedCONTEXTBase):
|
||||
class EnhancedCONTEXT32(EnhancedCONTEXTBase, CONTEXT32):
|
||||
default_dump = ('Eip', 'Esp', 'Eax', 'Ebx', 'Ecx', 'Edx', 'Ebp', 'Edi', 'Esi', 'EFlags')
|
||||
pc_reg = 'Eip'
|
||||
special_reg_type = {'EFlags': Eflags}
|
||||
|
||||
class EnhancedCONTEXTWOW64(EnhancedCONTEXTBase, WOW64_CONTEXT):
|
||||
default_dump = ('Eip', 'Esp', 'Eax', 'Ebx', 'Ecx', 'Edx', 'Ebp', 'Edi', 'Esi', 'EFlags')
|
||||
pc_reg = 'Eip'
|
||||
special_reg_type = {'EFlags': Eflags}
|
||||
|
||||
|
||||
class EnhancedCONTEXT64(EnhancedCONTEXTBase):
|
||||
class EnhancedCONTEXT64(EnhancedCONTEXTBase, CONTEXT64):
|
||||
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}
|
||||
|
||||
if windows.current_process.bitness == 32:
|
||||
@classmethod
|
||||
def new_aligned(cls):
|
||||
"""Return a new EnhancedCONTEXT64 aligned on 16 bits
|
||||
temporary workaround or horrible hack ? choose your side
|
||||
"""
|
||||
size = ctypes.sizeof(cls)
|
||||
nb_qword = (size + 8) / ctypes.sizeof(ULONGLONG)
|
||||
buffer = (nb_qword * ULONGLONG)()
|
||||
struct_address = ctypes.addressof(buffer)
|
||||
if (struct_address & 0xf) not in [0, 8]:
|
||||
raise ValueError("ULONGLONG array not aligned on 8")
|
||||
if (struct_address & 0xf) == 8:
|
||||
struct_address += 8
|
||||
self = cls.from_address(struct_address)
|
||||
# Keep the raw buffer alive
|
||||
self._buffer = buffer
|
||||
return self
|
||||
|
||||
def bitness():
|
||||
"""Return 32 or 64"""
|
||||
import platform
|
||||
bits = platform.architecture()[0]
|
||||
return int(bits[:2])
|
||||
|
||||
if bitness() == 32:
|
||||
EnhancedCONTEXT = EnhancedCONTEXT32
|
||||
else:
|
||||
EnhancedCONTEXT = EnhancedCONTEXT64
|
||||
@@ -192,3 +241,17 @@ class WithExceptionHandler(object):
|
||||
def __exit__(self, exc_type, exc_value, traceback):
|
||||
windows.winproxy.RemoveVectoredExceptionHandler(self.value)
|
||||
return False
|
||||
|
||||
class DumpContextOnException(WithExceptionHandler):
|
||||
def __init__(self, exit=False):
|
||||
self.exit = exit
|
||||
super(DumpContextOnException, self).__init__(self.print_context_result)
|
||||
|
||||
def print_context_result(self, exception_pointers):
|
||||
except_record = exception_pointers[0].ExceptionRecord[0]
|
||||
exception_pointers[0].dump()
|
||||
sys.stdout.flush()
|
||||
if self.exit:
|
||||
windows.current_process.exit()
|
||||
return 0
|
||||
|
||||
|
||||
+38
-2
@@ -139,7 +139,23 @@ class WinThread(THREADENTRY32, AutoHandle):
|
||||
|
||||
@property
|
||||
def context(self):
|
||||
x = windows.vectored_exception.EnhancedCONTEXT()
|
||||
if self.owner.bitness == 32 and windows.current_process.bitness == 64:
|
||||
# Wow64
|
||||
x = windows.vectored_exception.EnhancedCONTEXTWOW64()
|
||||
x.ContextFlags = CONTEXT_FULL
|
||||
winproxy.Wow64GetThreadContext(self.handle, x)
|
||||
return x
|
||||
|
||||
if self.owner.bitness == 64 and windows.current_process.bitness == 32:
|
||||
x = windows.vectored_exception.EnhancedCONTEXT64.new_aligned()
|
||||
x.ContextFlags = CONTEXT_FULL
|
||||
windows.syswow64.NtGetContextThread_32_to_64(self.handle, x)
|
||||
return x
|
||||
|
||||
if self.owner.bitness == 32:
|
||||
x = windows.vectored_exception.EnhancedCONTEXT32()
|
||||
else:
|
||||
x = windows.vectored_exception.EnhancedCONTEXT64.new_aligned()
|
||||
x.ContextFlags = CONTEXT_FULL
|
||||
winproxy.GetThreadContext(self.handle, x)
|
||||
return x
|
||||
@@ -147,6 +163,20 @@ class WinThread(THREADENTRY32, AutoHandle):
|
||||
def set_context(self, context):
|
||||
return winproxy.SetThreadContext(self.handle, context)
|
||||
|
||||
@property
|
||||
def start_address(self):
|
||||
if windows.current_process.bitness == 32 and self.owner.bitness == 64:
|
||||
res = ULONGLONG()
|
||||
windows.syswow64.NtQueryInformationThread_32_to_64(self.handle, ThreadQuerySetWin32StartAddress, byref(res), ctypes.sizeof(res))
|
||||
return res.value
|
||||
res_size = max(self.owner.bitness, windows.current_process.bitness)
|
||||
if res_size == 32:
|
||||
res = ULONG()
|
||||
else:
|
||||
res = ULONGLONG()
|
||||
winproxy.NtQueryInformationThread(self.handle, ThreadQuerySetWin32StartAddress, byref(res), ctypes.sizeof(res))
|
||||
return res.value
|
||||
|
||||
def exit(self, code=0):
|
||||
return winproxy.TerminateThread(self.handle, code)
|
||||
|
||||
@@ -536,9 +566,15 @@ class WinProcess(PROCESSENTRY32, Process):
|
||||
windows.syswow64.NtQueryInformationProcess_32_to_64(self.handle, ProcessInformation=data, ProcessInformationLength=ctypes.sizeof(x))
|
||||
peb_offset = x.PebBaseAddress.offset
|
||||
peb_addr = struct.unpack("<Q", data[x.PebBaseAddress.offset: x.PebBaseAddress.offset+8])[0]
|
||||
elif windows.current_process.bitness == 64 and self.bitness == 32:
|
||||
information_type = 26
|
||||
y = ULONGLONG()
|
||||
windows.winproxy.NtQueryInformationProcess(self.handle, information_type, byref(y), sizeof(y))
|
||||
peb_addr = y.value
|
||||
else:
|
||||
information_type = 0
|
||||
x = PROCESS_BASIC_INFORMATION()
|
||||
windows.winproxy.NtQueryInformationProcess(self.handle, 0, x)
|
||||
windows.winproxy.NtQueryInformationProcess(self.handle, information_type, x)
|
||||
peb_addr = ctypes.cast(x.PebBaseAddress, PVOID).value
|
||||
if peb_addr is None:
|
||||
raise ValueError("Could not get peb addr of process {0}".format(self.name))
|
||||
|
||||
Reference in New Issue
Block a user