int3 黑魔法

发现和 tracing 有关的好多内容,脱离了 int3 都无法很好的展开。要么需要额外写很多内容,要么是不够详细,所以想在这里单独说明一下 int3 的黑魔法。

1. 程序执行视角

众所周知,可执行程序,在 CPU 看来,只是很多的指令罢了,对于不同的指令,CPU 会有不同的反应,如果学习过汇编,可能会了解 mov add sub mul push pop call 这些最常见的指令,但是 CPU 还有非常特殊的一类指令,是 CPU 设计者(硬件)专门用来方便软件调试的一种手段,CPU 执行完这条指令之后,会触发异常,然后执行操作系统预先设置好的异常处理函数,这样,软件开发人员就能对于程序有了更多的掌控:只需要想办法触发 int3 异常,就可以进入到异常处理函数中,只需要进入异常处理函数,就能实现各种 fancy 的功能(比如统计程序中的 int3 指令个数(dog)……)。

2. 如何触发 int3

那究竟如何让一个好端端的程序,触发 int3 异常呢?

对于一个正常编译的程序,显然会在 CPU 中顺利执行完所有指令,安详地过完它的一生。Sure,聪明的你可能想到了,我们可以通过内嵌汇编代码的方式,手动添加 int3 指令,不过那不是我们想要的效果了,就没有一个手段,在不修改源代码的情况下,去 hack 一个再正常不过的程序吗?

这就要用到 text poke 了。

一般来说,程序可以大体分为数据段和代码段,并且被分别载入虚拟内存中的不同区域,分配不同的权限,数据段是可读可写,代码段是可读可执行。之所以设计成这样,是非常容易理解的,如果数据段可执行,那么,就有一万种黑掉你电脑的手段,而代码段之所以不可写,是因为这是一条 slow slow slow path,只有在极少数情况下,才会有将代码段动态修改的需求,这也同时导致了 icache 不会轻易刷新,因为不管是操作系统还是硬件,都会默认代码段是只读的。

不过,万事没有绝对,对于可执行程序而言,我们只需要记住一点:只要是在内存中的玩意,我们都可以想办法 hack!代码段只读是吧?那就 mmap 修改掉权限咯!icache 不会轻易刷新?那就手动给它刷新咯!虽然实际操作起来有很多 trivial 的地方需要注意,但是理论上完全可行。


本博客所有文章除特别声明外,均采用 CC BY-SA 4.0 协议 ,转载请注明出处!