久久国产成人av_抖音国产毛片_a片网站免费观看_A片无码播放手机在线观看,色五月在线观看,亚洲精品m在线观看,女人自慰的免费网址,悠悠在线观看精品视频,一级日本片免费的,亚洲精品久,国产精品成人久久久久久久

分享

VC知識庫文章

 quasiceo 2013-12-13
稱號:未設(shè)置
簡介:...
  • 文章概要:
  • 本文前面討論了用幾種不同的方法來獲取進程及其相關(guān) DLLs 的信息,,例如通過 PSAPI,、NTDLL 以及TOOLHELP32 庫提供的 APIs,,在這一部分,,作者給出了幾種獲得系統(tǒng)級信息的非常規(guī)方法,你可以輕松將它們集成到自己的工具包中,。本文范例包含三個實用工具: LoadLibrarySpy,,監(jiān)視并掃描應(yīng)用程序加載了哪些 DLLs; WindowDump,,獲取任何窗口的的內(nèi)容以及窗口的詳細(xì)描述信息,; FileUsage,重定向控制臺程序,,揭示哪個進程正在使用打開的文件,;

本文假設(shè)你熟悉 Win32,DLL

定制調(diào)試診斷工具和實用程序――擺脫DLL"地獄"(DLL Hell)的困擾(一)

定制調(diào)試診斷工具和實用程序――擺脫DLL"地獄"(DLL Hell)的困擾(二)

定制調(diào)試診斷工具和實用程序――擺脫DLL"地獄"(DLL Hell)的困擾(三)

定制調(diào)試診斷工具和實用程序――擺脫DLL"地獄"(DLL Hell)的困擾(四)

定制調(diào)試診斷工具和實用程序――擺脫DLL"地獄"(DLL Hell)的困擾(五)

摘要

本文前面討論了用幾種不同的方法來獲取進程及其相關(guān) DLLs 的信息,,例如通過 PSAPI,、NTDLL 以及TOOLHELP32 庫提供的 APIs,在這一部分,,作者給出了幾種獲得系統(tǒng)級信息的非常規(guī)方法,,你可以輕松將它們集成到自己的工具包中。本文范例包含三個實用工具:

LoadLibrarySpy,,監(jiān)視并掃描應(yīng)用程序加載了哪些 DLLs,;

WindowDump,,獲取任何窗口的的內(nèi)容以及窗口的詳細(xì)描述信息;

FileUsage,,重定向控制臺程序,,揭示哪個進程正在使用打開的文件;

本文前面的部分討論了如何用有著良好文檔描述的 API 函數(shù)來獲取運行進程列表以及它們加載的 DLLs 信息,。接下來我將用不同的方法,,或者說是非正式的方法來獲取系統(tǒng)級信息,首先,,我將深入分析 Win32 調(diào)試 API 以及 Windows 加載器(Windows Loader)提供的痕跡來揭示給定進程是如何加載 DLL 的,。我將借助我的 CApplicationDebugger 可重用類,用幾種不同的方法來分析 DLL 重定位的原因,。

接著,,我將生成兩個工具。LoadLibrarySpy 掃描 DLL 重定位,。WindowDump 竊取任何窗口的內(nèi)容和詳細(xì)描述信息,。最后,在討論進程環(huán)境塊(PEB)內(nèi)部結(jié)構(gòu)之前,,我會向你展示如何操縱控制臺程序產(chǎn)生的輸出以便摸索尋找一些未公開的信息,。

回到 DLL Hell

前面我們已經(jīng)看到獲取所有靜態(tài)或動態(tài)加載的 DLLs 列表是很容易的事情。但是對動態(tài)加載的DLL而言,,情況比想象的稍微復(fù)雜一些,。例如,DllSpy 和 ProcessSpy 兩個工具依據(jù)某個時間點獲得的快照,。因此,,有可能出現(xiàn)來不及掃描某個被快速加載和卸載的DLL,。Win32 調(diào)試 API 提供了對這個問題的解決辦法:在調(diào)試程序時,, 這些 API 可以對被調(diào)試程序加載和卸載的任何DLL了如指掌。

要實現(xiàn)我的意圖,,并不需要一個功能完整,,名副其實的調(diào)試器,但我必須偵測到新 DLL 何時被加載到進程地址空間,。因此,,我將討論 Win32 調(diào)試 API 的基本知識以及它們在 Windows NT、Windows 2000 和 Windows XP 操作系統(tǒng)中有用的擴展,。

為了調(diào)試一個程序,,你首先必須使用用下面這些特殊的標(biāo)志之一調(diào)用 CreateProcess 來啟動擬調(diào)試的程序。DEBUG_PROCESS 表示請求來自被調(diào)試程序以及被調(diào)試程序啟動的每一個進程的事件,。DEBUG_ONLY_THIS_PROCESS 表示只請求來自被調(diào)試程序的事件(而不是來自其子進程的事件),。

使用 DEBUG_ONLY_THIS_PROCESS 標(biāo)志時,,調(diào)試器將接收不到來自被調(diào)試程序啟動的進程事件。性能監(jiān)視器(perfmon.exe)就是一個很好的例子,,此標(biāo)志對這個程序沒有作用,。性能監(jiān)視器是一個簡單的打包程序,其作用 只不過是啟動另外一個程序――微軟管理控制臺(MMC),,并傳遞任何所需的參數(shù)使它顯示性能計數(shù)器,。

在被調(diào)試程序的生命期內(nèi),Windows 通知調(diào)試器 Figure 1 所列出的事件,。這些事件由 DEBUG_EVENT 結(jié)構(gòu)描述,,如 Figure 2 所示。

Figure 1 Events Received by the Debugger

Event Value
Description
CREATE_PROCESS_DEBUG_EVENT
This is the first event received by the debugger, even before LOAD_DLL_ DEBUG_EVENT for statically linked DLLs.
EXIT_PROCESS_DEBUG_EVENT
This is the last event received by the debugger. It means the debuggee has reached the end of its life.
EXCEPTION_DEBUG_EVENT
An exception occurs. Its description is in u.Exception. It is received before any catch when the dw- FirstChance flag is set. If there is no catch, a second event is received before the debuggee is terminated.
CREATE_THREAD_DEBUG_EVENT
A new thread is created. Its description is in u.CreateThread.
EXIT_THREAD_DEBUG_EVENT
The description of an exiting thread is set in the u.ExitThread member.
LOAD_DLL_DEBUG_EVENT
When a DLL is mapped in the debuggee address space, either statically linked or dynamically loaded, this event is received by the debugger.
UNLOAD_DLL_DEBUG_EVENT
Unlike the previous event, this occurs only when a DLL is dynamically unloaded. This means it cannot be used to detect when each statically loaded DLL is unloaded at the end of the process life.
OUTPUT_DEBUG_STRING_EVENT
Each time the debuggee calls OutputDebugString, the debugger receives this event with the string in u.DebugString.lpDebug.StringData, but in the debuggee address space.
RIP_EVENT
According to the documentation, this event is received when a RIP-de-bugging event (system debugging error) occurs, but I have never seen this in practice.
01.Figure 2 DEBUG_EVENT
02. 
03.typedef struct _DEBUG_EVENT {
04.DWORD dwDebugEventCode;
05.DWORD dwProcessId;
06.DWORD dwThreadId;
07.union
08.{
09.EXCEPTION_DEBUG_INFO      Exception;
10.CREATE_THREAD_DEBUG_INFO  CreateThread;
11.CREATE_PROCESS_DEBUG_INFO CreateProcessInfo;
12.EXIT_THREAD_DEBUG_INFO    ExitThread;
13.EXIT_PROCESS_DEBUG_INFO   ExitProcess;
14.LOAD_DLL_DEBUG_INFO       LoadDll;
15.UNLOAD_DLL_DEBUG_INFO     UnloadDll;
16.OUTPUT_DEBUG_STRING_INFO  DebugString;
17.RIP_INFO                  RipInfo;
18.} u;
19.} DEBUG_EVENT, *LPDEBUG_EVENT;

為了接收這些事件,,調(diào)試器必須調(diào)用 WaitForDebugEvent,。該函數(shù)阻塞調(diào)試器的運行,直到被調(diào)試程序發(fā)生 Figure 1 所列的事件之一,,或者超時參數(shù)中給定的秒數(shù)為止,。當(dāng)調(diào)試器處理某個事件時,它調(diào)用 ContinueDebugEvent 讓被調(diào)試程序繼續(xù)其生命之旅,。注意:在調(diào)試器中,,當(dāng) WaitForDebugEvent 解除阻塞時,所有被調(diào)試者線程被凍結(jié),,在調(diào)用 ContinueDebugEvent 期間被解凍,。參見 Figure 3:

Figure 3 調(diào)試事件流

CApplicationDebugger

調(diào)用 CreateProcess 的線程必須是進入調(diào)試循環(huán)的線程。既然調(diào)試器阻塞于 WaitForDebugEvent,,因此最好讓這部分代碼運行在一個與主UI線程不同的專門線程中,。本文將其行為包裝在 CApplicationDebugger 類中,其聲明參見本文附帶源代碼中的 ApplicationDebugger.h 文件,,這個類的一部分靈感還來自 Matt Pietrek 的 LoadProf32(參見 MSJJul95.exe),。

CApplicationDebugger 是一個虛擬類,因為你得從它派生并實現(xiàn)自己的重寫版本,,以便特定的調(diào)試事件發(fā)生時進行相應(yīng)的調(diào)用,。這個類被用于生成 LoadLibrarySpy(參見 Figure 4),這是一個調(diào)試程序和監(jiān)控 DLL 加載和卸載的工具,,不論是靜態(tài)加載還是動態(tài)加載,,也不論是不是有加載地址沖突,它都能監(jiān)控,。

Figure 4 LoadLibrarySpy

調(diào)用 CreateProcess 是在 CApplicationDebugger::LoadTheProcess 中進行的,,為簡單起見,參數(shù)使用 DEBUG_ONLY_THIS_PROCESS。如果需要,,你可以將 CApplicationDebugger 擴展成能處理來自多個被調(diào)試進程的事件,,對于 MMC 管理單元(snap-ins)很有用。

CLoadLibrarySpyDlg 類負(fù)責(zé)對話框自身的處理,,同時也是暗中監(jiān)視 CApplicationDebugger 派生類的線程宿主,。CModuleListCtrl 類負(fù)責(zé)顯示附屬到每個DLL的詳細(xì)信息 CModuleInfo*;針對每個 DLL,,這個類存儲的詳細(xì)信息見 Figure 5,。

Figure 5 DLL Details

Type
Member
Description
CString
m_szName
Module name
DWORD
m_LoadAddress
hModule
DWORD
m_PreferedLoadAddress
Supposed loading address (at link time)
CString
m_szReason
Gets real info
BOOL
m_bDynamic
TRUE if loaded through LoadLibrary
BOOL
m_bAfterStartup
TRUE if loaded after the process starts
DWORD
m_nLoaded
Number of times it has been loaded
DWORD
m_nRemoved
Number of times it has been unloaded
CString
m_szFullPath
Full path name of the DLL
DWORD
m_Position
Loading position, starting from 1

當(dāng)某個 DLL 被加載,對話框便調(diào)用 AddModule 方法,;反之卸載DLL時,,則執(zhí)行 RemoveModule 方法。這兩個方法都以 UpdateModule 方法告終,,從而更新與該 DLL 對應(yīng)的 CModuleObject 對象的 m_nLoaded 或 m_nRemoved,。如果不存在這樣的對象,則會創(chuàng)建一個新的對象,,并將它添加到列表框中,。

不要為 m_nLoaded 或 m_nRemoved 而困惑。如果你針對某一行的相同 DLL 多次調(diào)用 LoadLibrary,,調(diào)試器只會收到 LOAD_DLL_DEBUG_EVENT 一次,,并且 m_nLoaded 被賦值為 1。如果調(diào)試器接收到某個 DLL 的 UNLOAD_DLL_DEBUG_EVENT,,你便可以確定該 DLL 不再被該進程使用,。因此,對于靜態(tài) DLLs 而言,,你決不會收到此事件,,即使可能在進程被啟動后,它們被動態(tài)加載并用 LoadLibrary/FreeLibrary 卸載,。

處理被調(diào)試程序的事件

一旦被調(diào)試程序的進程啟動后,,調(diào)試器便等待某些事件的發(fā)生。這就是為什么它應(yīng)該在一個與主 UI 線程不同的單獨線程中的原因,,當(dāng)主窗口是一個模式對話框時尤其如此,!

為了在 CLoadLibrarySpyDlg 中有效地使用 CApplicationDebugger,,GoThreadProc 線程過程首先聲明一個 CApplicationDebugger 對象,,指定要執(zhí)行的命令行并說明是否截獲來自被調(diào)試程序的 OutputDebugString 或 TRACE 輸出。接著,,DebugProcess 阻塞,,直到被調(diào)試程序終止(接收 EXIT_PROCESS_DEBUG_EVENT 或第二次的未處理異常),或者重寫的方法之一未返回 DBG_CONTINUE。

線程與對話框之間的溝通機制很簡單:當(dāng)某個被調(diào)試事件發(fā)生時,,調(diào)試器線程將 Figure 6 中所列的消息發(fā)送到對話框,。其中第一個消息是在加載了所有靜態(tài)鏈接的 DLLs 時發(fā)送;也就是說,,當(dāng) Windows 觸發(fā)第一個(偽)斷點時,,便發(fā)信號給調(diào)試器,然后調(diào)試器調(diào)用可重寫的 OnProcessRunning 將消息發(fā)送給對話框,。第二個消息是當(dāng)被調(diào)試程序卸載某個 DLL 時,,由可重寫的 OnUnloadDLLDebugEvent 調(diào)試事件處理例程發(fā)送

Figure 6 Debugger Thread Messages

ID
wParam
lParam
Description
UM_INITPROCESS
0
0
The statically linked DLLs have all been loaded. The icon changes from static to dynamic after this event.
UM_FREELIBRARY
0
CModuleInfo*
The DLL has been unloaded. The corresponding line is updated in CModuleListCtrl.
UM_LOADLIBRARY
0
CModuleInfo*
The DLL has been loaded. The corresponding line is added or updated in CModuleListCtrl.

第三個消息需要所解釋幾句,為了創(chuàng)建 CModuleInfo,,需要 DLL 的全路徑名,。而在本文第一部分中,我們沒有提供任何方法直接從其 hModule 或加載地址獲取 DLL 文件名,。即便是當(dāng)調(diào)試器接收到此事件時(因為它可能瀏覽到了它的 PE 頭),,DLL已經(jīng)被映射到被調(diào)試程序的地址空間,這時,,Windows 還沒有初始化 PSAPI 所需的數(shù)據(jù)結(jié)構(gòu),。

事實上,LoadDll.lpImageName 域是一個 LOAD_DLL_DEBUG_INFO 結(jié)構(gòu)成員,,LOAD_DLL_DEBUG_INFO 來自 DEBUG_EVENT 結(jié)構(gòu)中的聯(lián)合 u(參見 Figure 2),,LoadDll.lpImageName 總是指向被調(diào)試程序地址空間中一塊具備讀/寫/執(zhí)行權(quán)限的奇怪的內(nèi)存區(qū)域,LOAD_DLL_DEBUG_INFO 結(jié)構(gòu)定義如下:

1.typedef struct _LOAD_DLL_DEBUG_INFO {
2.HANDLE hFile;
3.LPVOID lpBaseOfDll;
4.DWORD  dwDebugInfoFileOffset;
5.DWORD  nDebugInfoSize;
6.LPVOID lpImageName;
7.WORD fUnicode;
8.} LOAD_DLL_DEBUG_INFO, *LPLOAD_DLL_DEBUG_INFO;     

被加載的DLL的路徑名就包含在此內(nèi)存塊中,。MSDN 在線幫助文檔是這樣描述 IpImageName 的:

“...與 hFile 關(guān)聯(lián)的文件名指針,。該成員可能為 NULL,也可能包含被調(diào)試進程地址空間中的串指針地址,。這個地址可能為 NULL 或者指向?qū)嶋H的文件名,。

如果 fUnicode 是一個非零值,則名字串是 Unicode,,否則是 ANSI 串,。該成員是可選項。調(diào)試器必須考慮處理 lpImageName 為 NULL 或 *lpImageName(在被調(diào)試進程的地址空間中)為 NULL 的情況,。很顯然,,系統(tǒng)決不會為某個創(chuàng)建進程事件提供映像名,同時它也不可能為第一個 DLL 事件傳遞映像名,。系統(tǒng)也決不會在源于 DebugActiveProcess 函數(shù)調(diào)用的調(diào)試事件中提供這個信息,。”

OnLoadDLLDebugEvent 可重寫方法將上述解釋翻譯為在 99% 的情況下可工作的純 C++ 代碼,。其余 1% 不工作的情況是指加載 ntdll.dll:這種情況既是文檔中所說的第一個 DLL 事件,。即使延遲到下一個被調(diào)試程序事件發(fā)生時(參見 CLoadLibraryDebugger 的 OnDebugEvent)才獲取路徑名,。在文檔的描述中,可以調(diào)用 SearchPath 從模塊名獲得全路徑名,,“system32”對于 ntdll.dll 并不感到驚訝,。這個 API 函數(shù)使用與 LoadLibrary 同樣的算法在文件系統(tǒng)中查找某個 DLL。從理論上講,,因為它是由調(diào)試器調(diào)用的,,有可能返回的文件并不是被調(diào)試程序加載的那個文件――例如,在調(diào)試器文件夾中存在另外一個版本的 ntdll.dll,。在實際應(yīng)用中,,ntdll.dll 得不到打補丁的機會,并且被拷貝到了某個與 system32 不同的目錄,。

防止泄漏

文檔中關(guān)于 Win32 調(diào)試 API 的另一方面的描述是必須釋放不同的 XXX_DEBUG_EVENT 結(jié)構(gòu)返回的句柄,。Matt Pietrek 在其 November 1995 MSJ“Under the Hood”專欄文章中指出:在 XXX_DEBUG_EVENT 結(jié)構(gòu)中返回到調(diào)試器的句柄應(yīng)該被關(guān)閉。事實上,,幾乎每個句柄都必須用 CloseHandle 關(guān)閉,。只有一個例外,就是存儲在 CREATE_THREAD_DEBUG_EVENT 中的線程句柄,,它應(yīng)該在進程終止時由系統(tǒng)來關(guān)閉,。其它的句柄如果不關(guān)閉,便會造成增長速度非??斓南到y(tǒng)資源泄漏,,有關(guān)的句柄如 Figure 7 所示。這類垃圾的收集由 CApplicationDebugger::HandleDebugEvent 自動處理,。

Figure 7 XXX_DEBUG_EVENT Handles

Event
Handles to Close
LOAD_DLL_DEBUG_EVENT
u.LoadDll.hFile
CREATE_PROCESS_DEBUG_EVENT
u.CreateProcessInfo.hFile
u.CreateProcessInfo.hProcess
u.CreateProcessInfo.hThread

不論你使用哪種清除方法,,每次你調(diào)試某個進程時,系統(tǒng)不可避免地要泄漏兩個句柄:信號機(semaphore )和端口(port),,兩者都沒有命名,。為了讓你確信 CApplicationDebugger 不負(fù)責(zé)處理這種泄漏,請允許我指出:用 sysinternals 的 ProcessExplorer 或 Windows Resource Kit 中的 DH.EXE 可以觀察到 Visual Studio 6.0 和 Visual Studio .NET 中同樣的泄漏行為,。

現(xiàn)在你已經(jīng)看到了如何用 Win32 調(diào)試 API 來獲取某個進程執(zhí)行期間在其地址空間中加載和卸載的 DLLs 確切列表,。Windows 本身提供了另外一個途徑來獲取有關(guān) DLLs 的其它詳細(xì)信息。

參考資料

The Win32 Debugging Application Programming Interface,;

Bugslayer: Windows 2000 and LDR Messages, A COM Symbol Engine, Finding Bloated Functions, and More DEB Sample: Debug Event Browser,;

Spawn Console Processes with Redirected Standard Handles;

GetWindowModuleFileName & GetModuleFileName Work Only with the Calling Process

在后續(xù)文章中,,我將介紹 Windows Loader,,它知道一切。

(待續(xù)) 

作者簡介

Christophe Nasarre 是法國 Business Objects 公司的技術(shù)經(jīng)理(technical manager),。他在 Windows 平臺上(3.0 以后的版本)編寫了若干個低級工具,。他的聯(lián)系方式:[email protected].

本文出自 MSDN Magazine 的 August 2002 期刊,,可通過當(dāng)?shù)貓髷偒@得,,或者最好是 訂閱

    本站是提供個人知識管理的網(wǎng)絡(luò)存儲空間,,所有內(nèi)容均由用戶發(fā)布,不代表本站觀點,。請注意甄別內(nèi)容中的聯(lián)系方式,、誘導(dǎo)購買等信息,謹(jǐn)防詐騙,。如發(fā)現(xiàn)有害或侵權(quán)內(nèi)容,,請點擊一鍵舉報。
    轉(zhuǎn)藏 分享 獻花(0

    0條評論

    發(fā)表

    請遵守用戶 評論公約

    類似文章 更多