From b459125f271df9f4f733a3d1d0ff0120f8a6a66e Mon Sep 17 00:00:00 2001 From: Clement Rouault Date: Mon, 13 Apr 2015 15:56:25 +0200 Subject: [PATCH] Add LOT of doc + play on vertored exception --- __init__.py | 18 +- doc/GetProcAddress_64b.asm | 151 ++++++++++++++ doc/NtCreateThreadStub_64b.asm | 140 +++++++++++++ generated_def/__init__.py | 27 +++ generated_def/windef.py | 89 +++++++- generated_def/winfuncs.py | 44 +++- generated_def/winstructs.py | 162 ++++++++++++++- hooks.py | 27 ++- k32testing.py | 27 +++ make.bat | 263 ++++++++++++++++++++++++ native_exec/__init__.py | 3 +- native_exec/native_function.py | 7 + native_exec/simple_x64.py | 26 +++ native_exec/simple_x86.py | 23 ++- network.py | 68 +++--- pe_parse.py | 8 +- remote_callback.py | 1 - source/conf.py | 364 +++++++++++++++++++++++++++++++++ source/generated_doc.rst | 19 ++ source/index.rst | 26 +++ source/native_exec.rst | 79 +++++++ source/pe_parse.rst | 179 ++++++++++++++++ source/rem_python.rst | 77 +++++++ source/test.rst | 13 ++ source/utils.rst | 4 + syswow64.py | 48 ++--- utils.py | 36 +++- vectored_exception.py | 124 +++++++++++ winobject.py | 251 ++++++++++++++++++----- 29 files changed, 2147 insertions(+), 157 deletions(-) create mode 100644 doc/GetProcAddress_64b.asm create mode 100644 doc/NtCreateThreadStub_64b.asm create mode 100644 make.bat create mode 100644 source/conf.py create mode 100644 source/generated_doc.rst create mode 100644 source/index.rst create mode 100644 source/native_exec.rst create mode 100644 source/pe_parse.rst create mode 100644 source/rem_python.rst create mode 100644 source/test.rst create mode 100644 source/utils.rst create mode 100644 vectored_exception.py diff --git a/__init__.py b/__init__.py index 631ba8b..470f9ce 100644 --- a/__init__.py +++ b/__init__.py @@ -1,8 +1,22 @@ +""" +Windows for Python +A lot of python object to help navigate windows stuff + +Exported: + + system : :class:`windows.winobject.System` + + current_process : :class:`windows.winobject.CurrentProcess` + + current_thread : :class:`windows.winobject.CurrentThread` +""" + import k32testing -from winobject import System, CurrentProcess +from winobject import System, CurrentProcess, CurrentThread from utils import VirtualProtected system = System() current_process = CurrentProcess() +current_thread = CurrentThread() -__all__ = ["system", "VirtualProtected", 'current_process'] \ No newline at end of file +__all__ = ["system", "VirtualProtected", 'current_process', 'current_thread'] \ No newline at end of file diff --git a/doc/GetProcAddress_64b.asm b/doc/GetProcAddress_64b.asm new file mode 100644 index 0000000..a1543aa --- /dev/null +++ b/doc/GetProcAddress_64b.asm @@ -0,0 +1,151 @@ +.CODE + +start: + +MY_FUNC PROC + call func + ret +func: + push rax ;Padding for calling alligned on 16 bytes + ret value + push rax + push rbx + push rcx + push rdx + push rsi + push rdi + push r8 + push r9 + push r10 + push r11 + push r12 + push r13 + ;TODO save the QUEEN (and the registers !) + mov rax, 5050505050505050h ; libname + mov r11, rax + mov rax, 5151515151515151h ; function name + mov r12, rax + ;String pushed ! + mov rax, GS:[60h] ; PEB + mov rax, [rax + 6 * 4] ; RAX = ldr (+ 6 for 64 cause of 2 ptr) + mov rax, [rax + 8 * 4] ; RAX on the first elt of the list (first module) + mov rdx, rax +a_dest: + mov rax, rdx + mov rbx, [rax + 4 * 8] ;RBX : first base ! (base of current module) + and rbx, rbx ;If no more Module : not fail = fail + jz a_fail + mov rcx, [rax + 10 * 8] ;RCX = NAME (UNICODE_STRING.Buffer) + + mov rdi, rcx + call strlen + mov rdi, rcx ; set RDI to good value + mov rcx, rax + mov rsi, r11 + repe cmpsw ;cmp with current dll name (unicode) + test rcx, rcx + jz dll_found + mov rdx, [rdx] + jmp a_dest +a_fail: + push 42424242h + ret +dll_found: ;Cool ! : here rbx = base + mov eax, [rbx + 15 * 4] ;rax = PEBASE RVA + add rax, rbx ;RAX = PEBASE + add rax, 24 ;OPTIONAL HEADER + mov ecx, [rax + 112] ;rcx = RVA export dir + add rcx, rbx ;rcx = export_dir + mov rax, rcx ;RAX = export_dir + push rax ;Save it for after function search + ; EBX = BASE | EAX = EXPORT DIR + mov ecx, [rax + 6 * 4] + mov r13, rcx ;r13 = NB names + mov edx, [rax + 8 * 4] ; EDX = names array RVA + add rdx, rbx + xor rcx, rcx + + search_loop: + mov esi, [rdx + rcx * 4] ;Get function name RVA + add rsi, rbx ;Get name addr + push rcx ;Save current index (could use x64 register) + mov rdi, r11 + mov rcx, 17 ;We know we want NtCreateThreadEx + repe cmpsb ;cmp with current export + mov eax, ecx + pop rcx ;Restore current function index + inc rcx + test eax, eax + jnz search_loop ;If not found not handled : WTF GetProcAddress not in Kernel32... + ; Func found ! + dec rcx + ; rcx is offset of the name, need to find the offset of the function + pop rax ;Restore export_dir addr + mov edx, [rax + 9 * 4] ;EDX = AddressOfNameOrdinals RVX + add rdx, rbx ;AddressOfNameOrdinals + base + mov cx, [rdx + rcx * 2] ; ecx = Ieme ordinal (short array) + and rcx, 0ffffh + mov edx, [rax + 7 * 4] ; AddressOfFunctions RVA + add rdx, rbx ; AddressOfFunctions + base + mov edx, [rdx + rcx * 4] ;functions[ecx] -> functions[ordinals[i]] + add rdx, rbx + mov r13, rdx ; r13 : REAL FUNC ADD + + ; room for the thread handle + push 0 + mov rcx, rsp ; arg1 + mov rdx, 1fffffh ; arg2 + mov r8, 0h ; arg3 + mov r9, 4040404040404040h ; arg4 (handle) + + mov rax, 0h + push rax ; arg11 + push rax ; arg10 + push rax ; arg9 + push rax ; arg8 + push rax ; arg7 + mov rax, 4242424242424242h + push rax ; arg6 (param) + mov rax, 4141414141414141h + push rax ; arg5 (addr) + + ; reserve space for register (calling convention) + push r9 + push r8 + push rdx + push rcx + call r13 + ; Write return value in first stack value pushed + mov [rsp + 29 * 8], rax + ; TODO CLEAN stack :D + add rsp, 8 * 8 + add rsp, 32 + 8 + add rsp, 32 + pop r13 + pop r12 + pop r11 + pop r10 + pop r9 + pop r8 + pop rdi + pop rsi + pop rdx + pop rcx + pop rbx + pop rax + pop rax ; Return value + ret +strlen:; arg in RDI + push rcx + xor rax, rax + xor rcx, rcx + dec rcx + repne scasw + not rcx + dec rcx + mov rax, rcx + pop rcx + ret +MY_FUNC ENDP +END + + diff --git a/doc/NtCreateThreadStub_64b.asm b/doc/NtCreateThreadStub_64b.asm new file mode 100644 index 0000000..39d3c97 --- /dev/null +++ b/doc/NtCreateThreadStub_64b.asm @@ -0,0 +1,140 @@ +.CODE + +start: + +MY_FUNC PROC + push rax ;Padding for calling alligned on 16 bytes + ret value + push rax + push rbx + push rcx + push rdx + push rsi + push rdi + push r8 + push r9 + push r10 + push r11 + push r12 + push r13 + ;TODO save the QUEEN (and the registers !) + mov rax, 0h + push rax + mov rax, 7845646165726854h + push rax + mov rax, 657461657243744eh + push rax + mov r11, rsp ;R11 NtCreateThreadEx + mov rbx, 41797261h + push rbx + mov rbx, 7262694c64616f4ch + push rbx + mov r12, rsp ;R12 : LoadLib + ;String pushed ! + ;mov rax, 0x60 + ;GS mov rax, [rax] ;PEB! + mov rax, GS:[60h] + mov rax, [rax + 6 * 4] ; RAX = ldr (+ 6 for 64 cause of 2 ptr) + mov rax, [rax + 8 * 4] ; RAX on the first elt of the list (first module) + mov rdx, rax +a_dest: + mov rax, rdx + mov rbx, [rax + 4 * 8] ;RBX : first base ! (base of current module) + and rbx, rbx ;If no more Module : not fail = fail + jz a_fail + mov rcx, [rax + 10 * 8] ;RCX = NAME (UNICODE_STRING.Buffer) + + mov rcx, [rcx] ;GET WCHAR + cmp ecx, 74006eh ;ntdll if : ecx == 74006eh (nt unicode) + jz dll_found + mov rdx, [rax] + jmp a_dest +a_fail: + push 42424242h + ret +dll_found: ;Cool ! : here rbx = base + mov eax, [rbx + 15 * 4] ;rax = PEBASE RVA + add rax, rbx ;RAX = PEBASE + add rax, 24 ;OPTIONAL HEADER + mov ecx, [rax + 112] ;rcx = RVA export dir + add rcx, rbx ;rcx = export_dir + mov rax, rcx ;RAX = export_dir + push rax ;Save it for after function search + ; EBX = BASE | EAX = EXPORT DIR + mov ecx, [rax + 6 * 4] + mov r13, rcx ;r13 = NB names + mov edx, [rax + 8 * 4] ; EDX = names array RVA + add rdx, rbx + xor rcx, rcx + search_loop: + mov esi, [rdx + rcx * 4] ;Get function name RVA + add rsi, rbx ;Get name addr + push rcx ;Save current index (could use x64 register) + mov rdi, r11 + mov rcx, 17 ;We know we want NtCreateThreadEx + repe cmpsb ;cmp with current export + mov eax, ecx + pop rcx ;Restore current function index + inc rcx + test eax, eax + jnz search_loop ;If not found not handled : WTF GetProcAddress not in Kernel32... + ; Func found ! + dec rcx + ; rcx is offset of the name, need to find the offset of the function + pop rax ;Restore export_dir addr + mov edx, [rax + 9 * 4] ;EDX = AddressOfNameOrdinals RVX + add rdx, rbx ;AddressOfNameOrdinals + base + mov cx, [rdx + rcx * 2] ; ecx = Ieme ordinal (short array) + and rcx, 0ffffh + mov edx, [rax + 7 * 4] ; AddressOfFunctions RVA + add rdx, rbx ; AddressOfFunctions + base + mov edx, [rdx + rcx * 4] ;functions[ecx] -> functions[ordinals[i]] + add rdx, rbx + mov r13, rdx ; r13 : REAL FUNC ADD + + + ; room for the thread handle + push 0 + mov rcx, rsp ; arg1 + mov rdx, 1fffffh ; arg2 + mov r8, 0h ; arg3 + mov r9, 4040404040404040h ; arg4 (handle) + + mov rax, 0h + push rax ; arg11 + push rax ; arg10 + push rax ; arg9 + push rax ; arg8 + push rax ; arg7 + mov rax, 4242424242424242h + push rax ; arg6 (param) + mov rax, 4141414141414141h + push rax ; arg5 (addr) + + ; reserve space for register (calling convention) + push r9 + push r8 + push rdx + push rcx + call r13 + ; Write return value in first stack value pushed + mov [rsp + 29 * 8], rax + ; TODO CLEAN stack :D + add rsp, 8 * 8 + add rsp, 32 + 8 + add rsp, 32 + pop r13 + pop r12 + pop r11 + pop r10 + pop r9 + pop r8 + pop rdi + pop rsi + pop rdx + pop rcx + pop rbx + pop rax + pop rax ; Return value + +MY_FUNC ENDP +END \ No newline at end of file diff --git a/generated_def/__init__.py b/generated_def/__init__.py index e69de29..ebcdaef 100644 --- a/generated_def/__init__.py +++ b/generated_def/__init__.py @@ -0,0 +1,27 @@ +import windows +import winstructs + +def bitness(): + """Return 32 or 64""" + import platform + bits = platform.architecture()[0] + return int(bits[:2]) + +# Use windows.current_process.bitness ? need to fix problem of this imported before the creation of windows.current_process +if bitness() == 32: + winstructs.CONTEXT = winstructs.CONTEXT32 + winstructs.PCONTEXT = winstructs.PCONTEXT32 + winstructs.LPCONTEXT = winstructs.LPCONTEXT32 + + winstructs.EXCEPTION_POINTERS = winstructs.EXCEPTION_POINTERS32 + winstructs.PEXCEPTION_POINTERS = winstructs.PEXCEPTION_POINTERS32 +else: + winstructs.CONTEXT = winstructs.CONTEXT64 + winstructs.PCONTEXT = winstructs.PCONTEXT64 + winstructs.LPCONTEXT = winstructs.LPCONTEXT64 + + winstructs.EXCEPTION_POINTERS = winstructs.EXCEPTION_POINTERS64 + winstructs.PEXCEPTION_POINTERS = winstructs.PEXCEPTION_POINTERS64 + +import winfuncs + diff --git a/generated_def/windef.py b/generated_def/windef.py index f9f8cf2..9103306 100644 --- a/generated_def/windef.py +++ b/generated_def/windef.py @@ -9,6 +9,8 @@ class Flag(long): def __repr__(self): return "{0}({1})".format(self.name, hex(self)) + + __str__ = __repr__ NULL = Flag("NULL", 0) MAX_PATH = Flag("MAX_PATH", 260) @@ -16,6 +18,7 @@ ANYSIZE_ARRAY = Flag("ANYSIZE_ARRAY", 1) ANY_SIZE = Flag("ANY_SIZE", 1) IMAGE_SIZEOF_SHORT_NAME = Flag("IMAGE_SIZEOF_SHORT_NAME", 8) IMAGE_NUMBEROF_DIRECTORY_ENTRIES = Flag("IMAGE_NUMBEROF_DIRECTORY_ENTRIES", 16) +INFINITE = Flag("INFINITE", 0xFFFFFFFF) STD_INPUT_HANDLE = Flag("STD_INPUT_HANDLE", -10) STD_OUTPUT_HANDLE = Flag("STD_OUTPUT_HANDLE", -11) STD_ERROR_HANDLE = Flag("STD_ERROR_HANDLE", -12) @@ -273,4 +276,88 @@ AF_INET6 = Flag("AF_INET6", 23) AF_CLUSTER = Flag("AF_CLUSTER", 24) AF_12844 = Flag("AF_12844", 25) AF_IRDA = Flag("AF_IRDA", 26) -AF_NETDES = Flag("AF_NETDES", 28) \ No newline at end of file +AF_NETDES = Flag("AF_NETDES", 28) +EXCEPTION_NONCONTINUABLE = Flag("EXCEPTION_NONCONTINUABLE", 0x1) +EXCEPTION_MAXIMUM_PARAMETERS = Flag("EXCEPTION_MAXIMUM_PARAMETERS", 15) +STATUS_WAIT_0 = Flag("STATUS_WAIT_0", ( 0x00000000 )) +STATUS_ABANDONED_WAIT_0 = Flag("STATUS_ABANDONED_WAIT_0", ( 0x00000080 )) +STATUS_USER_APC = Flag("STATUS_USER_APC", ( 0x000000C0 )) +STATUS_TIMEOUT = Flag("STATUS_TIMEOUT", ( 0x00000102 )) +STATUS_PENDING = Flag("STATUS_PENDING", ( 0x00000103 )) +DBG_EXCEPTION_HANDLED = Flag("DBG_EXCEPTION_HANDLED", ( 0x00010001 )) +DBG_CONTINUE = Flag("DBG_CONTINUE", ( 0x00010002 )) +STATUS_SEGMENT_NOTIFICATION = Flag("STATUS_SEGMENT_NOTIFICATION", ( 0x40000005 )) +DBG_TERMINATE_THREAD = Flag("DBG_TERMINATE_THREAD", ( 0x40010003 )) +DBG_TERMINATE_PROCESS = Flag("DBG_TERMINATE_PROCESS", ( 0x40010004 )) +DBG_CONTROL_C = Flag("DBG_CONTROL_C", ( 0x40010005 )) +DBG_PRINTEXCEPTION_C = Flag("DBG_PRINTEXCEPTION_C", ( 0x40010006 )) +DBG_RIPEXCEPTION = Flag("DBG_RIPEXCEPTION", ( 0x40010007 )) +DBG_CONTROL_BREAK = Flag("DBG_CONTROL_BREAK", ( 0x40010008 )) +DBG_COMMAND_EXCEPTION = Flag("DBG_COMMAND_EXCEPTION", ( 0x40010009 )) +STATUS_GUARD_PAGE_VIOLATION = Flag("STATUS_GUARD_PAGE_VIOLATION", ( 0x80000001 )) +STATUS_DATATYPE_MISALIGNMENT = Flag("STATUS_DATATYPE_MISALIGNMENT", ( 0x80000002 )) +STATUS_BREAKPOINT = Flag("STATUS_BREAKPOINT", ( 0x80000003 )) +STATUS_SINGLE_STEP = Flag("STATUS_SINGLE_STEP", ( 0x80000004 )) +STATUS_LONGJUMP = Flag("STATUS_LONGJUMP", ( 0x80000026 )) +STATUS_UNWIND_CONSOLIDATE = Flag("STATUS_UNWIND_CONSOLIDATE", ( 0x80000029 )) +DBG_EXCEPTION_NOT_HANDLED = Flag("DBG_EXCEPTION_NOT_HANDLED", ( 0x80010001 )) +STATUS_ACCESS_VIOLATION = Flag("STATUS_ACCESS_VIOLATION", ( 0xC0000005 )) +STATUS_IN_PAGE_ERROR = Flag("STATUS_IN_PAGE_ERROR", ( 0xC0000006 )) +STATUS_INVALID_HANDLE = Flag("STATUS_INVALID_HANDLE", ( 0xC0000008 )) +STATUS_INVALID_PARAMETER = Flag("STATUS_INVALID_PARAMETER", ( 0xC000000D )) +STATUS_NO_MEMORY = Flag("STATUS_NO_MEMORY", ( 0xC0000017 )) +STATUS_ILLEGAL_INSTRUCTION = Flag("STATUS_ILLEGAL_INSTRUCTION", ( 0xC000001D )) +STATUS_NONCONTINUABLE_EXCEPTION = Flag("STATUS_NONCONTINUABLE_EXCEPTION", ( 0xC0000025 )) +STATUS_INVALID_DISPOSITION = Flag("STATUS_INVALID_DISPOSITION", ( 0xC0000026 )) +STATUS_ARRAY_BOUNDS_EXCEEDED = Flag("STATUS_ARRAY_BOUNDS_EXCEEDED", ( 0xC000008C )) +STATUS_FLOAT_DENORMAL_OPERAND = Flag("STATUS_FLOAT_DENORMAL_OPERAND", ( 0xC000008D )) +STATUS_FLOAT_DIVIDE_BY_ZERO = Flag("STATUS_FLOAT_DIVIDE_BY_ZERO", ( 0xC000008E )) +STATUS_FLOAT_INEXACT_RESULT = Flag("STATUS_FLOAT_INEXACT_RESULT", ( 0xC000008F )) +STATUS_FLOAT_INVALID_OPERATION = Flag("STATUS_FLOAT_INVALID_OPERATION", ( 0xC0000090 )) +STATUS_FLOAT_OVERFLOW = Flag("STATUS_FLOAT_OVERFLOW", ( 0xC0000091 )) +STATUS_FLOAT_STACK_CHECK = Flag("STATUS_FLOAT_STACK_CHECK", ( 0xC0000092 )) +STATUS_FLOAT_UNDERFLOW = Flag("STATUS_FLOAT_UNDERFLOW", ( 0xC0000093 )) +STATUS_INTEGER_DIVIDE_BY_ZERO = Flag("STATUS_INTEGER_DIVIDE_BY_ZERO", ( 0xC0000094 )) +STATUS_INTEGER_OVERFLOW = Flag("STATUS_INTEGER_OVERFLOW", ( 0xC0000095 )) +STATUS_PRIVILEGED_INSTRUCTION = Flag("STATUS_PRIVILEGED_INSTRUCTION", ( 0xC0000096 )) +STATUS_STACK_OVERFLOW = Flag("STATUS_STACK_OVERFLOW", ( 0xC00000FD )) +STATUS_DLL_NOT_FOUND = Flag("STATUS_DLL_NOT_FOUND", ( 0xC0000135 )) +STATUS_ORDINAL_NOT_FOUND = Flag("STATUS_ORDINAL_NOT_FOUND", ( 0xC0000138 )) +STATUS_ENTRYPOINT_NOT_FOUND = Flag("STATUS_ENTRYPOINT_NOT_FOUND", ( 0xC0000139 )) +STATUS_CONTROL_C_EXIT = Flag("STATUS_CONTROL_C_EXIT", ( 0xC000013A )) +STATUS_DLL_INIT_FAILED = Flag("STATUS_DLL_INIT_FAILED", ( 0xC0000142 )) +STATUS_FLOAT_MULTIPLE_FAULTS = Flag("STATUS_FLOAT_MULTIPLE_FAULTS", ( 0xC00002B4 )) +STATUS_FLOAT_MULTIPLE_TRAPS = Flag("STATUS_FLOAT_MULTIPLE_TRAPS", ( 0xC00002B5 )) +STATUS_REG_NAT_CONSUMPTION = Flag("STATUS_REG_NAT_CONSUMPTION", ( 0xC00002C9 )) +STATUS_STACK_BUFFER_OVERRUN = Flag("STATUS_STACK_BUFFER_OVERRUN", ( 0xC0000409 )) +STATUS_INVALID_CRUNTIME_PARAMETER = Flag("STATUS_INVALID_CRUNTIME_PARAMETER", ( 0xC0000417 )) +STATUS_ASSERTION_FAILURE = Flag("STATUS_ASSERTION_FAILURE", ( 0xC0000420 )) +WAIT_IO_COMPLETION = Flag("WAIT_IO_COMPLETION", STATUS_USER_APC) +STILL_ACTIVE = Flag("STILL_ACTIVE", STATUS_PENDING) +EXCEPTION_ACCESS_VIOLATION = Flag("EXCEPTION_ACCESS_VIOLATION", STATUS_ACCESS_VIOLATION) +EXCEPTION_DATATYPE_MISALIGNMENT = Flag("EXCEPTION_DATATYPE_MISALIGNMENT", STATUS_DATATYPE_MISALIGNMENT) +EXCEPTION_BREAKPOINT = Flag("EXCEPTION_BREAKPOINT", STATUS_BREAKPOINT) +EXCEPTION_SINGLE_STEP = Flag("EXCEPTION_SINGLE_STEP", STATUS_SINGLE_STEP) +EXCEPTION_ARRAY_BOUNDS_EXCEEDED = Flag("EXCEPTION_ARRAY_BOUNDS_EXCEEDED", STATUS_ARRAY_BOUNDS_EXCEEDED) +EXCEPTION_FLT_DENORMAL_OPERAND = Flag("EXCEPTION_FLT_DENORMAL_OPERAND", STATUS_FLOAT_DENORMAL_OPERAND) +EXCEPTION_FLT_DIVIDE_BY_ZERO = Flag("EXCEPTION_FLT_DIVIDE_BY_ZERO", STATUS_FLOAT_DIVIDE_BY_ZERO) +EXCEPTION_FLT_INEXACT_RESULT = Flag("EXCEPTION_FLT_INEXACT_RESULT", STATUS_FLOAT_INEXACT_RESULT) +EXCEPTION_FLT_INVALID_OPERATION = Flag("EXCEPTION_FLT_INVALID_OPERATION", STATUS_FLOAT_INVALID_OPERATION) +EXCEPTION_FLT_OVERFLOW = Flag("EXCEPTION_FLT_OVERFLOW", STATUS_FLOAT_OVERFLOW) +EXCEPTION_FLT_STACK_CHECK = Flag("EXCEPTION_FLT_STACK_CHECK", STATUS_FLOAT_STACK_CHECK) +EXCEPTION_FLT_UNDERFLOW = Flag("EXCEPTION_FLT_UNDERFLOW", STATUS_FLOAT_UNDERFLOW) +EXCEPTION_INT_DIVIDE_BY_ZERO = Flag("EXCEPTION_INT_DIVIDE_BY_ZERO", STATUS_INTEGER_DIVIDE_BY_ZERO) +EXCEPTION_INT_OVERFLOW = Flag("EXCEPTION_INT_OVERFLOW", STATUS_INTEGER_OVERFLOW) +EXCEPTION_PRIV_INSTRUCTION = Flag("EXCEPTION_PRIV_INSTRUCTION", STATUS_PRIVILEGED_INSTRUCTION) +EXCEPTION_IN_PAGE_ERROR = Flag("EXCEPTION_IN_PAGE_ERROR", STATUS_IN_PAGE_ERROR) +EXCEPTION_ILLEGAL_INSTRUCTION = Flag("EXCEPTION_ILLEGAL_INSTRUCTION", STATUS_ILLEGAL_INSTRUCTION) +EXCEPTION_NONCONTINUABLE_EXCEPTION = Flag("EXCEPTION_NONCONTINUABLE_EXCEPTION", STATUS_NONCONTINUABLE_EXCEPTION) +EXCEPTION_STACK_OVERFLOW = Flag("EXCEPTION_STACK_OVERFLOW", STATUS_STACK_OVERFLOW) +EXCEPTION_INVALID_DISPOSITION = Flag("EXCEPTION_INVALID_DISPOSITION", STATUS_INVALID_DISPOSITION) +EXCEPTION_GUARD_PAGE = Flag("EXCEPTION_GUARD_PAGE", STATUS_GUARD_PAGE_VIOLATION) +EXCEPTION_INVALID_HANDLE = Flag("EXCEPTION_INVALID_HANDLE", STATUS_INVALID_HANDLE) +EXCEPTION_POSSIBLE_DEADLOCK = Flag("EXCEPTION_POSSIBLE_DEADLOCK", STATUS_POSSIBLE_DEADLOCK) +CONTROL_C_EXIT = Flag("CONTROL_C_EXIT", STATUS_CONTROL_C_EXIT) +EXCEPTION_EXECUTE_HANDLER = Flag("EXCEPTION_EXECUTE_HANDLER", 1) +EXCEPTION_CONTINUE_SEARCH = Flag("EXCEPTION_CONTINUE_SEARCH", 0) +EXCEPTION_CONTINUE_EXECUTION = Flag("EXCEPTION_CONTINUE_EXECUTION", -1) \ No newline at end of file diff --git a/generated_def/winfuncs.py b/generated_def/winfuncs.py index 425948d..30ecab6 100644 --- a/generated_def/winfuncs.py +++ b/generated_def/winfuncs.py @@ -3,7 +3,7 @@ from ctypes import * from ctypes.wintypes import * from winstructs import * -functions = ['ExitProcess', 'GetLastError', 'GetCurrentProcess', 'CreateFileA', 'CreateFileW', 'NtQuerySystemInformation', 'VirtualAlloc', 'VirtualAllocEx', 'VirtualProtect', 'VirtualQuery', 'GetModuleFileNameA', 'GetModuleFileNameW', 'CreateRemoteThread', 'VirtualProtect', 'CreateProcessA', 'CreateProcessW', 'GetThreadContext', 'SetThreadContext', 'OpenThread', 'OpenProcess', 'CloseHandle', 'ReadProcessMemory', 'WriteProcessMemory', 'CreateToolhelp32Snapshot', 'Thread32First', 'Thread32Next', 'Process32First', 'Process32Next', 'Process32FirstW', 'Process32NextW', 'GetProcAddress', 'LoadLibraryA', 'LoadLibraryW', 'OpenProcessToken', 'LookupPrivilegeValueA', 'LookupPrivilegeValueW', 'AdjustTokenPrivileges', 'FindResourceA', 'FindResourceW', 'SizeofResource', 'LoadResource', 'LockResource', 'GetVersionExA', 'GetVersionExW', 'GetVersion', 'GetCurrentThread', 'GetCurrentProcessorNumber', 'AllocConsole', 'GetStdHandle', 'SetStdHandle', 'SetThreadAffinityMask', 'WriteFile', 'GetExtendedTcpTable', 'GetExtendedUdpTable', 'SetTcpEntry'] +functions = ['ExitProcess', 'GetLastError', 'GetCurrentProcess', 'CreateFileA', 'CreateFileW', 'NtQuerySystemInformation', 'VirtualAlloc', 'VirtualAllocEx', 'VirtualProtect', 'VirtualQuery', 'GetModuleFileNameA', 'GetModuleFileNameW', 'CreateThread', 'CreateRemoteThread', 'VirtualProtect', 'CreateProcessA', 'CreateProcessW', 'GetThreadContext', 'SetThreadContext', 'OpenThread', 'OpenProcess', 'CloseHandle', 'ReadProcessMemory', 'WriteProcessMemory', 'CreateToolhelp32Snapshot', 'Thread32First', 'Thread32Next', 'Process32First', 'Process32Next', 'Process32FirstW', 'Process32NextW', 'GetProcAddress', 'LoadLibraryA', 'LoadLibraryW', 'OpenProcessToken', 'LookupPrivilegeValueA', 'LookupPrivilegeValueW', 'AdjustTokenPrivileges', 'FindResourceA', 'FindResourceW', 'SizeofResource', 'LoadResource', 'LockResource', 'GetVersionExA', 'GetVersionExW', 'GetVersion', 'GetCurrentThread', 'GetCurrentThreadId', 'GetCurrentProcessorNumber', 'AllocConsole', 'GetStdHandle', 'SetStdHandle', 'SetThreadAffinityMask', 'WriteFile', 'GetExtendedTcpTable', 'GetExtendedUdpTable', 'SetTcpEntry', 'AddVectoredContinueHandler', 'AddVectoredExceptionHandler', 'TerminateThread', 'ExitThread', 'RemoveVectoredExceptionHandler', 'ResumeThread', 'SuspendThread', 'WaitForSingleObject'] # ExitProcess(uExitCode): ExitProcessPrototype = WINFUNCTYPE(VOID, UINT) @@ -53,6 +53,10 @@ GetModuleFileNameAParams = ((1, 'hModule'), (1, 'lpFilename'), (1, 'nSize')) GetModuleFileNameWPrototype = WINFUNCTYPE(DWORD, HMODULE, LPWSTR, DWORD) GetModuleFileNameWParams = ((1, 'hModule'), (1, 'lpFilename'), (1, 'nSize')) +# CreateThread(lpThreadAttributes, dwStackSize, lpStartAddress, lpParameter, dwCreationFlags, lpThreadId): +CreateThreadPrototype = WINFUNCTYPE(HANDLE, LPSECURITY_ATTRIBUTES, SIZE_T, LPTHREAD_START_ROUTINE, LPVOID, DWORD, LPDWORD) +CreateThreadParams = ((1, 'lpThreadAttributes'), (1, 'dwStackSize'), (1, 'lpStartAddress'), (1, 'lpParameter'), (1, 'dwCreationFlags'), (1, 'lpThreadId')) + # CreateRemoteThread(hProcess, lpThreadAttributes, dwStackSize, lpStartAddress, lpParameter, dwCreationFlags, lpThreadId): CreateRemoteThreadPrototype = WINFUNCTYPE(HANDLE, HANDLE, LPSECURITY_ATTRIBUTES, SIZE_T, LPTHREAD_START_ROUTINE, LPVOID, DWORD, LPDWORD) CreateRemoteThreadParams = ((1, 'hProcess'), (1, 'lpThreadAttributes'), (1, 'dwStackSize'), (1, 'lpStartAddress'), (1, 'lpParameter'), (1, 'dwCreationFlags'), (1, 'lpThreadId')) @@ -74,7 +78,7 @@ GetThreadContextPrototype = WINFUNCTYPE(BOOL, HANDLE, LPCONTEXT) GetThreadContextParams = ((1, 'hThread'), (1, 'lpContext')) # SetThreadContext(hThread, lpContext): -SetThreadContextPrototype = WINFUNCTYPE(BOOL, HANDLE, POINTER(CONTEXT)) +SetThreadContextPrototype = WINFUNCTYPE(BOOL, HANDLE, LPCONTEXT) SetThreadContextParams = ((1, 'hThread'), (1, 'lpContext')) # OpenThread(dwDesiredAccess, bInheritHandle, dwThreadId): @@ -189,6 +193,10 @@ GetVersionParams = () GetCurrentThreadPrototype = WINFUNCTYPE(HANDLE) GetCurrentThreadParams = () +# GetCurrentThreadId(): +GetCurrentThreadIdPrototype = WINFUNCTYPE(DWORD) +GetCurrentThreadIdParams = () + # GetCurrentProcessorNumber(): GetCurrentProcessorNumberPrototype = WINFUNCTYPE(DWORD) GetCurrentProcessorNumberParams = () @@ -225,3 +233,35 @@ GetExtendedUdpTableParams = ((1, 'pUdpTable'), (1, 'pdwSize'), (1, 'bOrder'), (1 SetTcpEntryPrototype = WINFUNCTYPE(DWORD, PMIB_TCPROW) SetTcpEntryParams = ((1, 'pTcpRow'),) +# AddVectoredContinueHandler(FirstHandler, VectoredHandler): +AddVectoredContinueHandlerPrototype = WINFUNCTYPE(PVOID, ULONG, PVECTORED_EXCEPTION_HANDLER) +AddVectoredContinueHandlerParams = ((1, 'FirstHandler'), (1, 'VectoredHandler')) + +# AddVectoredExceptionHandler(FirstHandler, VectoredHandler): +AddVectoredExceptionHandlerPrototype = WINFUNCTYPE(PVOID, ULONG, PVECTORED_EXCEPTION_HANDLER) +AddVectoredExceptionHandlerParams = ((1, 'FirstHandler'), (1, 'VectoredHandler')) + +# TerminateThread(hThread, dwExitCode): +TerminateThreadPrototype = WINFUNCTYPE(BOOL, HANDLE, DWORD) +TerminateThreadParams = ((1, 'hThread'), (1, 'dwExitCode')) + +# ExitThread(dwExitCode): +ExitThreadPrototype = WINFUNCTYPE(VOID, DWORD) +ExitThreadParams = ((1, 'dwExitCode'),) + +# RemoveVectoredExceptionHandler(Handler): +RemoveVectoredExceptionHandlerPrototype = WINFUNCTYPE(ULONG, PVOID) +RemoveVectoredExceptionHandlerParams = ((1, 'Handler'),) + +# ResumeThread(hThread): +ResumeThreadPrototype = WINFUNCTYPE(DWORD, HANDLE) +ResumeThreadParams = ((1, 'hThread'),) + +# SuspendThread(hThread): +SuspendThreadPrototype = WINFUNCTYPE(DWORD, HANDLE) +SuspendThreadParams = ((1, 'hThread'),) + +# WaitForSingleObject(hHandle, dwMilliseconds): +WaitForSingleObjectPrototype = WINFUNCTYPE(DWORD, HANDLE, DWORD) +WaitForSingleObjectParams = ((1, 'hHandle'), (1, 'dwMilliseconds')) + diff --git a/generated_def/winstructs.py b/generated_def/winstructs.py index 07cdd53..e0a156d 100644 --- a/generated_def/winstructs.py +++ b/generated_def/winstructs.py @@ -18,13 +18,17 @@ CHAR = c_char UCHAR = c_char FARPROC = PVOID HGLOBAL = PVOID +PVECTORED_EXCEPTION_HANDLER = PVOID ULONGLONG = c_ulonglong +LONGLONG = c_longlong ULONG64 = c_ulonglong +DWORD64 = ULONG64 PULONG64 = POINTER(ULONG64) PHANDLE = POINTER(HANDLE) +LPCONTEXT = PVOID VOID = DWORD -structs = ['_LIST_ENTRY', '_PEB_LDR_DATA', '_LSA_UNICODE_STRING', '_RTL_USER_PROCESS_PARAMETERS', '_PEB', '_SECURITY_ATTRIBUTES', '_SYSTEM_VERIFIER_INFORMATION', '_LDR_DATA_TABLE_ENTRY', '_IMAGE_FILE_HEADER', '_IMAGE_DATA_DIRECTORY', '_IMAGE_SECTION_HEADER', '_IMAGE_OPTIONAL_HEADER64', '_IMAGE_OPTIONAL_HEADER', '_IMAGE_NT_HEADERS64', '_IMAGE_NT_HEADERS', '_IMAGE_IMPORT_DESCRIPTOR', '_IMAGE_IMPORT_BY_NAME', '_MEMORY_BASIC_INFORMATION', '_STARTUPINFOA', '_STARTUPINFOW', '_PROCESS_INFORMATION', '_FLOATING_SAVE_AREA', '_CONTEXT', 'tagPROCESSENTRY32W', 'tagPROCESSENTRY32', 'tagTHREADENTRY32', '_LUID', '_LUID_AND_ATTRIBUTES', '_TOKEN_PRIVILEGES', '_OSVERSIONINFOA', '_OSVERSIONINFOW', '_OSVERSIONINFOEXA', '_OSVERSIONINFOEXW', '_OVERLAPPED', '_MIB_TCPROW_OWNER_PID', '_MIB_TCPTABLE_OWNER_PID', '_MIB_UDPROW_OWNER_PID', '_MIB_UDPTABLE_OWNER_PID', '_MIB_UDP6ROW_OWNER_PID', '_MIB_UDP6TABLE_OWNER_PID', '_MIB_TCP6ROW_OWNER_PID', '_MIB_TCP6TABLE_OWNER_PID', '_MIB_TCPROW'] +structs = ['_LIST_ENTRY', '_PEB_LDR_DATA', '_LSA_UNICODE_STRING', '_RTL_USER_PROCESS_PARAMETERS', '_PEB', '_SECURITY_ATTRIBUTES', '_SYSTEM_VERIFIER_INFORMATION', '_LDR_DATA_TABLE_ENTRY', '_IMAGE_FILE_HEADER', '_IMAGE_DATA_DIRECTORY', '_IMAGE_SECTION_HEADER', '_IMAGE_OPTIONAL_HEADER64', '_IMAGE_OPTIONAL_HEADER', '_IMAGE_NT_HEADERS64', '_IMAGE_NT_HEADERS', '_IMAGE_IMPORT_DESCRIPTOR', '_IMAGE_IMPORT_BY_NAME', '_MEMORY_BASIC_INFORMATION', '_STARTUPINFOA', '_STARTUPINFOW', '_PROCESS_INFORMATION', '_FLOATING_SAVE_AREA', '_CONTEXT32', '_M128A', '_CONTEXT64', 'tagPROCESSENTRY32W', 'tagPROCESSENTRY32', 'tagTHREADENTRY32', '_LUID', '_LUID_AND_ATTRIBUTES', '_TOKEN_PRIVILEGES', '_OSVERSIONINFOA', '_OSVERSIONINFOW', '_OSVERSIONINFOEXA', '_OSVERSIONINFOEXW', '_OVERLAPPED', '_MIB_TCPROW_OWNER_PID', '_MIB_TCPTABLE_OWNER_PID', '_MIB_UDPROW_OWNER_PID', '_MIB_UDPTABLE_OWNER_PID', '_MIB_UDP6ROW_OWNER_PID', '_MIB_UDP6TABLE_OWNER_PID', '_MIB_TCP6ROW_OWNER_PID', '_MIB_TCP6TABLE_OWNER_PID', '_MIB_TCPROW', '_EXCEPTION_RECORD', '_EXCEPTION_POINTERS64', '_EXCEPTION_POINTERS32'] enums = ['_SYSTEM_INFORMATION_CLASS', '_TCP_TABLE_CLASS', '_UDP_TABLE_CLASS', '_MIB_TCP_STATE'] @@ -153,22 +157,53 @@ MIB_TCP_STATE_TIME_WAIT = 0xb MIB_TCP_STATE_DELETE_TCB = 0xc # Struct _LIST_ENTRY definitions # Self referencing struct tricks + +import ctypes + +def pretty_print_ctypes_type(t): + format = "{0}" + if issubclass(t, ctypes.Array): + format = "[{0}" + "* {0}]".format(t._length_) + t = t._type_ + + if issubclass(t, ctypes._Pointer): + format = format.format("Pointer({0})") + t = t._type_ + + if issubclass(t, ctypes.Structure): + return format.format(":class:`{0}`".format(t.__name__)) + return t + +def autodoc_ctypes_struct(struct): + doc = ["fields:"] + for name, type in struct._fields_: + doc.append(" {0} -> {1}".format(name, pretty_print_ctypes_type(type))) + + struct.__doc__ = "\n\n".join(doc) + return struct + + class _LIST_ENTRY(Structure): pass _LIST_ENTRY._fields_ = [ ("Flink", POINTER(_LIST_ENTRY)), ("Blink", POINTER(_LIST_ENTRY)), ] + +_LIST_ENTRY = autodoc_ctypes_struct(_LIST_ENTRY) + PLIST_ENTRY = POINTER(_LIST_ENTRY) LIST_ENTRY = _LIST_ENTRY PRLIST_ENTRY = POINTER(_LIST_ENTRY) # Struct _PEB_LDR_DATA definitions +@autodoc_ctypes_struct class _PEB_LDR_DATA(Structure): - _fields_ = [ + _fields_ = [ ("Reserved1", BYTE * 8), ("Reserved2", PVOID * 3), ("InMemoryOrderModuleList", LIST_ENTRY), ] + PPEB_LDR_DATA = POINTER(_PEB_LDR_DATA) PEB_LDR_DATA = _PEB_LDR_DATA @@ -196,6 +231,7 @@ PRTL_USER_PROCESS_PARAMETERS = POINTER(_RTL_USER_PROCESS_PARAMETERS) RTL_USER_PROCESS_PARAMETERS = _RTL_USER_PROCESS_PARAMETERS # Struct _PEB definitions +@autodoc_ctypes_struct class _PEB(Structure): _fields_ = [ ("Reserved1", BYTE * 2), @@ -522,8 +558,8 @@ class _FLOATING_SAVE_AREA(Structure): ] FLOATING_SAVE_AREA = _FLOATING_SAVE_AREA -# Struct _CONTEXT definitions -class _CONTEXT(Structure): +# Struct _CONTEXT32 definitions +class _CONTEXT32(Structure): _fields_ = [ ("ContextFlags", DWORD), ("Dr0", DWORD), @@ -551,9 +587,89 @@ class _CONTEXT(Structure): ("SegSs", DWORD), ("ExtendedRegisters", BYTE * 512), ] -PCONTEXT = POINTER(_CONTEXT) -LPCONTEXT = POINTER(_CONTEXT) -CONTEXT = _CONTEXT +PCONTEXT32 = POINTER(_CONTEXT32) +CONTEXT32 = _CONTEXT32 +LPCONTEXT32 = POINTER(_CONTEXT32) + +# Struct _M128A definitions +class _M128A(Structure): + _fields_ = [ + ("Low", ULONGLONG), + ("High", LONGLONG), + ] +M128A = _M128A +PM128A = POINTER(_M128A) + +# Struct _CONTEXT64 definitions +class _CONTEXT64(Structure): + _fields_ = [ + ("P1Home", DWORD64), + ("P2Home", DWORD64), + ("P3Home", DWORD64), + ("P4Home", DWORD64), + ("P5Home", DWORD64), + ("P6Home", DWORD64), + ("ContextFlags", DWORD), + ("MxCsr", DWORD), + ("SegCs", WORD), + ("SegDs", WORD), + ("SegEs", WORD), + ("SegFs", WORD), + ("SegGs", WORD), + ("SegSs", WORD), + ("EFlags", DWORD), + ("Dr0", DWORD64), + ("Dr1", DWORD64), + ("Dr2", DWORD64), + ("Dr3", DWORD64), + ("Dr6", DWORD64), + ("Dr7", DWORD64), + ("Rax", DWORD64), + ("Rcx", DWORD64), + ("Rdx", DWORD64), + ("Rbx", DWORD64), + ("Rsp", DWORD64), + ("Rbp", DWORD64), + ("Rsi", DWORD64), + ("Rdi", DWORD64), + ("R8", DWORD64), + ("R9", DWORD64), + ("R10", DWORD64), + ("R11", DWORD64), + ("R12", DWORD64), + ("R13", DWORD64), + ("R14", DWORD64), + ("R15", DWORD64), + ("Rip", DWORD64), + ("Header", M128A * 2), + ("Legacy", M128A * 8), + ("Xmm0", M128A), + ("Xmm1", M128A), + ("Xmm2", M128A), + ("Xmm3", M128A), + ("Xmm4", M128A), + ("Xmm5", M128A), + ("Xmm6", M128A), + ("Xmm7", M128A), + ("Xmm8", M128A), + ("Xmm9", M128A), + ("Xmm10", M128A), + ("Xmm11", M128A), + ("Xmm12", M128A), + ("Xmm13", M128A), + ("Xmm14", M128A), + ("Xmm15", M128A), + ("VectorRegister", M128A * 26), + ("VectorControl", DWORD64), + ("DebugControl", DWORD64), + ("LastBranchToRip", DWORD64), + ("LastBranchFromRip", DWORD64), + ("LastExceptionToRip", DWORD64), + ("LastExceptionFromRip", DWORD64), + ] +PCONTEXT64 = POINTER(_CONTEXT64) +CONTEXT64 = _CONTEXT64 +LPCONTEXT64 = POINTER(_CONTEXT64) # Struct tagPROCESSENTRY32W definitions class tagPROCESSENTRY32W(Structure): @@ -811,3 +927,35 @@ class _MIB_TCPROW(Structure): MIB_TCPROW = _MIB_TCPROW PMIB_TCPROW = POINTER(_MIB_TCPROW) +# Struct _EXCEPTION_RECORD definitions +# Self referencing struct tricks +class _EXCEPTION_RECORD(Structure): pass +_EXCEPTION_RECORD._fields_ = [ + ("ExceptionCode", DWORD), + ("ExceptionFlags", DWORD), + ("ExceptionRecord", POINTER(_EXCEPTION_RECORD)), + ("ExceptionAddress", PVOID), + ("NumberParameters", DWORD), + ("ExceptionInformation", ULONG_PTR * EXCEPTION_MAXIMUM_PARAMETERS), +] +PEXCEPTION_RECORD = POINTER(_EXCEPTION_RECORD) +EXCEPTION_RECORD = _EXCEPTION_RECORD + +# Struct _EXCEPTION_POINTERS64 definitions +class _EXCEPTION_POINTERS64(Structure): + _fields_ = [ + ("ExceptionRecord", PEXCEPTION_RECORD), + ("ContextRecord", PCONTEXT64), + ] +EXCEPTION_POINTERS64 = _EXCEPTION_POINTERS64 +PEXCEPTION_POINTERS64 = POINTER(_EXCEPTION_POINTERS64) + +# Struct _EXCEPTION_POINTERS32 definitions +class _EXCEPTION_POINTERS32(Structure): + _fields_ = [ + ("ExceptionRecord", PEXCEPTION_RECORD), + ("ContextRecord", PCONTEXT32), + ] +PEXCEPTION_POINTERS32 = POINTER(_EXCEPTION_POINTERS32) +EXCEPTION_POINTERS32 = _EXCEPTION_POINTERS32 + diff --git a/hooks.py b/hooks.py index 6af1fa8..9f5f277 100644 --- a/hooks.py +++ b/hooks.py @@ -10,38 +10,37 @@ from windows.generated_def.winstructs import * class Callback(object): def __init__(self, *types): self.types = types - + def __call__(self, func): func._types_info = self.types return func - + class KnownCallback(object): types = () def __call__(self, func): func._types_info = self.types return func - + def add_callback_to_module(callback): setattr(sys.modules[__name__], type(callback).__name__, callback) - + # Generate IATCallback decorator for all known functions for func in winfuncs.functions: - prototype = getattr(winfuncs, func + "Prototype") + prototype = getattr(winfuncs, func + "Prototype") callback_name = func + "Callback" - + class CallBackDeclaration(KnownCallback): types = (prototype._restype_,) + prototype._argtypes_ - + CallBackDeclaration.__name__ = callback_name add_callback_to_module(CallBackDeclaration()) - - + + class IATHook(object): + """Look at my hook <3""" - #callback_inject = python_native_execution.CallbackInjector() - def __init__(self, IAT_entry, callback, types=None): if types is None: if not hasattr(callback, "_types_info"): @@ -63,17 +62,17 @@ class IATHook(object): else: res.append(type) return res - + def enable(self): with utils.VirtualProtected(self.entry.addr, ctypes.sizeof(PVOID), windef.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): self.entry.value = self.entry.nonhookvalue self.is_enable = False - + def hook_callback(self, *args): original_args = args adapted_args = [] diff --git a/k32testing.py b/k32testing.py index 23e3d02..3d5c72b 100644 --- a/k32testing.py +++ b/k32testing.py @@ -141,6 +141,12 @@ GetCurrentThread = TransparentKernel32Proxy("GetCurrentThread") AllocConsole = TransparentKernel32Proxy("AllocConsole") GetStdHandle = TransparentKernel32Proxy("GetStdHandle") SetStdHandle = TransparentKernel32Proxy("SetStdHandle") +GetCurrentThreadId = TransparentKernel32Proxy("GetCurrentThreadId") + +TerminateThread = TransparentKernel32Proxy("TerminateThread") +ExitThread = TransparentKernel32Proxy("ExitThread") +SuspendThread = TransparentKernel32Proxy("SuspendThread") +ResumeThread = TransparentKernel32Proxy("ResumeThread") # This kind of function could be fully done by using paramflags @Kernel32Proxy("VirtualAlloc") @@ -151,6 +157,10 @@ def VirtualAlloc(lpAddress=0, dwSize=NeededParameter, flAllocationType=MEM_COMM def VirtualAllocEx(hProcess, lpAddress=0, dwSize=NeededParameter, flAllocationType=MEM_COMMIT, flProtect=PAGE_EXECUTE_READWRITE): return VirtualAllocEx.ctypes_function(hProcess, lpAddress, dwSize, flAllocationType, flProtect) +@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): @@ -269,6 +279,23 @@ 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") +def WaitForSingleObject(hHandle, dwMilliseconds=INFINITE): + return WaitForSingleObject.ctypes_function(hHandle, dwMilliseconds) + ###### ADVAPI32 ######## @Advapi32Proxy('OpenProcessToken') diff --git a/make.bat b/make.bat new file mode 100644 index 0000000..adee8f6 --- /dev/null +++ b/make.bat @@ -0,0 +1,263 @@ +@ECHO OFF + +REM Command file for Sphinx documentation + +if "%SPHINXBUILD%" == "" ( + set SPHINXBUILD=sphinx-build +) +set BUILDDIR=build +set ALLSPHINXOPTS=-d %BUILDDIR%/doctrees %SPHINXOPTS% source +set I18NSPHINXOPTS=%SPHINXOPTS% source +if NOT "%PAPER%" == "" ( + set ALLSPHINXOPTS=-D latex_paper_size=%PAPER% %ALLSPHINXOPTS% + set I18NSPHINXOPTS=-D latex_paper_size=%PAPER% %I18NSPHINXOPTS% +) + +if "%1" == "" goto help + +if "%1" == "help" ( + :help + echo.Please use `make ^` where ^ is one of + echo. html to make standalone HTML files + echo. dirhtml to make HTML files named index.html in directories + echo. singlehtml to make a single large HTML file + echo. pickle to make pickle files + echo. json to make JSON files + echo. htmlhelp to make HTML files and a HTML help project + echo. qthelp to make HTML files and a qthelp project + echo. devhelp to make HTML files and a Devhelp project + echo. epub to make an epub + echo. latex to make LaTeX files, you can set PAPER=a4 or PAPER=letter + echo. text to make text files + echo. man to make manual pages + echo. texinfo to make Texinfo files + echo. gettext to make PO message catalogs + echo. changes to make an overview over all changed/added/deprecated items + echo. xml to make Docutils-native XML files + echo. pseudoxml to make pseudoxml-XML files for display purposes + echo. linkcheck to check all external links for integrity + echo. doctest to run all doctests embedded in the documentation if enabled + echo. coverage to run coverage check of the documentation if enabled + goto end +) + +if "%1" == "clean" ( + for /d %%i in (%BUILDDIR%\*) do rmdir /q /s %%i + del /q /s %BUILDDIR%\* + goto end +) + + +REM Check if sphinx-build is available and fallback to Python version if any +%SPHINXBUILD% 2> nul +if errorlevel 9009 goto sphinx_python +goto sphinx_ok + +:sphinx_python + +set SPHINXBUILD=python -m sphinx.__init__ +%SPHINXBUILD% 2> nul +if errorlevel 9009 ( + echo. + echo.The 'sphinx-build' command was not found. Make sure you have Sphinx + echo.installed, then set the SPHINXBUILD environment variable to point + echo.to the full path of the 'sphinx-build' executable. Alternatively you + echo.may add the Sphinx directory to PATH. + echo. + echo.If you don't have Sphinx installed, grab it from + echo.http://sphinx-doc.org/ + exit /b 1 +) + +:sphinx_ok + + +if "%1" == "html" ( + %SPHINXBUILD% -b html %ALLSPHINXOPTS% %BUILDDIR%/html + if errorlevel 1 exit /b 1 + echo. + echo.Build finished. The HTML pages are in %BUILDDIR%/html. + goto end +) + +if "%1" == "dirhtml" ( + %SPHINXBUILD% -b dirhtml %ALLSPHINXOPTS% %BUILDDIR%/dirhtml + if errorlevel 1 exit /b 1 + echo. + echo.Build finished. The HTML pages are in %BUILDDIR%/dirhtml. + goto end +) + +if "%1" == "singlehtml" ( + %SPHINXBUILD% -b singlehtml %ALLSPHINXOPTS% %BUILDDIR%/singlehtml + if errorlevel 1 exit /b 1 + echo. + echo.Build finished. The HTML pages are in %BUILDDIR%/singlehtml. + goto end +) + +if "%1" == "pickle" ( + %SPHINXBUILD% -b pickle %ALLSPHINXOPTS% %BUILDDIR%/pickle + if errorlevel 1 exit /b 1 + echo. + echo.Build finished; now you can process the pickle files. + goto end +) + +if "%1" == "json" ( + %SPHINXBUILD% -b json %ALLSPHINXOPTS% %BUILDDIR%/json + if errorlevel 1 exit /b 1 + echo. + echo.Build finished; now you can process the JSON files. + goto end +) + +if "%1" == "htmlhelp" ( + %SPHINXBUILD% -b htmlhelp %ALLSPHINXOPTS% %BUILDDIR%/htmlhelp + if errorlevel 1 exit /b 1 + echo. + echo.Build finished; now you can run HTML Help Workshop with the ^ +.hhp project file in %BUILDDIR%/htmlhelp. + goto end +) + +if "%1" == "qthelp" ( + %SPHINXBUILD% -b qthelp %ALLSPHINXOPTS% %BUILDDIR%/qthelp + if errorlevel 1 exit /b 1 + echo. + echo.Build finished; now you can run "qcollectiongenerator" with the ^ +.qhcp project file in %BUILDDIR%/qthelp, like this: + echo.^> qcollectiongenerator %BUILDDIR%\qthelp\PyWindows.qhcp + echo.To view the help file: + echo.^> assistant -collectionFile %BUILDDIR%\qthelp\PyWindows.ghc + goto end +) + +if "%1" == "devhelp" ( + %SPHINXBUILD% -b devhelp %ALLSPHINXOPTS% %BUILDDIR%/devhelp + if errorlevel 1 exit /b 1 + echo. + echo.Build finished. + goto end +) + +if "%1" == "epub" ( + %SPHINXBUILD% -b epub %ALLSPHINXOPTS% %BUILDDIR%/epub + if errorlevel 1 exit /b 1 + echo. + echo.Build finished. The epub file is in %BUILDDIR%/epub. + goto end +) + +if "%1" == "latex" ( + %SPHINXBUILD% -b latex %ALLSPHINXOPTS% %BUILDDIR%/latex + if errorlevel 1 exit /b 1 + echo. + echo.Build finished; the LaTeX files are in %BUILDDIR%/latex. + goto end +) + +if "%1" == "latexpdf" ( + %SPHINXBUILD% -b latex %ALLSPHINXOPTS% %BUILDDIR%/latex + cd %BUILDDIR%/latex + make all-pdf + cd %~dp0 + echo. + echo.Build finished; the PDF files are in %BUILDDIR%/latex. + goto end +) + +if "%1" == "latexpdfja" ( + %SPHINXBUILD% -b latex %ALLSPHINXOPTS% %BUILDDIR%/latex + cd %BUILDDIR%/latex + make all-pdf-ja + cd %~dp0 + echo. + echo.Build finished; the PDF files are in %BUILDDIR%/latex. + goto end +) + +if "%1" == "text" ( + %SPHINXBUILD% -b text %ALLSPHINXOPTS% %BUILDDIR%/text + if errorlevel 1 exit /b 1 + echo. + echo.Build finished. The text files are in %BUILDDIR%/text. + goto end +) + +if "%1" == "man" ( + %SPHINXBUILD% -b man %ALLSPHINXOPTS% %BUILDDIR%/man + if errorlevel 1 exit /b 1 + echo. + echo.Build finished. The manual pages are in %BUILDDIR%/man. + goto end +) + +if "%1" == "texinfo" ( + %SPHINXBUILD% -b texinfo %ALLSPHINXOPTS% %BUILDDIR%/texinfo + if errorlevel 1 exit /b 1 + echo. + echo.Build finished. The Texinfo files are in %BUILDDIR%/texinfo. + goto end +) + +if "%1" == "gettext" ( + %SPHINXBUILD% -b gettext %I18NSPHINXOPTS% %BUILDDIR%/locale + if errorlevel 1 exit /b 1 + echo. + echo.Build finished. The message catalogs are in %BUILDDIR%/locale. + goto end +) + +if "%1" == "changes" ( + %SPHINXBUILD% -b changes %ALLSPHINXOPTS% %BUILDDIR%/changes + if errorlevel 1 exit /b 1 + echo. + echo.The overview file is in %BUILDDIR%/changes. + goto end +) + +if "%1" == "linkcheck" ( + %SPHINXBUILD% -b linkcheck %ALLSPHINXOPTS% %BUILDDIR%/linkcheck + if errorlevel 1 exit /b 1 + echo. + echo.Link check complete; look for any errors in the above output ^ +or in %BUILDDIR%/linkcheck/output.txt. + goto end +) + +if "%1" == "doctest" ( + %SPHINXBUILD% -b doctest %ALLSPHINXOPTS% %BUILDDIR%/doctest + if errorlevel 1 exit /b 1 + echo. + echo.Testing of doctests in the sources finished, look at the ^ +results in %BUILDDIR%/doctest/output.txt. + goto end +) + +if "%1" == "coverage" ( + %SPHINXBUILD% -b coverage %ALLSPHINXOPTS% %BUILDDIR%/coverage + if errorlevel 1 exit /b 1 + echo. + echo.Testing of coverage in the sources finished, look at the ^ +results in %BUILDDIR%/coverage/python.txt. + goto end +) + +if "%1" == "xml" ( + %SPHINXBUILD% -b xml %ALLSPHINXOPTS% %BUILDDIR%/xml + if errorlevel 1 exit /b 1 + echo. + echo.Build finished. The XML files are in %BUILDDIR%/xml. + goto end +) + +if "%1" == "pseudoxml" ( + %SPHINXBUILD% -b pseudoxml %ALLSPHINXOPTS% %BUILDDIR%/pseudoxml + if errorlevel 1 exit /b 1 + echo. + echo.Build finished. The pseudo-XML files are in %BUILDDIR%/pseudoxml. + goto end +) + +:end diff --git a/native_exec/__init__.py b/native_exec/__init__.py index bd8d3c2..07897c5 100644 --- a/native_exec/__init__.py +++ b/native_exec/__init__.py @@ -1,2 +1 @@ -from native_function import generate_callback_stub, create_function - \ No newline at end of file +from native_function import generate_callback_stub, create_function \ No newline at end of file diff --git a/native_exec/native_function.py b/native_exec/native_function.py index 796eca5..4484f1b 100644 --- a/native_exec/native_function.py +++ b/native_exec/native_function.py @@ -314,6 +314,13 @@ def generate_callback_stub(callback, types): generate_callback_stub.l = [] def create_function(code, types): + """Create a python function that call raw machine code + + :param str code: Raw machine code that will be called + :param list types: Return type and parameters type (see :mod:`ctypes`) + :return: the created function + :rtype: function + """ func_type = ctypes.CFUNCTYPE(*types) addr = allocator.write_code(code) return func_type(addr) diff --git a/native_exec/simple_x64.py b/native_exec/simple_x64.py index 6149d9c..f970e6e 100644 --- a/native_exec/simple_x64.py +++ b/native_exec/simple_x64.py @@ -8,9 +8,23 @@ from simple_x86 import MultipleInstr # This code should really be rewritten.. this_module = sys.modules[__name__] + +generated_instruction = [] def add_instruction(name, instruction): + generated_instruction.append((name, instruction)) setattr(this_module, name, instruction) + +def generate_module_doc(): + doc_lines = ["Here is the list of instruction in the modules:\n\n"] + for name, instruction in generated_instruction: + doc_lines.append(" | {0} -> <{1}>".format(name, instruction.mnemo)) + + this_module.__doc__ = "\n".join(doc_lines) + +reg_order = ['EAX', 'ECX', 'EDX', 'EBX', 'ESP', 'EBP', 'ESI', 'EDI'] +reg_opcode = {v : format(i, "03b") for i, v in enumerate(reg_order)} + reg_order = ['RAX', 'RCX', 'RDX', 'RBX', 'RSP', 'RBP', 'RSI', 'RDI'] reg_opcode = {v : format(i, "03b") for i, v in enumerate(reg_order)} @@ -54,14 +68,20 @@ class Ret(X64Instruction): mnemo = "ret" code = "C3" +generated_instruction.append(("Ret", Ret)) + class Int3(X64Instruction): mnemo = "int3" code = "CC" +generated_instruction.append(("Int3", Int3)) + class Retf(X64Instruction): mnemo = "retf" code = "CB" +generated_instruction.append(("Retf", Retf)) + class SimpleRegInstructionGenerator(object): name = "" @@ -75,10 +95,14 @@ class Mov_RAX_DX(OneBindX64Instruction): mnemo = 'mov rax, [{0}]' code = "48 a1 11 11 11 11 11 11 11 11" +generated_instruction.append(("Mov_RAX_DX", Mov_RAX_DX)) + class Mov_DX_RAX(OneBindX64Instruction): name = 'Mov_DX_RAX' mnemo = 'mov [{0}], rax' code = "48 a3 11 11 11 11 11 11 11 11" + +generated_instruction.append(("Mov_DX_RAX", Mov_DX_RAX)) def generate_simple_reg_instruction(instr_cls, include_new_reg=False): for reg_name, reg_bits in reg_opcode.items(): @@ -214,4 +238,6 @@ def generate_reg_reg_deref(): generate_reg_reg_deref() + +generate_module_doc() diff --git a/native_exec/simple_x86.py b/native_exec/simple_x86.py index 774bde9..2871fda 100644 --- a/native_exec/simple_x86.py +++ b/native_exec/simple_x86.py @@ -4,8 +4,19 @@ import sys this_module = sys.modules[__name__] +generated_instruction = [] + + def add_instruction(name, instruction): + generated_instruction.append((name, instruction)) setattr(this_module, name, instruction) + +def generate_module_doc(): + doc_lines = ["Here is the list of instruction in the modules:\n\n"] + for name, instruction in generated_instruction: + doc_lines.append(" | {0} -> <{1}>".format(name, instruction.mnemo)) + + this_module.__doc__ = "\n".join(doc_lines) reg_order = ['EAX', 'ECX', 'EDX', 'EBX', 'ESP', 'EBP', 'ESI', 'EDI'] reg_opcode = {v : format(i, "03b") for i, v in enumerate(reg_order)} @@ -43,10 +54,13 @@ class Ret(X86Instruction): mnemo = "ret" code = "C3" +generated_instruction.append(("Ret", Ret)) + class Int3(X86Instruction): mnemo = "int3" code = "CC" - + +generated_instruction.append(("Int3", Int3)) class SimpleRegInstructionGenerator(object): name = "" @@ -60,7 +74,8 @@ class OneBindX86Instruction(X86Instruction): class Push_X(OneBindX86Instruction): mnemo = "push {0}" code = "68 11 11 11 11" - + +generated_instruction.append(("Push_X", Push_X)) def generate_simple_reg_instruction(instr_cls): for reg_name, reg_bits in reg_opcode.items(): @@ -268,4 +283,8 @@ class MultipleInstr(object): def get_mnemo(self): return "\n".join(i.get_mnemo() for i in self.instrs) + + + +generate_module_doc() \ No newline at end of file diff --git a/network.py b/network.py index 478b4c6..1de42d9 100644 --- a/network.py +++ b/network.py @@ -9,102 +9,102 @@ from windows.generated_def.windef import * class TCP4Connection(MIB_TCPROW_OWNER_PID): - - @property + + @property def established(self): return self.dwState == MIB_TCP_STATE_ESTAB - - @property + + @property def remote_port(self): return socket.ntohs(self.dwRemotePort) - - @property + + @property def local_port(self): return socket.ntohs(self.dwLocalPort) - + @property def local_addr(self): return socket.inet_ntoa(struct.pack("".format(self.local_addr, self.local_port) return " {2}:{3}>".format(self.local_addr, self.local_port, self.remote_addr, self.remote_port) - - + + class TCP6Connection(MIB_TCP6ROW_OWNER_PID): - @staticmethod + @staticmethod def _str_ipv6_addr(addr): return ":".join(c.encode('hex') for c in addr) - - @property + + @property def established(self): return self.dwState == MIB_TCP_STATE_ESTAB - - @property + + @property def remote_port(self): return socket.ntohs(self.dwRemotePort) - - @property + + @property def local_port(self): return socket.ntohs(self.dwLocalPort) - + @property def local_addr(self): return self._str_ipv6_addr(self.ucLocalAddr) - + @property def remote_addr(self): return self._str_ipv6_addr(self.ucRemoteAddr) - + @property def remote_proto(self): return self.remote_port - + @property def remote_host(self): return self.remote_addr - + def close(self): - raise NotImplementedError("Closing IPV6 connection non implemented") - + raise NotImplementedError("Closing IPV6 connection non implemented") + def __repr__(self): if not self.established: return "".format(self.local_addr, self.local_port) return " {2}:{3}>".format(self.local_addr, self.local_port, self.remote_addr, self.remote_port) - + def get_MIB_TCPTABLE_OWNER_PID_from_buffer(buffer): x = windows.generated_def.winstructs.MIB_TCPTABLE_OWNER_PID.from_buffer(buffer) nb_entry = x.dwNumEntries - + class _GENERATED_MIB_TCPTABLE_OWNER_PID(ctypes.Structure): _fields_ = [ ("dwNumEntries", DWORD), @@ -121,11 +121,11 @@ def get_MIB_TCP6TABLE_OWNER_PID_from_buffer(buffer): _fields_ = [ ("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: @@ -136,7 +136,7 @@ def get_tcp_ipv4_sockets(): windows.k32testing.GetExtendedTcpTable(buffer, ctypes.byref(size), ulAf=windows.generated_def.windef.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: diff --git a/pe_parse.py b/pe_parse.py index a5d3103..14d66b0 100644 --- a/pe_parse.py +++ b/pe_parse.py @@ -1,5 +1,6 @@ import ctypes import windows +import hooks from generated_def.winstructs import * @@ -77,7 +78,6 @@ def PEFile(baseaddr): return '<{0} "{1}" ordinal {2}>'.format(self.__class__.__name__, self.name, self.ord) def set_hook(self, callback, types=None): - import hooks # TODO: set import at the beginning hook = hooks.IATHook(self, callback, types) self.hook = hook hook.enable() @@ -85,7 +85,7 @@ def PEFile(baseaddr): def remove_hook(self): if self.hook is None: - return None + return False self.hook.disable() self.hook = None return True @@ -241,4 +241,6 @@ def PEFile(baseaddr): # res.append((nb, func, name)) # return res # - return current_pe \ No newline at end of file + return current_pe + +tst = PEFile.__code__.co_consts[13] \ No newline at end of file diff --git a/remote_callback.py b/remote_callback.py index b6950ea..d4c5a5c 100644 --- a/remote_callback.py +++ b/remote_callback.py @@ -18,4 +18,3 @@ def createfile_callback(lpFileName, dwDesiredAccess, dwShareMode, lpSecurityAttr if "dick" in lpFileName: return 0x4242 return real_function() - \ No newline at end of file diff --git a/source/conf.py b/source/conf.py new file mode 100644 index 0000000..825033c --- /dev/null +++ b/source/conf.py @@ -0,0 +1,364 @@ +# -*- coding: utf-8 -*- +# +# PyWindows documentation build configuration file, created by +# sphinx-quickstart on Tue Apr 07 11:39:41 2015. +# +# This file is execfile()d with the current directory set to its +# containing dir. +# +# Note that not all possible configuration values are present in this +# autogenerated file. +# +# All configuration values have a default; values that are commented out +# serve to show the default. + +import sys +import os + +# If extensions (or modules to document with autodoc) are in another directory, +# add these directories to sys.path here. If the directory is relative to the +# documentation root, use os.path.abspath to make it absolute, like shown here. +#sys.path.insert(0, os.path.abspath('.')) + + + +sys.path.append(r"C:\Users\hakril\Documents\Work\PythonForWindows") + + +# -- General configuration ------------------------------------------------ + +# If your documentation needs a minimal Sphinx version, state it here. +#needs_sphinx = '1.0' + +# Add any Sphinx extension module names here, as strings. They can be +# extensions coming with Sphinx (named 'sphinx.ext.*') or your custom +# ones. +extensions = [ + 'sphinx.ext.intersphinx', + 'sphinx.ext.coverage', + 'sphinx.ext.ifconfig', + 'sphinx.ext.viewcode', + 'sphinx.ext.autodoc', +] + +autodoc_default_flags = ['show-inheritance', 'inherited-members'] + +# Add any paths that contain templates here, relative to this directory. +templates_path = ['_templates'] + +# The suffix of source filenames. +source_suffix = '.rst' + +# The encoding of source files. +#source_encoding = 'utf-8-sig' + +# The master toctree document. +master_doc = 'index' + +# General information about the project. +project = u'PyWindows' +copyright = u'2015, Clement Rouault' + +# The version info for the project you're documenting, acts as replacement for +# |version| and |release|, also used in various other places throughout the +# built documents. +# +# The short X.Y version. +version = '0.1' +# The full version, including alpha/beta/rc tags. +release = '0.1' + +# The language for content autogenerated by Sphinx. Refer to documentation +# for a list of supported languages. +# +# This is also used if you do content translation via gettext catalogs. +# Usually you set "language" from the command line for these cases. +language = None + +# There are two options for replacing |today|: either, you set today to some +# non-false value, then it is used: +#today = '' +# Else, today_fmt is used as the format for a strftime call. +#today_fmt = '%B %d, %Y' + +# List of patterns, relative to source directory, that match files and +# directories to ignore when looking for source files. +exclude_patterns = [] + +# The reST default role (used for this markup: `text`) to use for all +# documents. +#default_role = None + +# If true, '()' will be appended to :func: etc. cross-reference text. +#add_function_parentheses = True + +# If true, the current module name will be prepended to all description +# unit titles (such as .. function::). +#add_module_names = True + +# If true, sectionauthor and moduleauthor directives will be shown in the +# output. They are ignored by default. +#show_authors = False + +# The name of the Pygments (syntax highlighting) style to use. +pygments_style = 'sphinx' + +# A list of ignored prefixes for module index sorting. +#modindex_common_prefix = [] + +# If true, keep warnings as "system message" paragraphs in the built documents. +#keep_warnings = False + + +# -- Options for HTML output ---------------------------------------------- + +# The theme to use for HTML and HTML Help pages. See the documentation for +# a list of builtin themes. +html_theme = 'default' + +# Theme options are theme-specific and customize the look and feel of a theme +# further. For a list of options available for each theme, see the +# documentation. +#html_theme_options = {} + +# Add any paths that contain custom themes here, relative to this directory. +#html_theme_path = [] + +# The name for this set of Sphinx documents. If None, it defaults to +# " v documentation". +#html_title = None + +# A shorter title for the navigation bar. Default is the same as html_title. +#html_short_title = None + +# The name of an image file (relative to this directory) to place at the top +# of the sidebar. +#html_logo = None + +# The name of an image file (within the static path) to use as favicon of the +# docs. This file should be a Windows icon file (.ico) being 16x16 or 32x32 +# pixels large. +#html_favicon = None + +# Add any paths that contain custom static files (such as style sheets) here, +# relative to this directory. They are copied after the builtin static files, +# so a file named "default.css" will overwrite the builtin "default.css". +html_static_path = ['_static'] + +# Add any extra paths that contain custom files (such as robots.txt or +# .htaccess) here, relative to this directory. These files are copied +# directly to the root of the documentation. +#html_extra_path = [] + +# If not '', a 'Last updated on:' timestamp is inserted at every page bottom, +# using the given strftime format. +#html_last_updated_fmt = '%b %d, %Y' + +# If true, SmartyPants will be used to convert quotes and dashes to +# typographically correct entities. +#html_use_smartypants = True + +# Custom sidebar templates, maps document names to template names. +#html_sidebars = {} + +# Additional templates that should be rendered to pages, maps page names to +# template names. +#html_additional_pages = {} + +# If false, no module index is generated. +#html_domain_indices = True + +# If false, no index is generated. +#html_use_index = True + +# If true, the index is split into individual pages for each letter. +#html_split_index = False + +# If true, links to the reST sources are added to the pages. +#html_show_sourcelink = True + +# If true, "Created using Sphinx" is shown in the HTML footer. Default is True. +#html_show_sphinx = True + +# If true, "(C) Copyright ..." is shown in the HTML footer. Default is True. +#html_show_copyright = True + +# If true, an OpenSearch description file will be output, and all pages will +# contain a tag referring to it. The value of this option must be the +# base URL from which the finished HTML is served. +#html_use_opensearch = '' + +# This is the file name suffix for HTML files (e.g. ".xhtml"). +#html_file_suffix = None + +# Language to be used for generating the HTML full-text search index. +# Sphinx supports the following languages: +# 'da', 'de', 'en', 'es', 'fi', 'fr', 'hu', 'it', 'ja' +# 'nl', 'no', 'pt', 'ro', 'ru', 'sv', 'tr' +#html_search_language = 'en' + +# A dictionary with options for the search language support, empty by default. +# Now only 'ja' uses this config value +#html_search_options = {'type': 'default'} + +# The name of a javascript file (relative to the configuration directory) that +# implements a search results scorer. If empty, the default will be used. +#html_search_scorer = 'scorer.js' + +# Output file base name for HTML help builder. +htmlhelp_basename = 'PyWindowsdoc' + +# -- Options for LaTeX output --------------------------------------------- + +latex_elements = { +# The paper size ('letterpaper' or 'a4paper'). +#'papersize': 'letterpaper', + +# The font size ('10pt', '11pt' or '12pt'). +#'pointsize': '10pt', + +# Additional stuff for the LaTeX preamble. +#'preamble': '', + +# Latex figure (float) alignment +#'figure_align': 'htbp', +} + +# Grouping the document tree into LaTeX files. List of tuples +# (source start file, target name, title, +# author, documentclass [howto, manual, or own class]). +latex_documents = [ + ('index', 'PyWindows.tex', u'PyWindows Documentation', + u'Clement Rouault', 'manual'), +] + +# The name of an image file (relative to this directory) to place at the top of +# the title page. +#latex_logo = None + +# For "manual" documents, if this is true, then toplevel headings are parts, +# not chapters. +#latex_use_parts = False + +# If true, show page references after internal links. +#latex_show_pagerefs = False + +# If true, show URL addresses after external links. +#latex_show_urls = False + +# Documents to append as an appendix to all manuals. +#latex_appendices = [] + +# If false, no module index is generated. +#latex_domain_indices = True + + +# -- Options for manual page output --------------------------------------- + +# One entry per manual page. List of tuples +# (source start file, name, description, authors, manual section). +man_pages = [ + ('index', 'pywindows', u'PyWindows Documentation', + [u'Clement Rouault'], 1) +] + +# If true, show URL addresses after external links. +#man_show_urls = False + + +# -- Options for Texinfo output ------------------------------------------- + +# Grouping the document tree into Texinfo files. List of tuples +# (source start file, target name, title, author, +# dir menu entry, description, category) +texinfo_documents = [ + ('index', 'PyWindows', u'PyWindows Documentation', + u'Clement Rouault', 'PyWindows', 'One line description of project.', + 'Miscellaneous'), +] + +# Documents to append as an appendix to all manuals. +#texinfo_appendices = [] + +# If false, no module index is generated. +#texinfo_domain_indices = True + +# How to display URL addresses: 'footnote', 'no', or 'inline'. +#texinfo_show_urls = 'footnote' + +# If true, do not generate a @detailmenu in the "Top" node's menu. +#texinfo_no_detailmenu = False + + +# -- Options for Epub output ---------------------------------------------- + +# Bibliographic Dublin Core info. +epub_title = u'PyWindows' +epub_author = u'Clement Rouault' +epub_publisher = u'Clement Rouault' +epub_copyright = u'2015, Clement Rouault' + +# The basename for the epub file. It defaults to the project name. +#epub_basename = u'PyWindows' + +# The HTML theme for the epub output. Since the default themes are not optimized +# for small screen space, using the same theme for HTML and epub output is +# usually not wise. This defaults to 'epub', a theme designed to save visual +# space. +#epub_theme = 'epub' + +# The language of the text. It defaults to the language option +# or 'en' if the language is not set. +#epub_language = '' + +# The scheme of the identifier. Typical schemes are ISBN or URL. +#epub_scheme = '' + +# The unique identifier of the text. This can be a ISBN number +# or the project homepage. +#epub_identifier = '' + +# A unique identification for the text. +#epub_uid = '' + +# A tuple containing the cover image and cover page html template filenames. +#epub_cover = () + +# A sequence of (type, uri, title) tuples for the guide element of content.opf. +#epub_guide = () + +# HTML files that should be inserted before the pages created by sphinx. +# The format is a list of tuples containing the path and title. +#epub_pre_files = [] + +# HTML files shat should be inserted after the pages created by sphinx. +# The format is a list of tuples containing the path and title. +#epub_post_files = [] + +# A list of files that should not be packed into the epub file. +epub_exclude_files = ['search.html'] + +# The depth of the table of contents in toc.ncx. +#epub_tocdepth = 3 + +# Allow duplicate toc entries. +#epub_tocdup = True + +# Choose between 'default' and 'includehidden'. +#epub_tocscope = 'default' + +# Fix unsupported image types using the PIL. +#epub_fix_images = False + +# Scale large images. +#epub_max_image_width = 0 + +# How to display URL addresses: 'footnote', 'no', or 'inline'. +#epub_show_urls = 'inline' + +# If false, no index is generated. +#epub_use_index = True + + +# Example configuration for intersphinx: refer to the Python standard library. +intersphinx_mapping = {'http://docs.python.org/': None} diff --git a/source/generated_doc.rst b/source/generated_doc.rst new file mode 100644 index 0000000..615c4ea --- /dev/null +++ b/source/generated_doc.rst @@ -0,0 +1,19 @@ +GENERATED Documentation test +**************************** + +Testing1 +******** + +.. automodule:: windows.generated_def.winstructs + + + +Testing2 +******** + +.. autoclass:: _PEB_LDR_DATA +.. autoclass:: _PEB +.. autoclass:: _LIST_ENTRY + + + diff --git a/source/index.rst b/source/index.rst new file mode 100644 index 0000000..bf4c039 --- /dev/null +++ b/source/index.rst @@ -0,0 +1,26 @@ +.. PyWindows documentation master file, created by + sphinx-quickstart on Tue Apr 07 11:39:41 2015. + You can adapt this file completely to your liking, but it should at least + contain the root `toctree` directive. + +Welcome to PyWindows's documentation! +===================================== + +Contents: + +.. toctree:: + :maxdepth: 2 + + test.rst + pe_parse.rst + native_exec.rst + rem_python.rst + utils.rst + + +Indices and tables +================== + +* :ref:`modindex` +* :ref:`search` + diff --git a/source/native_exec.rst b/source/native_exec.rst new file mode 100644 index 0000000..c758b02 --- /dev/null +++ b/source/native_exec.rst @@ -0,0 +1,79 @@ +Native code execution +*********************** + +.. automodule:: windows.native_exec + :members: generate_callback_stub, create_function + + +The native_function submodule +""""""""""""""""""""""""""""" + +.. automodule:: windows.native_exec.native_function + :members: create_function + + +Simple machine code generation +"""""""""""""""""""""""""""""" + +These modules allow you to write some simple x86 / x64 shellcode. This is useful to use in adequation to :func:`create_function`. +The instruction name are as explicit as possible with the following convention: + + * This is `intel syntax` so `dest, src` + * X specify a value passed as parameters:: + + Mov_EAX_X(0x42) # mov eax, 0x42 + + * D is for `dereference`:: + + Mov_EAX_DX(0x42424242) # mov EAX, [0x42424242] + Mov_DEAX_EDI() # mov [EAX], EDI + +All instructions follow this interface: + +.. py:class:: Instruction + + .. py:method:: get_code(self) + + :returns: :class:`str`: The raw code of the instruction + + .. py:method:: get_mnemo(self) + + :returns: :class:`str`: The mnemonic of the instruction + +Example:: + + import windows.native_exec.simple_x86 as x86 + + i = x86.Mov_EAX_X(0x42434445) + i.get_code() + # '\xb8EDCB' + i.get_mnemo() + # 'mov EAX, 0x42434445' + + +You can also use a :class:`MultipleInstr` instance to merge instructions + +Example:: + + import windows.native_exec.simple_x86 as x86 + + code = x86.MultipleInstr() + code += x86.Mov_EAX_X(0x42434445) + code += x86.Push_EAX() + code += x86.Ret() + code.get_code() + # '\xb8EDCBP\xc3' + print(code.get_mnemo()) + # mov EAX, 0x42434445 + # push EAX + # ret + +simple_x86 instructions +----------------------- + +.. automodule:: windows.native_exec.simple_x86 + +simple_x64 instructions +----------------------- + +.. automodule:: windows.native_exec.simple_x64 \ No newline at end of file diff --git a/source/pe_parse.rst b/source/pe_parse.rst new file mode 100644 index 0000000..c880476 --- /dev/null +++ b/source/pe_parse.rst @@ -0,0 +1,179 @@ +Loaded DLL Exploration and IAT hooks +************************************ + +List of loaded modules +"""""""""""""""""""""" + +Accessible using:: + + import windows + windows.current_process.peb.modules[int].pe + +..note:: + See: :class:`windows.winobject.PEB` and :class:`windows.winobject.LoadedModule` + + + +DLL Import and IAT +"""""""""""""""""" + +.. py:class:: PEFile + + .. py:attribute:: imports + + The imports of the PE + + .. note:: + This is a :class:`dict` DLLName -> [:class:`IATEntry`] + + Example:: + + import windows + k32 = windows.current_process.peb.modules[2] + # + k32.pe.imports.keys() + # ['kernelbase.dll', 'api-ms-win-core-profile-l1-1-0.dll', ...] + k32.pe.imports['kernelbase.dll'] + # [, , ...] + [entry for entry in k32.pe.imports['kernelbase.dll'] if entry.name == "lstrcmpiW"][0] + # + + +.. py:class:: IATEntry + + | Reprensent An entry in the IAT of a module + | Can be used to get resolved value and setup hook + + .. py:attribute:: name + + | :class:`int` : The name of the import + + + .. py:attribute:: ord + + | :class:`int` : The ordinal of the import + + + .. py:attribute:: addr + + | :class:`int` : The address of the IAT entry + + .. py:attribute:: value + + | :class:`int` : The destination of the IAT entry + + .. warning:: + + `value` is a descriptor. Setting its value will actually CHANGE THE IAT ENTRY, resulting in a segfault if no VirtualProtect have been done. + + .. note:: + + See: :class:`windows.utils.VirtualProtected` + + + .. py:method:: set_hook(self, callback, types=None) + + Setup a hook, `callback` should respect the :ref:`hook_protocol`. If `callback` have no :ref:`type_information`, `types` should provide them. + + +IAT Hooking +""""""""""" + +.. _hook_protocol: + +The hook protocol +----------------- + +Callback arguments +'''''''''''''''''' + +A hook callback must have the same number of argument as the hooked API, PLUS a last argument `real_function`. + +The `real_function` argument is a callable that represent the hooked API, it can be called in two ways: + + * Without argument, the call will be done with the argument originaly passed to your callback. This allow simple redirection to the real API. + + * With arguments it will simply call the API with these. + + Example:: + + def createfile_callback(lpFileName, dwDesiredAccess, dwShareMode, lpSecurityAttributes, dwCreationDisposition, dwFlagsAndAttributes, hTemplateFile, real_function): + print("Trying to open {0}".format(lpFileName)) + if "secret" in lpFileName: + return 0xffffffff + # Perform the real call + return real_function() + + +.. _type_information: + +Type information +'''''''''''''''' + +In order make the magic behind Python Hook Callback, :mod:`ctypes` need to have type information about the API parameters. + +There is (again) two ways to give those informations to your hook callback. Both techniques use a decorator to setup type information to the callback. + + * Giving the type manualy using the decorator :class:`windows.hooks.Callback`:: + + from windows.hooks import * + # First type is return type, others are parameters types + @Callback(ctypes.c_void_p, ctypes.c_ulong) + def exit_callback(x, real_function): + print("Try to quit with {0} | {1}".format(x, type(x))) + if x == 3: + print("TRYING TO REAL EXIT") + return real_function(1234) + return 0x4242424243444546 + + * Using the `Callback` decorator generated from known functions:: + + from windows.hooks import * + # Decorator name is always API_NAME + "CallBack" + @CreateFileACallback + def createfile_callback(lpFileName, dwDesiredAccess, dwShareMode, lpSecurityAttributes, dwCreationDisposition, dwFlagsAndAttributes, hTemplateFile, real_function): + print("Trying to open {0}".format(lpFileName)) + if "secret" in lpFileName: + return 0xffffffff + return real_function() + + .. note:: + + See the list of known functions + + +Put the hook +------------ + +To setup your IAT hook you just need: + + * A callback that respect the :ref:`hook_protocol` + * The :class:`IATEntry` to hook + + +You just need to use the function :func:`IATEntry.set_hook` + + + Full Example:: + + import windows + from windows.hooks import * + + @CreateFileACallback + def createfile_callback(lpFileName, dwDesiredAccess, dwShareMode, lpSecurityAttributes, dwCreationDisposition, dwFlagsAndAttributes, hTemplateFile, real_function): + print("Trying to open {0}".format(lpFileName)) + if "secret" in lpFileName: + return 0xffffffff + return real_function() + + my_exe = windows.current_process.peb.modules[0] + imp = my_exe.pe.imports + + iat_create_file = [entry for entry in imp['kernel32.dll'] if entry.name == "CreateFileA"] + iat_create_file.set_hook(createfile_callback) + + + + + + diff --git a/source/rem_python.rst b/source/rem_python.rst new file mode 100644 index 0000000..7185e87 --- /dev/null +++ b/source/rem_python.rst @@ -0,0 +1,77 @@ +Remote Python Injection +*********************** + +It's possible to inject the python interpreter into remote process. All you need to do is to use the method :func:`windows.winobject.WinProcess.execute_python`. + +Calling this function will trigger the interpreter injection and the python code execution. + +For simpler interaction interaction with the remote python, you can an RPC master linked to the remote interpreter. + +RPython 101 +''''''''''' + +RPython is a very simple Pythonic (I hope so) RPC slave-master. It's goal is to allow easy manipulation of a remote interpreter. + +The only action up to the `slave` is the creation of it's `name_pool`: a namespace of object accessible by the `master`. +After that the `slave` will just wait for request and return the desired object. + +All slave object seen by the master are proxy that redirect operation to the slave. + +RPython RPC to remote process +''''''''''''''''''''''''''''' + +For easy manipulation of a remote Python interpreter, you can use the RPCInjection module:: + + import windows + import RPCInjection + calc = [x for x in windows.system.processes if x.name == "calc.exe"][0] + master = RPCInjection.launch_remote_slave(calc) + # Master is a RPC-master to a python interpreter in calc.exe + master['windows'] + # |> + + # This is a way to get our own (python.exe) pid + windows.current_process.pid + 3624 + # This is a way to get the pid of calc.exe + master['windows'].current_process + # |> + master['windows'].current_process.pid + 5052 + # We can also play with the peb of the remote process + master['windows'].current_process.peb + # |> + master['windows'].current_process.peb.commandline + # |> + + + # we can import new stuff + + x['json'] + # ... + # RPython.exchange.RemoteKeyError: + # .... + # KeyError: u'json' + x.imp('json') + # + x['json'] + # |> + + +.. note:: + + The slave `name_pool` in RPCInjection is filled with :mod:`windows`, :mod:`__import__`, :mod:`ctypes` and :mod:`self` (the RemotePythonSlave object) + +The master.RemotePython +''''''''''''''''''''''' + +.. autoclass:: RPython.master.RemotePython + + .. py:method:: __getitem__ + + Alias to :func:`ask_by_name` + +Remote IAT Hooking +'''''''''''''''''' + +See Examples directory \ No newline at end of file diff --git a/source/test.rst b/source/test.rst new file mode 100644 index 0000000..10ba740 --- /dev/null +++ b/source/test.rst @@ -0,0 +1,13 @@ +Overwiew of the `windows` objects +************************** + +object exported by `windows` +"""""""""""""""""""""""""""" + +.. automodule:: windows + +Principal classes for process exploration +""""""""""""""""""""""""""""""""""""""""" + +.. automodule:: windows.winobject + :members: diff --git a/source/utils.rst b/source/utils.rst new file mode 100644 index 0000000..27ce1a8 --- /dev/null +++ b/source/utils.rst @@ -0,0 +1,4 @@ +Helpers +******* + +.. automodule:: windows.utils \ No newline at end of file diff --git a/syswow64.py b/syswow64.py index 1dc8613..a8640f2 100644 --- a/syswow64.py +++ b/syswow64.py @@ -32,7 +32,7 @@ FF FF 1F 00 49 C7 C0 00 00 00 00 49 B9 40 40 40 41 41 41 41 41 41 50 41 51 41 50 52 51 41 FF D5 48 89 84 24 E8 00 00 00 48 83 C4 40 48 83 C4 28 48 83 C4 20 41 5D 41 5C 41 5B 41 5A 41 59 41 58 -5F 5E 5A 59 5B 58 58 +5F 5E 5A 59 5B 58 58 """ NtCreateThreadStub = Pretty_NtCreateThreadStub.replace(" ", "").replace("\n", "").decode('hex') @@ -72,39 +72,39 @@ def NtCreateThreadEx_32_to_64(process, addr, param): shellcode = shellcode.replace("\x41" * 8, struct.pack(" 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]]) + + +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))) + +class EnhancedCONTEXTBase(CONTEXT): + default_dump = () + + def regs(self, to_dump=None): + res = [] + if to_dump is None: + to_dump = self.default_dump + for name in to_dump: + res.append((name, getattr(self, name))) + return res + + def dump(self, to_dump=None): + regs = self.regs() + for name, value in regs: + print("{0} -> {1}".format(name, hex(value))) + +class EnhancedCONTEXT32(EnhancedCONTEXTBase): + default_dump = ('Eip', 'Esp', 'Eax', 'Ebx', 'Ecx', 'Ebp', 'Edi', 'Esi') + +class EnhancedCONTEXT64(EnhancedCONTEXTBase): + default_dump = ('Rip', 'Rsp', 'Rax', 'Rbx', 'Rcx', 'Rbp', 'Rdi', 'Rsi', + 'R9', 'R10', 'R11', 'R12', 'R13', 'R14', 'R15') + +if windows.current_process.bitness == 32: + EnhancedCONTEXT = EnhancedCONTEXT32 +else: + EnhancedCONTEXT = EnhancedCONTEXT64 + +class EnhancedEXCEPTION_POINTERS(ctypes.Structure): + _fields_ = [ + ("ExceptionRecord", ctypes.POINTER(EnhancedEXCEPTION_RECORD)), + ("ContextRecord", ctypes.POINTER(EnhancedCONTEXT)), + ] + + def dump(self): + record = self.ExceptionRecord[0] + print("Dumping Exception: ") + print(" ExceptionCode = {0} at {1}".format(record.ExceptionCode, hex(record.ExceptionAddress))) + regs = self.ContextRecord[0].regs() + for name, value in regs: + print(" {0} -> {1}".format(name, hex(value))) + + +class VectoredException(object): + func_type = ctypes.WINFUNCTYPE(ctypes.c_uint, ctypes.POINTER(EnhancedEXCEPTION_POINTERS)) + + def __init__(self): + pass + + def __call__(self, func): + self.func = func + return self.func_type(self.decorator) + + def decorator(self, exception_pointers): + print("IN DAT DECORATOR") + try: + x = self.func(exception_pointers) + print("PROUT") + return x + 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): + self.handler = handler + + def __enter__(self): + self.value = windows.k32testing.AddVectoredExceptionHandler(0, self.handler) + return self + + def __exit__(self, exc_type, exc_value, traceback): + windows.k32testing.RemoveVectoredExceptionHandler(self.value) + return False \ No newline at end of file diff --git a/winobject.py b/winobject.py index 6c3e657..b4610a2 100644 --- a/winobject.py +++ b/winobject.py @@ -12,11 +12,20 @@ import pe_parse class AutoHandle(object): + """An abstract class that allow easy handle creation/destruction""" def _get_handle(self): - raise NotImplementedError('_get_handle') + raise NotImplementedError("{0} is abstract".format(type(self).__name__)) @property def handle(self): + """A handle on the object + + .. note:: + The handle is automaticaly closed when the object is destroyed + + :type: int + + """ if hasattr(self, "_handle"): return self._handle self._handle = self._get_handle() @@ -27,17 +36,32 @@ class AutoHandle(object): kernel32proxy.CloseHandle(self._handle) class System(object): - + """Represent the current windows system python is running on""" @property def processes(self): + """The list of running processes + + :type: [:class:`WinProcess`] -- A list of Process + + """ return utils.enumerate_processes() @property def threads(self): + """The list of running threads + + :type: [:class:`WinThread`] -- A list of Thread + + """ return utils.enumerate_threads() @property def bitness(self): + """The bitness of the system + + :type: int -- 32 or 64 + + """ if os.environ["PROCESSOR_ARCHITECTURE"].lower() != "x86": return 64 if "PROCESSOR_ARCHITEW6432" in os.environ: @@ -48,12 +72,19 @@ class System(object): # May have a common class with WinProcess for is_wow_64 and stuff class WinThread(THREADENTRY32, AutoHandle): + """Represent a thread """ @property def tid(self): + """Thread ID""" return self.th32ThreadID @property def owner(self): + """The Process owning the thread + + :type: :class:`WinProcess` + + """ if hasattr(self, "_owner"): return self._owner self._owner = [process for process in utils.enumerate_processes() if process.pid == self.th32OwnerProcessID][0] @@ -65,8 +96,71 @@ class WinThread(THREADENTRY32, AutoHandle): def __repr__(self): return '<{0} {1} owner "{2}" at {3}>'.format(self.__class__.__name__, self.tid, self.owner.name, hex(id(self))) +class Process(AutoHandle): + @property + def is_wow_64(self): + """Is True if the process is a SysWow64 process -class CurrentProcess(object): + This means a 32bits process on a 64bits system + + :type: bool + """ + return utils.is_wow_64(self.handle) + + @property + def bitness(self): + """The bitness of the process + + :returns: int -- 32 or 64""" + if windows.system.bitness == 32: + return 32 + if self.is_wow_64: + return 32 + return 64 + + @property + def threads(self): + """The threads of the process + + :type: [:class:`WinThread`] -- A list of Thread + + """ + return [thread for thread in utils.enumerate_threads() if thread.th32OwnerProcessID == self.pid] + + def virtual_alloc(self, size): + raise NotImplementedError("virtual_alloc") + + def execute(self, code): + """Execute some raw code in the context of the process""" + x = self.virtual_alloc(len(code)) + self.write_memory(x, code) + return self.create_thread(x, 0) + + +class CurrentThread(AutoHandle): + """The current thread""" + @property + def tid(self): + """Thread ID""" + return kernel32proxy.GetCurrentThreadId() + + @property + def owner(self): + """The current process + + :type: :class:`CurrentProcess` + """ + return windows.current_process + + def _get_handle(self): + return kernel32proxy.GetCurrentThread() + + def exit(self, code=0): + """Exit the thread""" + return kernel32proxy.ExitThread(code) + +class CurrentProcess(Process): + """The current process""" get_peb = None get_peb_32_code = '64a130000000c3'.decode('hex') @@ -84,52 +178,101 @@ class CurrentProcess(object): self.get_peb = get_peb return get_peb + def _get_handle(self): + return kernel32proxy.GetCurrentProcess() + + @property + def pid(self): + """Process ID + + :type: int + """ + return os.getpid() + + # Is there a better way ? + @property + def ppid(self): + """Parent Process ID + + :type: int + """ + return [p for p in windows.system.processes if p.pid == self.pid][0].ppid + @property def peb(self): + """The Process Environment Block of the current process + + :type: :class:`PEB` + """ return PEB.from_address(self.get_peb_builtin()()) @property def bitness(self): - """Return 32 or 64""" + """The bitness of the process + + :returns: int -- 32 or 64""" import platform bits = platform.architecture()[0] return int(bits[:2]) - @property - def is_wow_64(self): - return utils.is_wow_64(kernel32proxy.GetCurrentProcess()) - def virtual_alloc(self, size): + """Allocate memory in the current process + + :returns: int + """ return kernel32proxy.VirtualAlloc(dwSize=size) def write_memory(self, addr, data): + """Write data at addr""" buffertype = (c_char * len(data)).from_address(addr) buffertype[:len(data)] = data return True def read_memory(self, addr, size): + """Read size from adddr""" buffer = (c_char * size).from_address(addr) return buffer[:] -class WinProcess(PROCESSENTRY32, AutoHandle): + def create_thread(self, lpStartAddress, lpParameter, dwCreationFlags=0): + """Create a new thread + + .. note:: + CreateThread https://msdn.microsoft.com/en-us/library/windows/desktop/ms682453%28v=vs.85%29.aspx + """ + return kernel32proxy.CreateThread(lpStartAddress=lpStartAddress, lpParameter=lpParameter, dwCreationFlags=dwCreationFlags) + + def exit(self, code=0): + """Exit the process""" + return kernel32proxy.ExitProcess(code) + +class WinProcess(PROCESSENTRY32, Process): + """A Process on the system""" is_pythondll_injected = 0 is_remote_slave_running = False @property def name(self): + """Name of the process + + :type: str + """ return self.szExeFile[:] @property def pid(self): + """Process ID + + :type: int + """ return self.th32ProcessID @property def ppid(self): - return self.th32ParentProcessID + """Parent Process ID - @property - def threads(self): - return [thread for thread in utils.enumerate_threads() if thread.th32OwnerProcessID == self.pid] + :type: int + """ + return self.th32ParentProcessID def _get_handle(self): return kernel32proxy.OpenProcess(dwProcessId=self.pid) @@ -137,22 +280,15 @@ class WinProcess(PROCESSENTRY32, AutoHandle): def __repr__(self): return '<{0} "{1}" pid {2} at {3}>'.format(self.__class__.__name__, self.name, self.pid, hex(id(self))) - @property - def is_wow_64(self): - return utils.is_wow_64(self.handle) - - @property - def bitness(self): - if windows.system.bitness == 32: - return 32 - if self.is_wow_64: - return 32 - return 64 - def virtual_alloc(self, size): + """Allocate memory in the process + + :returns: int + """ return kernel32proxy.VirtualAllocEx(self.handle, dwSize=size) def write_memory(self, addr, data): + """Write `data` at `addr`""" return kernel32proxy.WriteProcessMemory(self.handle, addr, lpBuffer=data) def low_read_memory(self, addr, buffer_addr, size): @@ -163,67 +299,58 @@ class WinProcess(PROCESSENTRY32, AutoHandle): return kernel32proxy.ReadProcessMemory(self.handle, addr, lpBuffer=buffer_addr, nSize=size) def read_memory(self, addr, size): + """Read `size` from `addr`""" buffer = ctypes.create_string_buffer(size) self.low_read_memory(addr, ctypes.byref(buffer), size) return buffer[:] def read_memory_into(self, addr, struct): + """Read a :mod:`ctypes` struct from `addr`""" self.low_read_memory(addr, ctypes.byref(struct), ctypes.sizeof(struct)) return struct def create_thread(self, addr, param): + """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 kernel32proxy.CreateRemoteThread(hProcess=self.handle, lpStartAddress=addr, lpParameter=param) def load_library(self, dll_path): + """Load the library in remote process""" x = self.virtual_alloc(0x1000) self.write_memory(x, dll_path) LoadLibrary = utils.get_func_addr('kernel32', 'LoadLibraryA') return self.create_thread(LoadLibrary, x) - def execute(self, code): - x = self.virtual_alloc(len(code)) - self.write_memory(x, code) - return self.create_thread(x, 0) - def execute_python(self, pycode): + """Execute Python code into the remote process""" return injection.execute_python_code(self, pycode) - #def NtCreateThreadEx(self, addr, param): - # print("CALLING SPECIAL NtCreateThreadEx") - # NtCreateThreadExAddr = utils.get_func_addr("ntdll.dll", "NtCreateThreadEx") - # NtCreateThreadEx = WINFUNCTYPE(HRESULT, PHANDLE, DWORD, PVOID, HANDLE, LPTHREAD_START_ROUTINE, LPVOID, BOOL, DWORD, DWORD, DWORD, PVOID)(NtCreateThreadExAddr) - # thread_handle = HANDLE() - # res = NtCreateThreadEx(byref(thread_handle), 0x1fffff, None, self.handle, addr, param, False, 0, 0, 0, None) - # print("RES = {0}".format(hex(res & 0xffffffff))) - # if res: - # raise WinError() - # - #def RtlCreateUserThread(self, addr, param): - # print("CALLING SPECIAL RtlCreateUserThread") - # RtlCreateUserThreadAddr = utils.get_func_addr("ntdll.dll", "RtlCreateUserThread") - # RtlCreateUserThread = WINFUNCTYPE(HRESULT, HANDLE, LPSECURITY_ATTRIBUTES, BOOL, ULONG, PULONG, PULONG, PVOID, PVOID, PHANDLE, PVOID)(RtlCreateUserThreadAddr) - # thread_handle = HANDLE() - # tmp1 = DWORD() - # tmp2 = DWORD() - # res = RtlCreateUserThread(self.handle, None, False, 0, None, None, addr, param, None, None) - # print("RES = {0}".format(hex(res & 0xffffffff))) - # if res: - # raise WinError() - class LoadedModule(LDR_DATA_TABLE_ENTRY): + """An entry in the PEB Ldr list""" @property def baseaddr(self): + """base address of the module + + :type: int + """ return self.DllBase @property def name(self): + """Name of the module + + :type: str + """ return self.BaseDllName.Buffer @property def fullname(self): + """Full name of the module (path) + + :type: str + """ return self.FullDllName.Buffer def __repr__(self): @@ -231,9 +358,14 @@ class LoadedModule(LDR_DATA_TABLE_ENTRY): @property def pe(self): + """A PE representation of the module + + :type: :class:`windows.pe_parse.PEFile` + """ return pe_parse.PEFile(self.baseaddr) class WinUnicodeString(LSA_UNICODE_STRING): + """LSA_UNICODE_STRING with a nice `__repr__`""" def __repr__(self): return """<{0} "{1}" at {2}>""".format(type(self).__name__, self.Buffer, hex(id(self))) @@ -243,22 +375,33 @@ class LIST_ENTRY_PTR(PVOID): return LDR_DATA_TABLE_ENTRY.from_address(self.value - sizeof(PVOID) * 2) -# May want to have all known fields.. class PEB(PEB): - + """The PEB (Process Environment Block) of the current process""" @property def imagepath(self): + """The ImagePathName of the PEB + + :type: :class:`WinUnicodeString` + """ raw_imagepath = self.ProcessParameters.contents.ImagePathName return WinUnicodeString.from_address(ctypes.addressof(raw_imagepath)) @property def commandline(self): + """The CommandLine of the PEB + + :type: :class:`WinUnicodeString` + """ # This or changing the __repr__ of LSA_UNICODE_STRING raw_cmd = self.ProcessParameters.contents.CommandLine return WinUnicodeString.from_address(ctypes.addressof(raw_cmd)) @property def modules(self): + """The loaded modules present in the PEB + + :type: [:class:`LoadedModule`] -- List of loaded modules + """ res = [] list_entry_ptr = ctypes.cast(self.Ldr.contents.InMemoryOrderModuleList.Flink, LIST_ENTRY_PTR) current_dll = list_entry_ptr.TO_LDR_ENTRY()