《python灰帽子》學習筆記:寫一個windos 調試器(一)


一、開發內容介紹

  為了對一個進程進行調試,你首先必須用一些方法把調試器和進程連接起來。所以, 我
們的調試器要不然就是裝載一個可執行程序然后運行它, 要不然就是動態的附加到一個運行
的進程。Windows 的調試接口(Windows debugging API)提供了一個非常簡單的方法完成
這兩點。
  運行一個程序和附加到一個程序有細微的差別。 打開一個程序的優點在於他能在程序運
行任何代碼之前完全的控制程序。 這在分析病毒或者惡意代碼的時候非常有用。 附加到一個
進程,僅僅是強行的進入一個已經運行了的進程內部,它允許你跳過啟動部分的代碼,分析
你感興趣的代碼。你正在分析的地方也就是程序目前正在執行的地方。
  第一種方法,其實就是從調試器本身調用這個程序(調試器就是父進程,對被調試進程
的控制權限更大) 。在 Windows 上創建一個進程用 CreateProcessA()函數。將特定的標志傳
進這個函數,使得目標進程能夠被調試。

目標:使用CreateProcess打開一個進程

二、需要用到的winAPI函數

CreateProcess

函數原型:

BOOL CreateProcess
(
LPCTSTR lpApplicationName,
LPTSTR lpCommandLine,
LPSECURITY_ATTRIBUTES lpProcessAttributes,
LPSECURITY_ATTRIBUTES lpThreadAttributes,
BOOL bInheritHandles,
DWORD dwCreationFlags,
LPVOID lpEnvironment,
LPCTSTR lpCurrentDirectory,
LPSTARTUPINFO lpStartupInfo,
LPPROCESS_INFORMATIONlpProcessInformation
);
參數:
lpApplicationName
指向一個NULL結尾的、用來指定可執行模塊的字符串。
這個字符串可以是可執行模塊的 絕對路徑,也可以是 相對路徑,在后一種情況下,函數使用當前 驅動器和目錄建立可執行模塊的路徑。
這個參數可以被設為NULL,在這種情況下,可執行模塊的名字必須處於 lpCommandLine 參數最前面並由 空格符與后面的字符分開。
lpCommandLine
指向一個以NULL結尾的字符串,該字符串指定要執行的命令行。
這個參數可以為空,那么函數將使用lpApplicationName參數指定的字符串當做要運行的程序的命令行。
如果lpApplicationName和lpCommandLine參數都不為空,那么lpApplicationName參數指定將要被運行的模塊,lpCommandLine參數指定將被運行的模塊的命令行。新運行的進程可以使用 GetCommandLine函數獲得整個命令行。C語言程序可以使用argc和argv參數。
lpProcessAttributes
指向一個SECURITY_ATTRIBUTES結構體,這個 結構體決定是否返回的句柄可以被子進程繼承。如果lpProcessAttributes參數為空(NULL),那么句柄不能被繼承。
在Windows NT中: SECURITY_ATTRIBUTES結構的lpSecurityDescriptor成員指定了新進程的 安全描述符,如果參數為空,新進程使用默認的安全描述符。
lpThreadAttributes
同lpProcessAttribute,不過這個參數決定的是線程是否被繼承.通常置為NULL.
bInheritHandles
指示新進程是否從調用進程處繼承了句柄。
如果參數的值為真,調用進程中的每一個可繼承的打開句柄都將被子進程繼承。被繼承的句柄與原進程擁有完全相同的值和訪問權限。
dwCreationFlags
指定附加的、用來控制優先類和進程的創建的標志。以下的創建標志可以以除下面列出的方式外的任何方式組合后指定。
⑴值:CREATE_DEFAULT_ERROR_MODE
含義:新的進程不繼承調用進程的錯誤模式。CreateProcess函數賦予新進程當前的默認錯誤模式作為替代。應用程序可以調用 SetErrorMode函數設置當前的默認錯誤模式。
這個標志對於那些運行在沒有硬件錯誤環境下的多線程 外殼程序是十分有用的。
對於CreateProcess函數,默認的行為是為新進程繼承調用者的錯誤模式。設置這個標志以改變默認的處理方式。
⑵值:CREATE_NEW_CONSOLE
含義:新的進程將使用一個新的控制台,而不是繼承 父進程的控制台。這個標志不能與DETACHED_PROCESS標志一起使用。
⑶值:CREATE_NEW_PROCESS_GROUP
含義:新進程將是一個 進程樹的根進程。進程樹中的全部進程都是根進程的子進程。新進程樹的 用戶標識符與這個進程的標識符是相同的,由lpProcessInformation參數返回。進程樹經常使用GenerateConsoleCtrlEvent函數允許發送CTRL+C或CTRL+BREAK信號到一組控制台進程。
⑷值:CREATE_SEPARATE_WOW_VDM
如果被設置,新進程將會在一個私有的虛擬DOS機(VDM)中運行。另外,默認情況下所有的16位Windows應用程序都會在同一個共享的VDM中以 線程的方式運行。單獨運行一個16位程序的優點是一個應用程序的崩潰只會結束這一個VDM的運行;其他那些在不同VDM中運行的程序會繼續正常的運行。同樣的,在不同VDM中運行的16位Windows應用程序擁有不同的輸入隊列,這意味着如果一個程序暫時失去響應,在獨立的VDM中的應用程序能夠繼續獲得輸入。
⑸值:CREATE_SHARED_WOW_VDM
如果WIN.INI中的Windows段的DefaultSeparateVDM選項被設置為真,這個標識使得CreateProcess函數越過這個選項並在共享的虛擬DOS機中運行新進程。
⑹值:CREATE_SUSPENDED
含義:新進程的 主線程會以暫停的狀態被創建,直到調用 ResumeThread函數被調用時才運行。
⑺值:CREATE_UNICODE_ENVIRONMENT
含義:如果被設置,由lpEnvironment參數指定的環境塊使用Unicode 字符,如果為空,環境塊使用ANSI字符。
⑻值:DEBUG_PROCESS
含義:如果這個標志被設置,調用進程將被當做一個 調試程序,並且新進程會被當做被調試的進程。系統把被調試程序發生的所有調試事件通知給調試器。
如果你使用這個標志創建進程,只有調用進程(調用CreateProcess函數的進程)可以調用 WaitForDebugEvent函數。
⑼值:DEBUG_ONLY_THIS_PROCESS
含義:如果此標志沒有被設置且調用進程正在被調試,新進程將成為調試調用進程的調試器的另一個調試對象。如果調用進程沒有被調試,有關調試的行為就不會產生。
⑽值:DETACHED_PROCESS
含義:對於控制台進程,新進程沒有訪問 父進程控制台的權限。新進程可以通過 AllocConsole函數自己創建一個新的控制台。這個標志不可以與CREATE_NEW_CONSOLE標志一起使用。
〔11〕值:CREATE_NO_WINDOW
含義:系統不為新進程創建CUI窗口,使用該標志可以創建不含窗口的CUI程序。
dwCreationFlags參數
還用來控制新進程的優先類,優先類用來決定此進程的 線程調度的優先級。如果下面的優先級類標志都沒有被指定,那么默認的優先類是NORMAL_PRIORITY_CLASS,除非被創建的進程是IDLE_PRIORITY_CLASS。在這種情況下子進程的默認優先類是IDLE_PRIORITY_CLASS。
可以選擇下面的標志中的一個:
優先級:HIGH_PRIORITY_CLASS
含義:指示這個進程將執行時間臨界的任務,所以它必須被立即運行以保證正確。這個優先級的程序優先於正常優先級或空閑優先級的程序。一個例子是Windows任務列表,為了保證當用戶調用時可以立刻響應,放棄了對系統負荷的考慮。確保在使用高優先級時應該足夠謹慎,因為一個高優先級的CPU關聯應用程序可以占用幾乎全部的CPU可用時間。
優先級:IDLE_PRIORITY_CLASS
含義:指示這個進程的線程只有在系統空閑時才會運行並且可以被任何高優先級的任務打斷。例如 屏幕保護程序。空閑優先級會被子進程繼承。
優先級:NORMAL_PRIORITY_CLASS
含義:指示這個進程沒有特殊的任務調度要求。
優先級:REALTIME_PRIORITY_CLASS
含義:指示這個進程擁有可用的最高優先級。一個擁有實時優先級的進程的 線程可以打斷所有其他進程線程的執行,包括正在執行重要任務的系統進程。例如,一個執行時間稍長一點的實時進程可能導致 磁盤緩存不足或鼠標反映遲鈍。
lpEnvironment
指向一個新進程的環境塊。如果此參數為空,新進程使用調用進程的環境。
一個環境塊存在於一個由以NULL結尾的字符串組成的塊中,這個塊也是以NULL結尾的。每個字符串都是name=value的形式。
因為相等標志被當做 分隔符,所以它不能被 環境變量當做變量名。
與其使用應用程序提供的環境塊,不如直接把這個參數設為空,系統驅動器上的當前目錄信息不會被自動傳遞給新創建的進程。對於這個情況的探討和如何處理,請參見注釋一節。
環境塊可以包含Unicode或ANSI字符。如果lpEnvironment指向的環境塊包含Unicode 字符,那么dwCreationFlags字段的CREATE_UNICODE_ENⅥRONMENT標志將被設置。如果塊包含ANSI字符,該標志將被清空。
請注意一個ANSI環境塊是由兩個零字節結束的:一個是字符串的結尾,另一個用來結束這個快。一個Unicode環境塊是由四個零字節結束的:兩個代表字符串結束,另兩個用來結束塊。
lpCurrentDirectory
指向一個以NULL結尾的字符串,這個字符串用來指定子進程的 工作路徑。這個字符串必須是一個包含驅動器名的 絕對路徑。如果這個參數為空,新進程將使用與調用進程相同的驅動器和目錄。這個選項是一個需要啟動應用程序並指定它們的驅動器和工作目錄的 外殼程序的主要條件。
lpStartupInfo
指向一個用於決定新進程的主 窗體如何顯示的 STARTUPINFO 結構體
lpProcessInformation
指向一個用來接收新進程的識別信息的 PROCESS_INFORMATION結構體。
三、代碼

創建兩個 Python 文件 my_debugger.py 和 my_debugger_defines.py。 我們將創建一個父類
debugger() 接着逐漸的增加各種調試函數。另外,把所有的結構,聯合,常量放 到
my_debugger_defines.py 方便以后維護。

# my_debugger_defines.py
from ctypes import *
# Let's map the Microsoft types to ctypes for clarity
WORD = c_ushort
DWORD = c_ulong
LPBYTE = POINTER(c_ubyte)
LPTSTR = POINTER(c_char)
HANDLE = c_void_p
# Constants
DEBUG_PROCESS = 0x00000001
CREATE_NEW_CONSOLE = 0x00000010
# Structures for CreateProcessA() function
class STARTUPINFO(Structure):
_fields_ = [
("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", LPBYTE),
("hStdInput", HANDLE),
("hStdOutput", HANDLE),
("hStdError", HANDLE),
]
class PROCESS_INFORMATION(Structure):
_fields_ = [
("hProcess", HANDLE),
("hThread", HANDLE),
("dwProcessId", DWORD),
("dwThreadId", DWORD),
]

# my_debugger.py
from ctypes import *
from my_debugger_defines import *
kernel32 = windll.kernel32

class debugger():
def __init__(self):
pass
def load(self,path_to_exe):
# dwCreation flag determines how to create the process
# set creation_flags = CREATE_NEW_CONSOLE if you want
# to see the calculator GUI
creation_flags = DEBUG_PROCESS
# instantiate the structs
startupinfo = STARTUPINFO()
process_information = PROCESS_INFORMATION()
# The following two options allow the started process
# to be shown as a separate window. This also illustrates
# how different settings in the STARTUPINFO struct can affect
# the debuggee.
startupinfo.dwFlags = 0x1
startupinfo.wShowWindow = 0x0
# We then initialize the cb variable in the STARTUPINFO struct
# which is just the size of the struct itself
startupinfo.cb = sizeof(startupinfo)
if kernel32.CreateProcessA(path_to_exe,
None,
None,
None,
None,
creation_flags,
None,
None,
byref(startupinfo),
byref(process_information)):
print "[*] We have successfully launched the process!"
print "[*] PID: %d" % process_information.dwProcessId
else:
print "[*] Error: 0x%08x." % kernel32.GetLastError()

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

運行結果:

Connected to pydev debugger (build 145.844)
[*] We have successfully launched the process!
[*] PID: 4720


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM