天天看點

在學習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,我覺得我的調試器可能有救了,明天再說。