梦魇骑士 发表于 2013-5-4 07:09:21

在ERA中安全的inline Hook

最近放假,搞得鸡血了,作息时间无规律,这么早就起来了,一会儿估计又想睡觉。不知道搞什么写点心得吧,高手勿喷,同时希望有兴趣的可以探讨下去。
在新生物插件中给生物解决自定义箭矢文件问题的。
由于h3以及早期的wog没有考虑太多的设计以外的生物的问题(这是时代背景和开发背景决定的,并非是设计不周密)。在处理箭矢的时候使用一个switch结构,类似于如下结构
switch(生物ID)
   case a,b,c,d:
      箭矢="yy.def"
   case e,f,g,h:
      箭矢="zz.def"
如果都没找到,返回了个默认的箭矢文件。就是说有自己箭矢的生物,是“写死”在代码里的。这个结构紧接着调用了一个函数,姑且叫函数A
箭矢保存在ecx中作为函数A的隐含参数,斗胆猜测这是个fastcall调用约定的函数,当然这个不是重点。
就是说只要在调用函数A前改变ecx中保存的箭矢文件指针,就可以自定义了。思路有了,于是作者用inline hook把这段调用函数A的代码,改成了调用自己的函数B
函数B修改ecx后跳转回函数A执行,具体的实现网上有不少例子不多说。
hook的汇编代码并不多,但是如果要完成一些复杂的功能,比如读取配置文件来选择对这些生物的处理方式,都会造成堆栈结构的破坏和寄存器取值的变化。极容易引起崩溃。因为我们是“硬塞”代码进去,而且我们在编译自己的DLL时还会被编译器进行优化,我们来一步步解决这些问题。
1.得到想要的汇编代码以及他的地址。
先上代码
LPVOID GetAsmHandlerPtr()
{
LPVOIDpRet = NULL;
__asm
{
lea eax, __label_code
mov pRet, eax
jmp __label_ret
}
__asm
{
__label_code:
   [跳转后要执行的代码]
}
__asm {__label_ret: }
return pRet;
}
上面的函数第一个asm段取标签__label_code的地址,就是我们真正要得asm的代码的地址,然后返回则个地址。从jmp __label_ret知道我们调用这个函数只会返回地址而执行不到__label_code里面的汇编代码,这样变相的避开函数优化得到一个纯粹的汇编代码段
2.完美解决寄存器和堆栈“污染”
当然这里说的“完美”其实是我自卖自夸的,因为没有解决标志位寄存器这些,不过在这里没有影响就是了。
首先是堆栈指针esp ebp
我们的目标是调用一个自己的C++函数,在这个函数里面很舒服的写一眼就能看得懂的c/c++代码。要达成这目标就必须要用到堆栈,然而堆栈是属于游戏线程的,这里就要用到我提出的“伪堆栈”。简单的说,我在dll中定义一个大小为1M的全局的字符数组变量,因为编译后在变量区,是可读写的,本身内存页属性就和堆栈类似。
要注意的问题是这个变量地址是从低地址向高地址增长的,就是在虚拟内存里面字符abc, b字符的内存地址比a字符大。而堆栈正好相反,是从高地址向低地址“增长”的,比如我们函数调用时压入参数esp反而变小了。这个问题立即就可以了,实际上系统并不知道自己在使用假堆栈,嘿嘿。
具体来实现就是定义全局变量dwESP, dwEBP用来保存原来的esp ebp,那么我们上面提到的__label_code里面的汇编代码就要开始做这些工作
//保存堆栈寄存器,注意这个时候堆栈还在游戏线程堆栈,这时的操作不影响堆栈指针
mov dwESP, esp
mov dwEBP, ebp
//取出伪堆栈的地址给esp,注意此后堆栈变化了,以后的函数调用会使用这个伪堆栈
lea esp, FACK_STACK
//上面提到增长方式的不同,我们给esp加上0x20000来使esp位于这个伪堆栈的里面
add esp, 0x20000
//一般的,把ebp也移动过来
mov ebp, esp
sub esp,0x200
//现在准备就绪,要保存其余的寄存器,用pushad全部压栈就可以了,注意这时候ecx(我们要修改的寄存器)也在第二个被压入堆栈
pushad
//下面和游戏场景有关系,这里原本的函数使用ebx-一些数据结构,在0x34偏移保存有正在处理的生物ID,ecx为上面switch下来的箭矢字符串指针
//这也是我们需要的,于是我们针对这个定义一个函数LPTSTR WINAPI 处理函数(LPTSTR 参数1,LPDWORD 参数2)
//原因是我们这里要在汇编里面调用他,定义WINAPI子函数会自己处理我们的压栈参数对堆栈的影响,比较方便,推荐使用。
//函数在调用后会按照我们的安排把寄存器的值给我们的参数,我们可以很方便的使用我们要得数据,这也算一个小技巧
push ebx //参数2
push ecx //参数1
call 处理函数
//简单的,我们写个配置文件定义好枪兵的箭矢文件是pikeshot.def,在处理函数里面发现时枪兵就返回pikeshot.def的指针
//函数调用过后 eax保存有这个指针,此时堆栈(esp)已经平衡到我们pushad时候的值,还记得之前我们保存所有寄存器的值
//其中第二个压入的是ecx,那么此时就是保存原来ecx值得地方,我们要用eax改写它
mov , eax
//新生物插件的作者在这里把原本游戏调用的函数地址保存的eax然后jmp跳到eax
//这样变相的改变的eax的值,在这里当然是没有问题,不过我得对得起帖子标题,我们把它保存到FACK_STACK里面去,反正可以用的空间很大
lea , FACK_STACK
movdword ptr , 游戏原函数地址
//恢复所有寄存器,注意恢复后相当于只有ecx被我们偷偷改写了,除了esp ebp都恢复了进入这段代码时的状态
popad
//恢复ebp, esp,离开“伪堆栈”,还记得之前我们保存的值吗
mov esp, dwESP
mov ebp, dwEBP
//至此,完全恢复了游戏原函数调用前的寄存器和堆栈状态,现在jmp到原来调用的函数
//这里有个细节,就是为什么不用call,因为call会压入函数调用后要执行的语句的地址入栈,而这个工作在进入这段代码前就做了。
//还记得我们是怎么来的吗,也是一个call指令啊
jmp dword ptr
这样就完成了一次不着痕迹的代码hook,非常安全,环保。
呵呵,到这里了,高手笑过罢了。

[ 本帖最后由 梦魇骑士 于 2013-5-4 07:14 编辑 ]

贤知有您 发表于 2013-5-4 08:13:02

看你写了这么多,我只能说: 你是高手
还说你不会光写汇编,这尽是汇编
静候你的新插件
;tl;

灰狼王子 发表于 2013-5-4 08:27:38

看起来灰常流弊的样子呢;aim; 专业人士啊

去日苦多 发表于 2013-5-4 08:47:55

;ft; 牛人啊!!

双子 发表于 2013-5-4 10:01:09

看着那一堆堆的“文字”,我感觉像是在看天书,显得自己是个文盲似的;wunai;

双子 发表于 2013-5-4 10:21:39

看了ride the lighting的视频,突然我点感触。也终于知道为什么中国难以出产好游戏了,因为作游戏的都不是玩家,而玩家又不会作游戏,游戏开发商基本都不听取玩家建议,只会想办法在游戏增加附加价值虚拟物件,专门捞钱,他们是为钱而制作游戏,而不是为玩家为恶制作游戏。

梦魇骑士 发表于 2013-5-4 14:17:31

回复 发帖回帖人人有责 6# 的帖子

这个是这样的,就拿h3来说,我觉得其实虽然经典,但是后来玩家添加的元素才最符合玩家的需要啊。

梦魇骑士 发表于 2013-5-4 14:20:06

回复 消灭零回复 2# 的帖子

你给我的老外的插件才全是汇编写的,比如那个button。不过我并不推荐这样做。第一人让想搞的人望而却步,第二写起来太复杂,我才写了这个帖子主要是对论坛里面懂编程的朋友降低他们对插件的恐惧,能有更多的人加入。

梦魇骑士 发表于 2013-5-4 14:21:26

回复 跟帖促繁荣 3# 的帖子

没有的,技术都是常用的,可能大家不接触所以觉得神秘,其实没有的。

双子 发表于 2013-5-4 14:47:31

原帖由 梦魇骑士 于 4-5-2013 02:17 PM 发表 http://www.h3wog.com/images/common/back.gif
这个是这样的,就拿h3来说,我觉得其实虽然经典,但是后来玩家添加的元素才最符合玩家的需要啊。

H5还不是有着一堆强大的MOD支持,谁知在H6的面前却是那么不堪一击。基础不扎实,任何强大的外来因素都是虚的。
就拿文明4来说,在无比强大的文明5面前,他仍然是个屹立不倒的巨人,在画面无比炫目的暗黑3面前,暗黑2的路越发的走得坚强与遥远。在生化危机越发火红的今天,也越发的体现了开发商的弱点,1-3的经典已经没有办法被延续了,为了迎合市场,硬生生由一个恐怖游戏变成射击游戏,生化迷吐糟的还嫌少吗?
只不过对于新生代的玩家来说游戏性好像压根地的不重要,画面胜于一切,游戏时间要短,通关不能过于复杂,最完美的就是能够利用金钱来实现强大的美梦,这种现象到底是游戏开发商迎合了玩家的需求,还是游戏开发商给宠出来的傻瓜式玩家?

灰狼王子 发表于 2013-5-4 23:15:40

以前就有转帖讨论过现在游戏市场的庸俗产业化,玩家和游戏制作者各自忙乎各自的,所关注的都大大超出游戏本身或者干脆偏离游戏本身;sign; 所以,H3和WOG的忠实粉丝一般来说都是有些理想主义的一代,也可能是最后一代了。。。

双子 发表于 2013-5-4 23:57:02

原帖由 灰狼王子 于 4-5-2013 11:15 PM 发表 http://www.h3wog.com/images/common/back.gif
以前就有转帖讨论过现在游戏市场的庸俗产业化,玩家和游戏制作者各自忙乎各自的,所关注的都大大超出游戏本身或者干脆偏离游戏本身;sign; 所以,H3和WOG的忠实粉丝一般来说都是有些理想主义的一代,也可能是最后一代 ...

老狼这句“也可能是最后一代了。。。”实在令我感触良多啊!
尽管这是个游戏百花齐放的年代,可是却缔造不了那曾经的辉煌了,毕竟竞争实在厉害!不过话说回来,在一些竞技类游戏里,新生代玩家倒是挺活跃的,而且成就也都不赖,或许新的时代的确需要新的东西来迎合吧。
换个角度来说,也有可能是我们这一代已经out了,与时代脱轨了。

不过我还是希望在H7上看到新的展望!

獠穴无名指 发表于 2013-5-16 11:52:57

;ft;
在我硬生生看了2\3之后
我瞎了~
完全不知所云....

heimukai 发表于 2013-6-13 10:08:54

看到堆栈我就怕了,这东西离我的理解力太远了,ERA你不要这么难不可以啊,
平民一点啊,这么高深让人怎么玩你啊
页: [1]
查看完整版本: 在ERA中安全的inline Hook

捐赠