From ebb23fd27518c0184f759d0b7ca348272375dfb9 Mon Sep 17 00:00:00 2001 From: Clement Rouault Date: Fri, 10 Jul 2015 16:50:46 +0200 Subject: [PATCH] Big commit <3 --- __init__.py | 9 +- generated_def/__init__.py | 6 +- generated_def/windef.py | 221 +++++++++---- generated_def/winfuncs.py | 8 +- generated_def/winstructs.py | 67 ++-- hooks.py | 10 +- injection.py | 95 +++--- k32testing.py | 24 +- native_exec/__init__.py | 2 +- native_exec/native_function.py | 197 +++++------ native_exec/simple_x64.py | 579 +++++++++++++++++++-------------- native_exec/simple_x86.py | 493 +++++++++++++--------------- pe_parse.py | 90 +++-- utils.py | 33 +- vectored_exception.py | 87 ++++- winobject.py | 102 +++++- 16 files changed, 1133 insertions(+), 890 deletions(-) diff --git a/__init__.py b/__init__.py index 470f9ce..c8d07d1 100644 --- a/__init__.py +++ b/__init__.py @@ -11,12 +11,15 @@ Exported: current_thread : :class:`windows.winobject.CurrentThread` """ -import k32testing -from winobject import System, CurrentProcess, CurrentThread -from utils import VirtualProtected +import windows.k32testing +from .utils import VirtualProtected +from .winobject import System, CurrentProcess, CurrentThread + system = System() current_process = CurrentProcess() current_thread = CurrentThread() +import windows.vectored_exception + __all__ = ["system", "VirtualProtected", 'current_process', 'current_thread'] \ No newline at end of file diff --git a/generated_def/__init__.py b/generated_def/__init__.py index ebcdaef..a0e9508 100644 --- a/generated_def/__init__.py +++ b/generated_def/__init__.py @@ -1,5 +1,5 @@ import windows -import winstructs +from . import winstructs def bitness(): """Return 32 or 64""" @@ -23,5 +23,7 @@ else: winstructs.EXCEPTION_POINTERS = winstructs.EXCEPTION_POINTERS64 winstructs.PEXCEPTION_POINTERS = winstructs.PEXCEPTION_POINTERS64 -import winfuncs +from . import winfuncs + + diff --git a/generated_def/windef.py b/generated_def/windef.py index 9103306..16d5491 100644 --- a/generated_def/windef.py +++ b/generated_def/windef.py @@ -1,5 +1,9 @@ #Generated file +import sys +if sys.version_info.major == 3: + long = int + class Flag(long): def __new__(cls, name, value): return super(Flag, cls).__new__(cls, value) @@ -79,59 +83,59 @@ PROFILE_USER = Flag("PROFILE_USER", 0x10000000) PROFILE_KERNEL = Flag("PROFILE_KERNEL", 0x20000000) PROFILE_SERVER = Flag("PROFILE_SERVER", 0x40000000) CREATE_IGNORE_SYSTEM_DEFAULT = Flag("CREATE_IGNORE_SYSTEM_DEFAULT", 0x80000000) -STATUS_WAIT_0 = Flag("STATUS_WAIT_0", ( 0x00000000L )) -STATUS_ABANDONED_WAIT_0 = Flag("STATUS_ABANDONED_WAIT_0", ( 0x00000080L )) -STATUS_USER_APC = Flag("STATUS_USER_APC", ( 0x000000C0L )) -STATUS_TIMEOUT = Flag("STATUS_TIMEOUT", ( 0x00000102L )) -STATUS_PENDING = Flag("STATUS_PENDING", ( 0x00000103L )) -DBG_EXCEPTION_HANDLED = Flag("DBG_EXCEPTION_HANDLED", ( 0x00010001L )) -DBG_CONTINUE = Flag("DBG_CONTINUE", ( 0x00010002L )) -STATUS_SEGMENT_NOTIFICATION = Flag("STATUS_SEGMENT_NOTIFICATION", ( 0x40000005L )) -DBG_TERMINATE_THREAD = Flag("DBG_TERMINATE_THREAD", ( 0x40010003L )) -DBG_TERMINATE_PROCESS = Flag("DBG_TERMINATE_PROCESS", ( 0x40010004L )) -DBG_CONTROL_C = Flag("DBG_CONTROL_C", ( 0x40010005L )) -DBG_PRINTEXCEPTION_C = Flag("DBG_PRINTEXCEPTION_C", ( 0x40010006L )) -DBG_RIPEXCEPTION = Flag("DBG_RIPEXCEPTION", ( 0x40010007L )) -DBG_CONTROL_BREAK = Flag("DBG_CONTROL_BREAK", ( 0x40010008L )) -DBG_COMMAND_EXCEPTION = Flag("DBG_COMMAND_EXCEPTION", ( 0x40010009L )) -STATUS_GUARD_PAGE_VIOLATION = Flag("STATUS_GUARD_PAGE_VIOLATION", ( 0x80000001L )) -STATUS_DATATYPE_MISALIGNMENT = Flag("STATUS_DATATYPE_MISALIGNMENT", ( 0x80000002L )) -STATUS_BREAKPOINT = Flag("STATUS_BREAKPOINT", ( 0x80000003L )) -STATUS_SINGLE_STEP = Flag("STATUS_SINGLE_STEP", ( 0x80000004L )) -STATUS_LONGJUMP = Flag("STATUS_LONGJUMP", ( 0x80000026L )) -STATUS_UNWIND_CONSOLIDATE = Flag("STATUS_UNWIND_CONSOLIDATE", ( 0x80000029L )) -DBG_EXCEPTION_NOT_HANDLED = Flag("DBG_EXCEPTION_NOT_HANDLED", ( 0x80010001L )) -STATUS_ACCESS_VIOLATION = Flag("STATUS_ACCESS_VIOLATION", ( 0xC0000005L )) -STATUS_IN_PAGE_ERROR = Flag("STATUS_IN_PAGE_ERROR", ( 0xC0000006L )) -STATUS_INVALID_HANDLE = Flag("STATUS_INVALID_HANDLE", ( 0xC0000008L )) -STATUS_INVALID_PARAMETER = Flag("STATUS_INVALID_PARAMETER", ( 0xC000000DL )) -STATUS_NO_MEMORY = Flag("STATUS_NO_MEMORY", ( 0xC0000017L )) -STATUS_ILLEGAL_INSTRUCTION = Flag("STATUS_ILLEGAL_INSTRUCTION", ( 0xC000001DL )) -STATUS_NONCONTINUABLE_EXCEPTION = Flag("STATUS_NONCONTINUABLE_EXCEPTION", ( 0xC0000025L )) -STATUS_INVALID_DISPOSITION = Flag("STATUS_INVALID_DISPOSITION", ( 0xC0000026L )) -STATUS_ARRAY_BOUNDS_EXCEEDED = Flag("STATUS_ARRAY_BOUNDS_EXCEEDED", ( 0xC000008CL )) -STATUS_FLOAT_DENORMAL_OPERAND = Flag("STATUS_FLOAT_DENORMAL_OPERAND", ( 0xC000008DL )) -STATUS_FLOAT_DIVIDE_BY_ZERO = Flag("STATUS_FLOAT_DIVIDE_BY_ZERO", ( 0xC000008EL )) -STATUS_FLOAT_INEXACT_RESULT = Flag("STATUS_FLOAT_INEXACT_RESULT", ( 0xC000008FL )) -STATUS_FLOAT_INVALID_OPERATION = Flag("STATUS_FLOAT_INVALID_OPERATION", ( 0xC0000090L )) -STATUS_FLOAT_OVERFLOW = Flag("STATUS_FLOAT_OVERFLOW", ( 0xC0000091L )) -STATUS_FLOAT_STACK_CHECK = Flag("STATUS_FLOAT_STACK_CHECK", ( 0xC0000092L )) -STATUS_FLOAT_UNDERFLOW = Flag("STATUS_FLOAT_UNDERFLOW", ( 0xC0000093L )) -STATUS_INTEGER_DIVIDE_BY_ZERO = Flag("STATUS_INTEGER_DIVIDE_BY_ZERO", ( 0xC0000094L )) -STATUS_INTEGER_OVERFLOW = Flag("STATUS_INTEGER_OVERFLOW", ( 0xC0000095L )) -STATUS_PRIVILEGED_INSTRUCTION = Flag("STATUS_PRIVILEGED_INSTRUCTION", ( 0xC0000096L )) -STATUS_STACK_OVERFLOW = Flag("STATUS_STACK_OVERFLOW", ( 0xC00000FDL )) -STATUS_DLL_NOT_FOUND = Flag("STATUS_DLL_NOT_FOUND", ( 0xC0000135L )) -STATUS_ORDINAL_NOT_FOUND = Flag("STATUS_ORDINAL_NOT_FOUND", ( 0xC0000138L )) -STATUS_ENTRYPOINT_NOT_FOUND = Flag("STATUS_ENTRYPOINT_NOT_FOUND", ( 0xC0000139L )) -STATUS_CONTROL_C_EXIT = Flag("STATUS_CONTROL_C_EXIT", ( 0xC000013AL )) -STATUS_DLL_INIT_FAILED = Flag("STATUS_DLL_INIT_FAILED", ( 0xC0000142L )) -STATUS_FLOAT_MULTIPLE_FAULTS = Flag("STATUS_FLOAT_MULTIPLE_FAULTS", ( 0xC00002B4L )) -STATUS_FLOAT_MULTIPLE_TRAPS = Flag("STATUS_FLOAT_MULTIPLE_TRAPS", ( 0xC00002B5L )) -STATUS_REG_NAT_CONSUMPTION = Flag("STATUS_REG_NAT_CONSUMPTION", ( 0xC00002C9L )) -STATUS_STACK_BUFFER_OVERRUN = Flag("STATUS_STACK_BUFFER_OVERRUN", ( 0xC0000409L )) -STATUS_INVALID_CRUNTIME_PARAMETER = Flag("STATUS_INVALID_CRUNTIME_PARAMETER", ( 0xC0000417L )) -STATUS_ASSERTION_FAILURE = Flag("STATUS_ASSERTION_FAILURE", ( 0xC0000420L )) +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 )) STATUS_POSSIBLE_DEADLOCK = Flag("STATUS_POSSIBLE_DEADLOCK", ( 0xC0000194 )) WAIT_IO_COMPLETION = Flag("WAIT_IO_COMPLETION", STATUS_USER_APC) STILL_ACTIVE = Flag("STILL_ACTIVE", STATUS_PENDING) @@ -176,12 +180,12 @@ TH32CS_SNAPMODULE32 = Flag("TH32CS_SNAPMODULE32", 0x00000010) TH32CS_SNAPALL = Flag("TH32CS_SNAPALL", ( TH32CS_SNAPHEAPLIST | TH32CS_SNAPPROCESS | TH32CS_SNAPTHREAD | TH32CS_SNAPMODULE )) TH32CS_INHERIT = Flag("TH32CS_INHERIT", 0x80000000) CONTEXT_I386 = Flag("CONTEXT_I386", 0x00010000) -CONTEXT_CONTROL = Flag("CONTEXT_CONTROL", 0x00000001L) -CONTEXT_INTEGER = Flag("CONTEXT_INTEGER", 0x00000002L) -CONTEXT_SEGMENTS = Flag("CONTEXT_SEGMENTS", 0x00000004L) -CONTEXT_FLOATING_POINT = Flag("CONTEXT_FLOATING_POINT", 0x00000008L) -CONTEXT_DEBUG_REGISTERS = Flag("CONTEXT_DEBUG_REGISTERS", 0x00000010L) -CONTEXT_EXTENDED_REGISTERS = Flag("CONTEXT_EXTENDED_REGISTERS", 0x00000020L) +CONTEXT_CONTROL = Flag("CONTEXT_CONTROL", 0x00000001) +CONTEXT_INTEGER = Flag("CONTEXT_INTEGER", 0x00000002) +CONTEXT_SEGMENTS = Flag("CONTEXT_SEGMENTS", 0x00000004) +CONTEXT_FLOATING_POINT = Flag("CONTEXT_FLOATING_POINT", 0x00000008) +CONTEXT_DEBUG_REGISTERS = Flag("CONTEXT_DEBUG_REGISTERS", 0x00000010) +CONTEXT_EXTENDED_REGISTERS = Flag("CONTEXT_EXTENDED_REGISTERS", 0x00000020) CONTEXT_FULL = Flag("CONTEXT_FULL", ( CONTEXT_CONTROL | CONTEXT_INTEGER | CONTEXT_SEGMENTS )) CONTEXT_ALL = Flag("CONTEXT_ALL", ( CONTEXT_FULL | CONTEXT_FLOATING_POINT | CONTEXT_DEBUG_REGISTERS | CONTEXT_EXTENDED_REGISTERS )) CONTEXT_FULL = Flag("CONTEXT_FULL", CONTEXT_I386 | CONTEXT_FULL) @@ -221,17 +225,17 @@ SEC_WRITECOMBINE = Flag("SEC_WRITECOMBINE", 0x40000000) SEC_LARGE_PAGES = Flag("SEC_LARGE_PAGES", 0x80000000) MEM_IMAGE = Flag("MEM_IMAGE", SEC_IMAGE) WRITE_WATCH_FLAG_RESET = Flag("WRITE_WATCH_FLAG_RESET", 0x01) -DELETE = Flag("DELETE", ( 0x00010000L )) -READ_CONTROL = Flag("READ_CONTROL", ( 0x00020000L )) -WRITE_DAC = Flag("WRITE_DAC", ( 0x00040000L )) -WRITE_OWNER = Flag("WRITE_OWNER", ( 0x00080000L )) -SYNCHRONIZE = Flag("SYNCHRONIZE", ( 0x00100000L )) -STANDARD_RIGHTS_REQUIRED = Flag("STANDARD_RIGHTS_REQUIRED", ( 0x000F0000L )) +DELETE = Flag("DELETE", ( 0x00010000 )) +READ_CONTROL = Flag("READ_CONTROL", ( 0x00020000 )) +WRITE_DAC = Flag("WRITE_DAC", ( 0x00040000 )) +WRITE_OWNER = Flag("WRITE_OWNER", ( 0x00080000 )) +SYNCHRONIZE = Flag("SYNCHRONIZE", ( 0x00100000 )) +STANDARD_RIGHTS_REQUIRED = Flag("STANDARD_RIGHTS_REQUIRED", ( 0x000F0000 )) STANDARD_RIGHTS_READ = Flag("STANDARD_RIGHTS_READ", ( READ_CONTROL )) STANDARD_RIGHTS_WRITE = Flag("STANDARD_RIGHTS_WRITE", ( READ_CONTROL )) STANDARD_RIGHTS_EXECUTE = Flag("STANDARD_RIGHTS_EXECUTE", ( READ_CONTROL )) -STANDARD_RIGHTS_ALL = Flag("STANDARD_RIGHTS_ALL", ( 0x001F0000L )) -SPECIFIC_RIGHTS_ALL = Flag("SPECIFIC_RIGHTS_ALL", ( 0x0000FFFFL )) +STANDARD_RIGHTS_ALL = Flag("STANDARD_RIGHTS_ALL", ( 0x001F0000 )) +SPECIFIC_RIGHTS_ALL = Flag("SPECIFIC_RIGHTS_ALL", ( 0x0000FFFF )) TOKEN_ASSIGN_PRIMARY = Flag("TOKEN_ASSIGN_PRIMARY", ( 0x0001 )) TOKEN_DUPLICATE = Flag("TOKEN_DUPLICATE", ( 0x0002 )) TOKEN_IMPERSONATE = Flag("TOKEN_IMPERSONATE", ( 0x0004 )) @@ -243,10 +247,10 @@ TOKEN_ADJUST_DEFAULT = Flag("TOKEN_ADJUST_DEFAULT", ( 0x0080 )) TOKEN_ADJUST_SESSIONID = Flag("TOKEN_ADJUST_SESSIONID", ( 0x0100 )) TOKEN_ALL_ACCESS_P = Flag("TOKEN_ALL_ACCESS_P", ( STANDARD_RIGHTS_REQUIRED | TOKEN_ASSIGN_PRIMARY | TOKEN_DUPLICATE | TOKEN_IMPERSONATE | TOKEN_QUERY | TOKEN_QUERY_SOURCE | TOKEN_ADJUST_PRIVILEGES | TOKEN_ADJUST_GROUPS | TOKEN_ADJUST_DEFAULT )) TOKEN_ALL_ACCESS = Flag("TOKEN_ALL_ACCESS", ( TOKEN_ALL_ACCESS_P | TOKEN_ADJUST_SESSIONID )) -SE_PRIVILEGE_ENABLED_BY_DEFAULT = Flag("SE_PRIVILEGE_ENABLED_BY_DEFAULT", ( 0x00000001L )) -SE_PRIVILEGE_ENABLED = Flag("SE_PRIVILEGE_ENABLED", ( 0x00000002L )) -SE_PRIVILEGE_REMOVED = Flag("SE_PRIVILEGE_REMOVED", ( 0X00000004L )) -SE_PRIVILEGE_USED_FOR_ACCESS = Flag("SE_PRIVILEGE_USED_FOR_ACCESS", ( 0x80000000L )) +SE_PRIVILEGE_ENABLED_BY_DEFAULT = Flag("SE_PRIVILEGE_ENABLED_BY_DEFAULT", ( 0x00000001 )) +SE_PRIVILEGE_ENABLED = Flag("SE_PRIVILEGE_ENABLED", ( 0x00000002 )) +SE_PRIVILEGE_REMOVED = Flag("SE_PRIVILEGE_REMOVED", ( 0X00000004 )) +SE_PRIVILEGE_USED_FOR_ACCESS = Flag("SE_PRIVILEGE_USED_FOR_ACCESS", ( 0x80000000 )) AF_UNSPEC = Flag("AF_UNSPEC", 0) AF_UNIX = Flag("AF_UNIX", 1) AF_INET = Flag("AF_INET", 2) @@ -360,4 +364,77 @@ EXCEPTION_POSSIBLE_DEADLOCK = Flag("EXCEPTION_POSSIBLE_DEADLOCK", STATUS_POSSIBL 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 +EXCEPTION_CONTINUE_EXECUTION = Flag("EXCEPTION_CONTINUE_EXECUTION", -1) +GENERIC_READ = Flag("GENERIC_READ", ( 0x80000000 )) +GENERIC_WRITE = Flag("GENERIC_WRITE", ( 0x40000000 )) +GENERIC_EXECUTE = Flag("GENERIC_EXECUTE", ( 0x20000000 )) +GENERIC_ALL = Flag("GENERIC_ALL", ( 0x10000000 )) +FILE_FLAG_WRITE_THROUGH = Flag("FILE_FLAG_WRITE_THROUGH", 0x80000000) +FILE_FLAG_OVERLAPPED = Flag("FILE_FLAG_OVERLAPPED", 0x40000000) +FILE_FLAG_NO_BUFFERING = Flag("FILE_FLAG_NO_BUFFERING", 0x20000000) +FILE_FLAG_RANDOM_ACCESS = Flag("FILE_FLAG_RANDOM_ACCESS", 0x10000000) +FILE_FLAG_SEQUENTIAL_SCAN = Flag("FILE_FLAG_SEQUENTIAL_SCAN", 0x08000000) +FILE_FLAG_DELETE_ON_CLOSE = Flag("FILE_FLAG_DELETE_ON_CLOSE", 0x04000000) +FILE_FLAG_BACKUP_SEMANTICS = Flag("FILE_FLAG_BACKUP_SEMANTICS", 0x02000000) +FILE_FLAG_POSIX_SEMANTICS = Flag("FILE_FLAG_POSIX_SEMANTICS", 0x01000000) +FILE_FLAG_OPEN_REPARSE_POINT = Flag("FILE_FLAG_OPEN_REPARSE_POINT", 0x00200000) +FILE_FLAG_OPEN_NO_RECALL = Flag("FILE_FLAG_OPEN_NO_RECALL", 0x00100000) +FILE_FLAG_FIRST_PIPE_INSTANCE = Flag("FILE_FLAG_FIRST_PIPE_INSTANCE", 0x00080000) +CREATE_NEW = Flag("CREATE_NEW", 1) +CREATE_ALWAYS = Flag("CREATE_ALWAYS", 2) +OPEN_EXISTING = Flag("OPEN_EXISTING", 3) +OPEN_ALWAYS = Flag("OPEN_ALWAYS", 4) +TRUNCATE_EXISTING = Flag("TRUNCATE_EXISTING", 5) +FILE_SHARE_READ = Flag("FILE_SHARE_READ", 0x00000001) +FILE_SHARE_WRITE = Flag("FILE_SHARE_WRITE", 0x00000002) +FILE_SHARE_DELETE = Flag("FILE_SHARE_DELETE", 0x00000004) +FILE_ATTRIBUTE_READONLY = Flag("FILE_ATTRIBUTE_READONLY", 0x00000001) +FILE_ATTRIBUTE_HIDDEN = Flag("FILE_ATTRIBUTE_HIDDEN", 0x00000002) +FILE_ATTRIBUTE_SYSTEM = Flag("FILE_ATTRIBUTE_SYSTEM", 0x00000004) +FILE_ATTRIBUTE_DIRECTORY = Flag("FILE_ATTRIBUTE_DIRECTORY", 0x00000010) +FILE_ATTRIBUTE_ARCHIVE = Flag("FILE_ATTRIBUTE_ARCHIVE", 0x00000020) +FILE_ATTRIBUTE_DEVICE = Flag("FILE_ATTRIBUTE_DEVICE", 0x00000040) +FILE_ATTRIBUTE_NORMAL = Flag("FILE_ATTRIBUTE_NORMAL", 0x00000080) +FILE_ATTRIBUTE_TEMPORARY = Flag("FILE_ATTRIBUTE_TEMPORARY", 0x00000100) +FILE_ATTRIBUTE_SPARSE_FILE = Flag("FILE_ATTRIBUTE_SPARSE_FILE", 0x00000200) +FILE_ATTRIBUTE_REPARSE_POINT = Flag("FILE_ATTRIBUTE_REPARSE_POINT", 0x00000400) +FILE_ATTRIBUTE_COMPRESSED = Flag("FILE_ATTRIBUTE_COMPRESSED", 0x00000800) +FILE_ATTRIBUTE_OFFLINE = Flag("FILE_ATTRIBUTE_OFFLINE", 0x00001000) +FILE_ATTRIBUTE_NOT_CONTENT_INDEXED = Flag("FILE_ATTRIBUTE_NOT_CONTENT_INDEXED", 0x00002000) +FILE_ATTRIBUTE_ENCRYPTED = Flag("FILE_ATTRIBUTE_ENCRYPTED", 0x00004000) +FILE_ATTRIBUTE_VIRTUAL = Flag("FILE_ATTRIBUTE_VIRTUAL", 0x00010000) +FILE_NOTIFY_CHANGE_FILE_NAME = Flag("FILE_NOTIFY_CHANGE_FILE_NAME", 0x00000001) +FILE_NOTIFY_CHANGE_DIR_NAME = Flag("FILE_NOTIFY_CHANGE_DIR_NAME", 0x00000002) +FILE_NOTIFY_CHANGE_ATTRIBUTES = Flag("FILE_NOTIFY_CHANGE_ATTRIBUTES", 0x00000004) +FILE_NOTIFY_CHANGE_SIZE = Flag("FILE_NOTIFY_CHANGE_SIZE", 0x00000008) +FILE_NOTIFY_CHANGE_LAST_WRITE = Flag("FILE_NOTIFY_CHANGE_LAST_WRITE", 0x00000010) +FILE_NOTIFY_CHANGE_LAST_ACCESS = Flag("FILE_NOTIFY_CHANGE_LAST_ACCESS", 0x00000020) +FILE_NOTIFY_CHANGE_CREATION = Flag("FILE_NOTIFY_CHANGE_CREATION", 0x00000040) +FILE_NOTIFY_CHANGE_SECURITY = Flag("FILE_NOTIFY_CHANGE_SECURITY", 0x00000100) +FILE_ACTION_ADDED = Flag("FILE_ACTION_ADDED", 0x00000001) +FILE_ACTION_REMOVED = Flag("FILE_ACTION_REMOVED", 0x00000002) +FILE_ACTION_MODIFIED = Flag("FILE_ACTION_MODIFIED", 0x00000003) +FILE_ACTION_RENAMED_OLD_NAME = Flag("FILE_ACTION_RENAMED_OLD_NAME", 0x00000004) +FILE_ACTION_RENAMED_NEW_NAME = Flag("FILE_ACTION_RENAMED_NEW_NAME", 0x00000005) +MAILSLOT_NO_MESSAGE = Flag("MAILSLOT_NO_MESSAGE", ( -1 )) +MAILSLOT_WAIT_FOREVER = Flag("MAILSLOT_WAIT_FOREVER", ( -1 )) +FILE_CASE_SENSITIVE_SEARCH = Flag("FILE_CASE_SENSITIVE_SEARCH", 0x00000001) +FILE_CASE_PRESERVED_NAMES = Flag("FILE_CASE_PRESERVED_NAMES", 0x00000002) +FILE_UNICODE_ON_DISK = Flag("FILE_UNICODE_ON_DISK", 0x00000004) +FILE_PERSISTENT_ACLS = Flag("FILE_PERSISTENT_ACLS", 0x00000008) +FILE_FILE_COMPRESSION = Flag("FILE_FILE_COMPRESSION", 0x00000010) +FILE_VOLUME_QUOTAS = Flag("FILE_VOLUME_QUOTAS", 0x00000020) +FILE_SUPPORTS_SPARSE_FILES = Flag("FILE_SUPPORTS_SPARSE_FILES", 0x00000040) +FILE_SUPPORTS_REPARSE_POINTS = Flag("FILE_SUPPORTS_REPARSE_POINTS", 0x00000080) +FILE_SUPPORTS_REMOTE_STORAGE = Flag("FILE_SUPPORTS_REMOTE_STORAGE", 0x00000100) +FILE_VOLUME_IS_COMPRESSED = Flag("FILE_VOLUME_IS_COMPRESSED", 0x00008000) +FILE_SUPPORTS_OBJECT_IDS = Flag("FILE_SUPPORTS_OBJECT_IDS", 0x00010000) +FILE_SUPPORTS_ENCRYPTION = Flag("FILE_SUPPORTS_ENCRYPTION", 0x00020000) +FILE_NAMED_STREAMS = Flag("FILE_NAMED_STREAMS", 0x00040000) +FILE_READ_ONLY_VOLUME = Flag("FILE_READ_ONLY_VOLUME", 0x00080000) +FILE_SEQUENTIAL_WRITE_ONCE = Flag("FILE_SEQUENTIAL_WRITE_ONCE", 0x00100000) +FILE_SUPPORTS_TRANSACTIONS = Flag("FILE_SUPPORTS_TRANSACTIONS", 0x00200000) +FILE_SUPPORTS_HARD_LINKS = Flag("FILE_SUPPORTS_HARD_LINKS", 0x00400000) +FILE_SUPPORTS_EXTENDED_ATTRIBUTES = Flag("FILE_SUPPORTS_EXTENDED_ATTRIBUTES", 0x00800000) +FILE_SUPPORTS_OPEN_BY_FILE_ID = Flag("FILE_SUPPORTS_OPEN_BY_FILE_ID", 0x01000000) +FILE_SUPPORTS_USN_JOURNAL = Flag("FILE_SUPPORTS_USN_JOURNAL", 0x02000000) \ No newline at end of file diff --git a/generated_def/winfuncs.py b/generated_def/winfuncs.py index 30ecab6..7b106c3 100644 --- a/generated_def/winfuncs.py +++ b/generated_def/winfuncs.py @@ -1,9 +1,9 @@ #Generated file from ctypes import * from ctypes.wintypes import * -from winstructs import * +from .winstructs import * -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'] +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', 'GetThreadId'] # ExitProcess(uExitCode): ExitProcessPrototype = WINFUNCTYPE(VOID, UINT) @@ -265,3 +265,7 @@ SuspendThreadParams = ((1, 'hThread'),) WaitForSingleObjectPrototype = WINFUNCTYPE(DWORD, HANDLE, DWORD) WaitForSingleObjectParams = ((1, 'hHandle'), (1, 'dwMilliseconds')) +# GetThreadId(Thread): +GetThreadIdPrototype = WINFUNCTYPE(DWORD, HANDLE) +GetThreadIdParams = ((1, 'Thread'),) + diff --git a/generated_def/winstructs.py b/generated_def/winstructs.py index e0a156d..588ad83 100644 --- a/generated_def/winstructs.py +++ b/generated_def/winstructs.py @@ -1,7 +1,7 @@ #Generated file from ctypes import * from ctypes.wintypes import * -from windef import * +from .windef import * PWSTR = LPWSTR SIZE_T = c_ulong @@ -28,7 +28,7 @@ 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', '_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'] +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', '_IMAGE_EXPORT_DIRECTORY', '_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', '_DEBUG_PROCESSOR_IDENTIFICATION_X86'] enums = ['_SYSTEM_INFORMATION_CLASS', '_TCP_TABLE_CLASS', '_UDP_TABLE_CLASS', '_MIB_TCP_STATE'] @@ -157,53 +157,22 @@ 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 @@ -231,7 +200,6 @@ 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), @@ -467,6 +435,24 @@ class _IMAGE_IMPORT_BY_NAME(Structure): PIMAGE_IMPORT_BY_NAME = POINTER(_IMAGE_IMPORT_BY_NAME) IMAGE_IMPORT_BY_NAME = _IMAGE_IMPORT_BY_NAME +# Struct _IMAGE_EXPORT_DIRECTORY definitions +class _IMAGE_EXPORT_DIRECTORY(Structure): + _fields_ = [ + ("Characteristics", DWORD), + ("TimeDateStamp", DWORD), + ("MajorVersion", WORD), + ("MinorVersion", WORD), + ("Name", DWORD), + ("Base", DWORD), + ("NumberOfFunctions", DWORD), + ("NumberOfNames", DWORD), + ("AddressOfFunctions", DWORD), + ("AddressOfNames", DWORD), + ("AddressOfNameOrdinals", DWORD), + ] +IMAGE_EXPORT_DIRECTORY = _IMAGE_EXPORT_DIRECTORY +PIMAGE_EXPORT_DIRECTORY = POINTER(_IMAGE_EXPORT_DIRECTORY) + # Struct _MEMORY_BASIC_INFORMATION definitions class _MEMORY_BASIC_INFORMATION(Structure): _fields_ = [ @@ -959,3 +945,14 @@ class _EXCEPTION_POINTERS32(Structure): PEXCEPTION_POINTERS32 = POINTER(_EXCEPTION_POINTERS32) EXCEPTION_POINTERS32 = _EXCEPTION_POINTERS32 +# Struct _DEBUG_PROCESSOR_IDENTIFICATION_X86 definitions +class _DEBUG_PROCESSOR_IDENTIFICATION_X86(Structure): + _fields_ = [ + ("Family", ULONG), + ("Model", ULONG), + ("Stepping", ULONG), + ("VendorString", CHAR * 16), + ] +DEBUG_PROCESSOR_IDENTIFICATION_X86 = _DEBUG_PROCESSOR_IDENTIFICATION_X86 +PDEBUG_PROCESSOR_IDENTIFICATION_X86 = POINTER(_DEBUG_PROCESSOR_IDENTIFICATION_X86) + diff --git a/hooks.py b/hooks.py index 9f5f277..9ebb4fc 100644 --- a/hooks.py +++ b/hooks.py @@ -1,10 +1,10 @@ import sys -import utils -import native_exec import ctypes -import windows.generated_def.winfuncs as winfuncs -import windows.generated_def.windef as windef -from windows.generated_def.winstructs import * + +import windows.utils as utils +from . import native_exec +from .generated_def import winfuncs, windef +from .generated_def.winstructs import * class Callback(object): diff --git a/injection.py b/injection.py index d55c03e..12c17d8 100644 --- a/injection.py +++ b/injection.py @@ -1,86 +1,91 @@ import sys import os -import utils -import windows +import windows +import windows.utils as utils + +from .native_exec import simple_x86 as x86 +from .native_exec import simple_x64 as x64 # 32 to 32 injection def generate_python_exec_shellcode_32(PYDLL_addr, PyInit, PyRun, PYCODE_ADDR): - from native_exec.simple_x86 import * LoadLibraryA = utils.get_func_addr('kernel32', 'LoadLibraryA') GetProcAddress = utils.get_func_addr('kernel32', 'GetProcAddress') - code = MultipleInstr() + print("LoadLibraryA = {0}".format(hex(LoadLibraryA))) + print("LoadLibraryA = {0}".format(hex(GetProcAddress))) + + code = x86.MultipleInstr() # Load python27.dll - code += Push_X(PYDLL_addr) - code += Mov_EAX_X(LoadLibraryA) - code += Call_EAX() + code += x86.Push(PYDLL_addr) + code += x86.Mov('EAX', LoadLibraryA) + code += x86.Call('EAX') # Get PyInit function into pythondll - code += Push_EAX() - code += Pop_EDI() - code += Push_X(PyInit) - code += Push_EDI() - code += Mov_EBX_X(GetProcAddress) - code += Call_EBX() + code += x86.Push('EAX') + code += x86.Pop('EDI') + code += x86.Push(PyInit) + code += x86.Push('EDI') + code += x86.Mov('EBX', GetProcAddress) + code += x86.Call('EBX') # Call PyInit - code += Call_EAX() + code += x86.Call('EAX') # Get PyRun function into pythondll - code += Push_X(PyRun) - code += Push_EDI() - code += Call_EBX() + code += x86.Push(PyRun) + code += x86.Push('EDI') + code += x86.Call('EBX') # Call PyRun with python code to exec - code += Push_X(PYCODE_ADDR) - code += Call_EAX() - code += Pop_EDI() - code += Ret() + code += x86.Push(PYCODE_ADDR) + code += x86.Call('EAX') + code += x86.Pop('EDI') + code += x86.Ret() return code.get_code() # 64 to 64 injection def generate_python_exec_shellcode_64(PYDLL_addr, PyInit, PyRun, PYCODE_ADDR): - from native_exec.simple_x64 import * + LoadLibraryA = utils.get_func_addr('kernel32', 'LoadLibraryA') GetProcAddress = utils.get_func_addr('kernel32', 'GetProcAddress') - Reserve_space_for_call = MultipleInstr([Push_RDI()] * 4) - Clean_space_for_call = MultipleInstr([Pop_RDI()] * 4) + Reserve_space_for_call = x64.MultipleInstr([x64.Push('RDI')] * 4) + Clean_space_for_call = x64.MultipleInstr([x64.Pop('RDI')] * 4) - code = MultipleInstr() + code = x64.MultipleInstr() # Do stack alignement - code += Push_RAX() + code += x64.Push('RAX') # Load python27.dll - code += Mov_RCX_X(PYDLL_addr) - code += Mov_RAX_X(LoadLibraryA) + code += x64.Mov('RCX', PYDLL_addr) + code += x64.Mov('RAX', LoadLibraryA) code += Reserve_space_for_call - code += Call_RAX() + code += x64.Call('RAX') code += Clean_space_for_call - code += Push_RAX() - code += Pop_RCX() + code += x64.Push('RAX') + code += x64.Pop('RCX') # Save RCX - code += Push_RCX() + code += x64.Push('RCX') # Align stack - code += Push_RDI() + code += x64.Push('RDI') # Get PyInit function into pythondll code += Reserve_space_for_call - code += Mov_RDX_X(PyInit) - code += Mov_RBX_X(GetProcAddress) - code += Call_RBX() + code += x64.Mov('RDX', PyInit) + code += x64.Mov('RBX', GetProcAddress) + code += x64.Call('RBX') # Call PyInit - code += Call_RAX() + code += x64.Call('RAX') code += Clean_space_for_call # Remove Stack align - code += Pop_RDI() + code += x64.Pop('RDI') # Restore pythondll base into rcx - code += Pop_RCX() + code += x64.Pop('RCX') # Get PyRun function into pythondll - code += Mov_RDX_X(PyRun) + code += x64.Mov('RDX', PyRun) code += Reserve_space_for_call - code += Call_RBX() + code += x64.Call('RBX') # Call PyInit with python code to exec - code += Mov_RCX_X(PYCODE_ADDR) - code += Call_RAX() + code += x64.Mov('RCX', PYCODE_ADDR) + code += x64.Call('RAX') code += Clean_space_for_call # Remove stack alignement - code += Pop_RAX() - code += Ret() + code += x64.Pop('RAX') + code += x64.Ret() return code.get_code() diff --git a/k32testing.py b/k32testing.py index 7f4aaab..0c1f377 100644 --- a/k32testing.py +++ b/k32testing.py @@ -45,12 +45,24 @@ class IphlpapiError(Kernel32Error): def no_error_check(func_name, result, func, args): """Nothing special""" return args + +def minus_one_error_check(func_name, result, func, args): + if result == -1: + raise Kernel32Error(func_name) + return args + def kernel32_error_check(func_name, result, func, args): """raise Kernel32Error if result is 0""" if not result: raise Kernel32Error(func_name) return args + +def kernel32_zero_check(func_name, result, func, args): + """raise Kernel32Error if result is NOT 0""" + if result: + raise Kernel32Error(func_name) + return args def iphlpapi_error_check(func_name, result, func, args): """raise IphlpapiError if result is NOT 0""" @@ -145,8 +157,14 @@ GetCurrentThreadId = TransparentKernel32Proxy("GetCurrentThreadId") TerminateThread = TransparentKernel32Proxy("TerminateThread") ExitThread = TransparentKernel32Proxy("ExitThread") -SuspendThread = TransparentKernel32Proxy("SuspendThread") -ResumeThread = TransparentKernel32Proxy("ResumeThread") +SuspendThread = TransparentKernel32Proxy("SuspendThread", minus_one_error_check) +ResumeThread = TransparentKernel32Proxy("ResumeThread", minus_one_error_check) +GetThreadId = TransparentKernel32Proxy("GetThreadId") + + +@Kernel32Proxy("CreateFileA") +def CreateFileA(lpFileName, dwDesiredAccess, dwShareMode=0, lpSecurityAttributes=None, dwCreationDisposition=OPEN_EXISTING, dwFlagsAndAttributes=FILE_ATTRIBUTE_NORMAL, hTemplateFile=None): + return CreateFileA.ctypes_function(lpFileName, dwDesiredAccess, dwShareMode, lpSecurityAttributes, dwCreationDisposition, dwFlagsAndAttributes, hTemplateFile) # This kind of function could be fully done by using paramflags @Kernel32Proxy("VirtualAlloc") @@ -292,7 +310,7 @@ def AddVectoredExceptionHandler(FirstHandler=1, VectoredHandler=NeededParameter) def RemoveVectoredExceptionHandler(Handler): return RemoveVectoredExceptionHandler.ctypes_function(Handler) -@Kernel32Proxy("WaitForSingleObject", no_error_check) +@Kernel32Proxy("WaitForSingleObject", kernel32_zero_check) def WaitForSingleObject(hHandle, dwMilliseconds=INFINITE): return WaitForSingleObject.ctypes_function(hHandle, dwMilliseconds) diff --git a/native_exec/__init__.py b/native_exec/__init__.py index 07897c5..804edf7 100644 --- a/native_exec/__init__.py +++ b/native_exec/__init__.py @@ -1 +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 437132e..f11fbaf 100644 --- a/native_exec/native_function.py +++ b/native_exec/native_function.py @@ -5,6 +5,9 @@ import platform import windows import windows.k32testing as k32api +from . import simple_x86 as x86 +#from . import simple_x64 as x64 + class PyObj(ctypes.Structure): _fields_ = [("ob_refcnt", ctypes.c_size_t), ("ob_type", ctypes.c_void_p)] #must be cast @@ -116,7 +119,7 @@ def analyse_callback(callback): # For windows 32 bits with stdcall def generate_stub_32(callback): - from simple_x86 import * + allocator = windows.current_process.allocator obj_id = analyse_callback(callback) @@ -124,65 +127,64 @@ def generate_stub_32(callback): gstate_save_addr = allocator.reserve_int() return_addr_save_addr = allocator.reserve_int() - save_ebx = allocator.reserve_int() - save_ecx = allocator.reserve_int() - save_edx = allocator.reserve_int() - save_esi = allocator.reserve_int() - save_edi = allocator.reserve_int() + save_ebx = x86.create_displacement(disp=allocator.reserve_int()) + save_ecx = x86.create_displacement(disp=allocator.reserve_int()) + save_edx = x86.create_displacement(disp=allocator.reserve_int()) + save_esi = x86.create_displacement(disp=allocator.reserve_int()) + save_edi = x86.create_displacement(disp=allocator.reserve_int()) ensure, objcall, release = get_functions() + code = x86.MultipleInstr() + ### Shellcode ### - code = MultipleInstr() - - code += Mov_DX_EBX(save_ebx) - code += Mov_DX_ECX(save_ecx) - code += Mov_DX_EDX(save_edx) - code += Mov_DX_ESI(save_esi) - code += Mov_DX_EDI(save_edi) - - code += Mov_EAX_X(ensure) - code += Call_EAX() - code += Mov_DX_EAX(gstate_save_addr) - + code += x86.Mov(save_ebx, 'EBX') + code += x86.Mov(save_ecx, 'ECX') + code += x86.Mov(save_edx, 'EDX') + code += x86.Mov(save_esi, 'ESI') + code += x86.Mov(save_edi, 'EDI') + + code += x86.Mov('EAX', ensure) + code += x86.Call('EAX') + code += x86.Mov(gstate_save_addr, 'EAX') + #Save real return addr (for good argument parsing by the callback) - - code += Pop_EAX() - code += Mov_DX_EAX(return_addr_save_addr) - + + code += x86.Pop('EAX') + code += x86.Mov(return_addr_save_addr, 'EAX') + # Set call_real_function to 0 (no call by default) - - code += Mov_EAX_X(c_callback) - code += Call_EAX() - + + code += x86.Mov('EAX', c_callback) + code += x86.Call('EAX') + # Restore real return value - code += Mov_EBX_DX(return_addr_save_addr) - code += Push_EBX() - + code += x86.Mov('EBX', return_addr_save_addr) + code += x86.Push('EBX') + # Save return value - code += Push_EAX() - code += Mov_EBX_DX(gstate_save_addr) - code += Push_EBX() - - code += Mov_EAX_X(release) - code += Call_EAX() - + code += x86.Push('EAX') + code += x86.Mov('EBX', gstate_save_addr) + code += x86.Push('EBX') + + code += x86.Mov('EAX', release) + code += x86.Call('EAX') + # Discard `release` argument - code += Pop_EAX() + code += x86.Pop('EAX') # Restore return value - code += Pop_EAX() - code += Mov_EBX_DX(save_ebx) - code += Mov_ECX_DX(save_ecx) - code += Mov_EDX_DX(save_edx) - code += Mov_ESI_DX(save_esi) - code += Mov_EDI_DX(save_edi) - code += Ret() + code += x86.Pop('EAX') + code += x86.Mov('EBX', save_ebx) + code += x86.Mov('ECX', save_ecx) + code += x86.Mov('EDX', save_edx) + code += x86.Mov('ESI', save_esi) + code += x86.Mov('EDI', save_edi) + code += x86.Ret() return code # For windows 32 bits with stdcall def generate_stub_64(callback): - import simple_x64 as x64 - from simple_x64 import * + allocator = windows.current_process.allocator obj_id = analyse_callback(callback) @@ -191,8 +193,9 @@ def generate_stub_64(callback): c_callback = ctypes.c_ulong.from_address(id(callback._objects['0']) + 3 * ctypes.sizeof(ctypes.c_void_p)).value register_to_save = ("RBX", "RCX", "RDX", "RSI", "RDI", "R8", "R9", "R10", "R11", "R12", "R13", "R14", "R15") - push_all_save_register = MultipleInstr([getattr(x64, "Push_" + reg)() for reg in register_to_save]) - pop_all_save_register = MultipleInstr([getattr(x64, "Pop_" + reg)() for reg in reversed(register_to_save)]) + + push_all_save_register = x64.MultipleInstr([x64.Push(reg) for reg in register_to_save]) + pop_all_save_register = x64.MultipleInstr([x64.Pop(reg) for reg in register_to_save]) # Reserve parallel `stack` save_register_space = allocator.reserve_int(len(register_to_save) + 1) save_register_space += REG_LEN # The + 1 is for the second-stack xchg @@ -208,94 +211,94 @@ def generate_stub_64(callback): save_r9 = save_register_space_end - REG_LEN - (REG_LEN * 6) - gstate_save_addr = allocator.reserve_int() - return_addr_save_addr = allocator.reserve_int() - return_value_save_addr = allocator.reserve_int() - - Reserve_space_for_call = MultipleInstr([Push_RDI()] * 4) - Clean_space_for_call = MultipleInstr([Pop_RDI()] * 4) - Do_stack_alignement = MultipleInstr([Push_RDI()] * 1) - Remove_stack_alignement = MultipleInstr([Pop_RDI()] * 1) + gstate_save_addr = create_displacement(disp=allocator.reserve_int()) + return_addr_save_addr = create_displacement(disp=allocator.reserve_int()) + return_value_save_addr = create_displacement(disp=allocator.reserve_int()) + Reserve_space_for_call = x64.MultipleInstr([Push('RDI')] * 4) + Clean_space_for_call = x64.MultipleInstr([Pop('RDI')] * 4) + Do_stack_alignement = MultipleInstr([x64.Push('RDI')] * 1) + Remove_stack_alignement = MultipleInstr([x64.Pop('RDI')] * 1) ensure, objcall, release = get_functions() ### Shellcode ### code = MultipleInstr() - code += Mov_RAX_X(save_register_space_end) + code += x64.Mov('RAX', save_register_space_end) # A lazy working xchg RSP <-> RAX - code += Push_RAX() - code += Push_RSP() - code += Pop_RAX() - code += Pop_RSP() + code += x64.Push('RAX') + code += x64.Push('RSP') + code += x64.Pop('RAX') + code += x64.Pop('RSP') code += push_all_save_register # Re-set RSP to its real value - code += Push_RAX() - code += Pop_RSP() + code += x64.Push('RAX') + code += x64.Pop('RSP') - code += Pop_RAX() # Remove the Push_RAX of lazy xchg + code += x64.Pop('RAX') # Remove the Push_RAX of lazy xchg # GOOO - code += Mov_RAX_X(ensure) + code += x64.Mov('RAX', ensure) code += Reserve_space_for_call code += Do_stack_alignement - code += Call_RAX() + code += x64.Call('RAX') code += Remove_stack_alignement code += Clean_space_for_call - code += Mov_DX_RAX(gstate_save_addr) + code += x64.Mov(gstate_save_addr, 'RAX') #Save real return addr (for good argument parsing by the callback) - code += Pop_RAX() - code += Mov_DX_RAX(return_addr_save_addr) + code += x64.Pop('RAX') + code += x64.Mov(return_addr_save_addr, 'RAX') # Restore parameters for real function call - code += Mov_RAX_X(save_rcx) - code += Mov_RCX_DRAX() - code += Mov_RAX_X(save_rdx) - code += Mov_RDX_DRAX() - code += Mov_RAX_X(save_r8) - code += Mov_R8_DRAX() - code += Mov_RAX_X(save_r9) - code += Mov_R9_DRAX() + code += x64.Mov('RAX', save_rcx) + code += x64.Mov('RCX', x64.create_displacement('RAX')) + code += x64.Mov('RAX', save_rdx) + code += x64.Mov('RDX', x64.create_displacement('RAX')) + code += x64.Mov('RAX', save_r8) + code += x64.Mov('R9', x64.create_displacement('RAX')) + code += x64.Mov('RAX', save_r9) + code += x64.Mov('R8', x64.create_displacement('RAX')) # Call python code - code += Mov_RAX_X(c_callback) + code += x64.Mov('RAX', c_callback) code += Reserve_space_for_call - code += Call_RAX() # no need for stack alignement here as we poped the return addr + code += x64.Call('RAX') # no need for stack alignement here as we poped the return addr code += Clean_space_for_call # Save return value - code += Mov_DX_RAX(return_value_save_addr) - code += Mov_RAX_DX(return_addr_save_addr) + code += x64.Mov(return_value_save_addr, 'RAX') # Repush real return value - code += Push_RAX() - code += Mov_RAX_DX(gstate_save_addr) - code += Push_RAX() - code += Pop_RCX() - code += Mov_RAX_X(release) + code += x64.Mov('RAX', return_addr_save_addr) + code += x64.Push('RAX') + # Call release(gstate_save) + code += x64.Mov_RAX_DX('RAX', gstate_save_addr) + code += x64.Push('RAX') + code += x64.Pop('RCX') + code += x64.Mov('RAX', release) code += Reserve_space_for_call code += Do_stack_alignement - code += Call_RAX() + code += x64.Call('RAX') code += Remove_stack_alignement code += Clean_space_for_call # Restore registers - code += Mov_RAX_X(save_register_space) + code += x64.Mov('RAX', save_register_space) # A lazy working xchg RSP <-> RAX - code += Push_RAX() - code += Push_RSP() - code += Pop_RAX() - code += Pop_RSP() + code += x64.Push('RAX') + code += x64.Push('RSP') + code += x64.Pop('RAX') + code += x64.Pop('RSP') code += pop_all_save_register # Re-set RSP to its real value - code += Push_RAX() - code += Pop_RSP() - code += Pop_RAX() # Remove the Push_RAX of lazy xchg + code += x64.Push('RAX') + code += x64.Pop('RSP') + code += x64.Pop('RAX') # Remove the Push_RAX of lazy xchg # Restore return value - code += Mov_RAX_DX(return_value_save_addr) - code += Ret() + code += x64.Mov_RAX_DX('RAX', return_value_save_addr) + code += x64.Ret() return code diff --git a/native_exec/simple_x64.py b/native_exec/simple_x64.py index 7199bed..0e500a0 100644 --- a/native_exec/simple_x64.py +++ b/native_exec/simple_x64.py @@ -1,254 +1,355 @@ -# You are going to see the most shameful code ever ! -# Yes this a a copy of x86 :D - +import collections import struct import sys -import codecs -from .simple_x86 import MultipleInstr -# This code should really be rewritten.. - -this_module = sys.modules[__name__] - -generated_instruction = [] - -long = int - -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)) +class BitArray(object): + def __init__(self, size, bits): + self.size = size + if len(bits) > size: + raise ValueError("size > len(bits)") + + bits_list = [] + for bit in bits: + x = int(bit) + if x not in [0, 1]: + raise ValueError("Not expected bits value {0}".format(x)) + bits_list.append(x) + + self.array = bits_list + if size > len(self.array): + self.array = ([0] * (size - len(self.array))) + self.array + + def dump(self): + res = [] + for i in range(self.size // 8): + c = 0 + for x in (self.array[i * 8: (i + 1) * 8]): + c = (c << 1) + x + res.append(c) + return bytearray((res)) - this_module.__doc__ = "\n".join(doc_lines) - -def decode_hex(s): - return codecs.decode(s.encode(), "hex").decode() - -def encode_hex(s): - return codecs.encode(s.encode(), "hex").decode() - -reg_order = ['EAX', 'ECX', 'EDX', 'EBX', 'ESP', 'EBP', 'ESI', 'EDI'] -reg_opcode = {v : format(i, "03b") for i, v in enumerate(reg_order)} + def __getitem__(self, slice): + return self.array[slice] + + def __setitem__(self, slice, value): + self.array[slice] = value + return True + + def __repr__(self): + return repr(self.array) + + def __add__(self, other): + if not isinstance(other, BitArray): + return NotImplemented + return BitArray(self.size + other.size, self.array + other.array) + + def __or__(self, other): + if not isinstance(other, BitArray): + return NotImplemented + if self.size != other.size: + raise ValueError("OR ON DIFF SIZE") + new_array = [(x | y) for x,y in zip(self.array, other.array)] + return BitArray(self.size, new_array) + + def to_int(self): + return int("".join([str(i) for i in self.array]), 2) + @classmethod + def from_string(cls): + l = [] + for c in bytearray(reversed(str_base)): + for i in range(8): + l.append(c & 1) + c = c >> 1 + self.array = l + @classmethod + def from_int(cls, size, x): + if x < 0: + x = x & ((2 ** size) - 1) + return cls(size, bin(x)[2:]) + +# Rules: bytes only !!!! + reg_order = ['RAX', 'RCX', 'RDX', 'RBX', 'RSP', 'RBP', 'RSI', 'RDI'] -reg_opcode = {v : format(i, "03b") for i, v in enumerate(reg_order)} new_reg_order = ['R8', 'R9', 'R10', 'R11', 'R12', 'R13', 'R14', 'R15'] -new_reg_opcode = {v : format(i, "03b") for i, v in enumerate(new_reg_order)} -all_regs = dict(reg_opcode) -all_regs.update(new_reg_opcode) -bin_h48 = bin(0x48)[2:] -class X64Instruction(object): - mnemo = "" - code = "" - biding = 0 + +x64_regs = reg_order + new_reg_order + +mem_access = collections.namedtuple('mem_access', ['base', 'index', 'squale', 'disp']) + + + +def create_displacement(base=None, index=None, squale=None, disp=0): + return mem_access(base, index, squale, disp) + + +class X64RegisterSelector(object): + + reg_opcode = {v : BitArray.from_int(size=3, x=i) for i, v in enumerate(reg_order)} + new_reg_opcode = {v : BitArray.from_int(size=3, x=i) for i, v in enumerate(new_reg_order)} - def __init__(self, *bind_values): - if len(bind_values) != self.biding: - raise ValueError("{0} expect {1} values got {2}".format(self.__class__.__name__, self.biding, len(bind_values))) - self.bind_values = bind_values - for i, v in enumerate(bind_values): - if not isinstance(v, (int, long)): - raise ValueError("{0} bindings must be 'int' got '{1}' instead".format(self.__class__.__name__, type(v).__name__)) - if not 0 <= v <= 0xffffffffffffffff: - raise ValueError("{0} bindings must be between 0 and 0xffffffffffffffff".format(self.__class__.__name__)) - - def get_unbinded_code(self): - return decode_hex(self.code.replace(" ", "")) + def accept_arg(self, previous, args): + x = args[0] + try: + return (1, self.reg_opcode[x], None) + except KeyError: + pass + try: + return (1, self.new_reg_opcode[x], BitArray.from_int(8, 0x41)) + except KeyError: + return (None, None, None) + + @classmethod + def get_reg_bits(cls, name): + try: + return cls.reg_opcode[name] + except KeyError: + return cls.new_reg_opcode[name] + +class RawBits(BitArray): + def accept_arg(self, previous, args): + return (0, self, None) + +class Imm64(object): + def accept_arg(self, previous, args): + try: + x = int(args[0]) + return (1, BitArray.from_int(64, X64.to_little_endian(x)), None) + except TypeError: + return (None, None, None) + +class Mov_RAX_OFF64(object): + def accept_arg(self, previous, args): + if args[0] != "RAX": + return (None, None, None) + arg2 = args[1] + if not (X64.is_mem_acces(arg2) and X64.mem_access_has_only(arg2, ["disp"])): + return (None, None, None) + return (2, BitArray.from_int(8, 0xa1) + BitArray.from_int(64, X64.to_little_endian(arg2.disp)) , BitArray.from_int(8, 0x48)) + +class Mov_OFF64_RAX(object): + def accept_arg(self, previous, args): + if args[1] != "RAX": + return (None, None, None) + arg2 = args[0] + if not (X64.is_mem_acces(arg2) and X64.mem_access_has_only(arg2, ["disp"])): + return (None, None, None) + return (2, BitArray.from_int(8, 0xa3) + BitArray.from_int(64, X64.to_little_endian(arg2.disp)) , BitArray.from_int(8, 0x48)) + + +class ModRM(object): + size = 8 + + def __init__(self, *sub_modrm): + self.sub = sub_modrm + + def accept_arg(self, previous, args): + if len(args) < 2: + raise ValueError("Missing arg for modrm") + arg1 = args[0] + arg2 = args[1] + for sub in self.sub: + #import pdb;pdb.set_trace() + if sub.match(arg1, arg2): + d = sub(arg1, arg2, 0) + previous[0][-2] = d.direction + rex = d.rex if d.is_rex_needed else None + return (2, d.mod + d.reg + d.rm + d.after, rex) + elif (not hasattr(sub, "refuse_reverse")) and sub.match(arg2, arg1): + d = sub(arg2, arg1, 1) + previous[0][-2] = d.direction + rex = d.rex if d.is_rex_needed else None + return (2, d.mod + d.reg + d.rm + d.after, rex) + return (None, None, None) + +class RexByte(BitArray): + def __init__(self): + super(RexByte, self).__init__(8, "") + self.is_needed = False + +class X64(object): + @staticmethod + def is_reg(name): + return name in x64_regs + + @staticmethod + def is_new_reg(name): + return name in new_reg_order + + @staticmethod + def is_mem_acces(data): + return isinstance(data, mem_access) + + @staticmethod + def mem_access_has_only(mem_access, names): + if not X64.is_mem_acces(mem_access): + raise ValueError("mem_access_has_only") + for f in mem_access._fields: + if getattr(mem_access, f) and f not in names: + return False + if "base" in names and mem_access.base is None: + return False + return True + + @staticmethod + def to_little_endian(i): + i = i & 0xffffffffffffffff + return struct.unpack("Q", i))[0] + +# Sub ModRM encoding + +class RexByte(object): + def __init__(self): + self.is_needed = False + self.pattern = BitArray(4, "0100") + self.w = BitArray(1, "0") + self.r = BitArray(1, "0") + self.x = BitArray(1, "0") + self.b = BitArray(1, "0") + + +class SubModRM(object): + def __init__(self): + self.mod = BitArray(2, "") + self.reg = BitArray(3, "") + self.rm = BitArray(3, "") + self.after = BitArray(0, "") + self.rex = BitArray(8, "01000000") + self.is_rex_needed = False + self.direction = 0 + + def setup_reg_as_register(self, name): + self.reg = X64RegisterSelector.get_reg_bits(name) + if X64.is_new_reg(name): + self.is_rex_needed = True + self.rex[5] = 1 + + def setup_rm_as_register(self, name): + self.rm = X64RegisterSelector.get_reg_bits(name) + if X64.is_new_reg(name): + self.is_rex_needed = True + self.rex[7] = 1 + +class ModRM_REG64__REG64(SubModRM): + @classmethod + def match(cls, arg1, arg2): + return X64.is_reg(arg1) and X64.is_reg(arg2) + + def __init__(self, arg1, arg2, reversed): + super(ModRM_REG64__REG64, self).__init__() + self.mod = BitArray(2, "11") + self.is_rex_needed = True + self.rex[4] = 1 + self.setup_reg_as_register(arg2) + self.setup_rm_as_register(arg1) + self.direction = 0 + +#class ModRM_REG__DEREF_IMM(SubModRM): +# @classmethod +# def match(cls, arg1, arg2): +# return X64.is_reg(arg1) and X64.is_mem_acces(arg2) and X64.mem_access_has_only(arg2, ["disp"]) +# +# def __init__(self, arg1, arg2, reversed): +# super(ModRM_REG__DEREF_IMM, self).__init__() +# self.mod = BitArray(2, "00") +# self.setup_reg_as_register(arg1) +# self.rm = BitArray(3, "101") +# self.after = BitArray.from_int(64, X64.to_little_endian(arg2.disp)) +# self.direction = not reversed + + +class ModRM_REG__DEREF_REG(SubModRM): + @classmethod + def match(cls, arg1, arg2): + return X64.is_reg(arg1) and X64.is_mem_acces(arg2) and X64.mem_access_has_only(arg2, ["base"]) and arg2.base not in ["RSP", "RBP"] + + def __init__(self, arg1, arg2, reversed): + super(ModRM_REG__DEREF_REG, self).__init__() + self.mod = BitArray(2, "00") + self.is_rex_needed = True + self.rex[4] = 1 + self.setup_reg_as_register(arg1) + self.setup_rm_as_register(arg2.base) + self.after = BitArray(0, "") + self.direction = not reversed +# +#class ModRM_REG__DEREF_REG_IMM(object): +# @classmethod +# def match(cls, arg1, arg2): +# return X86.is_reg(arg1) and X86.is_mem_acces(arg2) and X86.mem_access_has_only(arg2, ["base", "disp"]) +# +# def __init__(self, arg1, arg2, reversed): +# self.mod = BitArray(2, "10") +# self.reg = X86RegisterSelector.get_reg_bits(arg1) +# self.rm = X86RegisterSelector.get_reg_bits(arg2.base) +# self.after = BitArray.from_int(32, X86.to_little_endian(arg2.disp)) +# self.direction = not reversed +# + +class Instruction(object): + encoding = [] + + def __init__(self, *initial_args): + for type_encoding in self.encoding: + args = list(initial_args) + res = [] + full_rex = BitArray(8, "") + if hasattr(self, "default_32_bits") and self.default_32_bits: + full_rex = BitArray.from_int(8, 0x48) + for element in type_encoding: + arg_consum, value, rex = element.accept_arg(res, args) + if arg_consum is None: + break + res.append(value) + del args[:arg_consum] + if rex is not None: + full_rex = full_rex | rex + else: # if no break + if args: # if still args: fail + continue + self.value = sum(res, BitArray(0, "")) + if any(full_rex.array): + self.value = full_rex + self.value + return + raise ValueError("Cannot encode :(") +# +# +class Push(Instruction): + encoding = [(RawBits.from_int(5, 0x50 >> 3), X64RegisterSelector()),] +# (RawBits.from_int(8, 0x68), Imm32())] + +class Pop(Instruction): + encoding = [(RawBits.from_int(5, 0x58 >> 3), X64RegisterSelector())] + +class Call(Instruction): + encoding = [(RawBits.from_int(13, 0xffd0 >> 3), X64RegisterSelector())] + +class Ret(Instruction): + encoding = [(RawBits.from_int(8, 0xc3),)] + +class Mov(Instruction): + default_32_bits = True + encoding = [(RawBits.from_int(8, 0x89), ModRM(ModRM_REG64__REG64, ModRM_REG__DEREF_REG)), (RawBits.from_int(5, 0xb8 >> 3), X64RegisterSelector(), Imm64()), + (Mov_RAX_OFF64(),), (Mov_OFF64_RAX(),)] + +class MultipleInstr(object): + + def __init__(self, instrs=()): + self.instrs = list(instrs) + + def __iadd__(self, value): + if type(value) == MultipleInstr: + self.instrs.extend(value.instrs) + return self + self.instrs.append(value) + return self def get_code(self): - code = self.get_unbinded_code() - for i in range(self.biding): - to_search = codecs.decode(str(i + 1) * 16, 'hex') - import pdb;pdb.set_trace() - code = code.replace(to_search, struct.pack(" Not implemented -# continue -# class Reg_DReg_instruction(X64Instruction): -# mnemo = instr_cls.mnemo.format(reg_dst_name, reg_src_name) -# name = instr_cls.name.format(reg_dst_name, reg_src_name) -# if src_first: -# modr_code = generate_reg_indirect_modr_byte(reg_src_bits, reg_dst_bits) -# else: -# modr_code = generate_reg_indirect_modr_byte(reg_dst_bits, reg_src_bits) -# code = instr_cls.instruction_bits + chr(int(modr_code, 2)).encode("hex") -# Reg_DReg_instruction.__name__ = Reg_DReg_instruction.name -# add_instruction(Reg_DReg_instruction.__name__, Reg_DReg_instruction) - - - - -def generate_reg_indirect_modr_byte(reg_dst_bits, reg_src_bits, deref_first): - # reg, [reg] or [reg], reg - if deref_first: - return encode_hex(chr(int("00{0}{1}".format(reg_dst_bits, reg_src_bits), 2))) - else: - return encode_hex(chr(int("00{0}{1}".format(reg_src_bits, reg_dst_bits), 2))) - -def generate_reg_reg_deref(): - for reg1_name, reg1_bits in all_regs.items(): - for reg2_name, reg2_bits in all_regs.items(): - if reg1_name in ("RBP", "RSP", "R12", "R13") or reg2_name in ("RBP", "RSP", "R12", "R13"): - continue - is_reg1_new = reg1_name in new_reg_opcode - is_reg2_new = reg2_name in new_reg_opcode - - first_byte = encode_hex(chr(int("1001{0}0{1}".format(int(is_reg1_new), int(is_reg2_new)), 2))) - - class DReg_Reg_instruction(X64Instruction): - mnemo = "mov [{0}], {1}".format(reg1_name, reg2_name) - name = 'Mov_D{0}_{1}'.format(reg1_name, reg2_name) - modr_code = generate_reg_indirect_modr_byte(reg1_bits, reg2_bits, False) - code = first_byte + "89" + modr_code - - DReg_Reg_instruction.__name__ = DReg_Reg_instruction.name - add_instruction(DReg_Reg_instruction.__name__, DReg_Reg_instruction) - - class Reg_DReg_instruction(X64Instruction): - mnemo = "mov {0}, [{1}]".format(reg1_name, reg2_name) - name = 'Mov_{0}_D{1}'.format(reg1_name, reg2_name) - modr_code = generate_reg_indirect_modr_byte(reg1_bits, reg2_bits, True) - code = first_byte + "8B" + modr_code - - Reg_DReg_instruction.__name__ = Reg_DReg_instruction.name - add_instruction(Reg_DReg_instruction.__name__, Reg_DReg_instruction) - - -generate_reg_reg_deref() - -generate_module_doc() - diff --git a/native_exec/simple_x86.py b/native_exec/simple_x86.py index 70f9761..b995add 100644 --- a/native_exec/simple_x86.py +++ b/native_exec/simple_x86.py @@ -1,290 +1,238 @@ +import collections import struct import sys -import codecs -# This code should really be rewritten.. -this_module = sys.modules[__name__] - -generated_instruction = [] - -long = int - - -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) - -def decode_hex(s): - return codecs.decode(s.encode(), "hex").decode() - -def encode_hex(s): - return codecs.encode(s.encode(), "hex").decode() - -reg_order = ['EAX', 'ECX', 'EDX', 'EBX', 'ESP', 'EBP', 'ESI', 'EDI'] -reg_opcode = {v : format(i, "03b") for i, v in enumerate(reg_order)} - - -class X86Instruction(object): - mnemo = "" - code = "" - biding = 0 - - def __init__(self, *bind_values): - if len(bind_values) != self.biding: - raise ValueError("{0} expect {1} values got {2}".format(self.__class__.__name__, self.biding, len(bind_values))) - self.bind_values = bind_values - for i, v in enumerate(bind_values): - if not isinstance(v, (int, long)): - raise ValueError("{0} bindings must be 'int' got '{1}' instead".format(self.__class__.__name__, type(v).__name__)) - if not 0 <= v <= 0xffffffff: - raise ValueError("{0} bindings must be between 0 and 0xffffffff".format(self.__class__.__name__)) - - def get_unbinded_code(self): - print(self.code) - return codecs.decode(self.code.replace(" ", ""), 'hex') - - def get_code(self): - code = self.get_unbinded_code() - for i in range(self.biding): - to_search = codecs.decode(str(i + 1) * 8, 'hex') - code = code.replace(to_search, struct.pack(" size: + raise ValueError("size > len(bits)") -class SimpleRegInstructionGenerator(object): - name = "" - instruction_bits = '' - - -class OneBindX86Instruction(X86Instruction): - biding = 1 - - -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(): - class SimpleRegInstruction(X86Instruction): - mnemo = "{0} {1}".format(instr_cls.mnemo, reg_name) - code = format(int(instr_cls.instruction_bits + reg_bits, 2), 'x') + bits_list = [] + for bit in bits: + x = int(bit) + if x not in [0, 1]: + raise ValueError("Not expected bits value {0}".format(x)) + bits_list.append(x) - SimpleRegInstruction.__name__ = "{0}_{1}".format(instr_cls.name, reg_name) - add_instruction(SimpleRegInstruction.__name__, SimpleRegInstruction) - - -class Push_Reg(object): - name = 'Push' - mnemo = "push" - instruction_bits = '01010' - -generate_simple_reg_instruction(Push_Reg) - -class Pop_Reg(object): - name = 'Pop' - mnemo = "pop" - instruction_bits = '01011' - -generate_simple_reg_instruction(Pop_Reg) - -class Call_Reg(object): - name = 'Call' - mnemo = "call" - instruction_bits = '1111111111010' - -generate_simple_reg_instruction(Call_Reg) - - - -def generate_reg_instruction_onebind(instr_cls): - for reg_name, reg_bits in reg_opcode.items(): - class OneBindRegInstruction(OneBindX86Instruction): - mnemo = instr_cls.mnemo.format(reg_name) + self.array = bits_list + if size > len(self.array): + self.array = ([0] * (size - len(self.array))) + self.array - i = int(instr_cls.instruction_bits + reg_bits, 2) - code = bytes([i]) + b'11 11 11 11' # the biding + def dump(self): + res = [] + for i in range(self.size // 8): + c = 0 + for x in (self.array[i * 8: (i + 1) * 8]): + c = (c << 1) + x + res.append(c) + return bytearray((res)) + + def __getitem__(self, slice): + return self.array[slice] + + def __setitem__(self, slice, value): + self.array[slice] = value + return True + + def __repr__(self): + return repr(self.array) + + def __add__(self, other): + if not isinstance(other, BitArray): + return NotImplemented + return BitArray(self.size + other.size, self.array + other.array) + + def to_int(self): + return int("".join([str(i) for i in self.array]), 2) + + @classmethod + def from_string(cls): + l = [] + for c in bytearray(reversed(str_base)): + for i in range(8): + l.append(c & 1) + c = c >> 1 + self.array = l + + @classmethod + def from_int(cls, size, x): + if x < 0: + x = x & ((2 ** size) - 1) + return cls(size, bin(x)[2:]) + +# Rules: bytes only !!!! + +mem_access = collections.namedtuple('mem_access', ['base', 'index', 'squale', 'disp']) +x86_regs = ['EAX', 'ECX', 'EDX', 'EBX', 'ESP', 'EBP', 'ESI', 'EDI'] + +def create_displacement(base=None, index=None, squale=None, disp=0): + return mem_access(base, index, squale, disp) + + +class X86RegisterSelector(object): + size = 3 # bits + reg_order = ['EAX', 'ECX', 'EDX', 'EBX', 'ESP', 'EBP', 'ESI', 'EDI'] + reg_opcode = {v : BitArray.from_int(size=3, x=i) for i, v in enumerate(reg_order)} + + def accept_arg(self, previous, args): + x = args[0] + try: + return (1, self.reg_opcode[x]) + except KeyError: + return (None, None) + + @classmethod + def get_reg_bits(cls, name): + return cls.reg_opcode[name] + +class RawBits(BitArray): + def accept_arg(self, previous, args): + return (0, self) + +class Imm32(object): + def accept_arg(self, previous, args): + x = int(args[0]) + return (1, BitArray.from_int(32, X86.to_little_endian(x))) + +class ModRM(object): + size = 8 + + def __init__(self, *sub_modrm): + self.sub = sub_modrm + + def accept_arg(self, previous, args): + if len(args) < 2: + raise ValueError("Missing arg for modrm") + arg1 = args[0] + arg2 = args[1] + for sub in self.sub: #import pdb;pdb.set_trace() - #code = encode_hex(chr(int(instr_cls.instruction_bits + reg_bits, 2))) + b'11 11 11 11' # the biding - - OneBindRegInstruction.__name__ = instr_cls.name.format(reg_name) - add_instruction(OneBindRegInstruction.__name__, OneBindRegInstruction) - -class Mov_Reg_X(object): - name = 'Mov_{0}_X' - mnemo = 'mov {0}, {{0}}' - instruction_bits = '10111' - -generate_reg_instruction_onebind(Mov_Reg_X) - -def get_immediat_modr_byte(register_bits): - "Generate a modr-reg-r/m indicating a register and an immediat" - str_bits = "11000{0}".format(register_bits) - return encode_hex(chr(int(str_bits, 2))) - -def generate_reg_immediat_modr(instr_cls): - for reg_name, reg_bits in reg_opcode.items(): - class Reg_MEM_Instruction(OneBindX86Instruction): - mnemo = instr_cls.mnemo.format(reg_name) - code = instr_cls.instruction_bits + get_immediat_modr_byte(reg_bits) + '11 11 11 11' # the biding - - Reg_MEM_Instruction.__name__ = instr_cls.name.format(reg_name) - add_instruction(Reg_MEM_Instruction.__name__, Reg_MEM_Instruction) + if sub.match(arg1, arg2): + d = sub(arg1, arg2, 0) + previous[0][-2] = d.direction + return (2, d.mod + d.reg + d.rm + d.after) + elif sub.match(arg2, arg1): + d = sub(arg2, arg1, 1) + previous[0][-2] = d.direction + return (2, d.mod + d.reg + d.rm + d.after) + return (None, None) -class Add_Reg_X(object): - name = 'Add_{0}_X' - mnemo = 'add {0}, {{0}}' - instruction_bits = '81' +class X86(object): + @staticmethod + def is_reg(name): + return name in x86_regs + + @staticmethod + def is_mem_acces(data): + return isinstance(data, mem_access) + + @staticmethod + def mem_access_has_only(mem_access, names): + if not X86.is_mem_acces(mem_access): + raise ValueError("mem_access_has_only") + for f in mem_access._fields: + if getattr(mem_access, f) and f not in names: + return False + return True + + @staticmethod + def to_little_endian(i): + i = i & 0xffffffff + return struct.unpack("I", i))[0] -generate_reg_immediat_modr(Add_Reg_X) - -def get_simple_modr_byte(register_bits): - "Generate a simple modr-reg-r/m for a displacement only mode" - str_bits = "00{0}101".format(register_bits) - return encode_hex(chr(int(str_bits, 2))) - - -def generate_reg_modr(instr_cls): - for reg_name, reg_bits in reg_opcode.items(): - class Reg_MEM_Instruction(OneBindX86Instruction): - mnemo = instr_cls.mnemo.format(reg_name) - code = instr_cls.instruction_bits + get_simple_modr_byte(reg_bits) + '11 11 11 11' # the biding +class ModRM_REG__REG(object): + @classmethod + def match(cls, arg1, arg2): + return X86.is_reg(arg1) and X86.is_reg(arg2) + + def __init__(self, arg1, arg2, reversed): + self.mod = BitArray(2, "11") + self.reg = X86RegisterSelector.get_reg_bits(arg2) + self.rm = X86RegisterSelector.get_reg_bits(arg1) + self.after = BitArray(0, "") + self.direction = 0 + +class ModRM_REG__DEREF_REG(object): + @classmethod + def match(cls, arg1, arg2): + return X86.is_reg(arg1) and arg1 not in ["ESP", "EBP"] and X86.is_mem_acces(arg2) and X86.mem_access_has_only(arg2, ["base"]) + + def __init__(self, arg1, arg2, reversed): + self.mod = BitArray(2, "00") + self.reg = X86RegisterSelector.get_reg_bits(arg1) + self.rm = X86RegisterSelector.get_reg_bits(arg2.base) + self.after = BitArray(0, "") + self.direction = not reversed + +class ModRM_REG__DEREF_REG_IMM(object): + @classmethod + def match(cls, arg1, arg2): + return X86.is_reg(arg1) and X86.is_mem_acces(arg2) and X86.mem_access_has_only(arg2, ["base", "disp"]) + + def __init__(self, arg1, arg2, reversed): + self.mod = BitArray(2, "10") + self.reg = X86RegisterSelector.get_reg_bits(arg1) + self.rm = X86RegisterSelector.get_reg_bits(arg2.base) + self.after = BitArray.from_int(32, X86.to_little_endian(arg2.disp)) + self.direction = not reversed + +class ModRM_REG_IMM(object): + @classmethod + def match(cls, arg1, arg2): + return arg1 in x86_regs and arg2 in x86_regs + + def __init__(self, arg1, arg2): + self.mod = BitArray(2, "11") + self.reg = X86RegisterSelector.get_reg_bits(arg2) + self.rm = X86RegisterSelector.get_reg_bits(arg1) + self.direction = 0 + + +class Instruction(object): + encoding = [] + + def __init__(self, *initial_args): + for type_encoding in self.encoding: + args = list(initial_args) + res = [] + for element in type_encoding: + arg_consum, value = element.accept_arg(res, args) + if arg_consum is None: + break + res.append(value) + del args[:arg_consum] + else: # if no break + if args: # if still args: fail + continue + self.value = sum(res, BitArray(0, "")) + return + raise ValueError("Cannot encode :(") - Reg_MEM_Instruction.__name__ = instr_cls.name.format(reg_name) - add_instruction(Reg_MEM_Instruction.__name__, Reg_MEM_Instruction) - - - - -class Mov_Reg_DX(object): - name = 'Mov_{0}_DX' - mnemo = 'mov {0}, [{{0}}]' - instruction_bits = '8B' -generate_reg_modr(Mov_Reg_DX) - -class Mov_DX_Reg(object): - name = 'Mov_DX_{0}' - mnemo = 'mov [{{0}}], {0}' - instruction_bits = '89' - -generate_reg_modr(Mov_DX_Reg) - -def generate_reg_indirect_modr_byte(reg_dst_bits, reg_src_bits): - # reg, [reg] or [reg], reg - return "00{0}{1}".format(reg_dst_bits, reg_src_bits) +class Push(Instruction): + encoding = [(RawBits.from_int(5, 0x50 >> 3), X86RegisterSelector()), + (RawBits.from_int(8, 0x68), Imm32())] -def generate_reg_reg_deref(instr_cls, src_first=True): - "generate the Mov_Reg_DReg and Mov_DReg_Reg" - for reg_src_name, reg_src_bits in reg_opcode.items(): - for reg_dst_name, reg_dst_bits in reg_opcode.items(): - if reg_dst_name in ("EBP", "ESP") or reg_src_name in ("EBP", "ESP"): - # Not same encoding -> Not implemented - continue - class Reg_DReg_instruction(X86Instruction): - mnemo = instr_cls.mnemo.format(reg_dst_name, reg_src_name) - name = instr_cls.name.format(reg_dst_name, reg_src_name) - if src_first: - modr_code = generate_reg_indirect_modr_byte(reg_src_bits, reg_dst_bits) - else: - modr_code = generate_reg_indirect_modr_byte(reg_dst_bits, reg_src_bits) - code = encode_hex(instr_cls.instruction_bits + chr(int(modr_code, 2))) - Reg_DReg_instruction.__name__ = Reg_DReg_instruction.name - add_instruction(Reg_DReg_instruction.__name__, Reg_DReg_instruction) +class Pop(Instruction): + encoding = [(RawBits.from_int(5, 0x58 >> 3), X86RegisterSelector())] +class Mov(Instruction): + encoding = [(RawBits.from_int(8, 0x89), ModRM(ModRM_REG__REG, ModRM_REG__DEREF_REG, ModRM_REG__DEREF_REG_IMM)), + (RawBits.from_int(5, 0xb8 >> 3), X86RegisterSelector(), Imm32())] -class Mov_Reg_DReg(object): - name = 'Mov_{0}_D{1}' - mnemo = 'mov [{0}], {1}' - instruction_bits = '8B' +class Call(Instruction): + encoding = [(RawBits.from_int(13, 0xffd0 >> 3), X86RegisterSelector())] -generate_reg_reg_deref(Mov_Reg_DReg, False) - -class Mov_DReg_Reg(object): - name = 'Mov_D{0}_{1}' - mnemo = 'mov {0}, [{1}]' - instruction_bits = '89' - -generate_reg_reg_deref(Mov_DReg_Reg, True) - - -def generate_reg_reg_modr_byte(reg_dst_bits, reg_src_bits): - # reg, reg - return "11{0}{1}".format(reg_src_bits, reg_dst_bits) +class Ret(Instruction): + encoding = [(RawBits.from_int(8, 0xc3),)] -def generate_reg_reg_modr(instr_cls): - for reg_src_name, reg_src_bits in reg_opcode.items(): - for reg_dst_name, reg_dst_bits in reg_opcode.items(): - class Reg_Reg_instruction(X86Instruction): - mnemo = "{0} {1},{2}".format(instr_cls.mnemo, reg_dst_name, reg_src_name) - modr_code = format(int(generate_reg_reg_modr_byte(reg_dst_bits, reg_src_bits) , 2), 'x') - code = instr_cls.instruction_bits + modr_code - Reg_Reg_instruction.__name__ = "{0}_{1}_{2}".format(instr_cls.name, reg_dst_name, reg_src_name) - add_instruction(Reg_Reg_instruction.__name__, Reg_Reg_instruction) - -class Test_Reg_Reg(object): - mnemo = "tst" - name = "Tst" - instruction_bits = "85" - -generate_reg_reg_modr(Test_Reg_Reg) - -#### JUMP #### - -class JZ(OneBindX86Instruction): - code = "0F 84 11 11 11 11" - - def __init__(self, instr_block): - self.instr_block = instr_block - instr_block_size = len(instr_block.get_code()) - super(JZ, self).__init__(instr_block_size) - - def get_code(self): - return super(JZ, self).get_code() + self.instr_block.get_code() - -class JNZ(OneBindX86Instruction): - code = "0F 85 11 11 11 11" - - def __init__(self, instr_block): - self.instr_block = instr_block - instr_block_size = len(instr_block.get_code()) - super(JNZ, self).__init__(instr_block_size) - - def get_code(self): - return super(JNZ, self).get_code() + self.instr_block.get_code() - - - class MultipleInstr(object): - def __init__(self, init_instrs=()): - self.instrs = list(init_instrs) + def __init__(self): + self.instrs = [] def __iadd__(self, value): if type(value) == MultipleInstr: @@ -294,12 +242,7 @@ class MultipleInstr(object): return self def get_code(self): - return "".join(i.get_code() for i in self.instrs) - - def get_mnemo(self): - return "\n".join(i.get_mnemo() for i in self.instrs) - + if sys.version_info.major == 3: + return b"".join([x.value.dump() for x in self.instrs]) + return "".join([str(x.value.dump()) for x in self.instrs]) - -generate_module_doc() - \ No newline at end of file diff --git a/pe_parse.py b/pe_parse.py index 14d66b0..f9489dc 100644 --- a/pe_parse.py +++ b/pe_parse.py @@ -1,8 +1,8 @@ import ctypes import windows -import hooks +import windows.hooks as hooks -from generated_def.winstructs import * +from windows.generated_def.winstructs import * # This must go to windefs IMAGE_DIRECTORY_ENTRY_EXPORT = 0 @@ -118,14 +118,25 @@ def PEFile(baseaddr): import_descriptor_addr += ctypes.sizeof(self.IMAGE_IMPORT_DESCRIPTOR) current_import_descriptor = self.IMAGE_IMPORT_DESCRIPTOR.from_address(import_descriptor_addr) return res + + def get_EXPORT_DIRECTORY(self): + export_directory_rva = self.get_DataDirectory()[IMAGE_DIRECTORY_ENTRY_EXPORT].VirtualAddress + if export_directory_rva == 0: + return None + export_directory_addr = baseaddr + export_directory_rva + return self._IMAGE_EXPORT_DIRECTORY.from_address(export_directory_addr) - #def get_EXPORT_DIRECTORY(self): - # return self.get_OptionalHeader().get_EXPORT_DIRECTORY() - - #@property - #def exports(self): - # return self.get_EXPORT_DIRECTORY().get_exports() + @property + def exports(self): + res = {} + raw_exports = self.get_EXPORT_DIRECTORY().get_exports() + for id, rva_addr, rva_name in raw_exports: + res[id] = rva_addr.addr + if rva_name is not None: + res[rva_name.str] = rva_addr.addr + return res + # TODO: get imports by parsing other modules exports if no INT @property @@ -141,7 +152,7 @@ def PEFile(baseaddr): res.setdefault(import_descriptor.Name.str.lower(),[]).extend(IAT) return res - # Will be usable as `self.IMPORT_DESCRIPTOR` + # Will be usable as `self.IMAGE_IMPORT_DESCRIPTOR` class IMAGE_IMPORT_DESCRIPTOR(ctypes.Structure): _fields_ = transform_ctypes_fields(IMAGE_IMPORT_DESCRIPTOR, {"Name" : StringRVa, "OriginalFirstThunk" : RVA, "FirstThunk" : RVA}) @@ -171,7 +182,25 @@ def PEFile(baseaddr): iat_addr += ctypes.sizeof(THUNK_DATA) iat_entry = THUNK_DATA.from_address(iat_addr) return res - + + # Will be usable as `self._IMAGE_EXPORT_DIRECTORY` + class _IMAGE_EXPORT_DIRECTORY(ctypes.Structure): + _fields_ = transform_ctypes_fields(IMAGE_EXPORT_DIRECTORY, {"Name" : StringRVa, "AddressOfFunctions" : RVA, "AddressOfNames" : RVA, "AddressOfNameOrdinals": RVA}) + + def get_exports(self): + NameOrdinals = (WORD * self.NumberOfNames).from_address(self.AddressOfNameOrdinals.addr) + NameOrdinals = list(NameOrdinals) + Functions = (RVA * self.NumberOfFunctions).from_address(self.AddressOfFunctions.addr) + Names = (StringRVa * self.NumberOfNames).from_address(self.AddressOfNames.addr) + res = [] + for nb,func in enumerate(Functions): + if nb in NameOrdinals: + name = Names[NameOrdinals.index(nb)] + else: + name = None + res.append((nb, func, name)) + return res + current_pe = PEFile() class IMAGE_DOS_HEADER(ctypes.Structure): @@ -201,46 +230,7 @@ def PEFile(baseaddr): if windows.current_process.bitness == 32: return IMAGE_NT_HEADERS32.from_address(baseaddr + self.e_lfanew) return IMAGE_NT_HEADERS64.from_address(baseaddr + self.e_lfanew) - - # - # def get_EXPORT_DIRECTORY(self): - # export_directory_rva = self.DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT].VirtualAddress - # if export_directory_rva == 0: - # return None - # export_directory_addr = baseaddr + export_directory_rva - # return IMAGE_EXPORT_DIRECTORY.from_address(export_directory_addr) - # - - - #class IMAGE_EXPORT_DIRECTORY(ctypes.Structure): - # _fields_ = [ - # ("Characteristics", DWORD), - # ("TimeDateStamp", DWORD), - # ("MajorVersion", WORD), - # ("MinorVersion", WORD), - # ("Name", StringRVa), - # ("Base", DWORD), - # ("NumberOfFunctions", DWORD), - # ("NumberOfNames", DWORD), - # ("AddressOfFunctions", RVA), - # ("AddressOfNames", RVA), - # ("AddressOfNameOrdinals", RVA), - # ] - # - # def get_exports(self): - # NameOrdinals = (WORD * self.NumberOfNames).from_address(self.AddressOfNameOrdinals.addr) - # NameOrdinals = list(NameOrdinals) - # Functions = (RVA * self.NumberOfFunctions).from_address(self.AddressOfFunctions.addr) - # Names = (StringRVa * self.NumberOfNames).from_address(self.AddressOfNames.addr) - # res = [] - # for nb,func in enumerate(Functions): - # if nb in NameOrdinals: - # name = Names[NameOrdinals.index(nb)] - # else: - # name = None - # res.append((nb, func, name)) - # return res - # + return current_pe tst = PEFile.__code__.co_consts[13] \ No newline at end of file diff --git a/utils.py b/utils.py index 47978c4..f2aff35 100644 --- a/utils.py +++ b/utils.py @@ -4,11 +4,9 @@ import os import copy import sys -import k32testing as kernel32proxy -import generated_def.windef as windef -import winobject -import native_exec -from generated_def.winstructs import * +from . import k32testing as kernel32proxy +from .generated_def import windef +from .generated_def.winstructs import * # Function resolution ! @@ -21,29 +19,7 @@ def get_func_addr(dll_name, func_name): dll = ctypes.WinDLL(dll_name) return kernel32proxy.GetProcAddress(dll._handle, func_name) -# Used by system.processes -def enumerate_processes(): - process_entry = winobject.WinProcess() - process_entry.dwSize = ctypes.sizeof(process_entry) - snap = kernel32proxy.CreateToolhelp32Snapshot(windef.TH32CS_SNAPPROCESS, 0) - kernel32proxy.Process32First(snap, process_entry) - res = [] - res.append(swallow_ctypes_copy(process_entry)) - while kernel32proxy.Process32Next(snap, process_entry): - res.append(swallow_ctypes_copy(process_entry)) - return res -# Used by system.threads -def enumerate_threads(): - thread_entry = winobject.WinThread() - thread_entry.dwSize = ctypes.sizeof(thread_entry) - snap = kernel32proxy.CreateToolhelp32Snapshot(windef.TH32CS_SNAPTHREAD, 0) - threads = [] - kernel32proxy.Thread32First(snap, thread_entry) - threads.append(copy.copy(thread_entry)) - while kernel32proxy.Thread32Next(snap, thread_entry): - threads.append(copy.copy(thread_entry)) - return threads def is_wow_64(hProcess): try: @@ -81,6 +57,9 @@ def create_console(): stderr_handle = kernel32proxy.GetStdHandle(windef.STD_ERROR_HANDLE) console_stderr = create_file_from_handle(stderr_handle, "w") + #print(stderr_handle, console_stderr) + import os + #os.dup2(console_stderr.fileno(), 2) sys.stderr = console_stderr class VirtualProtected(object): diff --git a/vectored_exception.py b/vectored_exception.py index 10cdd4e..7bcb221 100644 --- a/vectored_exception.py +++ b/vectored_exception.py @@ -9,7 +9,6 @@ import time EXCEPTION_CONTINUE_SEARCH = (0x0) EXCEPTION_CONTINUE_EXECUTION = (0xffffffff) - exception_type = [ "EXCEPTION_ACCESS_VIOLATION", "EXCEPTION_DATATYPE_MISALIGNMENT", @@ -53,36 +52,96 @@ class EnhancedEXCEPTION_RECORD(EXCEPTION_RECORD): if x is None: return 0x0 return x + +class Eflags(int): + _flags_ = [("CF", 1), + ("RES_1", 1), + ("PF", 1), + ("RES_3", 1), + ("AF", 1), + ("RES_5", 1), + ("ZF", 1), + ("SF", 1), + ("TF", 1), + ("IF", 1), + ("DF", 1), + ("OF", 1), + ("IOPL_1", 1), + ("IOPL_2", 1), + ("NT", 1), + ("RES_15",1), + ("RF", 1), + ("VM", 1), + ("AC", 1), + ("VIF", 1), + ("VIP", 1), + ("ID", 1), + ] + + _flag_mask_ = dict([(name, 1<< i) for i,(name, size) in enumerate(_flags_)]) + + def __getattr__(self, name): + if name in self._flag_mask_: + return bool(self & self._flag_mask_[name]) + return super(Eflags, self).__getattr_(name) + + def dump(self): + res = [] + for name in self._flag_mask_: + if name.startswith("RES_"): + continue + if getattr(self, name): + res.append(name) + return "|".join(res) + + def __repr__(self): + return "{0}({1})".format(type(self).__name__, self.dump()) + + __str__ = __repr__ + + def __hex__(self): + return "{0}({1}:{2})".format(type(self).__name__, int.__hex__(self), self.dump()) class EnhancedCONTEXTBase(CONTEXT): default_dump = () pc_reg = '' + special_reg_type = {} 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))) + value = getattr(self, name) + if name in self.special_reg_type: + value = self.special_reg_type[name](value) + res.append((name, value)) return res def dump(self, to_dump=None): regs = self.regs() for name, value in regs: - print("{0} -> {1}".format(name, hex(value))) - - @property - def pc(self): + print("{0} -> {1}".format(name, hex(value))) + return None + + def get_pc(self): return getattr(self, self.pc_reg) + + def set_pc(self, value): + return setattr(self, self.pc_reg, value) + + pc = property(get_pc, set_pc, None, "Program Counter register (EIP or RIP)") class EnhancedCONTEXT32(EnhancedCONTEXTBase): - default_dump = ('Eip', 'Esp', 'Eax', 'Ebx', 'Ecx', 'Edx', 'Ebp', 'Edi', 'Esi') + default_dump = ('Eip', 'Esp', 'Eax', 'Ebx', 'Ecx', 'Edx', 'Ebp', 'Edi', 'Esi', 'EFlags') pc_reg = 'Eip' + special_reg_type = {'EFlags' : Eflags} class EnhancedCONTEXT64(EnhancedCONTEXTBase): default_dump = ('Rip', 'Rsp', 'Rax', 'Rbx', 'Rcx', 'Rdx', 'Rbp', 'Rdi', 'Rsi', - 'R9', 'R10', 'R11', 'R12', 'R13', 'R14', 'R15') + 'R9', 'R10', 'R11', 'R12', 'R13', 'R14', 'R15', 'EFlags') pc_reg = 'Rip' + special_reg_type = {'EFlags' : Eflags} if windows.current_process.bitness == 32: EnhancedCONTEXT = EnhancedCONTEXT32 @@ -107,10 +166,8 @@ class EnhancedEXCEPTION_POINTERS(ctypes.Structure): class VectoredException(object): func_type = ctypes.WINFUNCTYPE(ctypes.c_uint, ctypes.POINTER(EnhancedEXCEPTION_POINTERS)) - def __init__(self, quit_if_fail=False): - pass - - def __call__(self, func): + def __new__(cls, func): + self = object.__new__(cls) self.func = func return self.func_type(self.decorator) @@ -119,14 +176,12 @@ class VectoredException(object): return self.func(exception_pointers) except BaseException as e: print("Ignored Python Exception in Vectored Exception: {0}".format(e)) - if quit_if_fail: - return windows.current_thread.quit() return windef.EXCEPTION_CONTINUE_SEARCH - + class WithExceptionHandler(object): def __init__(self, handler): - self.handler = handler + self.handler = VectoredException(handler) def __enter__(self): self.value = windows.k32testing.AddVectoredExceptionHandler(0, self.handler) diff --git a/winobject.py b/winobject.py index 0d7ca7c..af59195 100644 --- a/winobject.py +++ b/winobject.py @@ -1,14 +1,20 @@ import ctypes import os +import codecs +import copy import windows -import utils -import k32testing as kernel32proxy -import injection -import native_exec +import windows.k32testing as kernel32proxy +import windows.injection as injection +import windows.native_exec as native_exec + +from . import utils + +from windows.generated_def.winstructs import * +from .generated_def import windef + +import windows.pe_parse as pe_parse -from generated_def.winstructs import * -import pe_parse class AutoHandle(object): @@ -44,7 +50,7 @@ class System(object): :type: [:class:`WinProcess`] -- A list of Process """ - return utils.enumerate_processes() + return self.enumerate_processes() @property def threads(self): @@ -53,7 +59,7 @@ class System(object): :type: [:class:`WinThread`] -- A list of Thread """ - return utils.enumerate_threads() + return self.enumerate_threads() @property def bitness(self): @@ -67,6 +73,30 @@ class System(object): if "PROCESSOR_ARCHITEW6432" in os.environ: return 64 return 32 + + @staticmethod + def enumerate_processes(): + process_entry = WinProcess() + process_entry.dwSize = ctypes.sizeof(process_entry) + snap = kernel32proxy.CreateToolhelp32Snapshot(windef.TH32CS_SNAPPROCESS, 0) + kernel32proxy.Process32First(snap, process_entry) + res = [] + res.append(utils.swallow_ctypes_copy(process_entry)) + while kernel32proxy.Process32Next(snap, process_entry): + res.append(utils.swallow_ctypes_copy(process_entry)) + return res + + @staticmethod + def enumerate_threads(): + thread_entry = WinThread() + thread_entry.dwSize = ctypes.sizeof(thread_entry) + snap = kernel32proxy.CreateToolhelp32Snapshot(windef.TH32CS_SNAPTHREAD, 0) + threads = [] + kernel32proxy.Thread32First(snap, thread_entry) + threads.append(copy.copy(thread_entry)) + while kernel32proxy.Thread32Next(snap, thread_entry): + threads.append(copy.copy(thread_entry)) + return threads class WinThread(THREADENTRY32, AutoHandle): @@ -85,15 +115,51 @@ class WinThread(THREADENTRY32, AutoHandle): """ if hasattr(self, "_owner"): return self._owner - self._owner = [process for process in utils.enumerate_processes() if process.pid == self.th32OwnerProcessID][0] + try: + self._owner = [process for process in windows.system.processes if process.pid == self.th32OwnerProcessID][0] + except IndexError: + return None return self._owner + + @property + def context(self): + x = windows.vectored_exception.EnhancedCONTEXT() + x.ContextFlags = CONTEXT_FULL + kernel32proxy.GetThreadContext(self.handle, x) + return x + + def set_context(self, context): + return kernel32proxy.SetThreadContext(self.handle, context) + + def exit(self, code=0): + return kernel32proxy.TerminateThread(self.handle, code) + + def resume(self): + return kernel32proxy.ResumeThread(self.handle) + + def suspend(self): + return kernel32proxy.SuspendThread(self.handle) def _get_handle(self): return kernel32proxy.OpenThread(dwThreadId=self.tid) def __repr__(self): - return '<{0} {1} owner "{2}" at {3}>'.format(self.__class__.__name__, self.tid, self.owner.name, hex(id(self))) - + owner = self.owner + if owner is None: + owner_name = "".format(hex(self.th32OwnerProcessID)) + else: + owner_name = owner.name + return '<{0} {1} owner "{2}" at {3}>'.format(self.__class__.__name__, self.tid, owner_name, hex(id(self))) + + @staticmethod + def _from_handle(handle): + tid = kernel32proxy.GetThreadId(handle) + print(tid) + try: + return [t for t in System().threads if t.tid == tid][0] + except IndexError: + return (tid, handle) + class Process(AutoHandle): @property def is_wow_64(self): @@ -123,7 +189,7 @@ class Process(AutoHandle): :type: [:class:`WinThread`] -- A list of Thread """ - return [thread for thread in utils.enumerate_threads() if thread.th32OwnerProcessID == self.pid] + return [thread for thread in windows.system.threads if thread.th32OwnerProcessID == self.pid] def virtual_alloc(self, size): raise NotImplementedError("virtual_alloc") @@ -160,11 +226,10 @@ class CurrentThread(AutoHandle): class CurrentProcess(Process): """The current process""" get_peb = None - get_peb_32_code = '64a130000000c3'.decode('hex') - + get_peb_32_code = codecs.decode(b'64a130000000c3', 'hex') # mov rax,QWORD PTR gs:0x60 # ret - get_peb_64_code = "65488B042560000000C3".decode('hex') + get_peb_64_code = codecs.decode(b"65488B042560000000C3", 'hex') allocator = native_exec.native_function.CustomAllocator() @@ -240,7 +305,8 @@ class CurrentProcess(Process): .. 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) + handle = kernel32proxy.CreateThread(lpStartAddress=lpStartAddress, lpParameter=lpParameter, dwCreationFlags=dwCreationFlags) + return WinThread._from_handle(handle) def exit(self, code=0): """Exit the process""" @@ -257,7 +323,7 @@ class WinProcess(PROCESSENTRY32, Process): :type: str """ - return self.szExeFile[:] + return self.szExeFile[:].decode() @property def pid(self): @@ -314,7 +380,7 @@ class WinProcess(PROCESSENTRY32, Process): """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) + return WinThread._from_handle(kernel32proxy.CreateRemoteThread(hProcess=self.handle, lpStartAddress=addr, lpParameter=param)) def load_library(self, dll_path): """Load the library in remote process"""