先谈下游戏,游戏每个动作都会调用函数。比方游戏设计人员是这样设计游戏的:
void zhixing() //执行函数
{
dazuo();
....... //@1
}
void dazuo() //打坐函数
{
jimi("...."); //加密函数
return;
}
void jimi(char *s)
{
//对s加密处理,赋值给str变量
fashong(str); //发送函数
return;
}
void fashong(char *s)
{
send(s);
return;
}
你要打坐,你就必须要调用相对应的函数。上面的游戏只要你调用dazuo()就会在游戏中执行打坐这个动作。 而通过OD可以把游戏执行在内存中的代码反汇编出来。
而为什么要DLL注入呢?
因为win NT系统是每个进程独立的,只有自己进程才能执行(调用自己进程的函数)
你把dll注入到游戏进程空间,dll就可以调用游戏进程空间中的函数了。
再打个比方:你call 0072e7a8,就是执行本进程空间的0072e7a8位置的函数。
你如果不注入,而在自己程序中执行call 0072e7a8,也就是调用自己进程0072e7a8地址的函数,那当然有问题啦,游戏的函数全在游戏进程中。你外挂进程0072e7a8是什么,与游戏执行无关。
再谈基址问题吧
游戏中任何数据都在内存中存在,不管是血,蓝,等级,怪物名字,地图名字,只要是数据。全在内存中。 只要你读取那个内存地址就能显示出血来。 其实说白了,游戏客户端显示的数据也是读取的内存中的数据而显示出来的。
struct renwu //人物结构
{
DWORD xue; //血
DWORD lan; //蓝
DWORD dengji; //等级
........
};
void init() //游戏初始化函数
{
struct renwu *dwRW;
dwRW = new struct renwu;
}
游戏初始化了,dwRW在内存中的地址是固定的,这就是基地址
而new是动态分配的,谁也不知道系统会分配哪个地址给你。
比如说dwRW在内存中的地址是0026762e, 这样,你只需要
[0026762e]就可以得到dwRW结构的地址,偏移0就是血的地址,
偏移4就是蓝的地址,偏移8就是等级的地址了。
再谈谈f12呼出外挂吧
想在游戏中按f12键呼出外挂,当然就是截取在游戏中按f12键的消息了。想到要截取某进程的某消息,
第一想法就应该是想到挂钩子。钩子就是hook,就是把游戏钩住的意思,比如你给游戏安装一个键盘钩子,这样,只要在游戏进程中的程序,有键盘消息,都会先执行你的钩子函数,所以,你只需要挂个钩子,在钩子函数中,判断按钮是否为F12,如果是,就显示外挂窗口,不是,就放弃这个消息,让游戏自己去处理。
如果只想在游戏执行后,在游戏中按F12,就呼出外挂,就必须把安装钩子和钩子函数写到dll,因为只有dll才能映射到别的进程空间去,exe程序是不行的,dll中的钩子函数映射到游戏进程空间去了,就可以只钩住这个进程的键盘消息了,(所以如果你不把安装钩子和钩子函数写到dll的话,就成了全局钩子了,全局钩子是钩住windows平台中所有的进程的,在任何时候你按f12,都会呼出外挂的)