他们只是假设 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 雪橇可以位于从0x0
一直到0x800000
(即8388608
或2^23
)范围内的任何位置的假设计算出这一点。因为我们的 NOP sled 是 256 字节长,所以我们不需要为每次猜测/迭代/蛮力增加 1,而是每次计算增加 256,从而为我们提供上述等式的结果0x800000 / 256 = 32768 == 2^15
。所以我们只需要暴力破解 32768 个可能的地址,因为其中一个地址将使我们降落在 NOP 雪橇的开始处,并一直向下滑动到我们的有效载荷。
如果我们的 NOP sled 是 500 字节(假设我们的漏洞利用允许我们安装这么大的 NOP sled),则等式将是:
0x8000000 / 500 = 268435
迭代以找到我们的 NOP 雪橇的起始地址。
这种方法对于 64 位系统来说不是很好的原因是因为以下等式:
2^32 / 256 = 16,777,216
(我们的 NOP sled 可以从超过 1600 万个可能的地址开始!即使您的 NOP sled 是 500 字节并且除以 500,您仍然有超过 850 万个地址可以开始您的 NOP sled!)
0000
0000
NNNN
NNNN
NNNN
PPPP
PPPP
PPPP
0000
0000
如果您想象上面是我的堆栈,我的总内存大小为 40。我有一个 12 字节的 NOP 雪橇(N)和一个 12 字节的有效负载(P)。所以我对这个可利用场景的等式是:
40 / 12 = 3
给我 3 个可能的地址,我的 NOP 雪橇可以在尽可能少的尝试中找到(12、24、32 或十六进制 0x0c、0x18 和 0x20)。
因此,如果我的漏洞利用只是从堆栈的开头开始并以 12 为增量计数,那么它将在第一次尝试时找到我的 NOP sled(将 4 个字节放入我的 NOP sled)。
根据评论更新:
就地址随机化的旁路技术而言,NOP sled 背后的想法不是猜测 NOP sled 的开始- 它是计算最少的地址 ,以确保您将在您的 NOP sled 中以最少的地址猜测进入/蛮力尽可能。当然,如果你想找到你的 NOP 雪橇的开始,你可以使用以下等式:
total_mem_size / NOP_size = least_amount_of_guesses_to_land_inside_payload + 1
但是请记住,通过添加额外的地址来尝试,您不再计算在执行有效负载之前要猜测的最少地址(这是我自己和您正在阅读的书正在计算的,因为这是使用NOP 雪橇)。
如果我们重新审视我上面的小“堆栈”,确实可能有 4 个总地址可以启动 NOP 雪橇,但等式计算保证找到 NOP 雪橇的 3 个地址(尽可能少的猜测是钥匙)。为了更清楚,您可以说漏洞利用开发人员将尝试通过增加 NOP sled 的大小(在可能的情况下)使这个数字尽可能小,这样他们就不会担心找到 NOP sled 的开始 -他们只想降落在 NOP 雪橇内。
索引的猜测12
会将 4 个字节放入 NOP sled,在到达有效负载之前只给你 8 个 NOP 执行。对 index 的猜测24
会让你进入有效负载的几个字节,导致崩溃,而对 index 的猜测32
会让你越过有效负载,也会导致崩溃。
让我们使用您的方法(使用总共 4 个地址)来说明为什么等式中没有考虑额外的地址:
0000
0000
NNNN
NNNN
NNNN
PPPP
PPPP
PPPP
0000
0000
让我们将 1 添加到我们的等式中,为我们提供 4 个可能的地址,其堆栈布局与之前相同:
40 / 12 = 3 + 1 = 4
所以现在我们有 4 个地址可以蛮力登陆我们的 NOP 雪橇0, 12, 24, 32
。因为我们有一个 12 字节的 NOP sled,所以仍然只有 1 个地址(索引 12 处的地址,其地址是原始方程找到的地址)将落在我们的 NOP sled 中,让我们在 shellcode 的开头开始执行 shellcode。索引 0 使我们在我们的 NOP 雪橇之前无法控制的数据进入堆栈。因此,在混合中再添加 1 个地址并不能帮助我们找到 NOP 雪橇——它只会增加尝试/蛮力/猜测的地址数量。
是的,你是对的 - 我只是增加更直观的地址以使示例更有意义,但是在实际执行期间堆栈上的“计数”或“增加”地址将从高地址开始。