是否可以在仅部分覆盖的情况下执行单字节指令?

Can a single byte instruction be executed while being only partially overwritten?

我做了一个实验,其中一个新线程使用这个简单的无限循环执行 shellcode:

NOP
JMP REL8 0xFE (-0x2)

这会生成以下 shellcode:

0x90, 0xEB, 0xFE

在此无限循环之后,还有其他指令以将目标字节覆盖回 -0x2 使其再次成为无限循环而结束,以及将线程发送回此无限循环的绝对跳转。

现在我在问自己是否有可能在目标的单个字节被其他线程仅部分覆盖时执行跳转指令。 例如,假设另一个线程将跳转的目标(0xFE,或二进制为 11111110)覆盖为 0x0(00000000)以释放此无限循环的线程。 是否会发生跳转至 0x1E (00011110) 的情况,因为目标字节在那个纳秒没有被完全覆盖? 在问这个问题之前,我自己在一个 C++ 程序中做了这个实验,我让它 运行 几个小时,它从来没有错过任何一次跳跃。 如果你想看看我为这个实验做的代码I have uploaded it to GitHub

根据这个实验,一条指令在只被部分覆盖的情况下被执行似乎是不可能的。 但是,我对汇编和处理器知之甚少,因此我在这里问这个问题: 有人可以证实我的观察吗?在被另一个线程部分覆盖的同时执行指令真的不可能吗?有谁知道为什么?

非常感谢您的帮助和了解,我不知道在哪里可以找到这样的信息。

不,字节存储总是 ,即使对于 交叉修改 代码也是如此。

Observing stale instruction fetching on x86 with self-modifying code for some links to Intel's manuals for cross modifying code. And maybe Reproducing Unexpected Behavior w/Cross-Modifying Code on x86-64 CPUs

当然,编写 高效 交叉修改代码(以及刚刚 JIT 编译的 运行ning 代码)的所有建议都涉及避免存储到页面中其他线程当前正在执行。


无论如何,你为什么要用“shellcode”来做这个?这应该是漏洞利用的一部分吗?如果没有,为什么不像普通人一样用 asm 编写代码,在 jmp 指令上加上标签,这样你就可以通过分配给 extern char jmp_bytes[2]?

从 C 中存储它

如果这应该是一种有效的跨线程通知机制……它不是。在数据加载和带有 pause 循环的条件分支上旋转将允许从循环中退出的延迟低于自修改代码机核弹,当您希望它最终做一些有用的事情时,它会立即刷新整个管道而不是浪费 CPU 时间。 至少是简单分支miss延迟的几倍.

更好的是,使用 OS 支持的条件变量,这样线程就可以休眠而不是加热你的 CPU(将 CPU 的热余量减少到高于其当有工作要做时,额定时钟会加速。


当前 CPUs 使用的机制是,如果检测到 EIP/RIP 附近的存储或管道中正在运行的任何指令,它会执行机器清除。 (perf counter machine_clears.smc,又名 machine nuke。)它甚至没有尝试“有效地”处理它,但是如果你做了一个 non-atomic 存储(例如实际上是两个单独的存储,或跨高速缓存行边界拆分的存储)目标 CPU 核心可以在不同部分看到它,并可能解码它,更新一些字节而其他字节不更新。但是单个字节总是以原子方式更新,因此不可能在一个字节内进行撕裂。

但是,x86 on paper doesn't guarantee that, but as Andy Glew (one of the architects of Intel's P6 microarchitecture family) says, implementing stronger behaviour 比纸质规范实际上是满足所有必需保证和 运行 速度的最有效方式。 (和/或避免破坏广泛使用的软件中的现有代码!)