def run_python_code_windows(pid, python_code, connect_debugger_tracing=False, show_debug_info=0): assert '\'' not in python_code, 'Having a single quote messes with our command.' from winappdbg import compat from winappdbg.process import Process if not isinstance(python_code, compat.bytes): python_code = compat.b(python_code) process = Process(pid) bits = process.get_bits() is_64 = bits == 64 if is_64 != is_python_64bit(): raise RuntimeError("The architecture of the Python used to connect doesn't match the architecture of the target.\n" "Target 64 bits: %s\n" "Current Python 64 bits: %s" % (is_64, is_python_64bit())) debug('Connecting to %s bits target' % (bits,)) assert resolve_label(process, compat.b('PyGILState_Ensure')) filedir = os.path.dirname(__file__) if is_64: suffix = 'amd64' else: suffix = 'x86' target_dll = os.path.join(filedir, 'attach_%s.dll' % suffix) if not os.path.exists(target_dll): raise RuntimeError('Could not find dll file to inject: %s' % target_dll) debug('Injecting dll') process.inject_dll(target_dll.encode('mbcs')) debug('Dll injected') process.scan_modules() attach_func = resolve_label(process, compat.b('AttachAndRunPythonCode')) assert attach_func debug('Allocating code in target process') code_address = process.malloc(len(python_code)) assert code_address debug('Writing code in target process') process.write(code_address, python_code) debug('Allocating return value memory in target process') return_code_address = process.malloc(ctypes.sizeof(ctypes.c_int)) assert return_code_address CONNECT_DEBUGGER = 2 startup_info = 0 if show_debug_info: SHOW_DEBUG_INFO = 1 startup_info |= SHOW_DEBUG_INFO # Uncomment to show debug info if connect_debugger_tracing: startup_info |= CONNECT_DEBUGGER process.write_int(return_code_address, startup_info) helper = GenShellCodeHelper(is_64) if is_64: # Interesting read: http://msdn.microsoft.com/en-us/library/ms235286.aspx # Overview of x64 Calling Conventions (for windows: Linux is different!) # Register Usage: http://msdn.microsoft.com/en-us/library/9z1stfyw.aspx # The registers RAX, RCX, RDX, R8, R9, R10, R11 are considered volatile and must be considered destroyed on function calls (unless otherwise safety-provable by analysis such as whole program optimization). # # The registers RBX, RBP, RDI, RSI, RSP, R12, R13, R14, and R15 are considered nonvolatile and must be saved and restored by a function that uses them. # # Important: RCX: first int argument with helper.push('rdi'): # This one REALLY must be pushed/poped with helper.push('rsp'): with helper.push('rbp'): with helper.push('rbx'): with helper.push('rdi'): # Note: pop is automatic. helper.mov_to_register_addr('rcx', helper.pack_address(code_address)) helper.mov_to_register_addr('rdx', helper.pack_address(return_code_address)) helper.mov_to_register_addr('rbx', helper.pack_address(attach_func)) helper.call('rbx') else: with helper.push('eax'): # Note: pop is automatic. with helper.push('ebp'): with helper.push('ebx'): with helper.preserve_stack(): # Put our code as a parameter in the stack (on x86, we push parameters to # the stack) helper.push_addr(helper.pack_address(return_code_address)) helper.push_addr(helper.pack_address(code_address)) helper.mov_to_register_addr('ebx', helper.pack_address(attach_func)) helper.call('ebx') helper.ret() code = helper.get_code() # Uncomment to see the disassembled version of what we just did... # with open('f.asm', 'wb') as stream: # stream.write(code) # # exe = r'x:\nasm\nasm-2.07-win32\nasm-2.07\ndisasm.exe' # if is_64: # arch = '64' # else: # arch = '32' # # subprocess.call((exe + ' -b %s f.asm' % arch).split()) debug('Injecting code to target process') thread, _thread_address = process.inject_code(code, 0) timeout = None # Could receive timeout in millis. debug('Waiting for code to complete') thread.wait(timeout) return_code = process.read_int(return_code_address) if return_code == 0: print('Attach finished successfully.') else: print('Error when injecting code in target process. Error code: %s (on windows)' % (return_code,)) process.free(thread.pInjectedMemory) process.free(code_address) process.free(return_code_address) return return_code
def run_python_code_windows(pid, python_code, connect_debugger_tracing=False, show_debug_info=0): assert '\'' not in python_code, 'Having a single quote messes with our command.' from winappdbg import compat from winappdbg.process import Process if not isinstance(python_code, compat.bytes): python_code = compat.b(python_code) process = Process(pid) bits = process.get_bits() is_64 = bits == 64 if is_64 != is_python_64bit(): raise RuntimeError( "The architecture of the Python used to connect doesn't match the architecture of the target.\n" "Target 64 bits: %s\n" "Current Python 64 bits: %s" % (is_64, is_python_64bit())) print('Connecting to %s bits target' % (bits, )) assert resolve_label(process, compat.b('PyGILState_Ensure')) filedir = os.path.dirname(__file__) if is_64: suffix = 'amd64' else: suffix = 'x86' target_dll = os.path.join(filedir, 'attach_%s.dll' % suffix) if not os.path.exists(target_dll): raise RuntimeError('Could not find dll file to inject: %s' % target_dll) print('Injecting dll') process.inject_dll(target_dll.encode('mbcs')) print('Dll injected') process.scan_modules() attach_func = resolve_label(process, compat.b('AttachAndRunPythonCode')) assert attach_func print('Allocating code in target process') code_address = process.malloc(len(python_code)) assert code_address print('Writing code in target process') process.write(code_address, python_code) print('Allocating return value memory in target process') return_code_address = process.malloc(ctypes.sizeof(ctypes.c_int)) assert return_code_address CONNECT_DEBUGGER = 2 startup_info = 0 if show_debug_info: SHOW_DEBUG_INFO = 1 startup_info |= SHOW_DEBUG_INFO # Uncomment to show debug info if connect_debugger_tracing: startup_info |= CONNECT_DEBUGGER process.write_int(return_code_address, startup_info) helper = GenShellCodeHelper(is_64) if is_64: # Interesting read: http://msdn.microsoft.com/en-us/library/ms235286.aspx # Overview of x64 Calling Conventions (for windows: Linux is different!) # Register Usage: http://msdn.microsoft.com/en-us/library/9z1stfyw.aspx # The registers RAX, RCX, RDX, R8, R9, R10, R11 are considered volatile and must be considered destroyed on function calls (unless otherwise safety-provable by analysis such as whole program optimization). # # The registers RBX, RBP, RDI, RSI, RSP, R12, R13, R14, and R15 are considered nonvolatile and must be saved and restored by a function that uses them. # # Important: RCX: first int argument with helper.push('rdi'): # This one REALLY must be pushed/poped with helper.push('rsp'): with helper.push('rbp'): with helper.push('rbx'): with helper.push('rdi'): # Note: pop is automatic. helper.mov_to_register_addr( 'rcx', helper.pack_address(code_address)) helper.mov_to_register_addr( 'rdx', helper.pack_address(return_code_address)) helper.mov_to_register_addr( 'rbx', helper.pack_address(attach_func)) helper.call('rbx') else: with helper.push('eax'): # Note: pop is automatic. with helper.push('ebp'): with helper.push('ebx'): with helper.preserve_stack(): # Put our code as a parameter in the stack (on x86, we push parameters to # the stack) helper.push_addr( helper.pack_address(return_code_address)) helper.push_addr(helper.pack_address(code_address)) helper.mov_to_register_addr( 'ebx', helper.pack_address(attach_func)) helper.call('ebx') helper.ret() code = helper.get_code() # Uncomment to see the disassembled version of what we just did... # with open('f.asm', 'wb') as stream: # stream.write(code) # # exe = r'x:\nasm\nasm-2.07-win32\nasm-2.07\ndisasm.exe' # if is_64: # arch = '64' # else: # arch = '32' # # subprocess.call((exe + ' -b %s f.asm' % arch).split()) print('Injecting code to target process') thread, _thread_address = process.inject_code(code, 0) timeout = None # Could receive timeout in millis. print('Waiting for code to complete') thread.wait(timeout) return_code = process.read_int(return_code_address) if return_code == 0: print('Attach finished successfully.') else: print( 'Error when injecting code in target process. Error code: %s (on windows)' % (return_code, )) process.free(thread.pInjectedMemory) process.free(code_address) process.free(return_code_address) return return_code
def run_python_code_windows(pid, python_code, connect_debugger_tracing=False, show_debug_info=0): assert '\'' not in python_code, 'Having a single quote messes with our command.' from winappdbg.process import Process if not isinstance(python_code, bytes): python_code = python_code.encode('utf-8') process = Process(pid) bits = process.get_bits() is_64 = bits == 64 if is_64 != is_python_64bit(): raise RuntimeError("The architecture of the Python used to connect doesn't match the architecture of the target.\n" "Target 64 bits: %s\n" "Current Python 64 bits: %s" % (is_64, is_python_64bit())) print('Connecting to %s bits target' % (bits,)) assert resolve_label(process, b'PyGILState_Ensure') filedir = os.path.dirname(__file__) if is_64: suffix = 'amd64' else: suffix = 'x86' target_dll = os.path.join(filedir, 'attach_%s.dll' % suffix) if not os.path.exists(target_dll): raise RuntimeError('Could not find dll file to inject: %s' % target_dll) print('Injecting dll') process.inject_dll(target_dll.encode('mbcs')) print('Dll injected') process.scan_modules() attach_func = resolve_label(process, b'AttachAndRunPythonCode') assert attach_func print('Allocating code in target process') assert isinstance(python_code, bytes) code_address = process.malloc(len(python_code)) assert code_address print('Writing code in target process') process.write(code_address, python_code) print('Allocating return value memory in target process') attach_info_address = process.malloc(ctypes.sizeof(ctypes.c_int)) assert attach_info_address CONNECT_DEBUGGER = 2 attach_info = 0 if show_debug_info: SHOW_DEBUG_INFO = 1 attach_info |= SHOW_DEBUG_INFO # Uncomment to show debug info if connect_debugger_tracing: attach_info |= CONNECT_DEBUGGER # Note: previously the attach_info address was treated as read/write to have the return # value, but it seems that sometimes when the program wrote back the memory became # unreadable with the stack trace below when trying to read, so, we just write and # no longer inspect the return value. # i.e.: # Traceback (most recent call last): # File "X:\pydev\plugins\org.python.pydev.core\pysrc\pydevd_attach_to_process\attach_pydevd.py", line 72, in <module> # main(process_command_line(sys.argv[1:])) # File "X:\pydev\plugins\org.python.pydev.core\pysrc\pydevd_attach_to_process\attach_pydevd.py", line 68, in main # setup['pid'], python_code, connect_debugger_tracing=True, show_debug_info=show_debug_info_on_target_process) # File "X:\pydev\plugins\org.python.pydev.core\pysrc\pydevd_attach_to_process\add_code_to_python_process.py", line 392, in run_python_code_windows # return_code = process.read_int(return_code_address) # File "X:\pydev\plugins\org.python.pydev.core\pysrc\pydevd_attach_to_process\winappdbg\process.py", line 1673, in read_int # return self.__read_c_type(lpBaseAddress, b'@l', ctypes.c_int) # File "X:\pydev\plugins\org.python.pydev.core\pysrc\pydevd_attach_to_process\winappdbg\process.py", line 1568, in __read_c_type # packed = self.read(address, size) # File "X:\pydev\plugins\org.python.pydev.core\pysrc\pydevd_attach_to_process\winappdbg\process.py", line 1598, in read # if not self.is_buffer(lpBaseAddress, nSize): # File "X:\pydev\plugins\org.python.pydev.core\pysrc\pydevd_attach_to_process\winappdbg\process.py", line 2843, in is_buffer # mbi = self.mquery(address) # File "X:\pydev\plugins\org.python.pydev.core\pysrc\pydevd_attach_to_process\winappdbg\process.py", line 2533, in mquery # return win32.VirtualQueryEx(hProcess, lpAddress) # File "X:\pydev\plugins\org.python.pydev.core\pysrc\pydevd_attach_to_process\winappdbg\win32\kernel32.py", line 3742, in VirtualQueryEx # raise ctypes.WinError() # PermissionError: [WinError 5] Access is denied. # Process finished with exitValue: 1 process.write_int(attach_info_address, attach_info) helper = GenShellCodeHelper(is_64) if is_64: # Interesting read: http://msdn.microsoft.com/en-us/library/ms235286.aspx # Overview of x64 Calling Conventions (for windows: Linux is different!) # Register Usage: http://msdn.microsoft.com/en-us/library/9z1stfyw.aspx # The registers RAX, RCX, RDX, R8, R9, R10, R11 are considered volatile and must be considered destroyed on function calls (unless otherwise safety-provable by analysis such as whole program optimization). # # The registers RBX, RBP, RDI, RSI, RSP, R12, R13, R14, and R15 are considered nonvolatile and must be saved and restored by a function that uses them. # # Important: RCX: first int argument with helper.push('rdi'): # This one REALLY must be pushed/poped with helper.push('rsp'): with helper.push('rbp'): with helper.push('rbx'): with helper.push('rdi'): # Note: pop is automatic. helper.mov_to_register_addr('rcx', helper.pack_address(code_address)) helper.mov_to_register_addr('rdx', helper.pack_address(attach_info_address)) helper.mov_to_register_addr('rbx', helper.pack_address(attach_func)) helper.call('rbx') else: with helper.push('eax'): # Note: pop is automatic. with helper.push('ebp'): with helper.push('ebx'): with helper.preserve_stack(): # Put our code as a parameter in the stack (on x86, we push parameters to # the stack) helper.push_addr(helper.pack_address(attach_info_address)) helper.push_addr(helper.pack_address(code_address)) helper.mov_to_register_addr('ebx', helper.pack_address(attach_func)) helper.call('ebx') helper.ret() code = helper.get_code() # Uncomment to see the disassembled version of what we just did... # with open('f.asm', 'wb') as stream: # stream.write(code) # # exe = r'x:\nasm\nasm-2.07-win32\nasm-2.07\ndisasm.exe' # if is_64: # arch = '64' # else: # arch = '32' # # subprocess.call((exe + ' -b %s f.asm' % arch).split()) print('Injecting code to target process') thread, _thread_address = process.inject_code(code, 0) timeout = None # Could receive timeout in millis. print('Waiting for code to complete') thread.wait(timeout) # return_code = process.read_int(attach_info_address) # if return_code == 0: # print('Attach finished successfully.') # else: # print('Error when injecting code in target process. Error code: %s (on windows)' % (return_code,)) process.free(thread.pInjectedMemory) process.free(code_address) process.free(attach_info_address) return 0