天天看点

在学习GrayHatPython构建Window调试器过程中遇到的问题

编程环境:PyCharm 2017.1.3

Python 3.6.0

参考教程:《Python灰帽子》(Justin Seitz著)

暑期自学《Python灰帽子》的过程中在掌握基本理论知识后学习第三章的过程中一上手就遇到了下面的异常,称之为异常是因为我编写的代码与书中所写无异,考虑到Python2.x与3.x版本之间的差异修改了部分函数,测试的时候却出现了问题。一再检查调试问题没有得到解决,故将其中信息列举在此,希望可以从互联网上得到帮助,或者自己在未来解决问题后可以来填坑。
本页的所有代码可以在我的[Github对应仓库](https://github.com/LicheeGit/PythonProject/tree/master/GrayHatPython)找到
首先是两个python脚本文件——my_debugger.py及my_debugger_defines.py。
           
# my_debugger.py
from ctypes import *
# from GrayHatPython.my_debugger_defines import *
from my_debugger_defines import *
# -*- coding: utf-8 -*-

kernel32 = windll.kernel32


class debugger():
    def __init__(self):
        pass

    def load(self, path_to_exe):
        # 参数 dwCreationFlags中的标志位控制着进程的创建方式。
        # 若希望新创建的进程独占一个新的控制台窗口,而不是与父进程共用同一个控制台
        # 可以加上标志位CREATE_NEW_CONSOLE

        creation_flags = DEBUG_PROCESS

        # 实例化之前定义的结构体
        startupinfo = STARTUPINFO()
        process_information = PROCESS_INFORMATION()

        '''
        在以下两个成员变量的共同作用下,新建进程将在一个单独的窗体中被显示,
        可以通过改变结构体STARTUPINFO中的各成员变量的值来控制debugger进程的行为
        '''

        startupinfo.dwFlags = 0x1
        startupinfo.wShowWindow = 0x0

        # 设置结构体STARTUPINFO中的成员变量cb的值,用以表示结构体本身的大小
        startupinfo.cb = sizeof(startupinfo)
        if kernel32.CreateProcessW(path_to_exe,
                                    None,
                                    None,
                                    None,
                                    None,
                                    creation_flags,
                                    None,
                                    None,
                                    byref(startupinfo),
                                    byref(process_information)):
            print("[*] We have successfully launched the process!")  # 问题出在这句之前,processInformation获取不到
            # print(path_to_exe)  # b'C:\\Windows\\System32\\calc.exe'
            print("[*] PID: %d" % process_information.dwProcessId)  # 获取PID的时候抛异常,其实是在启动进程的时候出了问题
            # Process finished with exit code -1073741819 (0xC0000005)

        else:
            print("[*] Error: 0x%08x." % kernel32.GetLastError())
           

其中创建了一个核心基类debugger(),后续会逐步添加各项调试功能。

将所有的结构体,联合体及常值定义放置于脚本文件my_debugger_defines.py之中。

# my_debugger_defines.py
# -*- coding: utf-8 -*-
from ctypes import *

# 为ctype变量创建符合匈牙利命名风格的匿名,这样可以使代码更接近Win32的风格
WORD = c_ushort
DWORD = c_ulong
LPBYTE = POINTER(c_ubyte)
LPTSTR = POINTER(c_char)
HANDLE = c_void_p

# 常值定义
DEBUG_PROCESS = 0x00000001
CREATE_NEW_CONSOLE = 0x00000010


# 定义函数CreateProcessA()所需的结构体
class STARTUPINFO(Structure):
    _fileds_ = [
        ("cb", DWORD),
        ("lpReserved", LPTSTR),
        ("lpDesktop", LPTSTR),
        ("lpTitle", LPTSTR),
        ("dwX", DWORD),
        ("dwY", DWORD),
        ("dwXSize", DWORD),
        ("dwYSize", DWORD),
        ("dwXCountChars", DWORD),
        ("dwYCountChars", DWORD),
        ("dwFillAttribute", DWORD),
        ("dwFlags", DWORD),
        ("wShowWindow", WORD),
        ("cbReserved2", WORD),
        ("lpReserved2", LPTSTR),
        ("hStdInput", HANDLE),
        ("hStdOutput", HANDLE),
        ("hStdError", HANDLE),
    ]


class PROCESS_INFORMATION(Structure):
    _fileds_ = [
        ("hProcess", HANDLE),
        ("hThread", HANDLE),
        ("dwProcessId", DWORD),
        ("dwThreadId", DWORD),
    ]
           

下面构建了一个测试用例my_test.py,与现有源码文件置于相同目录底下。

# my_test.py
import my_debugger
debugger = my_debugger.debugger()
debugger.load("C:\\Windows\\System32\\calc.exe")

           
测试脚本选择了Windows自带的计算器calc.exe作为测试对象,按照书上的讲解,通过命令行终端或者从IDE下执行这个脚本文件,会孵化一个新的计算器进程并输出相应的PID后退出。我在运行了测试脚本后情况如下——
![my_test.py运行结果](https://img-blog.csdn.net/20170914231811612?watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvbTBfMzc3ODY4NTk=/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/SouthEast)
首先输出了"[*] We have successfully launched the process!"然后在过了一段时间后输出进程结束并返回一个0xC0000005。经查阅相关信息,发现此错误通常出现在PyCharm上,在更换命令行终端运行后,仍能输出"[*] We have successfully launched the process!",仍然要等很长时间之后程序弹出窗口提示Python停止运行。之后在PyCharm中加断点调试也没看出个所以然,程序执行到需要从PID获取信息时就出现了停滞。我怀疑是在使用CreateProcessW()的时候出现了问题,因为在测试过程中查看进程并没有出现我们开启的计算器进程,估计是程序不停地在尝试开启进程最终出现了进程数过多导致测试异常。
原书中作者使用了CreateProcessA()创建进程,考虑到Python3.x改用了[CreateProcessW()](https://msdn.microsoft.com/en-us/library/windows/desktop/ms682425%28v=vs.85%29.aspx)(忘记当时为什么要改成这个了,MSDN相关网页不知道何由打不开了故无法查询相关信息),测试使用CreateProcessA()时得到的结果是
           

[*] Error: 0x00000002.

Process finished with exit code 0

我的解决思路是接下来将先充分理解Python脚本开启进程的代码。

刚刚发现了一篇讲CreateProcess用法的blog,里面涉及到了Unicode,我觉得我的调试器可能有救了,明天再说。