日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 运维知识 > windows >内容正文

windows

插入DLL和挂接API——Windows核心编程学习手札之二十二

發布時間:2025/4/16 windows 35 豆豆
生活随笔 收集整理的這篇文章主要介紹了 插入DLL和挂接API——Windows核心编程学习手札之二十二 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

插入DLL和掛接API

——Windows核心編程學習手札之二十二

如下情況,可能要打破進程的界限,訪問另一個進程的地址空間:

1)為另一個進程創建的窗口建立子類時;

2)需要調試幫助時,如需要確定另一個進程正在使用那個DLL

3)需要掛接其他進程時;

基于之上情況,下面的方法可將DLL插入到另一個進程的地址空間中,一旦DLL進入另一個進程的地址空間,就可以對另一個進程為所欲為。

使用注冊表來插入DLL

整個系統的配置都是在注冊表中維護的,可調整其設置改變系統的行為特性,下面的關鍵字:

HKEY_LOCAL_MACHINE/Software/Microsoft/WindowsNT/

CurrentVersion/Windows/AppInit_DLLS

AppInit_DLLS關鍵字包含一個DLL文件或一組DLL文件名(用空格或逗號隔開,避免使用含空格的文件名),列出第一個DLL文件名可包含其路徑,但包含路徑的其他DLL將被忽略,因此最好將DLL放入Windows的系統目錄中,這樣不需要設定路徑,如設置該關鍵字值為C:/MyLib.dll。當重啟計算機及Windows進行初始化時,系統將保存這個關鍵字的值。然后當User32.dll庫被映射到進程中時,將接收到一個DLL_PROCESS_ATTACH通知,這個通知被處理時,User32.dll便檢索保存這個關鍵字的值,并且為字符串中指定的每個DLL調用LoadLibrary函數。當每個庫被加載時,便調用和該庫相關的DllMain函數,其fdwReason的值是DLL_PROCESS_ATTACH,如此,每個庫就能夠對自己進行初始化。該方法簡單,但不足有:

1)系統是在初始化時讀取該關鍵字的值,因此修改該值需重新啟動計算機;

2 插入的DLL是映射到使用User32.dll進程中,所有基于GUI的應用程序都使用User32.dll,不過多數基于GUI的應用程序不使用插入的DLL,而且如將DLL插入編譯器或鏈接程序,這種方法將不起作用;

3)插入的DLL被映射到每個基于GUI應用程序中,DLL映射的進程太多,“容器”進程崩潰的可能性越大;

4)插入的DLL被映射到每個基于GUI應用程序中,應僅必要時保持DLL的插入狀態。

使用Windows掛鉤來插入DLL

進程A(類似Microsoft Spy++的一個實用程序)安裝了一個掛鉤WN_GETMESSAGE,以便查看系統中的各個窗口處理的消息,掛鉤是通過調用SetWindowsHookEx函數來安裝的:

?????? HHOOK hHook=SetWindowsHookEx(WH_GETMESSAGE,GetMsgProc,hinstDll,0);

參數WH_GETMESSAGE用于指明要安裝的掛鉤類型,參數GetMsgProc是窗口準備處理一個消息時系統調用的函數的地址(在進程地址空間內);第三個參數hinstDll是包含GetMsgProc函數的DLL。在Windows中,DLLhinstDll的值用于標識DLL被映射到的進程的地址空間中的虛擬內存地址,最后一個參數0表示要掛接的線程,也可以傳遞系統中另一個線程的ID,傳遞0告訴系統想要掛接系統中的所有GUI線程。安裝掛鉤后的情況:

1)進程B中的一個線程準備將一條消息發送到一個窗口;

2)系統查看該線程上是否已安裝了WH_GETMESSAGE掛鉤;

3)系統查看包含GetMsgProc函數的DLL是否包含被映射到進程B的地址空間中;

4)如果該DLL尚未被映射,系統將強制該DLL映射到進程B的地址空間,并且將進程B中的DLL映像的自動跟蹤計數遞增1

5)當DLLhinstDll用于進程B時,系統查看該函數,并且檢查該DLLhinstDll是否與其在進程A時所處的位置相同;如果兩個hinstDll在系統位置上,那GetMsgProc函數的內存地址在兩個進程的地址空間中的位置也是相同的,這種情況下,系統只要調用進程A的地址空間中的GetMsgProc函數即可;如位置不同,則系統要確定進程B的地址空間中GetMsgProc函數的虛擬內存地址,用下面公式確定:

?????? GetMsgProc B=hinstDll B+(GetMsgProc A-hinstDll A)

GetMsgProc A的地址減去hinstDll A的地址,得到GetMsgProc函數的地址位移(以字節為計量單位),將這個位移與hinstDll B的地址相加,得到GetMsgProc函數在用于進程B的地址空間中該DLL的映像時其位置;

6)系統將進程B中的DLL映像的自動跟蹤計數遞增1

7)系統調用進程B的地址空間中的GetMsgProc函數;

8)當GetMsgProc函數返回時,系統將進程B中的DLL映像的自動跟蹤計數遞減1

當系統插入或者映射包含掛鉤過濾器函數的DLL時,整個DLL均被映射,而不只是掛鉤過濾器函數被映射,這意味著DLL中包含的任何一個函數或所有函數都被映射,可被進程B的環境運行的線程所調用。

若要為進程B中的線程創建的窗口建立子類,首先可以在創建該窗口的掛鉤上設置一個WH_GETMESSAGE掛鉤,然后在GetMsgProc函數被調用時,調用SetWindowLongPtr函數來建立窗口的子類,子類的過程與GetMsgProc函數在同一DLL文件中。

進程B中不再需要DLL時刪除DLL映像,方法是調用:

?????? BOOL UnhookWindowsHookEx(HHOOK hhook);

當洋線程調用UnhookWindowsHookEx函數時,系統將遍歷將DLL插入到的各個進程的內部列表,并對DLL的自動跟蹤計數進行遞減,當遞減到0時,DLL就從進程的地址空間中被刪除。

使用遠程線程來插入DLL

Windows的多數函數允許進程只對自己進行操作,可防止一個進程破壞另一個進程的運行,但對調試程序和一些工具而言,則需要操作其他進程。使用遠程線程來插入DLL的方法要求目標進程中的線程調用LoadLibrary函數來加載必要的DLL,需要在目標進程中創建一個新線程,Windows提供了這樣一個函數:

?????? HANDLE CreateRomoteThread(

????????????????????????????????????????? HANDLE hProcess,

????????????????????????????????????????? PSECURITY_ATTRIBUTES psa,

????????????????????????????????????????? DWORD dwStackSize,

????????????????????????????????????????? PTHREAD_START_ROUTINE pfnStartAddr,

????????????????????????????????????????? PVOID pvParam,

????????????????????????????????????????? DWORD fdwCreate,

????????????????????????????????????????? PDWORD pdwThreadId);

參數hProcess指明擁有新創建線程的進程,參數pfnStartAddr指明線程函數的內存地址,該內存地址和遠程進程是相關的,線程函數代碼不能在自己進程的地址空間中。

如執行下面代碼:

?????? HANDLE hThread=CreateRemoteThread(hProcessRemote,NULL,0,

????????????????????? LoadLibraryA,”C://MyLib.dll”,0,NULL);

或選用Unicode

??? HANDLE hThread=CreateRemoteThread(hProcessRemote,NULL,0,

?????? ???????????????LoadLibraryW,L”C://MyLib.dll”,0,NULL);

當在遠程進程中創建新線程時,該線程立即調用LoadLibrary函數,并將DLL的路徑名的地址傳遞給它。直接將LoadLibrary作為線程執行函數(所傳遞的參數就是遠程線程的起始地址),會出先問題:

1)當編譯或鏈接一個程序時,產生的二進制代碼包含一個輸入節,這一節是有一系列輸入函數的形式替換程序(thunk)組成。當代碼調用一個如LoadLibrary函數時,鏈接程序將生成一個模塊輸入節中的形實替換程序并調用,然后,該形實替換程序便轉移到實際的函數。在CreateRemoteThread的調用中使用一個對LoadLibrary的直接調用,則將在模塊的輸入節中轉換成LoadLibrary的形實替換程序的地址,將形實替換程序的地址作為遠程線程的起始地址來傳遞,會導致線程開始執行莫名其妙的代碼。

解決方法是:若要強制直接調用LoadLibrary函數,避開形實替換程序,必須調用GetProcAddress函數,獲取LoadLibrary的準確內存位置。對CreateRemoteThread調用的前提是,Kernel32.dll已經被同時映射到本地和遠程進程的地址空間中,每個應用程序都需要Kernel32.dll,將Kernel32.dll映射到每個進程的同一個地址,如調用如下函數:

?????? PTHREAD_START_ROUTINE pfnThreadRtn=(PTHREAD_START_ROUTINE)

???????????????? GetProcAddress(GetModuleHandle(TEXT(“Kernel32”)),”LoadLibraryA”);

?????? HANDLE hThread=CreateRemoteThread(hProcessRemote,NULL,0,

?????????????????????? pfnThreadRtn,” C://MyLib.dll”,0,NULL);

或選用Unicode

PTHREAD_START_ROUTINE pfnThreadRtn=(PTHREAD_START_ROUTINE)

???????????????? GetProcAddress(GetModuleHandle(TEXT(“Kernel32”)),”LoadLibraryW”);

?????? HANDLE hThread=CreateRemoteThread(hProcessRemote,NULL,0,

?????????????????????? pfnThreadRtn,L” C://MyLib.dll”,0,NULL);

2)字符串” C://MyLib.dll”是在調用進程的地址空間中,該字符串的地址已經被賦予新創建的遠程線程,該線程將其傳遞給LoadLibrary,但是,當LoadLibrary取消對內存地址的引用時,DLL路徑名字符串將不再存在,遠程進程的線程就可能引發訪問違規,向用戶顯示一個未處理的異常條件消息框,并終止遠程進程。

解決方法是:將DLL的路徑名字符串放入遠程進程的地址空間中,然后當CreateRemoteThread函數被調用時,必須將放置該字符串的地址(相對于遠程進程的地址)傳遞給它,Windows提供了一個函數使得一個進程能夠分配另一個進程的地址空間中的內存:

?????? PVOID VirtualAllcoEx(

?????????????????????????????????? HANDLE hProcess,

?????????????????????????????????? PVOID pvAddress,

?????????????????????????????????? SIZE_T dwSize,

?????????????????????????????????? DWORD flAllocationType,

?????????????????????????????????? DWORD flProtect);

另一個函數則能夠釋放該內存:

?????? BOOL VirtualFreeEx(

?????????????????????????????????? HANDLE hProcess,

?????????????????????????????????? PVOID pvAddress,

?????????????????????????????????? SIZE_T dwSize,

?????????????????????????????????? DWORD dwFreeType);

一旦為該字符串分配內存,還需要將該字符串從進程的地址空間拷貝到遠程進程的地址空間中,Windows提供了一些函數,使得一個進程能夠從另一個進程的地址空間中讀取數據,并將數據寫入另一個進程的地址空間。

?????? BOOL ReadProcessMemory(

????????????????????????????????????????? HANDLE hProcess,

????????????????????????????????????????? PVOID pvAddressRemote,

????????????????????????????????????????? PVOID pvBufferlocal,

???????????????????????????????????????? DWORD dwSize,

????????????????????????????????????????? PDWORD pdwNumbytesRead);

?????? BOOL WriteProcessMemory(

????????????????????????????????????????? HANDLE hProcess,

????????????????????????????????????????? PVOID pvAddressRemote,

????????????????????????????????????????? PVOID pvBufferlocal,

???????????????????????????????????????? DWORD dwSize,

????????????????????????????????????????? PDWORD pdwNumbytesWritten);

遠程進程由hProcess參數來標識,參數pvAddressRemote用于指明遠程進程的地址,參數pvBufferlocal是本地進程中的內存地址,參數dwSize需要傳送的字節數,pdwNumbytesWrittenpdwNumbytesRead用于指明實際傳送的字節數,當函數返回時,可查看這兩個參數的值。

對此,執行步驟歸納如下:

1)? 使用VirtualAllocEx函數,分配遠程進程的地址空間中的內存;

2)? 使用WriteProcessMemory函數,將DLL路徑名拷貝到第一個步驟中已經分配的內存中;

3)? 使用GetProcAddress函數,獲取LoadLibrary函數的實地址(在kernel32.dll中);

4)? 使用CreateRemoteThread函數,在遠程進程中創建一個線程,調用正確的LoadLibrary函數,為其傳遞第一個步驟中分配的內存地址;此時,DLL已經被插入到遠程進程的地址空間中,同時DLLDllMain函數接收到一個DLL_PROCESS_ATTACH通知,并且能夠執行需要的代碼,當DllMain函數返回時,遠程線程從它對LoadLibrary的調用返回到BaseThreadStart函數,然后BaseThreadStart調用ExitThread,使遠程線程終止運行;現在遠程進程擁有第一個步驟中分配的內存塊,而DLL仍保留在它的地址空間中,若要將它刪除,需要在遠程線程退出后執行下面步驟:

5)? 使用VirtualFreeEx函數,釋放第一個步驟中分配的內存;

6)? 使用GetProcAddress函數,獲得FreeLibrary函數的實地址(在Kernel32.dll中);

7)? 使用CreateRemotThread函數,在遠程進程中創建一個線程,調用FreeLibrary函數,傳遞遠程DLLHINSTANCE

使用遠程線程調用DLL只適用Windows2000Windows1998不支持。

使用特洛伊DLL來插入DLL

該方法是取代所知道進程將要加載的DLL,如當你知道一個進程將要加載Xyz.dll,則創建自己的DLL并賦予相同的文件名,而把原來的Xyz.dll改成別的名字。在自己定義的Xyz.dll中,輸出的全部符號必須與原始的Xyz.dll輸出的符號相同,使用函數轉發器可掛接某些函數。如果只想在單個應用程序中使用這個方法,那可以為自己的DLL給個獨一的名字,并改變應用程序的.exe模塊的輸入節,并且只包含模塊需要的DLL的名字。可搜索文件中的這個輸入節,并且將它改變,使加載程序加載自己的DLL,需要數字.exeDLL文件的格式。

DLL作為調試程序來插入

調試程序能夠對調試的進程執行特殊的操作,當被調試進程加載時,在被調試進程的地址空間作好準備,但是被調試進程的主線程尚未執行任何代碼之前,系統將自動將這個情況統治調試程序,這時,調試程序可以強制將某些代碼插入被調試進程的地址空間中(比如使用WriteProcessMemory函數來插入),然后使被調試進程的主線程執行該代碼。這種方法要求對被調試線程的CONTEXT結構進行操作,意味著要編寫特定CPU的代碼,因此需要修改源代碼,使之能夠在不同的CPU平臺上正確地運行,且對被調試進程執行的機器語言指令進行硬編碼。調試程序和被調試程序之間必須存在固定關系。如果調試程序終止運行,Windows將自動撤消被調試進程。

Windows98上的內存映射文件插入代碼

Windows98上運行的所有32Windows應用程序均共享同樣的最上面的2GB地址空間,如果分配這里面的某些存儲器,那該存儲器在每個進程的地址空間中均可以使用。若要分配2GB以上的存儲器,則可使用內存映射文件。可以創建一個內存映射文件,然后調用函數MapViewOfFile顯示,然后將數據寫入地址空間區域(所有進程地址空間中的相同區域),必須使用硬編碼的機器語言來進行這項操作,其結果是這種解決方案很難移植到別的CPU平臺。

CreateProcess插入代碼

如果是父子進程關系,父進程可以得到子進程的主線程的句柄,使用該句柄,可以修改線程執行的代碼。控制子進程的主線程執行:

1)使進程生成暫停運行的子進程;

2)從.exe模塊的頭文件中檢索主線程的起始內存地址;

3)將機器指令保存在該內存地址中;

4)將某些硬編碼的機器指令強制放入該地址中,這些指令應該調用LoadLibrary函數來加載DLL

5)繼續運行子進程的主線程,使該代碼得以執行;

6)將原始指令重新放入起始地址;

7)讓進程繼續從起始地址開始執行,如同沒有任何事一樣。

掛接API

DLL插入進程的地址空間是確定進程運行狀況的方法,但僅僅插入DLL無法提供足夠信息,有時需要知道進程中的線程是如何調用各個函數的,也可能需要修改Windows函數的功能。

例子:某DLL是由一個數據庫產品加載的,該DLL作用是增強和擴展數據庫產品的功能。當數據庫產品終止運行時,該DLL就會收到DLL_PROCESS_DETACH通知,并且只有在這時才執行它的所有清除代碼,清除工作包括調用其他DLL中的函數,以關閉套接字連接、文件和其他資源,但是在該DLL收到DLL_PROCESS_DETACH通知時,進程地址空間中的其他DLL已經收到它們的DLL_PROCESS_DETACH通知。因此,當該DLL視圖清除時,其所調用其他DLL中的函數都失敗,因為其他DLL已經撤消了初始化信息。

解決建議是掛接函數ExitProcess,調用ExitProcess將導致系統向該DLL發送DLL_PROCESS_DETACH通知,這個通知將在任何DLL得到DLL_PROCESS_DETACH通知之前進來,因此進程中的所有DLL仍然處于初始狀態,并且能夠正常運行,此時,該DLL知道進程將要終止,從而能夠成功執行它的全部清除操作。然后,操作系統的ExitProcess函數被調用,使所有DLL收到它們的DLL_PROCESS_DETACH通知并進行清除操作,當該公司的DLL收到這個通知時,它將不執行專門清除操作,因為之前已經執行了。

插入DLL是隨意進行,當該DLL被加載時,必須掃描所有已經加載的可執行模塊和DLL模塊,以便找出對ExitProcess的調用。該DLL必須修改調用ExitProcess的模塊,該模塊能調用該DLL中的函數,而不是調用操作系統的ExitProcess函數,一旦該DLL中的ExitProcess替換函數(掛鉤函數)執行它的清除代碼,操作系統的ExitProcess函數(在Kernel32.dll文件中)就被調用。下面是通過改寫代碼掛接API的具體操作方法:

1)找到要掛接的函數在內存中的地址(如Kernel32.dll中的ExitProcess);

2)將該函數的頭幾個字節保存在你自己的內存中;

3 用一個JUMP CPU指令改寫該函數的頭幾個字節,該指令會轉移到替換函數的內存地址。當然,替換函數的標記必須與掛接的函數標記完全相同,即所有參數必須一樣,返回值一樣,調用規則一樣;

4)當一個線程調用已經掛接的函數時,JUMP指令實際上將轉移到替換函數,這樣就能夠執行任何代碼;

5)取消函數的掛接狀態,取出第二步保存的字節,將它們放會掛接函數的開頭;

6)調用掛接函數(已不再掛接),該函數將執行其通常的處理操作;

7)但原始函數返回時,再次執行第二步和第三步,如此,替換函數就可以被調用;

該方法對CPU依賴性很大,需使用手工編碼的機器指令才能使這個方法生效,且在搶占式多線程環境中根本不起作用。

另一種掛接方法是通過操作模塊的輸入節來實現。模塊的輸入節包含一組該模塊運行時需要的DLL,另外,還包含該模塊從每個DLL輸入的符號的列表,當模塊調用一個輸入函數時,線程實際上要從模塊的輸入節中捕獲需要的輸入函數的地址,然后轉移到該地址。要掛接一個特定函數,只要改變模塊輸入節中的地址。

總結

以上是生活随笔為你收集整理的插入DLL和挂接API——Windows核心编程学习手札之二十二的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。