进行DLL注入的三种方法
作者:陶冶(无邪)
MAIL:taoy5178@hotmail.com
OICQ:24149877
在WINDOWS中,每个进程都有自己独立的地址空间,这样一个应用程序就无法进入另一个进程的地址空间而不会破坏另一个进程的运行,这样使得系统更加的稳定。但这样一来,相反的,如果我们要对我们感兴趣的进程进行操作也就变得复杂起来。比如,我们要为另一个进程创建的窗口建立子类或是要想从其中一个感兴趣的进程中取得一些有趣的信息(比如你想得到WIN2000用户登录的密码)。而DLL注入技术就是正好可以解决这些问题。DLL注入就是将DLL插入到其它你指定的进程的地址空间中,使得我们可以对感兴趣的进程进行操作。
在我们的DLL注入到指定的进程空间时,为了可以使我们更清楚地看到它已经成功对注入到了指定的进程空间,所以我们有需要使用一个简单的工具来查看指定的进程空间中所载入的所有模块,以便确定我们的DLL是否已经成功注入。如果你系统中装有WINDOWS优化大师,那么你可以利用它提供的进程工具来查看,没有也没关系,我用BCB写了一个小工具,虽然不怎么方便,但也可以清楚地看到指定进程空间中的所有载入模块。
该工具的主要代码如下:
//---------------------------------------------------------------------------
void __fastcall TfrmMain::btLookClick(TObject *Sender)
{
DWORD dwProcessId;
BOOL bRet;
MODULEENTRY32 hMod = {sizeof(hMod)};
HANDLE hthSnapshot = NULL;
BOOL bMoreMods = FALSE;
ListView->Clear();
if (Edit->Text == "")
return;
else
dwProcessId = StrToInt(Edit->Text);
// 为进程建立一个快照
hthSnapshot = CreateToolhelp32Snapshot(TH32CS_SNAPMODULE,
dwProcessId);
if (hthSnapshot == NULL)
{
MessageBox(Handle,("CreateToolhelp32Snapshot failed with error "
+ IntToStr(GetLastError())).c_str(),"Error!",
MB_ICONINFORMATION + MB_OK);
return;
}
// 获取模块列表中的模块
bMoreMods = Module32First(hthSnapshot, &hMod);
if (bMoreMods == FALSE)
{
MessageBox(Handle,("Module32First failed with error "
+ IntToStr(GetLastError())).c_str(),"Error!",
MB_ICONINFORMATION + MB_OK);
return;
}
for (; bMoreMods; bMoreMods = Module32Next(hthSnapshot, &hMod))
{
TListItem *Item;
Item = ListView->Items->Add();
Item->Caption = String(hMod.szExePath);
Item->ImageIndex = 0;
}
// 关闭句柄
CloseHandle(hthSnapshot);
}
接下来就开始我们的正题吧。
DLL注入主要有三种方法,即应用HOOK技术、创建远程线程和特洛伊DLL三种。
一、应用HOOK技术进行DLL注入
我原来写过有关HOOK的介绍,如果你看过了或者是以前写过HOOK程序,那么你已经会这种DLL注入了。它其它就是为系统或某个线程安装一个钩子。这里要说的是,如果是全局钩子,那么你的DLL将会在进程调用时载入到任意一个调用的进程的地址空间中,这样是相当浪费资源的。因此我在下载的演示中就只对某一个指定的线程安装线程钩子。
1、用BCB建立一个DLL工程(如果你用的是VC或其它,请自己对照),输入以下代码:
//===========================================================================
// 文件: UnitLib.cpp
// 说明: 演示利用钩子技术进行DLL注入.
// 将本DLL中的代码注入到指定的进程空间.
// 作者: 陶冶(无邪)
//===========================================================================
// 函数声明
extern "C" __declspec(dllexport) __stdcall
bool SetHook(DWORD dwThreadId);
extern "C" __declspec(dllexport) __stdcall
LRESULT CALLBACK MyProc(int nCode, WPARAM wParam, LPARAM lParam);
static HHOOK hHook = NULL; // 钩子句柄
static HINSTANCE hInst; // 当前DLL句柄
int WINAPI DllEntryPoint(HINSTANCE hinst, unsigned long reason, void* lpReserved)
{
hInst = hinst;
return 1;
}
//---------------------------------------------------------------------------
// 安装钩子函数
bool __declspec(dllexport) __stdcall SetHook(DWORD dwThreadId)
{
if (dwThreadId != 0)
{
MessageBox(NULL, ("DLL已经注入!\nThreadId = " +
IntToStr(dwThreadId)).c_str(),"DLL",
MB_ICONINFORMATION + MB_OK);
// 安装指定线程的钩子
hHook = SetWindowsHookEx(WH_GETMESSAGE, (HOOKPROC)MyProc,
hInst,dwThreadId);
if (hHook != NULL)
return true;
}else
{
MessageBox(NULL, "DLL即将从记事本进程空间中撤出!","DLL",
MB_ICONINFORMATION + MB_OK);
return (UnhookWindowsHookEx(hHook));
}
return true;
}
// 钩子函数
LRESULT CALLBACK __declspec(dllexport) __stdcall
MyProc(int nCode, WPARAM wParam, LPARAM lParam)
{
// 因为只是演示DLL注入,所以这里什么也不做,交给系统处理
return (CallNextHookEx(hHook, nCode, wParam, lParam));
}
//---------------------------------------------------------------------------
该DLL中有两个函数,一个为安装钩子函数(SetHook),另一个为钩子函数(MyProc)。其中安装钩子函数提供了一个参数,由该参数指定安装到哪个线程,如果该参数为0,则卸载钩子。
编译该工程,即生成我们要用来注入到指定进程中的DLL文件了。
2、建立测试工程。用BCB建立一个应用程序工程,在窗体中添加两个按钮,一个用来安装线程钩子,一个用来卸载。代码如下:
//---------------------------------------------------------------------------
// SetHook函数原型声明
typedef BOOL (WINAPI *LPSETHOOK)(unsigned long dwThreadId);
//---------------------------------------------------------------------------
__fastcall TfrmMain::TfrmMain(TComponent* Owner)
: TForm(Owner)
{
}
//---------------------------------------------------------------------------
// 安装钩子
void __fastcall TfrmMain::Button1Click(TObject *Sender)
{
String szPath;
LPSETHOOK lproc;
HANDLE hDll;
BOOL bRet;
PROCESS_INFORMATION info;
STARTUPINFO start;
memset(&start, 0, sizeof(start));
// 取得要载入的DLL文件名
szPath = Application->ExeName;
szPath = szPath.SubString(0, szPath.Length()
- String(StrRScan(szPath.c_str(),'\\')).Length());
szPath = szPath + "\\DllLib.dll";
// 载入DLL
hDll = LoadLibrary(szPath.c_str());
if (hDll != NULL)
{
lproc = (LPSETHOOK)GetProcAddress(hDll,"SetHook");
if (lproc != NULL)
{
// 因为没有适当的工具可以取得线程ID,也为了简单起见,所以这里新创建了一个记事本进程,以便取得它的线程ID,对其安装钩子,把我们的DLL注入到记事本进程中。
bRet = CreateProcess(NULL,
"c:\\winnt\\system32\\notepad.exe",
NULL,
NULL,
TRUE,
0,
NULL,
NULL,
&start,
&info);
if (bRet != 0)
{
if((*lproc)(info.dwThreadId) == false)
ShowMessage("Sethook failed with error " +
IntToStr(GetLastError()));
}
else
{
ShowMessage("CreateProcess failed with error " +
IntToStr(GetLastError()));
}
}
}
}
//---------------------------------------------------------------------------
//卸载钩子
void __fastcall TfrmMain::Button2Click(TObject *Sender)
{
String szPath;
LPSETHOOK lproc;
HANDLE hDll;
szPath = Application->ExeName;
szPath = szPath.SubString(0, szPath.Length()
- String(StrRScan(szPath.c_str(),'\\')).Length());
szPath = szPath + "\\DllLib.dll";
hDll = LoadLibrary(szPath.c_str());
if (hDll != NULL)
{
lproc = (LPSETHOOK)GetProcAddress(hDll,"SetHook");
if (lproc != NULL)
(*lproc)(0);
}
}
//---------------------------------------------------------------------------
接下来生成可执行文件,点击第一个安装钩子按钮,然后你就可以用我们最开始写的查看模块的工具来查看了,你将会在模块中看到你刚才DLL的路径及文件名,这表明我们已经成功地将自己的DLL注入到了记事本进程空间。点击卸载按钮后,再查看记事本进程中的模块,将不会看到我们DLL文件的完整文件名,这表明已经成功撤消了对记事本进程的注入。
二、利用远程线程来进行DLL注入
这种方法同前一种方法相比,要显得复杂一些,并且这种方法只能在WIN2000中使用(XP,和最新的2003不知道)。具体步骤如下:
1)、取得远程进程的进程ID;
2)、在远程进程空间中分配一段内存用来存放要注入的DLL完整路径;
3)、将要注入的DLL的路径写到刚才分配的远程进程空间;
4)、从Kernel32.dll中取得LoadLibray的地址;
5)、调用CreateRemoteThread函数以从Kernel32.dll中取得的LoadLibrary函数的地址为线程函数的地址,以我们要注入的DLL文件名为参数,创建远程线程;
在第二三步中,为什么要把我们要注入的DLL的文件名写到远程进程的地址空间进行操作,《WINDOWS核心编程》中是这样描述的:
“(要注入的DLL文件名)字符串是在调用进程的地址空间中。该字符串的地址已经被赋予新创建的远程线程,该线程将它传递给L o a d L i b r a r y A。但是,当L o a d L i b r a r y A取消对内存地址的引用时, D L L路径名字符串将不再存在,远程进程的线程就可能引发访问违规”;
至于第四步中为什么不直接对LoadLibrary进行调用,《WINDOWS核心编程》中是这样描述的:
“如果在对C r e a t e R e m o t e T h r e a d的调用中使用一个对L o a d L i b r a r y A的直接引用,这将在你的模块的输入节中转换成L o a d L i b r a r y A的形实替换程序的地址。将形实替换程序的地址作为远程线程的起始地址来传递,会导致远程线程开始执行一些令人莫名其妙的东西。其结果很可能造成访问违规。”
好了,下面开始我们的例子。
1、同上面应用HOOK来进行DLL注入一样,我们先创建一个DLL工程,这个DLL完全可以不编写任何代码,因为我们只想将DLL注入到指定进程就达到目的了,但为了好看,我还是随便在其中写一个API函数。代码如下:
extern "C" __declspec(dllexport) __stdcall void About();
int WINAPI DllEntryPoint(HINSTANCE hinst, unsigned long reason, void* lpReserved)
{
return 1;
}
//---------------------------------------------------------------------------
void __declspec(dllexport) __stdcall About()
{
MessageBox(NULL,"这个DLL模块演示了DLL的注入技术。\n"
"通过程序的调用LoadLibrary将本模块注入到指定的"
"进程的地址空间中。", "DLL注入技术",
MB_ICONINFORMATION + MB_OK);
}
编译它,就得到我们用来注入的DLL文件了。接下来是测试工程。
2、编写测试工程。用BCB建立一个应用程序工程,在窗体中放入两个按钮,一个用来注入,一个用来撤消,另外还有一个文本框控件,用来等待用户输入进程ID号。代码如下:
//---------------------------------------------------------------------------
// DLL注入函数
BOOL WINAPI LoadLib(DWORD dwProcessId, LPWSTR lpszLibName)
{
HANDLE hProcess = NULL,
hThread = NULL;
LPWSTR lpszRemoteFile = NULL;
// 打开远程进程
hProcess = OpenProcess(PROCESS_CREATE_THREAD
| PROCESS_VM_OPERATION
| PROCESS_VM_WRITE,
FALSE,
dwProcessId);
if (hProcess == NULL)
{
MessageBox(NULL, ("OpenProcess failed with error "
+ IntToStr(GetLastError())).c_str(), "Error",
MB_ICONINFORMATION + MB_OK);
return FALSE;
}
// 在远程进程中分配存贮DLL文件名的空间
lpszRemoteFile = (LPWSTR)VirtualAllocEx(hProcess, NULL,
sizeof(WCHAR) * lstrlenW(lpszLibName) + 1,
MEM_COMMIT, PAGE_READWRITE);
if (lpszRemoteFile == NULL)
{
MessageBox(NULL, ("VirtualAllocEx failed with error "
+ IntToStr(GetLastError())).c_str(), "Error",
MB_ICONINFORMATION + MB_OK);
return FALSE;
}
// 复制DLL文件名到远程刚分配的进程空间
if (!WriteProcessMemory(hProcess, lpszRemoteFile,
(PVOID)lpszLibName, sizeof(WCHAR) * lstrlenW(lpszLibName) + 1,
NULL))
{
MessageBox(NULL, ("WriteProcessMemory failed with error "
+ IntToStr(GetLastError())).c_str(), "Error",
MB_ICONINFORMATION + MB_OK);
return FALSE;
}
// 取得LoadLibrary函数在Kennel32.dll中的地址
PTHREAD_START_ROUTINE pfnThreadRtn =
(PTHREAD_START_ROUTINE)GetProcAddress(
GetModuleHandle("Kernel32.dll"),"LoadLibraryW");
if (pfnThreadRtn == NULL)
{
MessageBox(NULL, ("GetProcAddress failed with error "
+ IntToStr(GetLastError())).c_str(), "Error",
MB_ICONINFORMATION + MB_OK);
return FALSE;
}
// 创建远程线程
hThread = CreateRemoteThread(hProcess,
NULL,
0,
pfnThreadRtn, // LoadLibrary地址
lpszRemoteFile, // 要加载的DLL名
0,
NULL);
if (hThread == NULL)
{
MessageBox(NULL, ("CreateRemoteThread failed with error "
+ IntToStr(GetLastError())).c_str(), "Error",
MB_ICONINFORMATION + MB_OK);
return FALSE;
}
// 等待线程返回
WaitForSingleObject(hThread, INFINITE);
// 释放进程空间中的内存
VirtualFreeEx(hProcess, lpszRemoteFile, 0, MEM_RELEASE);
// 关闭句柄
CloseHandle(hThread);
CloseHandle(hProcess);
return TRUE;
}
// 在进程空间释放注入的DLL
BOOL WINAPI FreeLib(DWORD dwProcessId, LPTSTR lpszLibName)
{
HANDLE hProcess = NULL,
hThread = NULL,
hthSnapshot = NULL;
MODULEENTRY32 hMod = {sizeof(hMod)};
BOOL bFound;
// 取得指定进程的所有模块映象
hthSnapshot = CreateToolhelp32Snapshot(TH32CS_SNAPMODULE,
dwProcessId);
if (hthSnapshot == NULL)
{
MessageBox(NULL, ("CreateRemoteThread failed with error "
+ IntToStr(GetLastError())).c_str(), "Error",
MB_ICONINFORMATION + MB_OK);
return FALSE;
}
// 取得所有模块列表中的指定的模块
BOOL bMoreMods = Module32First(hthSnapshot, &hMod);
if (bMoreMods == FALSE)
{
MessageBox(NULL, ("Module32First failed with error "
+ IntToStr(GetLastError())).c_str(), "Error",
MB_ICONINFORMATION + MB_OK);
return FALSE;
}
// 循环取得想要的模块
for (;bMoreMods; bMoreMods = Module32Next(hthSnapshot, &hMod))
{
//ShowMessage(String(hMod.szExePath) + " | " + String(lpszLibName));
if ((strcmp(hMod.szExePath, lpszLibName) == 0) ||
(strcmp(hMod.szModule, lpszLibName) == 0))
break;
}
// 打开进程
hProcess = OpenProcess(PROCESS_CREATE_THREAD | PROCESS_VM_OPERATION,
FALSE, dwProcessId);
if (hProcess == NULL)
{
MessageBox(NULL, ("OpenProcess failed with error "
+ IntToStr(GetLastError())).c_str(), "Error",
MB_ICONINFORMATION + MB_OK);
return FALSE;
}
// 取得FreeLibrary函数在Kernel32.dll中的地址
PTHREAD_START_ROUTINE pfnThreadRtn =
(PTHREAD_START_ROUTINE)GetProcAddress(
GetModuleHandle("Kernel32.dll"), "FreeLibrary");
if (pfnThreadRtn == NULL)
{
MessageBox(NULL, ("GetProcAddress failed with error "
+ IntToStr(GetLastError())).c_str(), "Error",
MB_ICONINFORMATION + MB_OK);
return FALSE;
}
// 创建远程线程来执行FreeLibrary函数
hThread = CreateRemoteThread(hProcess,
NULL,
0,
pfnThreadRtn,
hMod.modBaseAddr,
0,
NULL);
if (hThread == NULL)
{
MessageBox(NULL, ("CreateRemoteThread failed with error "
+ IntToStr(GetLastError())).c_str(), "Error",
MB_ICONINFORMATION + MB_OK);
return FALSE;
}
// 等待线程返回
WaitForSingleObject(hThread, INFINITE);
// 关闭句柄
CloseHandle(hThread);
CloseHandle(hthSnapshot);
CloseHandle(hProcess);
return TRUE;
}
//---------------------------------------------------------------------------
void __fastcall TfrmMain::btLoadClick(TObject *Sender)
{
m_szDllFile = Application->ExeName;
m_szDllFile = m_szDllFile.SubString(0, m_szDllFile.Length()
- String(StrRScan(m_szDllFile.c_str(),'\\')).Length());
m_szDllFile = m_szDllFile + "\\DllLib.dll";
m_dwProcessId = StrToInt(Edit->Text);
LoadLib(m_dwProcessId, WideString(m_szDllFile).c_bstr());
}
//---------------------------------------------------------------------------
void __fastcall TfrmMain::btUnloadClick(TObject *Sender)
{
FreeLib(m_dwProcessId, m_szDllFile.c_str());
}
//---------------------------------------------------------------------------
好了,把上面的工程编译成生EXE文件,接下来我们就可以进行DLL的注入测试了。先打开记事本(当然你也可以打开其它的进程,或直接在已经加载的进程测试),通过WINDOWS的任务管理器,找到它的进程ID。然后运行我们的测试工程,在文本框中输入进程ID,点击注入。这时我们就可以通过我们最先写的小工具来查看它的进程空间中所包含的模块了,你会发现,我们的DLL已经成功加载到了它的进程空间中。点击卸载,取消DLL的注入。
三、利用特洛伊DLL进行注入
这种方法的原理就是由自己写一个与原有进程调用的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核心编程》
==============================================
写了很久了,没来得及发出来~~~~呵呵~~~~,有不对之处,欢迎大家指正~~