本人使用易语言
也有10几年时间,至今留下来的也只是一些怀念和情节,还记得上一次在本论坛发帖是在10年前,期间也只是零星来论坛看看。平常使用也纯属兴趣爱好,并非科班出身,一些见解如若有误,忘大神包含。我们知道目前易语言是支持32位编译,后期估计也不会有所改善了,这似乎已经成为一门被放弃的失败品。既然如此面对如今64位系统的普及,易语言爱好者面对64位程序的操作层面上就显得有些无奈和悲哀。好在有着一些执着的爱好者不想放弃,依然在鼓励解决这些问题,如本论坛的一个开源模块WOW64Ext,就为易语言操作64位模块进程提供了一些基本的封装,本人也借此基础上封装了几个功能,作为进一步扩展,有兴许的朋友可以继续完善。
一:浅谈64位进程远程hook技术
关于HOOK这个话题,网络上铺天盖地并无新鲜,故在此我就不讲述什么是HOOK这些无聊话题了,本文主要阐述一些64位下远程HOOK与32位的主要区别。首先我们来看看要实现一个远程HOOK的构成顺序:1:在目标进程申请内存空间,存放我们截断后的穿插代码与HOOK原代码2:修改HOOK目标位置的指令为跳转至1申请的内存空间中3:穿插代码中把我们需要的寄存器或其他通过通讯手段传达到我们程序的回调接口中去,在这个过程中如果只需要取值,穿插代码不需要等待,如果需要修改生效,穿插代码需要等待回调接口的返回,并把修改内容写回。4:穿插代码最后跳回到HOOK位置长度的下一跳指令,或指定的位置。5:完成整个HOOK过程了解了整个过程看上去似乎很简单,确实要做到这个过程是不难的,只是要做到相对完美要考虑的情况有很多。比如对跳转使用的选择情况:HOOK跳转代码肯定是越短越好,像32位JMP跳转只需要5字节即可,但是在64位进程中情况确截然不同。32位进程寻址能力为4字节,而64位进程寻址能力变成了8字节,然而64位汇编中所有的跳转直接寻址只支持4字节,这在32位中当然不是什么问题,因为32位最大寻址本来就不会超越4字节,不存在超限的说法:但64位中想要达到长转移,必须借用寄存器或地址偏移,那么一般在64位中HOOK的跳转代码在不影响寄存器的情况下一般使用如下办法FFFFFFFFFFFFFFFF作为跳转目标地址:为了不影响寄存器必须提前压入一个寄存器
--------------------------Push raxMov rax, FFFFFFFFFFFFFFFF
JMP rax 或 call rax
在内部要取回rax ,这里注意JMP和call的区别,最后平栈
--------------------------
Push rax
Mov rax,FFFFFFFFFFFFFFFF
Push rax
Ret
在内部要取回rax ,最后平栈
有的朋友看到这要问了 我不能直接 JMP FFFFFFFFFFFFFFFF或者 push FFFFFFFFFFFFFFFF 啊,您要这么问,我实在不知如何回答你,表示无语,您还是直接下个源码玩玩算了。
其他类似的列子我就不一一举例了,总结也是差不多形态,以上列子共占用13字节长度,这还是堆栈放在了内部平,否则还要+1个字节长度,如果放弃其中一个寄存器可以-1个字节长度,所以一般网上现有的64位hook一般都在12字节以上,但是一个好用的hook要占用13字节的长度,对我而言无疑无法忍受,难道真的没有其他办法了吗,要保护寄存器且支持长转移,是不是还有其他办法,那么其实是有办法的,就是通过JMP [rip] 机器码形态为 FF 25 00 00 00 00 这句代码占用6字节,那么这是什么意思呢 FF 25 = jmp ,00 00 00 00为偏移长度 对一个支持2G的字节转移长度,JMP [rip]在调试器中可以解释为 jmp qword ptr ds:[0x地址],对了,也就是读取这个偏移位置中的8字节数值作为跳转地址转移过去,如果偏移为00 00 00 00 那么就代表 JMP [rip]的下一条指令处8字节数据。想到这你也许会问 那么这个意思不就是JMP [rip]6字节+8字节长度吗,对如果是连起来确实如此,但是我们可以给他个偏移啊,不就可以分开了吗,我们只需要搜索同模块的其他位置中00或CC等连续8字节无用代码位置,把跳转的地址写入其中,那么JMP [rip]就可以通过偏移读取到跳转地址了。我们也就能实现6字节的HOOK,这个方式的亮点是改写长度小,且不影响寄存器和rsp堆栈指针,也算是达到曲线救国的目的。
比如对穿插代码中数据传递的问题:我们要获得16个通用寄存器RAX—R15的每个值,这些值我们又如何传递过去。一般远程HOOK数据传递使用消息或者远线程,因为这两种方式汇编改写量小一点,相对容易实现,在这我们不讨论远线程,我们来看看消息传递,一般是两个函数的选