這種方法的原理就是由自己寫一個與原有進程調用的DLL具有相同介面函數的DLL,再用我們的DLL替換原有的DLL。在替換的過程中,由我們自己編寫感興趣的函數替換原有函數,而對其它不感興趣的函數,則以函數轉寄的形式調用原有DLL中的函數。這裡面有個前提,就是你在編寫DLL時你必須知道原有DLL中的函數都有哪些,以免導至其它進程調用DLL時找不到相應的API函數,特別是在替換系統DLL檔案時更要小心。
下面就來示範一下這種方式。我是這樣做的,首先寫一個DLL作為被替換的DLL,名為DllLib.dll(最後更名為_DllLib.dll),然後寫特洛伊DLL,名為TroyDll.Dll(最後更名為原有DLL名,即DllLib.dll),與DllLib.Dll具有相同的API函數過程,但是對其中的一個API函數做更改,使其完成我們的工作,因為另外還有一個API函數需要對其進行函數轉寄,轉給原來的DLL,即(更名為_DllLib.dll的DllLib.dll)。這時我們的測試程式本來是調用的DllLib.dll的,但由於DllLib.dll已經被TroyDll.dll替換了,所以測試程式實際上調用的是TroyDll.dll,而對於做轉寄的函數,則是通過TroyDll.Dll調用DllLib.dll(更名後的_DllLib.dll)完成的。此時我們的特洛伊DLL實際上已經注入到我們的測試程式的進程空間中來了。
1、編寫本來的DLL。DllLib.dll(更名後為_DllLib.dll)的代碼如下:
//===========================================================================
// 檔案: UnitLib.cpp
// 說明: 示範用特洛伊DLL進行DLL注入.這個是本身的DLL,另一個特洛伊DLL將
// 對它進行函數轉寄,並實現另外的功能.
// 作者: 陶冶(無邪)
//===========================================================================
// 函式宣告
extern "C" __declspec(dllexport) __stdcall void About();
extern "C" __declspec(dllexport) __stdcall int Add(int a, int b);
int WINAPI DllEntryPoint(HINSTANCE hinst, unsigned long reason, void* lpReserved)
{
return 1;
}
//---------------------------------------------------------------------------
void __declspec(dllexport) __stdcall About()
{
try
{
MessageBox(NULL,"這是本來的DLL檔案!","原來的DLL",
MB_ICONINFORMATION + MB_OK);
}catch(Exception &e)
{
MessageBox(NULL,e.Message.c_str(),"DllLib",MB_OK);
}
}
// 兩數相加(注意:這裡是兩數相加)
int __declspec(dllexport) __stdcall Add(int a, int b)
{
return (a + b);
}
2、編寫特洛伊DLL。TroyDll.dll的代碼如下:
//===========================================================================
// 檔案: UnitTroy.cpp
// 說明: 這個是特洛伊DLL,呆會將它的DLL檔案更改為要替換的DLL檔案名稱
// 作者: 陶冶
//===========================================================================
extern "C" __declspec(dllexport) __stdcall void About();
extern "C" __declspec(dllexport) __stdcall int Add(int a, int b);
int Multiply(int a, int b);
// DLL中的函數原形聲明
typedef void (WINAPI *ABOUT)();
typedef int (WINAPI *ADD)(int a, int b);
static String szDllName;
int WINAPI DllEntryPoint(HINSTANCE hinst, unsigned long reason, void* lpReserved)
{
szDllName = Application->ExeName;
szDllName = szDllName.SubString(0,szDllName.Length()
- String(StrRScan(szDllName.c_str(),'/')).Length());
// 更名後的DllLib.dll檔案名稱
szDllName = szDllName + "/_DllLib.dll";
return 1;
}
//---------------------------------------------------------------------------
void __declspec(dllexport) __stdcall About()
{
// 直接做函數轉寄
HANDLE hDll = NULL;
hDll = LoadLibrary(szDllName.c_str());
ABOUT about;
try
{
if (hDll != NULL)
{
about = (ABOUT)GetProcAddress(hDll,"About");
if (about != NULL)
about();
}
else
MessageBox(NULL,"載入原來的DLL錯誤!", "特洛伊DLL",
MB_ICONINFORMATION + MB_OK);
}catch(Exception &e)
{
MessageBox(NULL,e.Message.c_str(),"DllTroy",MB_OK);
}
}
int __declspec(dllexport) __stdcall Add(int a, int b)
{
int nRet;
HANDLE hDll = NULL;
ADD add;
hDll = LoadLibrary(szDllName.c_str());
if (hDll != NULL)
{
// 為了方便示範,這裡再做一次函數轉寄,以便看到本來應該返回的值。
add = (ADD)GetProcAddress(hDll,"Add");
if (add != NULL)
nRet = add(a, b);
ShowMessage("這是本來DLL中的調用結果:" + IntToStr(nRet));
}
else
MessageBox(NULL, "載入本來的DLL錯誤!", "特洛伊DLL", MB_OK);
// 將原來完成兩數相加的更改為兩數相乘,返回兩數的積。
nRet = Multiply(a, b);
return nRet;
}
int Multiply(int a, int b)
{
return (a * b);
}
3、編寫測試工程。在表單中添加兩個按鈕,分別調用DllLib.dll中的兩個API函數。代碼如下:
typedef (WINAPI *ABOUT)();
typedef int (WINAPI *ADD)(int a, int b);
//---------------------------------------------------------------------------
__fastcall TfrmMain::TfrmMain(TComponent* Owner)
: TForm(Owner)
{
}
//---------------------------------------------------------------------------
void __fastcall TfrmMain::FormCreate(TObject *Sender)
{
m_szDllName = Application->ExeName;
m_szDllName = m_szDllName.SubString(0,m_szDllName.Length()
- String(StrRScan(m_szDllName.c_str(),'/')).Length());
m_szDllName = m_szDllName + "/DllLib.dll";
}
//---------------------------------------------------------------------------
// 調用About()函數
void __fastcall TfrmMain::Button1Click(TObject *Sender)
{
HANDLE hDll;
ABOUT about;
try
{
hDll = LoadLibrary(m_szDllName.c_str());
if (hDll != NULL)
{
about = (ABOUT)GetProcAddress(hDll,"About");
if (about != NULL)
about();
}
}catch(Exception &e)
{
MessageBox(Handle, e.Message.c_str(), "ERROR",MB_OK);
}
}
//---------------------------------------------------------------------------
// 調用Add()函數
void __fastcall TfrmMain::Button2Click(TObject *Sender)
{
HANDLE hDll;
ADD add;
int nRet;
hDll = LoadLibrary(m_szDllName.c_str());
if (hDll != NULL)
{
add = (ADD)GetProcAddress(hDll,"Add");
if (add != NULL)
nRet = add(10, 2);
ShowMessage("從特洛伊DLL中返回的結果 : " + IntToStr(nRet));
}
}
4、測試。將DllLib.dll更名為_DllLib.dll,將TroyDll.dll更名為DllLib.dll,即完成了DLL的替換。下面運行我們的測試工程,單擊調用About()函數的按鈕,因為About()是通過DllLib.dll(即TroyDll.dll)做的函數轉寄(轉以給原DLL,即_DllLib.dll),所以看到的是原DLL(即_DllLib.dll)中彈出的資訊框。此時用查看進程模組的工具來查看進程空間,你會發現,你的特洛伊DLL(更名後的DllLib.dll)已經成功以注入到了測試程式的進程空間中了。
單擊調用Add()函數的按鈕,你會看到本來是完成兩數相加的,返回的結果卻是兩數的積,因為我們已經在特洛伊DLL中對它做了手腳。這下是利用它的關鍵了。WINDOWS登入時的GINA,知道吧,想得到別人登入的密碼嗎?那麼就是用這種方法了,把MSGINA.dll這個東西給它替換成自己的DLL,再複雜的密碼也照樣得到啊,一點也不費精神,呵呵~。(想自己寫嗎?看看《WinLogon登入管理和GINA簡介》吧)
環境:
WIN2000 Server + BCB 6.0
附:本文相關程式碼
參考:《WINDOWS核心編程》