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

问题描述 投票:1回答:1

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

NOP
JMP REL8 0xFE (-0x2)

这会生成以下shellcode:

0x90, 0xEB, 0xFE

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

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

根据该实验,似乎不可能在仅部分重写的情况下执行指令。但是,我对装配和处理器知之甚少,因此我在这里问这个问题:有人能证实我的观察吗?是否确实不可能在被另一个线程部分覆盖的情况下执行指令?有谁知道为什么肯定?

非常感谢你的帮助和知识,我不知道在哪里寻找这样的信息。

multithreading assembly thread-safety overwrite
1个回答
3
投票

不,字节存储总是atomic on x86,即使是交叉修改代码。

有关交叉修改代码的英特尔手册的一些链接,请参阅Observing stale instruction fetching on x86 with self-modifying code。也许Reproducing Unexpected Behavior w/Cross-Modifying Code on x86-64 CPUs

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


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

如果这应该是一个有效的跨线程通知机制......事实并非如此。使用pause循环在数据加载和条件分支上旋转将允许比自修改代码机器核更低的延迟退出循环,当你希望它最终做一些有用而不是浪费CPU时,它会冲洗整个管道时间。至少是简单分支未命中延迟的几倍。

更好的是,使用操作系统支持的条件变量,这样线程就可以睡眠而不是加热你的CPU(当有工作要做的时候,减少CPU的热量余量,使其高于其额定时钟速度以上的涡轮增压)。


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

但是,x86 on paper doesn't guarantee that,但正如Andy Glew(英特尔P6微体系结构系列的架构师之一)所说,implementing stronger behaviour比纸质规范实际上可以是满足所有必需保证并快速运行的最有效方式。 (和/或避免破坏广泛使用的软件中的现有代码!)

© www.soinside.com 2019 - 2024. All rights reserved.