其实,若干天之前,我就想写一下今天这个东西。
故事是这样的。
呃,首先啊,本文不持任何立场,不做任何评价,只说现象以及个人想法。
在若干天之前,我接到一个电话,一个某东的兄弟,对方要和我讨论一下技术。
(可能是别人介绍的吧),然后我俩就聊呗。聊呗。聊呗。
然后那哥们找了个话题,我俩聊到API HOOK。那就聊呗,聊呗,聊呗。
他问我,API HOOK有哪些要点。
这玩意,我就吧啦吧啦吧啦,说了几个,然后他说,还有,还有,还有。
我有些没反应过来,所以放弃了,就问他,还有啥啊。
然后他跟我说:
“在64位上HOOK,因为目标地址不可预期,所以内联点的指令长度不可预期,
如果需要跳转的内存距离超过4G,那么跳转指令就要用9字节指令跳转,
这种情况下,需要对目标线程做处理,防止写跳转指令的时候目标线程在执行”
不是原话,大致意思如上。
然后我现场给他提供了另一种解决方案,
首先,即便64位程序,inline HOOK 也不一定非要写9字节的跳转,普通的五字节跳转也能实现,
如果五字节跳转能实现的话,只要取到目标线程的Context就可以保证目标线程的控制权不在我要写的那五个字节的指令上,
或者我可以直接取一条长于5字节的指令的位置来写跳板,
如果是这种情况下,可以直接使用 Interlocked 64相关的函数,比如 InterlockedExchange64 ,
这玩意,在 Win7 之后就全都支持了。不是啥新鲜玩意。
然后那个兄弟反驳我说:“如果这样的话,要求目标地址和当前地址间距在4G范围内。”
然后我说:“我可以在HOOK地址的4G范围内申请内存写ShellCode。”
那个兄弟反驳我说:“目前我们无法在指定的地址申请内存。”
那兄弟问我:“没看过微软的代码么”。然后我就有点急了。
以上为全部对话流程。
我感觉,我说的、做的,都已经够了。如果我把下面的话说出来,那么真的有点算埋汰人了。
这里我说一下几个槽点。
1:实际上我们是可以在指定的地址申请内存的,
最简单通用的方法是
R3:VirtualAlloc 、VirtualAllocEx 相关函数。
https://docs.microsoft.com/en-us/windows/win32/api/memoryapi/nf-memoryapi-virtualalloc
XP以上就支持了,学Windows 开发的时候,最开始学的内存申请函数不就是这逼玩意么。
R0:MDL 方式申请MDL,后 IoAllocateMdl 申请内存。
https://docs.microsoft.com/zh-cn/windows-hardware/drivers/ddi/wdm/nf-wdm-ioallocatemdl
都是可以指定地址的。
2:即便9字节长度也有办法一次写入,
Win8之后,微软提供了一个新的函数叫做。。。 InterlockedCompareExchange128
锁总线方式出了128了,惊喜不惊喜,意外不意外。
当然了,如果你就跟我矫情Win7不行的话,我认怂,我秒怂,我超级怂。
3:我真的没有必要去看微软的这种代码(但是后来我仔细想了一下我可能还真的看过,只是临时记不起来了),
就算看了我也记不住,要看的东西那么多,要了解的东西那么多,根本记不全,记不住,
因为这个小问题就要我去看微软的代码?我有多种方式来解决这个问题,可能不是全平台通用,
但是我依旧不认为这是可能导致出现问题的位置。
因为之前我肯定有处理这个位置,只是我不会以他的方法来处理,我会用其他方法。
4:其实,如果真的说有问题的话,不应该是控制权的问题,
应该是 cache 的问题,这个是我之后才想到的。
这个是什么情况呢,这个情况是,我虽然做 inline HOOK ,但是实际上我做的操作是写内存操作,
写内存操作就可能出现一个问题,就是,我虽然写内存了,但是操作系统可不是只有一个内存,
还有多级缓存,以及指令缓存等各种 cache ,内存里面的内容变了,但是缓存可是被优先使用的,
虽然缓存会刷新,但是万一缓存没刷新呢,万一缓存里面数据和内存里面数据不同呢,万一这个不同的数据被CPU用了呢,
(这种万一的可能性非常低,但是当基数增加的时候,再小的概率也会爆发式的上升)。
其实也没啥,随便写写,就当一个教训吧。但是估计下次我们再讨论,我依旧还是记不住那个点。
但是,我最大的优势,我的核心优势就是:
1,可以快速解决未知的问题。
2,如果按照我的节奏来做,我有很多工具可以快速排查位置问题。
3,如果根据我的设计来做,当出现未知问题的时候,我们可以在最短的时间内发现并排查。