Fix GetContext 32<->64 + Thread.start_address 32<->64

This commit is contained in:
Clement Rouault
2015-12-22 17:23:38 +01:00
parent 5a29c59b3e
commit 3ed7c651da
5 changed files with 174 additions and 29 deletions
+7 -1
View File
@@ -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
View File
@@ -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)
+32
View File
@@ -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
View File
@@ -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
View File
@@ -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))