Add NTSTATUS to windef/add virtual_free/improve nativeutils GetProcAddr + test

This commit is contained in:
Clement Rouault
2016-02-02 18:45:24 +01:00
parent 3f06e95617
commit 9fbba331ca
8 changed files with 3576 additions and 1729 deletions
-6
View File
@@ -17,19 +17,13 @@ TODO:
- Real API arround alloc/free memory in WinProcess..
- Threading
- code injection do real thread stuff
- Quid IAT hook stub ? just einit threads and remove this ?
- Injection
- code generated by generate_python_exec_shellcode_64[32] may be reused
Just need to passe the address of the python string as argument
- GetProcesAddress
- CRASH if API is not found..
- fail when resolving xxxW function
- Debugger:
- clean Syswow64 debugging code (_handle_syswow64_exception)
FIXME:
- WMI
+49
View File
@@ -1,6 +1,7 @@
import sys
import os
import os.path
import re
import dummy_wintypes
import struct_parser
@@ -8,6 +9,7 @@ import func_parser
import def_parser
TYPE_EQUIVALENCE = [
('PWSTR', 'LPWSTR'),
('SIZE_T', 'c_ulong'),
@@ -54,10 +56,12 @@ known_type = dummy_wintypes.names + list([x[0] for x in TYPE_EQUIVALENCE])
FUNC_FILE = "winfunc.txt"
STRUCT_FILE = "winstruct.txt"
DEF_FILE = "windef.txt"
NTSTATUS_FILE = "ntstatus.txt"
GENERATED_STRUCT_FILE = "winstructs"
GENERATED_FUNC_FILE = "winfuncs"
GENERATED_DEF_FILE = "windef"
GENERATED_NTSTATUS_FILE = "ntstatus"
OUT_DIRS = ["..\windows\generated_def"]
if len(sys.argv) > 1:
@@ -204,6 +208,14 @@ structs, enums = struct_parser.WinStructParser(structs_code).parse()
validate_structs(structs, enums, defs)
verif_funcs_type(funcs, structs, enums)
# Create Flags for ntstatus
nt_status_defs = []
for line in open(NTSTATUS_FILE):
code, name, descr = line.split("|", 2)
nt_status_defs.append(def_parser.WinDef(name, code))
defs = nt_status_defs + defs
defs_ctypes = generate_defs_ctypes(defs)
funcs_ctypes = generate_funcs_ctypes(funcs)
structs_ctypes = generate_struct_ctypes(structs, enums)
@@ -216,5 +228,42 @@ write_to_out_file(GENERATED_DEF_FILE, defs_ctypes)
write_to_out_file(GENERATED_FUNC_FILE, funcs_ctypes)
write_to_out_file(GENERATED_STRUCT_FILE, structs_ctypes)
NTSTATUS_HEAD = """
class NtStatusException(Exception):
ALL_STATUS = {}
def __init__(self , code):
try:
x = self.ALL_STATUS[code]
except KeyError:
x = (code, 'UNKNOW_ERROR', 'Error non documented in ntstatus.py')
self.code = x[0]
self.name = x[1]
self.descr = x[2]
return super(NtStatusException, self).__init__(*x)
def __str__(self):
return "{e.name}(0x{e.code:x}): {e.descr}".format(e=self)
@classmethod
def register_ntstatus(cls, code, name, descr):
if code in cls.ALL_STATUS:
return # Use the first def
cls.ALL_STATUS[code] = (code, name, descr)
"""
nt_status_exceptions = [NTSTATUS_HEAD]
for line in open(NTSTATUS_FILE):
code, name, descr = line.split("|", 2)
code = int(code, 0)
b = descr
descr = re.sub(" +", " ", descr[:-1]) # remove \n
descr = descr.replace('"', "'")
nt_status_exceptions.append('NtStatusException.register_ntstatus({0}, "{1}", "{2}")'.format(hex(code), name, descr))
write_to_out_file(GENERATED_NTSTATUS_FILE, "\n".join(nt_status_exceptions))
for out_dir in OUT_DIRS:
print("Files generated in <{0}>".format(os.path.abspath(out_dir)))
+27 -49
View File
@@ -83,8 +83,8 @@ class Debugger(object):
def _dispatch_breakpoint(self, exception, addr):
bp = self.breakpoints[addr]
bp.trigger(self, exception)
return bp
x = bp.trigger(self, exception)
return x
def _setup_breakpoint_BP(self, bp, targets):
for target in targets:
@@ -153,58 +153,30 @@ class Debugger(object):
def _handle_unknown_debug_event(self, debug_event):
raise NotImplementedError("dwDebugEventCode = {0}".format(debug_event.dwDebugEventCode))
def _handle_syswow64_exception(self, debug_event):
exception = debug_event.u.Exception
self._update_debugger_state(debug_event)
exception.__class__ = windows.vectored_exception.EEXCEPTION_DEBUG_INFO64
excp_code = exception.ExceptionRecord.ExceptionCode
excp_addr = exception.ExceptionRecord.ExceptionAddress
if excp_code in [EXCEPTION_BREAKPOINT, 0x4000001f] and excp_addr in self.breakpoints:
self._dispatch_breakpoint(exception, excp_addr)
self._pass_breakpoint(excp_addr)
return
elif excp_code in [EXCEPTION_SINGLE_STEP, 0x4000001e]:
if self.current_thread.tid in self._breakpoint_to_reput:
addr = self._breakpoint_to_reput[self.current_thread.tid]
del self._breakpoint_to_reput[self.current_thread.tid]
# Re-put the breakpoint
self.current_process.write_memory(addr, "\xcc")
elif excp_addr in self.breakpoints:
# Verif that's not a standard BP ?
bp = self.breakpoints[excp_addr]
bp.trigger(self, exception)
ctx = self.current_thread.context
ctx.EEFlags.RF = 1
self.current_thread.set_context(ctx)
else:
self.on_exception(exception)
else: # Do not trigger self.on_exception if breakpoint was registered
self.on_exception(exception)
def _handle_exception(self, debug_event):
"""Handle EXCEPTION_DEBUG_EVENT"""
exception = debug_event.u.Exception
self._update_debugger_state(debug_event)
if windows.current_process.bitness == 64 and self.current_process.bitness == 32:
return self._handle_syswow64_exception(debug_event)
if self.current_process.bitness == 32:
if windows.current_process.bitness == 32:
exception.__class__ = windows.vectored_exception.EEXCEPTION_DEBUG_INFO32
else:
exception.__class__ = windows.vectored_exception.EEXCEPTION_DEBUG_INFO64
excp_code = exception.ExceptionRecord.ExceptionCode
excp_addr = exception.ExceptionRecord.ExceptionAddress
if excp_code == EXCEPTION_BREAKPOINT and excp_addr in self.breakpoints:
self._dispatch_breakpoint(exception, excp_addr)
if excp_code in [EXCEPTION_BREAKPOINT, STATUS_WX86_BREAKPOINT] and excp_addr in self.breakpoints:
continue_flag = self._dispatch_breakpoint(exception, excp_addr)
self._pass_breakpoint(excp_addr)
return
elif excp_code == EXCEPTION_SINGLE_STEP:
return continue_flag
elif excp_code in [EXCEPTION_SINGLE_STEP, STATUS_WX86_SINGLE_STEP]:
if self.current_thread.tid in self._breakpoint_to_reput:
addr = self._breakpoint_to_reput[self.current_thread.tid]
del self._breakpoint_to_reput[self.current_thread.tid]
# Re-put the breakpoint
self.current_process.write_memory(addr, "\xcc")
return DBG_CONTINUE
elif excp_addr in self.breakpoints:
# Verif that's not a standard BP ?
bp = self.breakpoints[excp_addr]
@@ -212,10 +184,11 @@ class Debugger(object):
ctx = self.current_thread.context
ctx.EEFlags.RF = 1
self.current_thread.set_context(ctx)
return DBG_CONTINUE
else:
self.on_exception(exception)
return self.on_exception(exception)
else: # Do not trigger self.on_exception if breakpoint was registered
self.on_exception(exception)
return self.on_exception(exception)
def _handle_create_process(self, debug_event):
@@ -228,14 +201,14 @@ class Debugger(object):
self.processes[self.current_process.pid] = self.current_process
self._update_debugger_state(debug_event)
self._setup_pending_breakpoints(self.current_process)
self.on_create_process(create_process)
return self.on_create_process(create_process)
# TODO: clode hFile
def _handle_exit_process(self, debug_event):
"""Handle EXIT_PROCESS_DEBUG_EVENT"""
self._update_debugger_state(debug_event)
exit_process = debug_event.u.ExitProcess
self.on_exit_process(exit_process)
retvalue = self.on_exit_process(exit_process)
del self.threads[self.current_thread.tid]
del self.processes[self.current_process.pid]
# Hack IT, ContinueDebugEvent will close the HANDLE for us
@@ -243,6 +216,7 @@ class Debugger(object):
dbgprint("Removing handle {0} for {1} (will be closed by continueDebugEvent".format(hex(self.current_process._handle), self.current_process), "HANDLE")
del self.current_process._handle
del self.current_thread._handle
return retvalue
def _handle_create_thread(self, debug_event):
"""Handle CREATE_THREAD_DEBUG_EVENT"""
@@ -250,48 +224,52 @@ class Debugger(object):
self.current_thread = WinThread._from_handle(create_thread.hThread)
self.threads[self.current_thread.tid] = self.current_thread
self._setup_pending_breakpoints(self.current_thread)
self.on_create_thread(create_thread)
return self.on_create_thread(create_thread)
def _handle_exit_thread(self, debug_event):
"""Handle EXIT_THREAD_DEBUG_EVENT"""
self._update_debugger_state(debug_event)
exit_thread = debug_event.u.ExitThread
self.on_exit_thread(exit_thread)
retvalue = self.on_exit_thread(exit_thread)
del self.threads[self.current_thread.tid]
# Hack IT, ContinueDebugEvent will close the HANDLE for us
# Should we make another handle instead ?
dbgprint("Removing handle {0} for {1} (will be closed by continueDebugEvent".format(hex(self.current_thread._handle), self.current_thread), "HANDLE")
del self.current_thread._handle
return retvalue
def _handle_load_dll(self, debug_event):
"""Handle LOAD_DLL_DEBUG_EVENT"""
self._update_debugger_state(debug_event)
load_dll = debug_event.u.LoadDll
self.on_load_dll(load_dll)
return self.on_load_dll(load_dll)
def _handle_unload_dll(self, debug_event):
"""Handle UNLOAD_DLL_DEBUG_EVENT"""
self._update_debugger_state(debug_event)
unload_dll = debug_event.u.UnloadDll
self.on_unload_dll(unload_dll)
return self.on_unload_dll(unload_dll)
def _handle_output_debug_string(self, debug_event):
"""Handle OUTPUT_DEBUG_STRING_EVENT"""
self._update_debugger_state(debug_event)
debug_string = debug_event.u.DebugString
self.on_output_debug_string(debug_string)
return self.on_output_debug_string(debug_string)
def _handle_rip(self, debug_event):
"""Handle RIP_EVENT"""
self._update_debugger_state(debug_event)
rip_info = debug_event.u.RipInfo
self.on_rip(rip_info)
return self.on_rip(rip_info)
# Public API
def loop(self):
for debug_event in self._debug_event_generator():
self._dispatch_debug_event(debug_event)
self._finish_debug_event(debug_event, windef.DBG_CONTINUE)
dbg_continue_flag = self._dispatch_debug_event(debug_event)
if dbg_continue_flag is None:
dbg_continue_flag = DBG_CONTINUE
self._finish_debug_event(debug_event, dbg_continue_flag)
if not self.processes:
break
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
+22 -8
View File
@@ -65,7 +65,7 @@ GetProcAddress64 += x64.Mov("RAX", "RDX")
GetProcAddress64 += x64.Mov("RBX", x64.mem("[RAX + 32]")) # RBX : first base ! (base of current module)
#GetProcAddress64 += x64.Mov("RBX ", x64.mem("[RAX + 32]")) # RBX : first base ! (base of current module)
GetProcAddress64 += x64.Cmp("RBX", 0)
GetProcAddress64 += x64.Jz(":NOT_FOUND")
GetProcAddress64 += x64.Jz(":DLL_NOT_FOUND")
GetProcAddress64 += x64.Mov("RCX", x64.mem("[RAX + 80]")) # RCX = NAME (UNICODE_STRING.Buffer)
GetProcAddress64 += x64.Call(":FUNC_STRLENW64")
GetProcAddress64 += x64.Mov("RDI", "RCX")
@@ -92,6 +92,8 @@ GetProcAddress64 += x64.Mov("EDX", x64.mem("[RAX + 32] ")) # EDX = names array
GetProcAddress64 += x64.Add("RDX", "RBX") # RDX = names array
GetProcAddress64 += x64.Xor("RCX", "RCX")
GetProcAddress64 += x64.Label(":SEARCH_LOOP")
GetProcAddress64 += x64.Cmp("RCX", "R13")
GetProcAddress64 += x64.Jz(":API_NOT_FOUND")
GetProcAddress64 += x64.Mov("ESI", x64.mem("[RDX + RCX * 4]")) # ;Get function name RVA
GetProcAddress64 += x64.Add("RSI", "RBX") # ;Get name addr
GetProcAddress64 += x64.Push("RCX") # ;Save current index (could use x64 register)
@@ -118,6 +120,7 @@ GetProcAddress64 += x64.Add("RDX", "RBX")
GetProcAddress64 += x64.Mov("EDX", x64.mem("[RDX + RCX * 4]"))
GetProcAddress64 += x64.Add("RDX", "RBX")
GetProcAddress64 += x64.Mov("RAX", "RDX")
GetProcAddress64 += x64.Label(":RETURN")
GetProcAddress64 += x64.Pop("R13")
GetProcAddress64 += x64.Pop("R12")
GetProcAddress64 += x64.Pop("R11")
@@ -130,9 +133,13 @@ GetProcAddress64 += x64.Pop("RDX")
GetProcAddress64 += x64.Pop("RCX")
GetProcAddress64 += x64.Pop("RBX")
GetProcAddress64 += x64.Ret()
GetProcAddress64 += x64.Label(":NOT_FOUND")
GetProcAddress64 += x64.Xor("RAX", "RAX")
GetProcAddress64 += x64.Ret()
GetProcAddress64 += x64.Label(":DLL_NOT_FOUND")
GetProcAddress64 += x64.Mov("RAX", 0xfffffffffffffffe)
GetProcAddress64 += x64.Jmp(":RETURN")
GetProcAddress64 += x64.Label(":API_NOT_FOUND")
GetProcAddress64 += x64.Pop("RAX")
GetProcAddress64 += x64.Mov("RAX", 0xffffffffffffffff)
GetProcAddress64 += x64.Jmp(":RETURN")
# Ajout des dependances
GetProcAddress64 += StrlenW64
GetProcAddress64 += StrlenA64
@@ -191,7 +198,7 @@ GetProcAddress32 += x86.Label(":a_dest")
GetProcAddress32 += x86.Mov("EAX", "EDX")
GetProcAddress32 += x86.Mov("EBX", x86.mem("[EAX + 0x18]")) # EBX : first base ! (base of current module)
GetProcAddress32 += x86.Cmp("EBX", 0)
GetProcAddress32 += x86.Jz(":NOT_FOUND")
GetProcAddress32 += x86.Jz(":DLL_NOT_FOUND")
GetProcAddress32 += x86.Mov("ECX", x86.mem("[EAX + 0x30]")) # RCX = NAME (UNICODE_STRING.Buffer)
GetProcAddress32 += x86.Push("ECX")
GetProcAddress32 += x86.Call(":FUNC_STRLENW32")
@@ -219,6 +226,8 @@ GetProcAddress32 += x86.Add("EDX", "EBX") # RDX = names array
GetProcAddress32 += x86.Xor("ECX", "ECX")
GetProcAddress32 += x86.Mov("ESI", x86.mem("[ESP + 0x20]"))
GetProcAddress32 += x86.Label(":SEARCH_LOOP")
GetProcAddress32 += x86.Cmp("ECX", "EBP")
GetProcAddress32 += x86.Jz(":API_NOT_FOUND")
GetProcAddress32 += x86.Mov("EDI", x86.mem("[EDX + ECX * 4]")) # ;Get function name RVA
GetProcAddress32 += x86.Add("EDI", "EBX") # ;Get name addr
GetProcAddress32 += x86.Push("ECX") # Save current index
@@ -255,14 +264,19 @@ GetProcAddress32 += x86.Add("EDX", "EBX")
GetProcAddress32 += x86.Mov("EDX", x86.mem("[EDX + ECX * 4]"))
GetProcAddress32 += x86.Add("EDX", "EBX")
GetProcAddress32 += x86.Mov("EAX", "EDX")
GetProcAddress32 += x86.Label(":RETURN")
GetProcAddress32 += x86.Pop("EBP")
GetProcAddress32 += x86.Pop("ESI")
GetProcAddress32 += x86.Pop("EDI")
GetProcAddress32 += x86.Pop("ECX")
GetProcAddress32 += x86.Pop("EBX")
GetProcAddress32 += x86.Ret()
GetProcAddress32 += x86.Label(":NOT_FOUND")
GetProcAddress32 += x86.Xor("EAX", "EAX")
GetProcAddress32 += x86.Ret()
GetProcAddress32 += x86.Label(":DLL_NOT_FOUND")
GetProcAddress32 += x86.Mov("EAX", 0xfffffffe)
GetProcAddress32 += x86.Jmp(":RETURN")
GetProcAddress32 += x86.Label(":API_NOT_FOUND")
GetProcAddress32 += x86.Pop("EAX")
GetProcAddress32 += x86.Mov("EAX", 0xffffffff)
GetProcAddress32 += x86.Jmp(":RETURN")
GetProcAddress32 += StrlenW32
GetProcAddress32 += StrlenA32
+7
View File
@@ -371,6 +371,9 @@ class NativeUtilsTestCase(unittest.TestCase):
# Put name in test to know which function caused the assert fails
self.assertEqual((name, hex(addr)), (name, hex(compute_addr)))
self.assertEqual(getprocaddr64("YOLO.DLL", "whatever"), 0xfffffffffffffffe)
self.assertEqual(getprocaddr64("KERNEL32.DLL", "YOLOAPI"), 0xffffffffffffffff)
@process_32bit_only
def test_strlenw32(self):
strlenw32 = windows.native_exec.create_function(nativeutils.StrlenW32.get_code(), [UINT, LPCWSTR])
@@ -396,6 +399,10 @@ class NativeUtilsTestCase(unittest.TestCase):
self.assertEqual((name, hex(addr)), (name, hex(compute_addr)))
self.assertEqual(getprocaddr32("YOLO.DLL", "whatever"), 0xfffffffe)
self.assertEqual(getprocaddr32("KERNEL32.DLL", "YOLOAPI"), 0xffffffff)
class DebuggerTestCase(unittest.TestCase):
def debuggable_calc_32(self):
+18 -7
View File
@@ -309,6 +309,9 @@ class Process(AutoHandle):
def virtual_alloc(self, size):
raise NotImplementedError("virtual_alloc")
def virtual_free(self):
raise NotImplementedError("virtual_free")
@property
def exit_code(self):
"""The exit code of the process : ``STILL_ACTIVE`` means the process is not dead
@@ -327,13 +330,13 @@ class Process(AutoHandle):
"""
return self.exit_code != STILL_ACTIVE
#@contextmanager
#def allocated_memory(self, size):
# addr = self.virtual_alloc(size)
# try:
# yield addr
# finally:
# windows.winproxy.VirtualFreeEx(self.handle, size)
@contextmanager
def allocated_memory(self, size):
addr = self.virtual_alloc(size)
try:
yield addr
finally:
windows.winproxy.VirtualFreeEx(self.handle, addr)
def execute(self, code, parameter=0):
"""Execute some native code in the context of the process
@@ -548,6 +551,10 @@ class CurrentProcess(Process):
"""
return winproxy.VirtualAlloc(dwSize=size)
def virtual_free(self, addr):
"""Free memory in the process by virtual_alloc"""
return winproxy.VirtualFree(addr)
def write_memory(self, addr, data):
"""Write data at addr"""
buffertype = (c_char * len(data)).from_address(addr)
@@ -633,6 +640,10 @@ class WinProcess(PROCESSENTRY32, Process):
"""
return winproxy.VirtualAllocEx(self.handle, dwSize=size)
def virtual_free(self, addr):
"""Free memory in the process by virtual_alloc"""
return winproxy.VirtualFreeEx(self.handle, addr)
def write_memory(self, addr, data):
"""Write `data` at `addr`"""
return winproxy.WriteProcessMemory(self.handle, addr, lpBuffer=data)