我目前正在阅读计算机系统简介:程序员的观点(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 雪橇,但我很难理解书中给出的与 NOP 雪橇相关的地址计算。我将在这里引用它:-
(假设堆栈上程序的起始地址在 32 位系统上的范围为 2^23,在 64 位系统上的范围为 2^32)
"如果我们设置一个256字节的NOP雪橇,那么n=2^23的随机化可以通过枚举2^15个起始地址来破解,这对于一个坚定的攻击者来说是完全可行的。对于 64 位的情况,尝试枚举 2^24 个地址更加令人生畏。
作者是如何为32位和64位情况分别得出数字2^15和2^24的?解释会很有帮助。
他们只是假设 32 位系统上的最大总共享内存 (kbytes) 这将是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 雪橇长 256 字节,所以我们不需要为每个猜测/迭代/蛮力增加 1,而是每次计数增加 256,得到上述等式的结果 0x800000 / 256 = 32768 == 2^15
. 因此,我们只需要蛮力破解 32768 个可能的地址,因为其中一个地址将在我们的 NOP 雪橇开始时降落并一直滑到我们的有效载荷。
如果我们的NOP雪橇是500字节(假设我们的漏洞允许我们安装这么大的NOP雪橇),则等式为:
0x8000000 / 500 = 268435
迭代以找到我们的NOP雪橇的起始地址。
这种方法对于 64 位系统不是那么好的原因是因为以下等式:
2^32 / 256 = 16,777,216
(我们的NOP雪橇可以启动的超过1600万个可能的地址!即使您的NOP雪橇是500字节并且除以500,您仍将有超过850万个地址,您的NOP雪橇可以开始!
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 雪橇(将 4 字节放入我的 NOP 雪橇)。
根据评论进行更新:
就地址随机化的旁路技术而言,NOP 雪橇背后的想法不是猜测 NOP 雪橇的开始 - 而是计算最少的地址数量,以保证您将以尽可能少的地址猜测/蛮力降落在 NOP 雪橇内。 当然,如果你想找到你的NOP雪橇的起点,你可以使用以下公式:
total_mem_size / NOP_size = least_amount_of_guesses_to_land_inside_payload + 1
但请记住,通过添加额外的地址来尝试,您不再计算在获得有效负载执行之前要猜测的最少地址数量(这是我和您正在阅读的书正在计算的内容,因为这是使用 NOP 雪橇背后的想法)。
如果我们重新访问我上面的小"堆栈",确实可能有 4 个地址可以开始 NOP 雪橇,但等式计算了保证找到 NOP 雪橇的 3 个地址(尽可能少的猜测是关键)。 为了更清楚地说明,您可以说漏洞利用开发人员将尝试通过增加NOP雪橇的大小(在可能的情况下)来使这个数字尽可能小,这样他们就不会担心找到NOP雪橇的起点 - 他们只想降落在NOP雪橇内。
对索引12
的猜测将使4个字节进入NOP雪橇,在到达有效载荷之前,只需执行8个NOP。 索引24
的猜测会使您进入有效负载几个字节,从而导致崩溃,而索引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 雪橇,所以仍然只有 1 个地址(索引 12 处的地址,其地址是原始方程找到的)将落在我们的 NOP 雪橇中,让我们在 shellcode 的开头开始执行 shellcode。 索引 0 将我们放在堆栈上,在 NOP 雪橇之前我们无法控制的数据。 因此,在组合中添加 1 个地址并不能帮助我们找到 NOP 雪橇 - 它只会增加要尝试/暴力/猜测的地址数量。
是的,你是对的 - 我只是递增更直观的地址,以使示例更有意义,但在实际执行期间堆栈上的"计数"或"递增"地址将从高地址开始。
不确定这是否对您有帮助:
对于 32 位系统,如果 NOP 雪橇为 256 字节(即 2^8),并且堆栈的范围为 2^23 字节,则只需要 2^15 个实例(即 2^23/2^8 = 2 ^15)的(非重叠)雪橇。
64 位系统也是如此