计算 NOP sled 中的地址

Calculating addresses in NOP sleds

我目前正在阅读计算机系统简介:程序员的视角 (http://www.amazon.com/Computer-Systems-Programmers-Perspective-2nd/dp/0136108040/ref=sr_1_2?s=books&ie=UTF8&qid=1421029641&sr=1-2&keywords=introduction+to+computer+systems) 并试图了解阻止缓冲区溢出的方法。

我理解为什么在使用地址随机化时我们需要 NOP sled 以及如何编写 exploit,但我很难理解书中给出的与 NOP sled 相关的地址计算。我会在这里引用它:-

(假设程序在栈上的起始地址在32位系统上为2^23,在64位系统上为2^32)

"If we set up a 256 byte NOP sled, then the randomization over n=2^23 can be cracked by enumerating 2^15 starting addresses which is entirely feasible for a determined attacker. For the 64 bit case, trying to enumerate 2^24 addresses is more daunting."

作者是如何分别为 32 位和 64 位情况得出数字 2^15 和 2^24 的?解释会很有帮助。

不确定这是否对您有帮助:

对于 32 位系统,如果您的 NOP sled 是 256 字节(即 2^8),并且如果您的堆栈范围为 2^23 字节,则您只需要 2^15 个实例(即 2^23 / (非重叠)雪橇的 2^8 = 2 ^15)。

64位系统同理

他们只是假设 32 位系统上的最大总共享内存(千字节)为 8388608,这就是他们提出 2^23

的地方

2^15 计算如下:

(8388608 / 256) = 32768 == 2^15

换句话说:

total_memory_size / NOP_sled_length = total_iterations_to_find_NOP_sled_in_memory

他们根据假设我们的 NOP sled 可以在从 0x0 一直到 0x800000(即 83886082^23).因为我们的 NOP sled 是 256 字节长,所以我们不需要为每个 guess/iteration/brute 力递增 1,而是每次递增 256 来计算上面等式 0x800000 / 256 = 32768 == 2^15 的结果。所以我们只需要暴力破解 32768 个可能的地址,因为其中一个地址将使我们到达 NOP sled 的起点,并一直向下滑动到我们的有效载荷。

如果我们的 NOP sled 是 500 字节(假设我们的 exploit 允许我们安装那么大的 NOP sled),则等式为:

0x8000000 / 500 = 268435 次迭代以找到我们的 NOP sled 的起始地址。

这种方法对于 64 位系统不是很好的原因是因为以下等式:

2^32 / 256 = 16,777,216(我们的 NOP sled 可以从超过 1600 万个可能的地址开始!即使您的 NOP sled 是 500 字节并且除以 500,您的 NOP sled 仍然会有超过 850 万个地址可以开始了!)

0000
0000
NNNN
NNNN
NNNN
PPPP
PPPP
PPPP
0000
0000

如果您想象上面是我的堆栈,我的总内存大小为 40。我有一个 12 字节的 NOP sled(N)和 12 字节的有效负载(P)。所以我对这个可利用场景的等式是:

40 / 12 = 3 给我 3 个可能的地址,我的 NOP sled 可以在(12、24、32 或十六进制 0x0c、0x18 和 0x20)中找到,尝试次数越少越好。

因此,如果我的漏洞利用从堆栈的开头开始并以 12 为增量计数,那么它会在第一次尝试时找到我的 NOP sled(将 4 个字节放入我的 NOP sled)。

根据评论更新:

就地址随机化的旁路技术而言,NOP sled 背后的想法不是猜测 NOP sled 的 start - 它是计算 最少的地址 将保证您将降落在您的NOP雪橇内guesses/brute 力 尽可能。当然,如果您想找到 NOP sled 的起点,您可以使用以下等式:

total_mem_size / NOP_size = least_amount_of_guesses_to_land_inside_payload + 1

但请记住,通过添加额外的地址进行尝试,您不再计算在执行负载之前要猜测的最少地址数量(这是我和您正在阅读的书正在计算,因为这是使用 NOP sled 背后的想法)。

如果我们重新审视上面的小"stack",确实可以有 4 个地址,NOP sled 可以从这些地址开始,但是方程式计算了 保证 找到 NOP sled(尽可能少的猜测是关键)。为了更清楚,你可以说漏洞利用开发人员将通过增加 NOP sled 的大小(在可能的情况下)来尝试使这个数字尽可能小,这样他们就不会担心找到 NOP sled 的开始 -他们只想降落在 NOP 雪橇内。

索引 12 的猜测将使您将 4 个字节放入 NOP sled 中,让您在到达有效负载之前仅需执行 8 个 NOP。对索引 24 的猜测会让你进入有效载荷中几个字节,导致崩溃,而对索引 32 的猜测会让你越过有效载荷,也会导致崩溃。

让我们使用您的方法(使用总共 4 个地址)来说明为什么在等式中没有考虑额外地址:

0000
0000
NNNN
NNNN
NNNN
PPPP
PPPP
PPPP
0000
0000

让我们在等式中加 1,得到 4 个可能的地址,堆栈布局与之前相同:

40 / 12 = 3 + 1 = 4

所以现在我们有 4 个地址可以暴力登陆我们的 NOP sled 0, 12, 24, 32。因为我们有一个 12 字节的 NOP sled,所以仍然只有 1 个地址(索引 12 处的地址,原始方程找到的地址)将落在我们的 NOP sled 中,让我们在 shellcode 的开头开始执行 shellcode。索引 0 使我们在 NOP 雪橇之前无法控制的数据处进入堆栈。因此,再添加 1 个地址并不能帮助我们找到 NOP sled - 它只会将地址数量增加到 try/brute/guess.

是的,你是对的——我只是递增更直观的地址,以使示例更有意义,但在实际执行期间,堆栈上的 "counting" 或 "incrementing" 地址将从高地址。