#!/usr/bin/env python # -*- coding: utf-8 -*- # Copyright (c) 2009-2014, Mario Vilas # All rights reserved. # # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions are met: # # * Redistributions of source code must retain the above copyright notice, # this list of conditions and the following disclaimer. # * Redistributions in binary form must reproduce the above copyright # notice,this list of conditions and the following disclaimer in the # documentation and/or other materials provided with the distribution. # * Neither the name of the copyright holder nor the names of its # contributors may be used to endorse or promote products derived from # this software without specific prior written permission. # # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" # AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE # IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE # ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE # LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR # CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF # SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS # INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN # CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) # ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE # POSSIBILITY OF SUCH DAMAGE. """ Common definitions. """ # TODO # + add TCHAR and related types? __revision__ = "$Id$" import ctypes import functools from winappdbg import compat #------------------------------------------------------------------------------ # Some stuff from ctypes we'll be using very frequently. addressof = ctypes.addressof sizeof = ctypes.sizeof SIZEOF = ctypes.sizeof POINTER = ctypes.POINTER Structure = ctypes.Structure Union = ctypes.Union WINFUNCTYPE = ctypes.WINFUNCTYPE windll = ctypes.windll # The IronPython implementation of byref() was giving me problems, # so I'm replacing it with the slower pointer() function. try: ctypes.c_void_p(ctypes.byref(ctypes.c_char())) # this fails in IronPython byref = ctypes.byref except TypeError: byref = ctypes.pointer # XXX DEBUG # The following code can be enabled to make the Win32 API wrappers log to # standard output the dll and function names, the parameter values and the # return value for each call. ##WIN32_VERBOSE_MODE = True WIN32_VERBOSE_MODE = False if WIN32_VERBOSE_MODE: class WinDllHook(object): def __getattr__(self, name): if name.startswith('_'): return object.__getattr__(self, name) return WinFuncHook(name) class WinFuncHook(object): def __init__(self, name): self.__name = name def __getattr__(self, name): if name.startswith('_'): return object.__getattr__(self, name) return WinCallHook(self.__name, name) class WinCallHook(object): def __init__(self, dllname, funcname): self.__dllname = dllname self.__funcname = funcname self.__func = getattr(getattr(ctypes.windll, dllname), funcname) def __copy_attribute(self, attribute): try: value = getattr(self, attribute) setattr(self.__func, attribute, value) except AttributeError: try: delattr(self.__func, attribute) except AttributeError: pass def __call__(self, *argv): self.__copy_attribute('argtypes') self.__copy_attribute('restype') self.__copy_attribute('errcheck') print("-"*10) print("%s ! %s %r" % (self.__dllname, self.__funcname, argv)) retval = self.__func(*argv) print("== %r" % (retval,)) return retval windll = WinDllHook() #============================================================================== # This is used later on to calculate the list of exported symbols. _all = None _all = set(vars().keys()) #============================================================================== def RaiseIfZero(result, func = None, arguments = ()): """ Error checking for most Win32 API calls. The function is assumed to return an integer, which is C{0} on error. In that case the C{WindowsError} exception is raised. """ if not result: raise ctypes.WinError() return result def RaiseIfNotZero(result, func = None, arguments = ()): """ Error checking for some odd Win32 API calls. The function is assumed to return an integer, which is zero on success. If the return value is nonzero the C{WindowsError} exception is raised. This is mostly useful for free() like functions, where the return value is the pointer to the memory block on failure or a C{NULL} pointer on success. """ if result: raise ctypes.WinError() return result def RaiseIfNotErrorSuccess(result, func = None, arguments = ()): """ Error checking for Win32 Registry API calls. The function is assumed to return a Win32 error code. If the code is not C{ERROR_SUCCESS} then a C{WindowsError} exception is raised. """ if result != ERROR_SUCCESS: raise ctypes.WinError(result) return result class GuessStringType(object): """ Decorator that guesses the correct version (A or W) to call based on the types of the strings passed as parameters. Calls the B{ANSI} version if the only string types are ANSI. Calls the B{Unicode} version if Unicode or mixed string types are passed. The default if no string arguments are passed depends on the value of the L{t_default} class variable. @type fn_ansi: function @ivar fn_ansi: ANSI version of the API function to call. @type fn_unicode: function @ivar fn_unicode: Unicode (wide) version of the API function to call. @type t_default: type @cvar t_default: Default string type to use. Possible values are: - type('') for ANSI - type(u'') for Unicode """ # ANSI and Unicode types t_ansi = type('') t_unicode = type(u'') # Default is ANSI for Python 2.x t_default = t_ansi def __init__(self, fn_ansi, fn_unicode): """ @type fn_ansi: function @param fn_ansi: ANSI version of the API function to call. @type fn_unicode: function @param fn_unicode: Unicode (wide) version of the API function to call. """ self.fn_ansi = fn_ansi self.fn_unicode = fn_unicode # Copy the wrapped function attributes. try: self.__name__ = self.fn_ansi.__name__[:-1] # remove the A or W except AttributeError: pass try: self.__module__ = self.fn_ansi.__module__ except AttributeError: pass try: self.__doc__ = self.fn_ansi.__doc__ except AttributeError: pass def __call__(self, *argv, **argd): # Shortcut to self.t_ansi t_ansi = self.t_ansi # Get the types of all arguments for the function v_types = [ type(item) for item in argv ] v_types.extend( [ type(value) for (key, value) in compat.iteritems(argd) ] ) # Get the appropriate function for the default type if self.t_default == t_ansi: fn = self.fn_ansi else: fn = self.fn_unicode # If at least one argument is a Unicode string... if self.t_unicode in v_types: # If al least one argument is an ANSI string, # convert all ANSI strings to Unicode if t_ansi in v_types: argv = list(argv) for index in compat.xrange(len(argv)): if v_types[index] == t_ansi: argv[index] = compat.unicode(argv[index]) for (key, value) in argd.items(): if type(value) == t_ansi: argd[key] = compat.unicode(value) # Use the W version fn = self.fn_unicode # If at least one argument is an ANSI string, # but there are no Unicode strings... elif t_ansi in v_types: # Use the A version fn = self.fn_ansi # Call the function and return the result return fn(*argv, **argd) class DefaultStringType(object): """ Decorator that uses the default version (A or W) to call based on the configuration of the L{GuessStringType} decorator. @see: L{GuessStringType.t_default} @type fn_ansi: function @ivar fn_ansi: ANSI version of the API function to call. @type fn_unicode: function @ivar fn_unicode: Unicode (wide) version of the API function to call. """ def __init__(self, fn_ansi, fn_unicode): """ @type fn_ansi: function @param fn_ansi: ANSI version of the API function to call. @type fn_unicode: function @param fn_unicode: Unicode (wide) version of the API function to call. """ self.fn_ansi = fn_ansi self.fn_unicode = fn_unicode # Copy the wrapped function attributes. try: self.__name__ = self.fn_ansi.__name__[:-1] # remove the A or W except AttributeError: pass try: self.__module__ = self.fn_ansi.__module__ except AttributeError: pass try: self.__doc__ = self.fn_ansi.__doc__ except AttributeError: pass def __call__(self, *argv, **argd): # Get the appropriate function based on the default. if GuessStringType.t_default == GuessStringType.t_ansi: fn = self.fn_ansi else: fn = self.fn_unicode # Call the function and return the result return fn(*argv, **argd) def MakeANSIVersion(fn): """ Decorator that generates an ANSI version of a Unicode (wide) only API call. @type fn: callable @param fn: Unicode (wide) version of the API function to call. """ @functools.wraps(fn) def wrapper(*argv, **argd): t_ansi = GuessStringType.t_ansi t_unicode = GuessStringType.t_unicode v_types = [ type(item) for item in argv ] v_types.extend( [ type(value) for (key, value) in compat.iteritems(argd) ] ) if t_ansi in v_types: argv = list(argv) for index in compat.xrange(len(argv)): if v_types[index] == t_ansi: argv[index] = t_unicode(argv[index]) for key, value in argd.items(): if type(value) == t_ansi: argd[key] = t_unicode(value) return fn(*argv, **argd) return wrapper def MakeWideVersion(fn): """ Decorator that generates a Unicode (wide) version of an ANSI only API call. @type fn: callable @param fn: ANSI version of the API function to call. """ @functools.wraps(fn) def wrapper(*argv, **argd): t_ansi = GuessStringType.t_ansi t_unicode = GuessStringType.t_unicode v_types = [ type(item) for item in argv ] v_types.extend( [ type(value) for (key, value) in compat.iteritems(argd) ] ) if t_unicode in v_types: argv = list(argv) for index in compat.xrange(len(argv)): if v_types[index] == t_unicode: argv[index] = t_ansi(argv[index]) for key, value in argd.items(): if type(value) == t_unicode: argd[key] = t_ansi(value) return fn(*argv, **argd) return wrapper #--- Types -------------------------------------------------------------------- # http://msdn.microsoft.com/en-us/library/aa383751(v=vs.85).aspx # Map of basic C types to Win32 types LPVOID = ctypes.c_void_p CHAR = ctypes.c_char WCHAR = ctypes.c_wchar BYTE = ctypes.c_ubyte SBYTE = ctypes.c_byte WORD = ctypes.c_uint16 SWORD = ctypes.c_int16 DWORD = ctypes.c_uint32 SDWORD = ctypes.c_int32 QWORD = ctypes.c_uint64 SQWORD = ctypes.c_int64 SHORT = ctypes.c_short USHORT = ctypes.c_ushort INT = ctypes.c_int UINT = ctypes.c_uint LONG = ctypes.c_long ULONG = ctypes.c_ulong LONGLONG = ctypes.c_int64 # c_longlong ULONGLONG = ctypes.c_uint64 # c_ulonglong LPSTR = ctypes.c_char_p LPWSTR = ctypes.c_wchar_p INT8 = ctypes.c_int8 INT16 = ctypes.c_int16 INT32 = ctypes.c_int32 INT64 = ctypes.c_int64 UINT8 = ctypes.c_uint8 UINT16 = ctypes.c_uint16 UINT32 = ctypes.c_uint32 UINT64 = ctypes.c_uint64 LONG32 = ctypes.c_int32 LONG64 = ctypes.c_int64 ULONG32 = ctypes.c_uint32 ULONG64 = ctypes.c_uint64 DWORD32 = ctypes.c_uint32 DWORD64 = ctypes.c_uint64 BOOL = ctypes.c_int FLOAT = ctypes.c_float # Map size_t to SIZE_T try: SIZE_T = ctypes.c_size_t SSIZE_T = ctypes.c_ssize_t except AttributeError: # Size of a pointer SIZE_T = {1:BYTE, 2:WORD, 4:DWORD, 8:QWORD}[sizeof(LPVOID)] SSIZE_T = {1:SBYTE, 2:SWORD, 4:SDWORD, 8:SQWORD}[sizeof(LPVOID)] PSIZE_T = POINTER(SIZE_T) # Not really pointers but pointer-sized integers DWORD_PTR = SIZE_T ULONG_PTR = SIZE_T LONG_PTR = SIZE_T # Other Win32 types, more may be added as needed PVOID = LPVOID PPVOID = POINTER(PVOID) PSTR = LPSTR PWSTR = LPWSTR PCHAR = LPSTR PWCHAR = LPWSTR LPBYTE = POINTER(BYTE) LPSBYTE = POINTER(SBYTE) LPWORD = POINTER(WORD) LPSWORD = POINTER(SWORD) LPDWORD = POINTER(DWORD) LPSDWORD = POINTER(SDWORD) LPULONG = POINTER(ULONG) LPLONG = POINTER(LONG) PDWORD = LPDWORD PDWORD_PTR = POINTER(DWORD_PTR) PULONG = LPULONG PLONG = LPLONG CCHAR = CHAR BOOLEAN = BYTE PBOOL = POINTER(BOOL) LPBOOL = PBOOL TCHAR = CHAR # XXX ANSI by default? UCHAR = BYTE DWORDLONG = ULONGLONG LPDWORD32 = POINTER(DWORD32) LPULONG32 = POINTER(ULONG32) LPDWORD64 = POINTER(DWORD64) LPULONG64 = POINTER(ULONG64) PDWORD32 = LPDWORD32 PULONG32 = LPULONG32 PDWORD64 = LPDWORD64 PULONG64 = LPULONG64 ATOM = WORD HANDLE = LPVOID PHANDLE = POINTER(HANDLE) LPHANDLE = PHANDLE HMODULE = HANDLE HINSTANCE = HANDLE HTASK = HANDLE HKEY = HANDLE PHKEY = POINTER(HKEY) HDESK = HANDLE HRSRC = HANDLE HSTR = HANDLE HWINSTA = HANDLE HKL = HANDLE HDWP = HANDLE HFILE = HANDLE HRESULT = LONG HGLOBAL = HANDLE HLOCAL = HANDLE HGDIOBJ = HANDLE HDC = HGDIOBJ HRGN = HGDIOBJ HBITMAP = HGDIOBJ HPALETTE = HGDIOBJ HPEN = HGDIOBJ HBRUSH = HGDIOBJ HMF = HGDIOBJ HEMF = HGDIOBJ HENHMETAFILE = HGDIOBJ HMETAFILE = HGDIOBJ HMETAFILEPICT = HGDIOBJ HWND = HANDLE NTSTATUS = LONG PNTSTATUS = POINTER(NTSTATUS) KAFFINITY = ULONG_PTR RVA = DWORD RVA64 = QWORD WPARAM = DWORD LPARAM = LPVOID LRESULT = LPVOID ACCESS_MASK = DWORD REGSAM = ACCESS_MASK PACCESS_MASK = POINTER(ACCESS_MASK) PREGSAM = POINTER(REGSAM) # Since the SID is an opaque structure, let's treat its pointers as void* PSID = PVOID # typedef union _LARGE_INTEGER { # struct { # DWORD LowPart; # LONG HighPart; # } ; # struct { # DWORD LowPart; # LONG HighPart; # } u; # LONGLONG QuadPart; # } LARGE_INTEGER, # *PLARGE_INTEGER; # XXX TODO # typedef struct _FLOAT128 { # __int64 LowPart; # __int64 HighPart; # } FLOAT128; class FLOAT128 (Structure): _fields_ = [ ("LowPart", QWORD), ("HighPart", QWORD), ] PFLOAT128 = POINTER(FLOAT128) # typedef struct DECLSPEC_ALIGN(16) _M128A { # ULONGLONG Low; # LONGLONG High; # } M128A, *PM128A; class M128A(Structure): _fields_ = [ ("Low", ULONGLONG), ("High", LONGLONG), ] PM128A = POINTER(M128A) #--- Constants ---------------------------------------------------------------- NULL = None INFINITE = -1 TRUE = 1 FALSE = 0 # http://blogs.msdn.com/oldnewthing/archive/2004/08/26/220873.aspx ANYSIZE_ARRAY = 1 # Invalid handle value is -1 casted to void pointer. try: INVALID_HANDLE_VALUE = ctypes.c_void_p(-1).value #-1 #0xFFFFFFFF except TypeError: if sizeof(ctypes.c_void_p) == 4: INVALID_HANDLE_VALUE = 0xFFFFFFFF elif sizeof(ctypes.c_void_p) == 8: INVALID_HANDLE_VALUE = 0xFFFFFFFFFFFFFFFF else: raise MAX_MODULE_NAME32 = 255 MAX_PATH = 260 # Error codes # TODO maybe add more error codes? # if they're too many they could be pickled instead, # or at the very least put in a new file ERROR_SUCCESS = 0 ERROR_INVALID_FUNCTION = 1 ERROR_FILE_NOT_FOUND = 2 ERROR_PATH_NOT_FOUND = 3 ERROR_ACCESS_DENIED = 5 ERROR_INVALID_HANDLE = 6 ERROR_NOT_ENOUGH_MEMORY = 8 ERROR_INVALID_DRIVE = 15 ERROR_NO_MORE_FILES = 18 ERROR_BAD_LENGTH = 24 ERROR_HANDLE_EOF = 38 ERROR_HANDLE_DISK_FULL = 39 ERROR_NOT_SUPPORTED = 50 ERROR_FILE_EXISTS = 80 ERROR_INVALID_PARAMETER = 87 ERROR_BUFFER_OVERFLOW = 111 ERROR_DISK_FULL = 112 ERROR_CALL_NOT_IMPLEMENTED = 120 ERROR_SEM_TIMEOUT = 121 ERROR_INSUFFICIENT_BUFFER = 122 ERROR_INVALID_NAME = 123 ERROR_MOD_NOT_FOUND = 126 ERROR_PROC_NOT_FOUND = 127 ERROR_DIR_NOT_EMPTY = 145 ERROR_BAD_THREADID_ADDR = 159 ERROR_BAD_ARGUMENTS = 160 ERROR_BAD_PATHNAME = 161 ERROR_ALREADY_EXISTS = 183 ERROR_INVALID_FLAG_NUMBER = 186 ERROR_ENVVAR_NOT_FOUND = 203 ERROR_FILENAME_EXCED_RANGE = 206 ERROR_MORE_DATA = 234 WAIT_TIMEOUT = 258 ERROR_NO_MORE_ITEMS = 259 ERROR_PARTIAL_COPY = 299 ERROR_INVALID_ADDRESS = 487 ERROR_THREAD_NOT_IN_PROCESS = 566 ERROR_CONTROL_C_EXIT = 572 ERROR_UNHANDLED_EXCEPTION = 574 ERROR_ASSERTION_FAILURE = 668 ERROR_WOW_ASSERTION = 670 ERROR_DBG_EXCEPTION_NOT_HANDLED = 688 ERROR_DBG_REPLY_LATER = 689 ERROR_DBG_UNABLE_TO_PROVIDE_HANDLE = 690 ERROR_DBG_TERMINATE_THREAD = 691 ERROR_DBG_TERMINATE_PROCESS = 692 ERROR_DBG_CONTROL_C = 693 ERROR_DBG_PRINTEXCEPTION_C = 694 ERROR_DBG_RIPEXCEPTION = 695 ERROR_DBG_CONTROL_BREAK = 696 ERROR_DBG_COMMAND_EXCEPTION = 697 ERROR_DBG_EXCEPTION_HANDLED = 766 ERROR_DBG_CONTINUE = 767 ERROR_ELEVATION_REQUIRED = 740 ERROR_NOACCESS = 998 ERROR_CIRCULAR_DEPENDENCY = 1059 ERROR_SERVICE_DOES_NOT_EXIST = 1060 ERROR_SERVICE_CANNOT_ACCEPT_CTRL = 1061 ERROR_SERVICE_NOT_ACTIVE = 1062 ERROR_FAILED_SERVICE_CONTROLLER_CONNECT = 1063 ERROR_EXCEPTION_IN_SERVICE = 1064 ERROR_DATABASE_DOES_NOT_EXIST = 1065 ERROR_SERVICE_SPECIFIC_ERROR = 1066 ERROR_PROCESS_ABORTED = 1067 ERROR_SERVICE_DEPENDENCY_FAIL = 1068 ERROR_SERVICE_LOGON_FAILED = 1069 ERROR_SERVICE_START_HANG = 1070 ERROR_INVALID_SERVICE_LOCK = 1071 ERROR_SERVICE_MARKED_FOR_DELETE = 1072 ERROR_SERVICE_EXISTS = 1073 ERROR_ALREADY_RUNNING_LKG = 1074 ERROR_SERVICE_DEPENDENCY_DELETED = 1075 ERROR_BOOT_ALREADY_ACCEPTED = 1076 ERROR_SERVICE_NEVER_STARTED = 1077 ERROR_DUPLICATE_SERVICE_NAME = 1078 ERROR_DIFFERENT_SERVICE_ACCOUNT = 1079 ERROR_CANNOT_DETECT_DRIVER_FAILURE = 1080 ERROR_CANNOT_DETECT_PROCESS_ABORT = 1081 ERROR_NO_RECOVERY_PROGRAM = 1082 ERROR_SERVICE_NOT_IN_EXE = 1083 ERROR_NOT_SAFEBOOT_SERVICE = 1084 ERROR_DEBUGGER_INACTIVE = 1284 ERROR_PRIVILEGE_NOT_HELD = 1314 ERROR_NONE_MAPPED = 1332 RPC_S_SERVER_UNAVAILABLE = 1722 # Standard access rights import sys if sys.version_info[0] >= 3: long = int DELETE = long(0x00010000) READ_CONTROL = long(0x00020000) WRITE_DAC = long(0x00040000) WRITE_OWNER = long(0x00080000) SYNCHRONIZE = long(0x00100000) STANDARD_RIGHTS_REQUIRED = long(0x000F0000) STANDARD_RIGHTS_READ = READ_CONTROL STANDARD_RIGHTS_WRITE = READ_CONTROL STANDARD_RIGHTS_EXECUTE = READ_CONTROL STANDARD_RIGHTS_ALL = long(0x001F0000) SPECIFIC_RIGHTS_ALL = long(0x0000FFFF) #--- Structures --------------------------------------------------------------- # typedef struct _LSA_UNICODE_STRING { # USHORT Length; # USHORT MaximumLength; # PWSTR Buffer; # } LSA_UNICODE_STRING, # *PLSA_UNICODE_STRING, # UNICODE_STRING, # *PUNICODE_STRING; class UNICODE_STRING(Structure): _fields_ = [ ("Length", USHORT), ("MaximumLength", USHORT), ("Buffer", PVOID), ] # From MSDN: # # typedef struct _GUID { # DWORD Data1; # WORD Data2; # WORD Data3; # BYTE Data4[8]; # } GUID; class GUID(Structure): _fields_ = [ ("Data1", DWORD), ("Data2", WORD), ("Data3", WORD), ("Data4", BYTE * 8), ] # From MSDN: # # typedef struct _LIST_ENTRY { # struct _LIST_ENTRY *Flink; # struct _LIST_ENTRY *Blink; # } LIST_ENTRY, *PLIST_ENTRY, *RESTRICTED_POINTER PRLIST_ENTRY; class LIST_ENTRY(Structure): _fields_ = [ ("Flink", PVOID), # POINTER(LIST_ENTRY) ("Blink", PVOID), # POINTER(LIST_ENTRY) ] #============================================================================== # This calculates the list of exported symbols. _all = set(vars().keys()).difference(_all) ##__all__ = [_x for _x in _all if not _x.startswith('_')] ##__all__.sort() #==============================================================================