|
将这些代码注入到被外挂游戏程序进程内存空间中,不然游戏进程根本不会访问到替代函数代码。注入方法有很多,如利用全局钩子注入、利用注册表注入挡截user32库中的api函数、利用createremotethread注入(仅限于 nt/2000)、利用bho注入等。在后面的实例中,将继续利用这个全局钩子。至于其它几种注入方法,如果感兴趣可参阅msdn有关内容。
有了以上理论基础,下面就开始制作一个挡截messageboxa和recv函数的实例,在开发游戏外挂程序 时,可以此实例为框架,加入相应的替代函数和处理代码即可。此实例的开发过程如下:
(1) 打开前面创建的activekey项目。
(2) 在activekey.h文件中加入hookapi结构,此结构用来存储被挡截api函数名称、原api函数地址和替代函数地址。
typedef struct tag_hookapi
{
lpcstr szfunc;//被hook的api函数名称。
proc pnewproc;//替代函数地址。
proc poldproc;//原api函数地址。
}hookapi, *lphookapi;
(3) 打开activekey.cpp文件,首先加入一个函数,用于定位输入库在输入数据段中的iat地址。代码如下:
extern "c" __declspec(dllexport)pimage_import_descriptor
locationiat(hmodule hmodule, lpcstr szimportmod)
//其中,hmodule为进程模块句柄;szimportmod为输入库名称。
{
//检查是否为dos程序,如是返回null,因dos程序没有 iat。
pimage_dos_header pdosheader = (pimage_dos_header) hmodule;
if(pdosheader->e_magic != image_dos_signature) return null;
//检查是否为nt标志,否则返回null。
pimage_nt_headers pntheader = (pimage_nt_headers)((dword)pdosheader+ (dword)(pdosheader->e_lfanew));
if(pntheader->signature != image_nt_signature) return null;
//没有iat表则返回null。
if(pntheader->optionalheader.datadirectory[image_directory_entry_import].virtualaddress == 0) return null;
//定位第一个iat位置。
pimage_import_descriptor pimportdesc = (pimage_import_descriptor)((dword)pdosheader + (dword)(pntheader->optionalheader.datadirectory[image_directory_entry_import].virtualaddress));
//根据输入库名称循环检查所有的iat,如匹配则返回该iat地址,否则检测下一个iat。
while (pimportdesc->name)
{
//获取该iat描述的输入库名称。
pstr szcurrmod = (pstr)((dword)pdosheader + (dword)(pimportdesc->name));
if (stricmp(szcurrmod, szimportmod) == 0) break;
pimportdesc++;
}
if(pimportdesc->name == null) return null;
return pimportdesc;
}
再加入一个函数,用来定位被挡截api函数的iat项并修改其内容为替代函数地址。代码如下:
extern "c" __declspec(dllexport)
hookapibyname( hmodule hmodule, lpcstr szimportmod, lphookapi phookapi)
//其中,hmodule为进程模块句柄;szimportmod为输入库名称;phookapi为hookapi结构指针。
{
//定位 szimportmod输入库在输入数据段中的iat地址。
pimage_import_descriptor pimportdesc = locationiat(hmodule, szimportmod);
if (pimportdesc == null) return false;
//第一个thunk地址。
pimage_thunk_data porigthunk = (pimage_thunk_data)((dword)hmodule + (dword)(pimportdesc->originalfirstthunk));
//第一个iat项的thunk地址。
pimage_thunk_data prealthunk = (pimage_thunk_data)((dword)hmodule + (dword)(pimportdesc->firstthunk));
//循环查找被截api函数的iat项,并使用替代函数地址修改其值。
while(porigthunk->u1.function)
{
//检测此thunk是否为 iat项。
if((porigthunk->u1.ordinal & image_ordinal_flag) != image_ordinal_flag)
{
//获取此iat项所描述的函数名称。
pimage_import_by_name pbyname =(pimage_import_by_name)((dword)hmodule+(dword)(porigthunk->u1.addressofdata));
if(pbyname->name[0] == '\0') return false;
//检测是否为挡截函数。
if(strcmpi(phookapi->szfunc, (char*)pbyname->name) == 0)
{
memory_basic_information mbi_thunk;
//查询修改页的信息。
virtualquery(prealthunk, &mbi_thunk, sizeof(memory_basic_information));
//改变修改页保护属性为page_readwrite。
virtualprotect(mbi_thunk.baseaddress,mbi_thunk.regionsize, page_readwrite, &mbi_thunk.protect);
//保存原来的api函数地址。
if(phookapi->poldproc == null)
phookapi->poldproc = (proc)prealthunk->u1.function;
//修改api函数iat项内容为替代函数地址。
prealthunk->u1.function = (pdword)phookapi->pnewproc;
//恢复修改页保护属性。
dword dwoldprotect;
virtualprotect(mbi_thunk.baseaddress, mbi_thunk.regionsize, mbi_thtnk.protecu, &dwoldprotect);
}
}
porigthunk++;
prealthunk++;
}
setlasterror(error_success); //设置错误为error_success,表示成功。
return true;
}
(4) 定义替代函数,此实例中只给messageboxa和recv两个api进行挡截。代码如下:
static int winapi messageboxa1 (hwnd hwnd , lpctstr lptext, lpctstr lpcaption, uint utype)
{
//过滤掉原messageboxa的正文和标题内容,只显示如下内容。
return messagebox(hwnd, "hook api ok!", "hook api", utype);
}
static int winapi recv1(socket s, char far *buf, int len, int flags )
{
//此处可以挡截游戏服务器发送来的网络数据包,可以加入分析和处理数据代码。
return recv(s,buf,len,flags);
}
(5) 在keyboardproc函数中加入激活挡截api代码,在if( wparam == 0x79 )语句中后面加入如下else if语句:
......
//当激活f11键时,启动挡截api函数功能。
else if( wparam == 0x7a )
{
hookapi api[2];
api[0].szfunc ="messageboxa";//设置被挡截函数的名称。
api[0].pnewproc = (proc)messageboxa1;//设置替代函数的地址。
api[1].szfunc ="recv";//设置被挡截函数的名称。
api[1].pnewproc = (proc)recv1; //设置替代函数的地址。
//设置挡截user32.dll库中的messageboxa函数。
hookapibyname(getmodulehandle(null),"user32.dll",&api[0]);
// 设置挡截wsock32.dll库中的recv函数。
hookapibyname(getmodulehandle(null),"wsock32.dll",&api[1]);
}
......
(6) 在activekey.cpp中加入头文件声明 "#include "wsock32.h"。 从“工程”菜单中选择“设置”,弹出project setting对话框,选择link标签,在“对象/库模块”中输入ws2_32..lib。
(7) 重新编译activekey项目,产生activekey.dll文件,将其拷贝到simulate.exe目录下。运行simulate.exe并启动全局钩子。激活任意应用程序,按f11键后,运行此程序中可能调用messageboxa函数的操作,看看信息框是不是有所变化。同样,如此程序正在接收网络数据包,就可以实现封包功能了。 |
|