1. DLL文件劫持應該怎麼辦 急啊
DLL當一個可執行文件運行時,Windows載入器將可執行模塊映射到進程的地址空間中,載入器分析可執行模塊的輸入表,並設法找出任何需要的DLL,並將它們映射到進程的地址空間中。
由於輸入表中只包含DLL名而沒有它的路徑名,因此載入程序必須在磁碟上搜索DLL文件。首先會嘗試從當前程序所在的目錄載入DLL,如果沒找到,則在Windows系統目錄查找,最後是在環境變數中列出的各個目錄下查找。利用這個特點,先偽造一個系統同名的DLL,提供同樣的輸出表,每個輸出函數轉向真正的系統DLL。程序調用系統DLL時會先調用當前目錄下偽造的DLL,完成相關功能後,再跳到系統DLL同名函數里執行,如圖18.4。這個過程用個形象的詞來描述就是系統DLL被劫持(hijack)了。
184.gif
利用這種方法取得控制權後,可以對主程序進行補丁。此種方法只對除kernel32.dll, ntdll.dll等核心系統庫以外的DLL有效,如網路應用程序的ws2_32.dll,游戲程序中的d3d8.dll,還有大部分應用程序都調用的lpk.dll,這些DLL都可被劫持。
利用5.6.2章提供的CrackMeNet.exe來演示一下如何利用劫持技術製作補丁,目標文件用Themida v1.9.2.0加殼保護。
1.補丁地址
去除這個CrackMe網路驗證方法參考第5章,將相關補丁代碼存放到函數PatchProcess( )里。例如將401496h改成:
代碼:00401496 EB 29 jmp short 004014C1
補丁編程實現就是:
代碼:
unsigned char p401496[2] = {0xEB, 0x29};
WriteProcessMemory(hProcess,(LPVOID)0x401496, p401496, 2, NULL);
p401496這個數組的數據格式,可以用OllyDBG插件獲得,或十六進制工具轉換。例如Hex Workshop打開文件,執行菜單Edit/Copy As/Source即可得到相應的代碼格式。
2.構建輸出函數
查看實例CrackMeNet.exe輸入表,會發現名稱為ws2_32.dll的DLL,因此構造一個同名的DLL來完成補丁任務。偽造的ws2_32.dll有著真實ws2_32.dll一樣的輸出函數,完整源碼見光碟。實現時,可以利用DLL模塊中的函數轉發器來實現這個目標,其會將對一個函數的調用轉至另一個DLL中的另一個函數。可以這樣使用一個pragma指令:
代碼:#pragma comment(linker, "/EXPORT:SomeFunc=DllWork.someOtherFunc")
這個pragma告訴鏈接程序,被編譯的DLL應該輸出一個名叫SomeFunc的函數。但是SomeFunc函數的實現實際上位於另一個名叫SomeOtherFunc的函數中,該函數包含在稱為DllWork. dll的模塊中。
如要達到劫持DLL的目的,生成的DLl輸出函數必須與目標DLL輸出函數名一樣,本例可以這樣構造pragma指令:
代碼:#pragma comment(linker, "/EXPORT:WSAStartup=_MemCode_WSAStartup,@115")
編譯後的DLL,會有與ws2_32.dll同名的一個輸出函數WSAStartup,實際操作時,必須為想要轉發的每個函數創建一個單獨的pragma代碼行,讀者可以寫一個工具或用其他辦法,將ws2_32.dll輸出函數轉換成相應的pragma指令。
當應用程序調用偽裝ws2_32.dll的輸出函數時,必須將其轉到系統ws2_32.dl中去,這部分的代碼自己實現。例如WSAStartup輸出函數如下構造:
代碼:
ALCDECL MemCode_WSAStartup(void)
{
GetAddress("WSAStartup");
__asm JMP EAX;//轉到系統ws2_32.dll的WSAStartup輸出函數
}
其中GetAddress()函數的代碼如下:
代碼:
// MemCode 命名空間
namespace MemCode
{
HMODULE m_hMole = NULL; //原始模塊句柄
DWORD m_dwReturn[500] = {0}; //原始函數返回地址
// 載入原始模塊
inline BOOL WINAPI Load()
{
TCHAR tzPath[MAX_PATH]={0};
TCHAR tzTemp[MAX_PATH]={0};
GetSystemDirectory(tzPath, sizeof(tzPath));
strcat(tzPath,"\\ws2_32.dll");
m_hMole = LoadLibrary(tzPath);//載入系統系統目錄下ws2_32.dll
if (m_hMole == NULL)
{
wsprintf(tzTemp, TEXT("無法載入 %s,程序無法正常運行。"), tzPath);
MessageBox(NULL, tzTemp, TEXT("MemCode"), MB_ICONSTOP);
}
return (m_hMole != NULL);
}
// 釋放原始模塊
inline VOID WINAPI Free()
{
if (m_hMole)
FreeLibrary(m_hMole);
}
// 獲取原始函數地址
FARPROC WINAPI GetAddress(PCSTR pszProcName)
{
FARPROC fpAddress;
TCHAR szProcName[16]={0};
TCHAR tzTemp[MAX_PATH]={0};
if (m_hMole == NULL)
{
if (Load() == FALSE)
ExitProcess(-1);
}
fpAddress = GetProcAddress(m_hMole, pszProcName);
if (fpAddress == NULL)
{
if (HIWORD(pszProcName) == 0)
{
wsprintf(szProcName, "%d", pszProcName);
pszProcName = szProcName;
}
wsprintf(tzTemp, TEXT("無法找到函數 %hs,程序無法正常運行。"), pszProcName);
MessageBox(NULL, tzTemp, TEXT("MemCode"), MB_ICONSTOP);
ExitProcess(-2);
}
return fpAddress;
}
}
using namespace MemCode;
編譯後,用LordPE查看偽造的ws2_32.dll輸出函數,和真實ws2_32.dll完全一樣,如圖18.5所示。
185.gif
查看偽造的ws2_32.dll中任意一個輸出函數,例如WSACleanup:
代碼:
.text:10001CC0 ; int __stdcall WSACleanup()
.text:10001CC0 WSACleanup proc near
.text:10001CC0 push offset aWsacleanup ;"WSACleanup"
.text:10001CC5 call sub_10001000 ;GetAddress(WSACleanup)
.text:10001CCA jmp eax
.text:10001CCA WSACleanup endp
會發現輸出函數WSACleanup()首先調用GetAddress(WSACleanup),獲得真實ws2_32.dll中WSACleanup的地址,然後跳過去執行,也就是說ws2_32.dll各輸出函數被HOOK了。
3.劫持輸出函數
ws2_32.dll有許多輸出函數,經分析,程序發包或接包時,WSAStartup輸出函數調用的比較早,因此在這個輸出函數放上補丁的代碼。代碼如下:
代碼:
ALCDECL MemCode_WSAStartup(void)
{
hijack();
GetAddress("WSAStartup");
__asm JMP EAX;
}
hijack()函數主要是判斷是不是目標程序,如是就調用PatchProcess()進行補丁。
void hijack()
{
if (isTarget(GetCurrentProcess())) //判斷主程序是不是目標程序,是則補丁之
{
PatchProcess(GetCurrentProcess());
}
}
偽造的ws2_32.dll製作好後,放到程序當前目錄下,這樣當原程序調用WSASTartup函數時就調用了偽造的ws2_32.dll的WSASTartup函數,此時hijack()函數負責核對目標程序校驗,並將相關數據補丁好,處理完畢後,轉到系統目錄下的ws2_32.dll執行。
這種補丁技術,對加殼保護的軟體很有效,選擇掛接的函數最好是在殼中沒有被調用,當掛接函數被執行時,相關的代碼己被解壓,可以直接補丁了。有些情況下,必須用計數器統計掛接的函數的調用次數來接近OEP。此方法巧妙地繞過了殼的復雜檢測,很適合加殼程序的補丁製作。
一些木馬或病毒也會利用DLL劫持技術搞破壞,因此當在應用程序目錄下發現系統一些DLL文件存在時,應引起注意。