Example #1
0
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_target_process_64 = bits == 64

    # Note: this restriction no longer applies (we create a process with the proper bitness from
    # this process so that the attach works).
    # if is_target_process_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_target_process_64, is_python_64bit()))

    with _acquire_mutex('_pydevd_pid_attach_mutex_%s' % (pid,), 10):
        print('--- Connecting to %s bits target (current process is: %s) ---' % (bits, 64 if is_python_64bit() else 32))

        with _win_write_to_shared_named_memory(python_code, pid):

            target_executable = get_target_filename(is_target_process_64, 'inject_dll_', '.exe')
            if not target_executable:
                raise RuntimeError('Could not find expected .exe file to inject dll in attach to process.')

            target_dll = get_target_filename(is_target_process_64)
            if not target_dll:
                raise RuntimeError('Could not find expected .dll file in attach to process.')

            print('\n--- Injecting attach dll: %s into pid: %s ---' % (os.path.basename(target_dll), pid))
            args = [target_executable, str(pid), target_dll]
            subprocess.check_call(args)

            # Now, if the first injection worked, go on to the second which will actually
            # run the code.
            target_dll_run_on_dllmain = get_target_filename(is_target_process_64, 'run_code_on_dllmain_', '.dll')
            if not target_dll_run_on_dllmain:
                raise RuntimeError('Could not find expected .dll in attach to process.')

            with _create_win_event('_pydevd_pid_event_%s' % (pid,)) as event:
                print('\n--- Injecting run code dll: %s into pid: %s ---' % (os.path.basename(target_dll_run_on_dllmain), pid))
                args = [target_executable, str(pid), target_dll_run_on_dllmain]
                subprocess.check_call(args)

                if not event.wait_for_event_set(10):
                    print('Timeout error: the attach may not have completed.')
            print('--- Finished dll injection ---\n')

    return 0
Example #2
0
 def get_process(self):
     """
     @rtype:  L{Process}
     @return: Parent Process object.
     """
     if self.__process is not None:
         return self.__process
     self.__load_Process_class()
     self.__process = Process(self.get_pid())
     return self.__process
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 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.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