Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add basic support to debug processes on Windows #2327

Merged
merged 7 commits into from
Mar 29, 2024

Conversation

peace-maker
Copy link
Member

Currently only windbg.debug() and windbg.attach() are implemented, which open a WinDbg instance and attach to the process.

Currently only `windbg.debug()` and `windbg.attach()` are implemented,
which open a WinDbg instance and attach to the process.
@peace-maker
Copy link
Member Author

Can you test and review again please @masthoon?

pwnlib/windbg.py Outdated Show resolved Hide resolved
pwnlib/util/proc.py Outdated Show resolved Hide resolved
@masthoon
Copy link

I added some comments.
I can't think of a way to wait for the debugger without using Windows specific code.

There is a issue with CREATE_SUSPENDED process in case the thread is not resumed (for example: using invalid windbg command line argument or if windbg is not installed).
On exit, the tube atexit callback will call process.close that first calls poll() then closes the file descriptors and finally stops the process. The problem is that the stdout fd.close is blocking (I don't know why) thus Python stops responding (even to ctrl+c) and it must be killed manually using Task Manager.
Reproduce with:

from pwn import *
process(['cmd.exe'], creationflags=4)
exit(0)

One way to fix this issue is to first terminate the process then close the file descriptors in process.close:

# pwntools\pwnlib\tubes\process.py
    def close(self):
        if self.proc is None:
            return

        # First check if we are already dead
        self.poll()
        # Terminate before closing fd
        if not self._stop_noticed:
            try:
                self.proc.kill()
                self.proc.wait()
                self._stop_noticed = time.time()
                self.info('Stopped process %r (pid %i)' % (self.program, self.pid))
            except OSError:
                pass

        # close file descriptors
        for fd in [self.proc.stdin, self.proc.stdout, self.proc.stderr]:
            if fd is not None:
                try:
                    fd.close()
                except IOError as e:
                    if e.errno != errno.EPIPE and e.errno != errno.EINVAL:
                        raise

Also resuming the process would work.
I'm not sure if changing the cleanup order in 'process.close' would cause issues on other operating systems.

peace-maker and others added 2 commits January 17, 2024 22:53
Only require PROCESS_QUERY_INFORMATION access and check for errors when opening the process.
@peace-maker
Copy link
Member Author

Maybe we can use psutil.Process.resume() to resume all threads in the process atexit, but that seems finicky.

Changing the order of killing and closing file descriptiors doesn't seem to matter. The file descriptors were closed in #576 and the testcase of starting and killing lots of processes still doesn't leave dangling file descriptors around when moving the closing after the killing of the process. So I think we can switch up the order and be fine on Linux too.

peace-maker and others added 3 commits January 17, 2024 23:57
@peace-maker peace-maker merged commit 8ba1bdf into Gallopsled:dev Mar 29, 2024
13 checks passed
@peace-maker peace-maker deleted the windbg branch March 29, 2024 15:04
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants