史萊姆論壇

返回   史萊姆論壇 > 教學文件資料庫 > 作業系統操作技術文件
忘記密碼?
論壇說明 標記討論區已讀

歡迎您來到『史萊姆論壇』 ^___^

您目前正以訪客的身份瀏覽本論壇,訪客所擁有的權限將受到限制,您可以瀏覽本論壇大部份的版區與文章,但您將無法參與任何討論或是使用私人訊息與其他會員交流。若您希望擁有完整的使用權限,請註冊成為我們的一份子,註冊的程序十分簡單、快速,而且最重要的是--註冊是完全免費的!

請點擊這裡:『註冊成為我們的一份子!』

Google 提供的廣告


 
 
主題工具 顯示模式
舊 2004-07-01, 09:47 AM   #1
psac
榮譽會員
 
psac 的頭像
榮譽勳章
UID - 3662
在線等級: 級別:30 | 在線時長:1048小時 | 升級還需:37小時級別:30 | 在線時長:1048小時 | 升級還需:37小時級別:30 | 在線時長:1048小時 | 升級還需:37小時級別:30 | 在線時長:1048小時 | 升級還需:37小時級別:30 | 在線時長:1048小時 | 升級還需:37小時
註冊日期: 2002-12-07
住址: 木柵市立動物園
文章: 17381
現金: 5253 金幣
資產: 33853 金幣
預設 用DLL控制Windows中行程的方法

 在Microsoft Windows中,每個行程都有它自己的私有位址空間。當使用游標來引用記憶體時,游標的值將引用你自己行程的位址空間中的一個記憶體位址。


你的行程不能新增一銎湟用屬於另一個行程的記憶體游標。因此,如果你的行程存在一個錯誤,改寫了一個隨機位址系記憶體,那麼這個錯誤不會影響另一個行程使用的記憶體。


Windows 98 在Windows 98下執行的各個行程共享2 GB的位址空間,該位址空間從0x80000000至0xFFFFFFFF。只有記憶體映像文件和統元件才能映射到這個區域。

  獨立的位址空間對於編程人員和用戶來說都是非常有利的。



對於編程人員來說,系統更容易捕獲隨意的記憶體讀取和寫入操作。對於用戶來說,操作系統將變得更加健壯,因個應用程式無法破壞另一個行程或操作系統的執行。



當然,操作系統的這個健壯特性是要凍代價的,因為要編寫能夠與其他行程進行通信,或者能夠對其他行程進行操作的應用程式難得多。

  有些情況下,必須打破行程的界限,訪問另一個行程的位址空間,這些情況包括:

  當你想要為另一個行程新增的視窗建立子類時。
  當你需要偵錯說明 時(例如,當你需要確定另一個行程正在使用哪個DLL時)。
  當你想要掛接其他行程時。

  這裡將介紹兩種方法,可以用來將DLL插入到另一個行程的位址空間中。一旦你的DLL進入另一個行程的位址空間,就可以對另一個行程為所欲為。這一定會使你非常害怕,因此,究竟應該怎樣做,要三思而後行。

  1 插入DLL:一個例子

  假設你想為由另一個行程新增的視窗建立一個子類。



你可能記得,建立子類就能夠改變視窗的行為特性。若要建立子類,只需要使用SetWindowLongPtr函數,改變視窗的內嬋中的視窗程序位址,指向一個新的(你自己的) WndProc。


Platform SDK我的文件說,套用程蠆能為另一個行程新增的視窗建立子類。這並不完全正確。為另一個行程的視窗建立子類的關鍵問題與行程位址空間的邊界有關。

  當使用下面所顯示的SetWindowsLongPtr函數,建立一個視窗的子類時,你告訴系統,傳送到或者顯示在hwnd設定的視窗中的所有消息都應該送往MySubclassProc,而不是送往口的正常視窗程序:

  行程A中程式碼:

EXE file:
LRESUlT WndProc(HWND hend,UNIT uMsg,...){.....}
USER32.DLL file
LONG DispatchMessage(CONST MSG*msg)
{
LONG lRet;
WNDPROC lpfnWndProc=
(WNDPROC)GetWindowLongPtr(msg,hwnd,GWLP_WNDPROC
);
lRet=lpfnWndProc(msg.hwnd,msg.message,msg.wParam,mag.
lParam);
return lRet;
}
行程B中:
EXE file
void Somefunc(void)
{
HWND hwnd=Findwindow("class A",NULL);
SetWindowLongPtr(hwnd,GWLP_WNDPROC,MySubclassProc);
}
USER32.DLL file ......


  換句話說,當系統需要將消息傳送到指定視窗的WndProc時,要檢視它的位址,然後直接使用WndProc。在本例中,系統發現MySubclassProc函數的位址與視窗相關聯,因此就直接使用MySubclassProc函數。

  為另一個行程新增的視窗建立子類時遇到的問題是,建立子類的程序位於另一個位址空間中。


下面舉個例子,說明視窗程序是如何接受消息的。行程A正在執行,並且已經新增了一個視窗。文件User32.dll被映射到行程A的位址空間中。

  對User32.dll文件的映射是為了接收和傳送在行程A中執行的任何線程新增的任何視窗中傳送和顯示的消息。




當User32.dll的映像發現一個消息時,它首先要確定視窗的WndProc的位址,然後使用該位址,傳遞視窗的句柄、消息和wParam和lParam值。


當WndProc處理該消息後,User32.dll便循環執行,並等待另一個視窗消息被處理。

  行程B中的線程試突為行程A中的線程新增的視窗建立子類現在假設你的行程是行程B,你想為行程A中的線程新增的視窗建立子類。


你在行程B中的程式碼必須首先確定你想要建立子類的視窗的句柄。這個操作使

用的方法很多。


上面的例子只是使用FindWindow函數來獲得需要的視窗。


接著,行程B中的線程使用SetWindowLongPtr函數,試突改變視窗的WndProc的位址。請注意我說的「試突」二字。



這個函數使用⒉進行什麼操作,它只是返回NULL。

SetWindowLongPtr函數中的程式碼要檢視是否有一個行程正在試突改變另一個行程新增的視窗的WndProc位址,然後將忽略這個函數的使用。

  如果SetWindowLongPtr函數能夠改變視窗的WndProc,那將出現什麼情況呢?系

統將把MySubclassProc的位址與特定的視窗關聯起來。


然後,當有一條消息被傳送到這個視窗中時,行程A中的User32程式碼將檢索該消息,獲得MySubclassProc的位址,並試突使用這個位址。


但是,這時可能遇到一個大問題。MySubclassProc將位於行程B的位址空間中,而行程A是個活動行程。



顯然,如果User32想要使用該位址,它就要使用行程A的位址空間中的一個位址,這就可能造成記憶體訪問的違規。

  為了避免這個問題的產生,應該讓系統知道M y S u b c l a s s P r o c是在行程B的位址空間中,然後,在使用子類的程序之前,讓系統執行一次上下文轉換。


M i c r o s o f t沒有實現這個輔助函數功能,原因是:應用程式很少需要為其他行程的線程新增的視窗建立子類。


大多數應用程式只是為它們自己新增的視窗建立子類,Wi n d o w s的記憶體結構並不阻止這種新增操作。

  切換活動行程需要佔用許多C P U時間。

  行程B中的線程必須執行M y S u b c l a s s P r o c中的程式碼。系統究竟應該使用哪個線程呢?是現有的線程,還是新線程呢?

  U s e r 3 2 . d l l怎樣才能說明與視窗相關的位址是用於另一個行程中的程序,還是用於同一個行程中的程序呢?

  由於對這個問題的解決並沒有什麼萬全之策,因此M i c r o s o f t決定不讓S e t Wi n d o w s L o n g P t r改變另一個行程新增的視窗程序。


不過仍然可以為另一個行程新增的視窗建立子類—只需要用另一種方法來進行這項操作。

這並不是建立子類的問題,而是行程的位址空間邊界的問題。如果能將你的子類程序的程式碼放入行程A的位址空間,就可以方便地使用S e t Wi n d o w L o n g P t r函數,將行程A的位址傳遞給M y S u b c l a s s P r o c函數。我將這個方法稱為將D L L「插入」行程的位址空間。有若干種方法可以用來進行這項操作。下面將逐個介紹它們


  2.通過掛鉤插入DLL

  可以使用掛鉤將D L L插入行程的位址空間。為了使掛鉤能夠像它們在1 6位Wi n d o w s中那樣工作,M i c r o s o f t不得不設計了一種方法,使得D L L能夠插入另一個行程的位址空間中。

  下面讓我們來看一個例子。行程A(類似Microsoft Spy++的一個實用程序)安裝了一個掛鉤W N _ G E T M E S S A G E,以便檢視系統中的各個視窗處理的消息。

該掛鉤是通過使用下面的S e t Wi n d o w s H o o k E x函數來安裝的:

  第一個參數W H _ G E T M E S S A G E用於指明要安裝的掛鉤的類型。

第二個參數G e t M s g P r o c用於指明視窗準備處理一個消息時系統應該使用的函數的位址(在你的}房占渲校5三個參數h i n s t D l l用於指明包含G e t M s g P r o c函數的D L L。


在Wi n d o w s中,D L L的h i n s t D l l的值用於標識DLL被映射到的行程的位址空間中的虛擬記憶體位址。最後一個參數0用於指明要掛接的線程。

  對於一個線程來說,它可以使用S e t Wi n d o w s H o o k E x函數,傳遞系統中的另一個線程的I D。


通過為這個參數傳遞0,就告訴系統說,我們想要掛接系統中的所有G U I線程。

  現在讓我們來看一看將會發生什麼情況:

  1) 行程B中的一個線程準備將一條消息傳送到一個視窗。

  2) 系統檢視該線程上是否已經安裝了W H _ G E T M E S S A G E掛鉤。

  3) 系統檢視包含G e t M s g P r o c函數的D L L是否被映射到行程B的位址空間中。

  4) 如果該D L L尚未被映射,系統將強制該D L L映射到行程B的位址空間,並且將行程B中的D L L映像的自動跟蹤計數遞增1。

  5) 當D L L的h i n s t D l l用於行程B時,系統檢視該函數,並檢查該D L L的h i n s t D l l是否與它用於行程A時所處的位置相同。

  如果兩個h i n s t D l l是在相同的位置上,那麼G e t M s g P r o c函數的記憶體位址在兩個行程的位址空間中的位置也是相同的。


在這種情況下,系統只需要使用行程A的位址空間中的G e t M s g P r o c函數即可。

  如果h i n s t D l l的位置不同,那麼系統必須確定行程B的位址空間中G e t M s g P r o c函數的虛擬記憶體位址。



這個位址可以使用下面的公式來確定:

  將GetMsgProc A的位址減去hinstDll A的位址,就可以得到G e t M s g P r o c函數的位址位移(以字元為計量服務機構)。



將這個位移與hinstDll B的位址相加,就得出G e t M s g P r o c函數在用於行程B的位址空間中該D L L的映像時它的位置。

  6) 系統將行程B中的D L L映像的自動跟蹤計數遞增1。

  7) 系統使用行程B的位址空間中的G e t M s g P r o c函數。

  8) 當G e t M s g P r o c函數返回時,系統將行程B中的D L L映像的自動跟蹤計數遞減1。

  注意,當系統插入或者映射包含掛鉤過濾器函數的D L L時,整個D L L均被映射,而只是掛鉤過濾器函數被映射。這意味著D L L中包含的任何一個函數或所有函數現在都存在,並且可以從行程B的環境下執行的線程中使用。

  若要為另一個行程中的線程新增的視窗建立子類,首先可以在新增該視窗的掛鉤上設定一個W H _ G E T M E S S A G E掛鉤,然後,當G e t M s g P r o c函數被使用時,使用S e t Wi n d o w L o n g P t r函數 來建立視窗的子類。當然,子類的程序必須與G e t M s g P r o c函數位於同一個D LL中。

  與插入D L L的註冊表方法不同,這個方法允許你在另一個行程的位址空間中不再需要DL L時刪除該D L L的映像,方法是使用下面的函數:

  當一個線程使用U n h o o k Wi n d o w s H o o k E x函數時,系統將遍歷它必須將D L L插入到的各個行程的內部列表,並且對D L L的自動跟蹤計數進行遞減。



當自動跟蹤計數時,D L L就自動從行程的位址空間中被刪除。應該記得,就在系統使用G e t M s g P r o c前,它對D L L的自動跟蹤計數進行了遞增(見上面的第6個步驟)。

  該自動跟蹤計數沒有遞增,那麼當行程B的線程試突執行G e t M s g P r o c函數中的代朧,系統中執行的另一個線程就可以使用U n l o o k Wi n d o w s H o o k E x函數。

  這一切意味著不能撤消該視窗的子類並且立即撤消該掛鉤。該掛鉤必須在該子類的壽命期內保持有效狀態。

  3.使用遠端線程插入DLL

  插入D L L的另一個方法是使用遠端線程。這種方法具有更大的靈活性。它要求你懂得若干個Wi n d o w s特性、如行程、線程、線程同步、虛擬記憶體管理、D L L和U n i c o d e等(如果對這些特性不清楚,請參閱本書中的有關章節)。Wi n d o w s的大多數函數市斫扆Q歡宰己進行操作。

  這是很好的一個特性,因為它能夠防止一個行程破壞另一個行程的執行。但是,有些函數卻允許一個行程對另一個行程進行操作。


這些函數大部分最初是為偵錯程序和其他工具杓的。不過任何函數都可以使用這些函數。

  這個D L L插入方法基本上要求目標行程中的線程使用L o a d L i b r a r y函數來載必要的D L L。

  由於除了自己行程中的線程外,我們無法方便地控制其他行程中的線程,因此這種解決方案要求我們在目標行程中新增一個新線程。



由於是自己新增這個線程,因此我們能夠控撲執行什麼程式碼。幸好,Wi n d o w s提供了一個稱為C r e a t e R e m o t e T h r e a d的函數,使我們能夠非常容易地在另一個行程中新增線程:

  C r e a t e R e m o t e T h r e a d與C r e a t e T h r e a d很相似,差別在於它增加了一個參數h P r o c e s s。該參數指明擁有新新增線程的行程。參數p f n S t a r t A d d r指明線程函數的記憶體位址。當然,該記憶體位址與遠端行程是相關的。


線程函數的程式碼不能位於你自己行程的位址空間中。

  Windows 98 在Windows 98中,C r e a t e R e m o t e T h r e a d函數不存在有用的實現程式碼,它只是返回N U L L。使用G e t L a s t E r r o r函數將返回E R R O R _ C A L L _ N O T _ I M P L E M E N T E D(C r e a t e T h r e a d函數包含用於在使用行程中新增線程的完整的實現程式碼)。由於C r e a t e R e m o t e T h r e a d沒有實現,因此,在Windows 98下,不能使用本方法來插入D L L。

  好了,現在你已經知道如何在另一個行程中新增線程了,但是,如何才能讓該線程載入我們的D L L呢?

答案很簡單,那就是需要該線程使用L o a d L i b r a r y函數。

  我們將詳細步驟說明如下:

  1) 使用Vi r t u a l A l l o c E x函數,分配遠端行程的位址空間中的記憶體。

  2) 使用Wr i t e P r o c e s s M e m o r y函數,將D L L的路徑名拷貝到第一個步驟中已經分配的記憶體中。

  3) 使用G e t P r o c A d d r e s s函數,獲取L o a d L i b r a r y A或L o a dL i b r a t y W函數的實位址(在K e r n e l 3 2 . d l l中)。

  4) 使用C r e a t e R e m o t e T h r e a d函數,在遠端行程中新增一個線程,它使用正確的L o a d L i b r a r y函數,為它傳遞第一個步驟中分配的記憶體的位址。



這時, D L L已經被插入遠端行程的位址空間中,同時D L L的D l l M a i n函數接收到一個D L L _ P R O C E S S _ AT TA C H通知,並且能夠執行需要的程式碼。當D l l M a in函數返回時,遠端線程

  從它對L o a d L i b r a r y的使用返回到B a s e T h r e a d S t a r t 函數(第6 章中已經介紹。然後B a s e T h r e a d S t a r t使用E x i t T h r e a d,使遠端線程終止執行。

  現在遠端行程擁有第一個街柚蟹峙淶哪詿嬋椋t鳧 L L則仍然保留在它的位址空間中。

  若要將它刪除,需要在遠端線程退出後執行下面的步驟:

  5) 使用Vi r t u a l F r e e E x函數,釋放第一個步驟中分配的記憶體。

  6) 使用G e t P r o c A d d r e s s函數,獲得F r e e L i b r a r y函數的實位址(在K e r n e l 3 2 . d l l中)。

  7) 使用C r e a t e R e m o t e T h r e a d函數,在遠端行程中新增一個線程,它使用F r e e L i b r a r y函數,傳遞遠端D L L的H I N S TA N C E。

  這就是它的基本操作步驟。這種插入D L L的方法存在的唯一一個不足是, Windows 98並不支持這樣的函數。只能在Windows 2000上使用這種方法。
psac 目前離線  
送花文章: 3, 收花文章: 1631 篇, 收花: 3205 次
 


主題工具
顯示模式

發表規則
不可以發文
不可以回覆主題
不可以上傳附加檔案
不可以編輯您的文章

論壇啟用 BB 語法
論壇啟用 表情符號
論壇啟用 [IMG] 語法
論壇禁用 HTML 語法
Trackbacks are 禁用
Pingbacks are 禁用
Refbacks are 禁用


所有時間均為台北時間。現在的時間是 04:45 PM


Powered by vBulletin® 版本 3.6.8
版權所有 ©2000 - 2024, Jelsoft Enterprises Ltd.


SEO by vBSEO 3.6.1