from
http://www.cnblogs.com/i_like_cpp/archive/2005/05/17/157594.html
-----------------------------------
在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核心编程》
posted @
2008-01-16 23:04 桂湖山 阅读(322) |
评论 (0) |
编辑 收藏
这种方法同前一种方法相比,要显得复杂一些,并且这种方法只能在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的形实替换程序的地址。将形实替换程序的地址作为远程线程的起始地址来传递,会导致远程线程开始执行一些令人莫名其妙的东西。其结果很可能造成访问违
规。”
另:
DLL注入的步骤
1。在受害进程中为DLL代码分配要占据的空间。
用到的函数:VirtualAllocEx
2。在受害进程中为要注入的DLL所需的参数分配空间。
用到的函数:VirtualAllocEx
3。把DLL的名字和代码写入受害进程的存储空间。
用到的函数:WriteProcessMemory
4。在受害进程中创建线程,运行新注入的DLL。
用到的函数:CreateRemoteThread
5。释放受害进程中的资源。
用到的函数:VirtualFreeEx
//sample
#include <windows.h> Dzm?锷w?
#include <winsvc.h> ?鵾 :.禔
#include <tlhelp32.h> lY篼rc柲v?
// DLL注入函数 ?W黲?銷
bool LoadLib(DWORD dwProcessId, LPWSTR lpszLibName) =孔觧f镏J
{ 焿t瞵醞
HANDLE hProcess = NULL; ?7?箨W?
HANDLE hThread = NULL; /?G#q腱Y
LPWSTR lpszRemoteFile = NULL; i缘?碫9?
諥?堃櫛
t~,?叮
// 打开远程进程 `2?q駉f<?
hProcess = OpenProcess(PROCESS_CREATE_THREAD eO`;*?=?
| PROCESS_VM_OPERATION 綂?咧v??
| PROCESS_VM_WRITE, ?{瘐?高
FALSE, 陾?柺&荗w
dwProcessId); 浙?(v
w??S甗J?
if (hProcess == NULL) 鑉?>Y祽?
{ 垺|}=Z肬_
MessageBox(NULL, "OpenProcess failed with error " , "Error", ?珚綛:
MB_ICONINFORMATION + MB_OK); )b禸齲;u
return FALSE; ]嘢W?缣
} 户喯?椱m?
?睟諗倝?
訬E)¥梥?
// 在远程进程中分配存贮DLL文件名的空间 庿}忸丁!
lpszRemoteFile = (LPWSTR)VirtualAllocEx(hProcess, NULL, 1闎u;P?茒
sizeof(WCHAR) * lstrlenW(lpszLibName) + 1, _tH硎娾]
MEM_COMMIT, PAGE_READWRITE); <?J?痲v
if (lpszRemoteFile == NULL) 徇??笯l*
{ (SNX~[70
MessageBox(NULL, "VirtualAllocEx failed with error " , "Error", 囖沒何M??
MB_ICONINFORMATION + MB_OK); ?眖ir?
return FALSE; 龝H﹢湷?
} 巙碋κm7巇
幚w薦?H
// 复制DLL文件名到远程刚分配的进程空间 QH洐迦?
if (!WriteProcessMemory(hProcess, lpszRemoteFile, ?鈷坘侄煝
(PVOID)lpszLibName, sizeof(WCHAR) * lstrlenW(lpszLibName) + 1, ?钍禵绚?
NULL)) P+??滁
{ E耦??C'?
MessageBox(NULL, "WriteProcessMemory failed with error " , "Error", p`j$潀1
MB_ICONINFORMATION + MB_OK); ?鮑征?
return FALSE; ?etXF?%M
} 01?熜?鑴
// 取得LoadLibrary函数在Kennel32.dll中的地址 _雨B芰?
PTHREAD_START_ROUTINE pfnThreadRtn = 'k1汕
(PTHREAD_START_ROUTINE)GetProcAddress( j澇x竳俘?
GetModuleHandle("Kernel32.dll"),"LoadLibraryW"); E?涽gm埳?
if (pfnThreadRtn == NULL) 伓ZC騷远?
{ >痥T ◣g?
MessageBox(NULL, "GetProcAddress failed with error " , "Error", y 广篋狤?
MB_ICONINFORMATION + MB_OK); MqQ?? }
return FALSE; A?`|9欛
} e?&嘄 ^?
// 创建远程线程 c,0髛jgeJ
hThread = CreateRemoteThread(hProcess, `剎^D鯩??
NULL, Ar鹼DH貂 ?
0, #W?枊R柑
pfnThreadRtn, // LoadLibrary地址 胭mG榍??
lpszRemoteFile, // 要加载的DLL名 楖8資啱
0, ?I挷?腺?
NULL); 熿騒I<簀?
if (hThread == NULL) 撪?殥?v?
{ }教z >?
MessageBox(NULL, "CreateRemoteThread failed with error " , "Error", 靁嘽g授G
MB_ICONINFORMATION + MB_OK); ? 4復?
return FALSE; .k钄z5.S
} m袁鹯?Z?
躎?b|钍
// 等待线程返回 媣l\\厝I0
WaitForSingleObject(hThread, INFINITE); 屰騉-7@e?
検蓮~R灔
// 释放进程空间中的内存 ?跑+e脌
VirtualFreeEx(hProcess, lpszRemoteFile, 0, MEM_RELEASE); 摶"汊驄&?
// 关闭句柄 钡領?荴
CloseHandle(hThread); ?蚊ˉ?
CloseHandle(hProcess); ?G.矽庉N
return TRUE; ZCu尢5賆
} 90??嫫蟄
螨^rn`+v膼
void main() J癋赖澝?
{ 嫟g賜jG??
LPWSTR m_szDllFile = L"D:\\FileHook\\APIHook_Dll\\Debug\\APIHook_Dll.dll"; ?k2R脽忮
DWORD m_dwProcessId = 0; }Υofe介?
PROCESSENTRY32 pe; 蝷?掆
// 创建快照句柄 pT唓虌赦⊿
HANDLE hSnapshot = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0); 葺?M櫍M
// 先搜索系统中第一个进程的信息 cG嫕l詻翘?
Process32First(hSnapshot, &pe); q杴5?Y?
// 下面对系统中的所有进程进行枚举,并保存其信息 _m?]5椃t
do{ FMJ?
if(strcmp(pe.szExeFile,"explorer.exe") == 0 ) h? |VH?
F塠昷籛^
{ 彞€?T胉eF
m_dwProcessId =pe.th32ProcessID; m災滑磁A?
break; /?漌2n?
} O??拻懠?
} 5謣軇旽j
while (Process32Next(hSnapshot, &pe)); M_:娌_樆?
// 关闭快照句柄 w?E眳
CloseHandle(hSnapshot); 鐭ㄘ碲ш潰
LoadLib(m_dwProcessId, m_szDllFile); ?8Y旡X苽{
} 唜#B鮘
?i淪渧"f?
posted @
2008-01-16 22:54 桂湖山 阅读(1522) |
评论 (0) |
编辑 收藏
从eXtremeComponents FAQ获知
0:(by yegang)
<ec:table action="/GX.../xyz.do"
..
/>中,其xyz.do必须是生成页面的action,若为中转的action则不能导出文件。
1.?如何使用导出功能
Q: 如何使用导出功能
A: 为了使用导出功能,只需要在web.xml文件中加入eXtremeComponents的导出过滤器的配置,内容如下:
<filter>
<filter-name>eXtremeExport</filter-name>
<filter-class>org.extremecomponents.table.filter.ExportFilter</filter-class>
<init-param>
<param-name>responseHeadersSetBeforeDoFilter</param-name>
<param-value>true</param-value>
</init-param>
</filter>
<filter-mapping>
<filter-name>eXtremeExport</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
5.?变量命名问题
Q:当变量名为"action",在IE下执行产生javascript错误
A:
内部使用了一些关键字,就目前我所知的为"action"、"submit"。建议大家命名时尽量避免,如果大家必须使用,则可以使用table标签的autoIncludeParameters参数设置为"false":
autoIncludeParameters="false"
7.?加入链接
Q:怎么样加入链接
A: 你可以参考下例:
<ec:table
var="pres"
items="presidents"
action="${pageContext.request.contextPath}/compact.run"
imagePath="${pageContext.request.contextPath}/images/table/compact/*.gif"
view="compact"
title="Compact Toolbar View"
showTooltips="false"
>
<ec:exportPdf
fileName="output.pdf"
tooltip="Export PDF"
headerColor="black"
headerBackgroundColor="#b6c2da"
headerTitle="Presidents"
text="PDF"
/>
<ec:exportXls
fileName="output.xls"
tooltip="Export Excel"
text="XLS"
/>
<ec:row>
<ec:column property="fullName" title="Name">
<a href="http://www.whitehouse.gov/history/presidents/">${pres.fullName}</a>
</ec:column>
<ec:column property="nickName"/>
<ec:column property="term"/>
<ec:column property="born" cell="date"/>
<ec:column property="died" cell="date"/>
<ec:column property="career"/>
</ec:row>
</ec:table>
Q: 我想使用行的高亮显示如何设置
A: 你只需要设置行标签的highlightRow属性:
highlightRow="true"。eXtremeComponents提供了很多接口允许用户按照自己的习惯来进行定制,包括:CSS、CELL、View。相关信息请参考指南。
posted @
2007-09-11 23:24 桂湖山 阅读(1134) |
评论 (0) |
编辑 收藏
要创建一个DB
LINK,必须先
在每个数据库服务器上设置链接字符串。
如:
在/var/opt/oracle/tnsnames.ora中有以下
一条和北京的数据库链接tobeijing,格式如下:
链接字符串的设置 |
说 明
|
tobeijing=(description= |
database link名称:tobeijing |
(address=(protocol=tcp) |
采用tcp/ip协议 |
(host=www.bj.col.com.cn) |
欲链接主机名称或IP地址 |
(port=1521)) |
网络端口1521 |
(connect_data=(sid=oracle7))) |
安装ORACLE采用的sid |
然后进入系统管理员SQL>操作符下,运行命令:
SQL>create public
database link beijing connect to scott identified by tiger
using 'tobeijing';
则创建了一个以scott用户和北京数据库的链接beijing,我们查询北京的scott数据:
SQL>select * from
emp@beijing;
这样就可以把深圳和北京scott用户的数据做成一个整体来处理。
posted @
2007-09-09 22:19 桂湖山 阅读(800) |
评论 (0) |
编辑 收藏
13978340110专业维修显示器(长城电脑软件公司维修人员)
是长城电脑软件公司维修部维修人员,现因上班时间短,本人可以我大家提供上门维修电脑.主机.显示器
同时收购电脑配件.(显示器.主机.主板.电源.....等电脑及周边的耗材)
电话:3913427 13978340110
posted @
2007-08-25 10:37 桂湖山 阅读(144) |
评论 (0) |
编辑 收藏
在java里 对文件的读写应通过:
InputStreamReader reader=new InputStreamReader(new FileInputStream("text.txt"),encoding);
或
OutputStreamWriter ow=new OutputStreamWriter(fFileOutputSteam,encoding);
类来完成,其中指定的encoding(如"gb2312","utf-8"等),可以保证读出、写入的文件以指定的编码方式存在。
因为具体执行读写的read/write方法是针对byte进行的,这两个类会进行必要的转换。
参考文章:
unicode简介
posted @
2007-08-21 23:47 桂湖山 阅读(1344) |
评论 (0) |
编辑 收藏
oracle TNS 错误
在安装了多个ora实例的机器中,其tn*.ora文件会有多个,
oracle可能会使用你未修改的那一个;故可搜索出所以的tn*.ora,
将本地服务名加入,即可。
posted @
2007-08-16 20:45 桂湖山 阅读(322) |
评论 (0) |
编辑 收藏
转载
posted @
2007-08-15 16:51 桂湖山 阅读(2160) |
评论 (41) |
编辑 收藏
1)控制台报The WebLogic Server did not start up properly.
java.io.InvalidClassException:
javax.management.MBeanAttributeInfo; local class incompatible: stream
classdesc serialVersionUID = 7043855487133450673, local class
serialVersionUID = 8644704819898565848
错误原因及解决办法:MBeanAttributeInfo
的serialVersionUID的版本控制id不一样,说明是使用这个被使用的bean被修改过了,很显然是版本不对;修改
startWebLogic.cmd文件,设置set
JAVA_HOME=D:\bea\jdk141_05,使其指向weblogic自带的jdk;重启startWebLogic.cmd问题消失。
2)错误报Deployment descriptor "web.xml" is malform
ed. Check against the DTD: org.xml.sax.SAXParseException: cvc-elt.1: Cannot find
错误原因及解决办法:解析web.xml出现问题,修改OPEN_CMS\webapp\WEB-INF\web.xml文件;在该文件的 最上面添加如下内容:
<!DOCTYPE web-app PUBLIC "-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN"
"http://java.sun.com/dtd/web-app_2_3.dtd">
重新启动错误消失。
posted @
2007-08-09 17:59 桂湖山 阅读(994) |
评论 (1) |
编辑 收藏
多纹理混合技术在地形渲染中用得很广泛,用来将非常不同的多个纹理(如石头和草)混合起来,看不出明显的边缘。例如,你可以用3个纹理(石头、草和沙子)来渲染一座山,在山的底部用草和沙子来混合。
在OpenGL中通过
ARB_multitexture和
ARB_texture_env_combine扩展来实现多纹理混合。分为以下几个步骤:
1. 计算3个纹理各自的贡献,并将它们编码进顶点的颜色中,这样颜色的RGB部分控制纹理阶段0和1间的插值,颜色的ALPHA控制纹理阶段1和2间的插值。
2. 使用GL_ARB_multitexture将3个纹理同时应用到物体表面。
3. 将第一个纹理设为纹理阶段0。
4. 在纹理阶段1,使用GL_INTERPOLATE_ARB在纹理阶段0的输出(第一个纹理)和阶段1的纹理之间用GL_SRC_COLOR(颜色的RGB部分)进行线性插值。
5. 在纹理阶段2,使用GL_INTERPOLATE_ARB在纹理阶段1的输出(第一个纹理和第二个纹理的混合体)和阶段2的纹理之间用GL_SRC_COLOR(颜色的ALPHA部分)进行线性插值。
典型的代码为:
glTexEnvi( GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_COMBINE_ARB );
glTexEnvi( GL_TEXTURE_ENV, GL_COMBINE_RGB_ARB, GL_INTERPOLATE_ARB );
glTexEnvi( GL_TEXTURE_ENV, GL_SOURCE0_RGB_ARB, GL_PREVIOUS_ARB );//前面的纹理阶段
glTexEnvi( GL_TEXTURE_ENV, GL_OPERAND0_RGB_ARB, GL_SRC_COLOR );
glTexEnvi( GL_TEXTURE_ENV, GL_SOURCE1_RGB_ARB, GL_TEXTURE );//当前的纹理
glTexEnvi( GL_TEXTURE_ENV, GL_OPERAND1_RGB_ARB, GL_SRC_COLOR );
glTexEnvi( GL_TEXTURE_ENV, GL_SOURCE2_RGB_ARB, GL_PRIMARY_COLOR_ARB);//基颜色
glTexEnvi( GL_TEXTURE_ENV, GL_OPERAND2_RGB_ARB, GL_SRC_COLOR );//或GL_SRC_ALPHA
在使用前,要先查看系统允许的纹理阶段数目:
int nTextureUnits = 0;
glGetIntegerv( GL_MAX_TEXTURE_UNITS_ARB, &nTextureUnits );
这种技术使用起来比较复杂,现在可以用shader来实现,简单得多,但为了兼顾旧显卡,这种技术还是很常用的。
posted @
2007-07-27 12:13 桂湖山 阅读(680) |
评论 (0) |
编辑 收藏