簡單敘述下自己研究DLL劫持的原因。
動態鏈接庫(Dynamic-Link-Library,縮寫dll), 是微軟公司在微軟視窗操作系統中實現共享函數庫概念的一種實現方式。這些庫函數的擴展名是.DLL、.OCX(包含ActiveX控制的庫)或者.DRV(舊式的系統的驅動程序)
所謂動態鏈接,就是把一些經常會共享的代碼(靜態鏈接的OBJ程序庫)制作成DLL檔,當可執行文件調用到DLL檔內的函數時,Windows操作系統才會把DLL檔加載進存儲器內,DLL檔本身的結構就是可執行檔,當程序有需求時函數才進行鏈接。通過動態鏈接方式,存儲器浪費的情形將可大幅降低。靜態鏈接庫則是直接鏈接到可執行文件
DLL的文件格式與視窗EXE文件一樣——也就是說,等同于32位視窗的可移植執行文件(PE)和16位視窗的New Executable(NE)。作為EXE格式,DLL可以包括源代碼、數據&action=edit&redlink=1)和資源&action=edit&redlink=1)的多種組合。
還有更廣泛的定義,這個沒必要去理解了。
一些與之相關的概念:
靜態庫與動態庫的比較
靜態庫被鏈接后直接嵌入可執行文件中
好處: 不需要外部函數支持,無環境依賴,兼容性好。
壞處: 容易浪費空間,不方便修復bug。
動態庫的好處與靜態庫相對。
Linux下靜態庫名字一般是: libxxx.a window則是: *.lib、*.h
Linux下動態庫名字一般是:libxxx.so window則是: .dll、.OCX(..etc)
DLL動態鏈接庫,是程序進行動態鏈接時加載的庫函數。
故動態鏈接最直接的好處是磁盤和內存的消耗減少,這也是dll最初的目的。
不過,dll也有缺點,就是容易造成版本沖突,比如不同的應用程序共享同一個dll,而它們需求的是不同的版本,這就會出現矛盾,解決辦法是把不同版本的dll放在不同的文件夾中。
1.采用vs2017新建DLL項目
2.分析DLL的組成
其中dllmain.cpp代碼如下
每個DLL都可以有一個入口點函數DllMain,系統會在不同時刻調用此函數。
// dllmain.cpp : 定義 DLL 應用程序的入口點。
#include "pch.h"
BOOL APIENTRY DllMain( HMODULE hModule, // 模塊句柄
DWORD ul_reason_for_call, // 調用原因
LPVOID lpReserved // 參數保留
)
{
switch (ul_reason_for_call) // 根據調用原因選擇不不同的加載方式
{
case DLL_PROCESS_ATTACH: // DLL被某個程序加載
case DLL_THREAD_ATTACH: // DLL被某個線程加載
case DLL_THREAD_DETACH: // DLL被某個線程卸載
case DLL_PROCESS_DETACH: //DLL被某個程序卸載
break;
}
return TRUE;
}
我們可以在該文件下引入Windows.h庫,然后編寫一個msg的函數。
#include <Windows.h>
void msg() {
MessageBox(0, L"Dll-1 load succeed!", 0, 0);
}
接下來在解決方案資源管理下的項目下打開頭文件中的framework.h來導出msg函數.
#pragma once
#define WIN32_LEAN_AND_MEAN // 從 Windows 頭文件中排除極少使用的內容
// Windows 頭文件
#include <windows.h>
extern "C" __declspec(dllexport) void msg(void);
然后點擊生成中的重新生成解決方案編譯得到TestDll.dll文件。
可以用16進制文件查看下dll的文件頭,正如上面所說的一樣,和exe是一樣的。
解決方案處右鍵新建一個項目,選擇>控制臺應用取名hello
修改hello.cpp的文件內容如下:
// hello.cpp : 此文件包含 "main" 函數。程序執行將在此處開始并結束。
#include <iostream>
#include <Windows.h>
using namespace std;
int main()
{
// 定義一個函數類DLLFUNC
typedef void(*DLLFUNC)(void);
DLLFUNC GetDllfunc = NULL;
// 指定動態加載dll庫
HINSTANCE hinst = LoadLibrary(L"TestDll.dll");
if (hinst != NULL) {
// 獲取函數位置
GetDllfunc = (DLLFUNC)GetProcAddress(hinst, "msg");
}
if (GetDllfunc != NULL) {
//運行msg函數
(*GetDllfunc)();
}
}
然后ctrl+F5,運行調試。
可以看到成功加載了我們寫的msg函數。
有關代碼中更多的細節的解釋可以參考: C++編寫DLL文件
什么是DLL劫持漏洞(DLL Hijacking Vulnerability)?
如果在進程嘗試加載一個DLL時沒有并沒有指定DLL的絕對路徑,那么Windows會嘗試去按照順序搜索這些特定目錄來查找這個DLL,如果攻擊者能夠將惡意的DLL放在優先于正常DLL所在的目錄,那么就能夠欺騙系統去加載惡意的DLL,形成”劫持”,CWE將其歸類為UntrustedSearch Path Vulnerability,比較直譯的一種解釋。
正如動態鏈接庫安全 、動態鏈接庫搜索順序微軟的官方文檔所說,
在Windows XP SP2 之前(不包括), 默認未啟用DLL搜索模式。
Windows查找DLL目錄及其順序如下:
The directory from which the application loaded.The current directory.The system directory. Use the GetSystemDirectory function to get the path of this directory.The 16-bit system directory. There is no function that obtains the path of this directory, but it is searched.The Windows directory. Use the GetWindowsDirectory function to get the path of this directory.The directories that are listed in the PATH environment variable. Note that this does not include the per-application path specified by the App Paths registry key. The App Paths key is not used when computing the DLL search path.
在Windows下, 幾乎每一種文件類型都會關聯一個對應的處理程序。
首先DLL會先嘗試搜索啟動程序所處的目錄(1),沒有找到,則搜索被打開文件所在的目錄(2),若還沒有找到,則搜索系統目錄(3),若還沒有找到,則向下搜索16位系統目錄,…Windows目錄…Path環境變量的各個目錄。
這樣的加載順序很容易導致一個系統dll被劫持,因為只要攻擊者將目標文件和惡意dll放在一起即可,導致惡意dll先于系統dll加載,而系統dll是非常常見的,所以當時基于這樣的加載順序,出現了大量受影響軟件。
后來為了減輕這個影響,默認情況下,從Windows XP Service Pack 2(SP2)開始啟用安全DLL搜索模式。
The directory from which the application loaded.The system directory. Use the GetSystemDirectory function to get the path of this directory.The 16-bit system directory. There is no function that obtains the path of this directory, but it is searched.The Windows directory. Use the GetWindowsDirectory function to get the path of this directory.The current directory.The directories that are listed in the PATH environment variable. Note that this does not include the per-application path specified by the App Paths registry key. The App Paths key is not used when computing the DLL search path.
可以看到當前目錄被放置在了后面,對系統dll起到一定的保護作用。
注:
強制關閉SafeDllSearchMode的方法:
創建注冊表項:
HKEY_LOCAL_MACHINE\System\CurrentControlSet\Control\Session Manager\SafeDllSearchMode
值為0
不過從上面分析可以知道,系統dll應該是經常調用的,如果我們對程序安裝的目錄擁有替換權限,比如裝在了非系統盤,那么我們同樣可以利用加載順序的(1)來劫持系統的DLL。
從Windows7 之后, 微軟為了更進一步的防御系統的DLL被劫持,將一些容易被劫持的系統DLL寫進了一個注冊表項中,那么凡是此項下的DLL文件就會被禁止從EXE自身所在的目錄下調用,而只能從系統目錄即SYSTEM32目錄下調用。注冊表路徑如下:
HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\Session Manager\KnownDLLs
win10的鍵值項,如圖:
這樣子就進一步保護了系統dll,防止這些常用的dll被劫持加載。
但是如果開發者濫用DLL目錄,依然會導致DLL劫持問題。(開發真難…orz)
這里我們需要使用一個工具:Process Monitor v3.60
操作過程如動態鏈接庫安全所說:
打開進程監視器的時候,會要求填入過濾器。
Include the following filters:
Operation is CreateFile
Operation is LoadImage
Path contains .cpl
Path contains .dll
Path contains .drv
Path contains .exe
Path contains .ocx
Path contains .scr
Path contains .sys
Exclude the following filters:
Process Name is procmon.exe
Process Name is Procmon64.exe
Process Name is System
Operation begins with IRP_MJ_
Operation begins with FASTIO_
Result is SUCCESS
Path ends with pagefile.sys
一次填好即可(通過上面的配置,我們可以過濾大量無關的信息,快速定位到DLL確實的路徑)
然后我們隨便打開一個程序,這里我使用的是深x服的EasyConnectInstaller:
可以看到這里最終會去嘗試加載當前目錄的一些dll,這里可以嘗試進行替換rattler中的payload.dll名字即可,點擊執行就可以彈出calc了。
1.下載地址:https://github.com/sensepost/rattler/releases/
2.使用
Rattler_x64.exe NDP461-KB3102438-Web.exe 1
結果發現這個并沒有檢測出來,可能是calc.exe啟動失敗的原因,個人感覺這個工具并不是很準確。
1.下載地址:[https://github.com/anhkgg/anhkgg-tools]
2.使用windbg導出module
然后打開chkDllHijack,粘貼處要驗證的DLL內容
然后讓他自己跑完即可,如果成功下面就會出現結果。
否則就是失敗:
綜合來說,我個人還是比較推薦采用Process monitor作為輔助工具,然后自己手工驗證這種挖掘思路的,不過自動化的確挺好的,可以嘗試自己重新定制下檢測判斷規則。本文依然傾向于入門的萌新選手,后面可能會回歸DLL代碼細節和免殺利用方面來展開(這個過程就比較需要耗時間Orz,慢慢填坑吧)。
.dll 文件編寫和使用
DLL劫持-免殺
DLL劫持漏洞自動化識別工具Rattler測試
注入技術系列:一個批量驗證DLL劫持的工具
惡意程序研究之DLL劫持
No.1概述
本文將介紹什么是用戶自定義DLL以及如何在Visual Studio中編譯用戶自定義DLL。
本教程是基于Visual Studio Community 2017 version 15.9而創建的,但這些步驟在更早的版本中也適用,版本間變化指出均有記載。本文也會討論其他的編譯器。請注意本文不涉及如何編寫DLL。
OpticStudio專業版和旗艦版允許用戶使用C或C++程序建立他們自己的組件。有很多編譯器可以編譯C/C++代碼,其中很常用的是Visual Studio。本文將提供一步步的編譯DLL的指南。
No.2什么是用戶自定義DLL
OpticStudio可以通過用戶自定義DLL來訂制。
在序列模式中,表面定義了光學材料的界面。表面類型可以是折射、反射、衍射或者漸變折射率。
OpticStudio支持超過65種不同的表面類型,包括非常普適的面型包括多項式面型和雙錐Zernike。
然而,很多時候用戶依舊希望可以根據他們的具體需求訂制一些功能。這就是用戶自定義面型有用且強大的地方了,因為OpticStudio包含使用它的界面。
在非序列模式中,物體定義了光學材料的界面。物體擁有帶有材料屬性的幾何形狀,且可以由面(face)來折射或者散射光線。材料也可以散射光線。物體也可以是個光源。
OpticStudio有針對上述所有類型的內建的物體和屬性。在非序列模式中。所有的這些都可以通過以下的DLL來訂制:用戶自定義物體、GRIN檔案、衍射算法、體散射算法、用戶自定義光源。
No.364位系統要求
過去,OpticStudio曾發布過32位和64位程序,這意味著根據不同的OpticStudio,32位和64位的DLL都可能存在。
現在,OpticStudio只支持64位的應用。如果在使用DLL時彈出以下錯誤信息,那么源代碼必須使用本文的步驟重新編譯。
注意:對于老的Visual Studio,可能需要安裝的額外的64位編譯程序包,具體步驟請見:
Windows SDK 7.1(https://www.microsoft.com/en-us/download/details.aspx?displaylang=en&id=8279)。
下載后,找到Tools…Options…Environment…Projects and Solutions。在該窗口勾選“Show advanced build configurations”且保證設置如下:
Tools…Options和“Project Properties”對話框里的不一樣。
No.4編譯DLL
一般設置
DLL文件(Dynamic Link Library)是Visual Studio的一種輸出類型。為了建立一個DLL,我們先創建project space來放置代碼。
在Visual Studio中找到File…New…Project。
在Visual C++選項下,選擇Windows Desktop Wizard項目類型。
接著選擇Application類型為“Dynamic Link Library (.dll):
當項目(project)創建好之后,第一件要做的事情是改變build type。找到Build…Configuration Manager…
在Configuration Manager中,我們把solution configuration設置為“Release”,以便在代碼運行后獲取一個DLL。選項platform需設為“x64”。這意味著代碼只會兼容64位系統和應用程序。
接下來,添加Source File。Source File必須是CPP類型,所以選擇“New Item…”并選擇C++ File。
在文件下{Zemax}\DLL下,有很多可以參照的用戶自定義代碼的示范文件。如果要使用其中某個文件,把其代碼復制到剛才新建的C++ Source File中。
當案例文檔載入后,一些項目屬性需要修改。右鍵點擊項目名稱并選擇“Properties”。
在Configuration Properties…General中,確保“Configuration Type”已經設為“Dynamic Library (.dll)”,并且把“Character Set”設置為“Use Multi-Byte Character Set”。
注意:更早版本的Visual Studio會需要本文開頭提到額外下載包,且需要設置它們的“Platform Toolset”為Windows7.1SDK。
然后,在Configuration Properties…C/C++…Code Generation選取“Runtime Library”。一般地,我們推薦使用Multi-threaded (/MT)或者Multi-threaded DLL (/MD)。
Multi-threaded與Multi-threaded DLL之間的選擇比較復雜,但大體上是兼容性和易用性之間的選擇。
如果該用戶自定義DLL不依賴于其他庫(library),那么使用Multi-Threaded runtime library意味著不考慮在本電腦上安裝使用該DLL的C++ redistributable。
如果在本DLL中,需要用到其他的庫,那么就需要在此選取那個runtime庫,而在絕大多數情況下,它都是Multi-Threaded DLL runtime。
對于簡單的不調用第三方庫的DLL,Multi-threaded runtime是最佳選擇。
一旦以上設定宣告完畢,點擊OK來退出Properties對話框。
No.5檢查代碼錯誤
現在,我們來檢查并確保所有內容都能被母代碼識別。如果該DLL是個用戶自定義表面且source file路徑不在{Zemax}\DLL\Surfaces文件夾,那么頭文件“usersurf.h”會不被認可(由紅色下劃線表征)。
為修正這個問題,直接把 {Zemax}\DLL\Surfaces下面的“usersurf.h”復制并粘貼到source code同一個路徑下,然后右鍵點擊“Header Files”來載入頭文件并導入現有的代碼中。
當頭文件載入到正確的路徑后,紅色下劃線應該就不再出現了。
如果有任何內容未被讀出,那么該DLL可能不能編譯。即使編譯了,也未必能正常運行。
No.6使用C++編譯器
絕大部分Zemax自帶的案例文件都是用C語言寫的。由于Visual Studio是個C++編譯器,這意味著必須對代碼進行一些修改來正確地編譯它們。
如果還沒添加。那么在代碼開頭的初始化功能區放入“extern “C” {}”。同時確保把“BOOL WINAPI DllMain”這一行注釋掉。
在C++編譯器里,程序(function)名往往會在后臺被修改,以使得每個程序都有其唯一的標識。
如果程序名變化了,那么OpticStudio會無法運行該DLL,因為OpticStudio會尋找具體的名稱(例如:UserDefinedSurface、UserObjectDefinition等)采用上述改變可以強制編譯器保持原來C代碼里的程序名且忽略任何可能造成的錯誤。
同樣地,可能也會需要無視由于C和C++的細微不同造成的警告,例如:
C4996: ‘srtcpy’: This function or variable may be unsafe. Consider using strcpy_s instead. To disable deprecation, use _CRT_SECURE_NO_WARNINGS.
如果這是編譯中唯一遇到的警告,那么以下的代碼可以繞過這個問題:
#pragma warning ( disable : 4996 )
這行代碼會允許編譯器提供特定于電腦或者操作系統的功能,同時保持C和C++語言上大體的兼容性。
Rebuild Solution
選擇Build…Rebuild Solution來編譯你的代碼,或者直接按鍵盤“Ctrl+F5”。編譯成功后會輸出以下內容:
這個DLL會在solution文件夾下。對于“My_Surface”這個項目而言,該DLL會在{Project directory}\My_Surface\x64\Release。
把這個DLL放在爭取的{Zemax}\DLL\ 文件夾下,OpticStudio就能讀取并使用它了。
No.7編譯器
以下版本的Visual Studio都可以用來編譯OpticStudio的用戶自定義DLL:
?? 2005, 2008
?? Express 2010, Express 2012
?? Community 2017, Community 2019
請閱讀
https://visualstudio.microsoft.com/zh-hans/vs/community/來了解使用條件。
除了Visual Studio之外,任何其他64位的C語言編譯器只要可以創建多線程的Windows DLL project,那么都可以使用。有很多不同的編譯器可供選擇。
如果在使用其他編譯器時遇到技術問題,請聯系他們的技術支持詢問以下問題:“如何創建空的多線程Win32 DLL project?”
OpticStudio中編寫特定面型、光源等DLL的功能是非常強大的,它可以建立更加準確的系統模型。
No.8參考資料
以下網頁可以幫助你理解案例代碼中用到的命令:
http://www.cplusplus.com/reference/
圖文來自Zemax官方