对使用 gdb 检查内存感到困惑?

对使用 gdb 检查内存感到困惑?

我开始阅读《黑客》,即利用的艺术,我对有关内存检查的一些事情感到困惑。

当我反汇编 main 时,我得到了各个汇编指令所在的所有内存的输出,对吧?每条指令不一定需要将内存地址加一,这就是为什么我可能会在后面加上 <main+1> 和 <main +3> 。所以命令这个截图显示程序的前 20 条指令。每个信息包含多少字节信息?

现在我很困惑,当使用 ir eip 时,我得到了这个寄存器的位置,即 0x8048384 (所以它存储为 main 的第一条指令吗?)在屏幕截图上。它旁边的值,是它保存的值,这里我想知道,它存储的是0x00fc45c7,但是这条指令不在上面的输出中?我认为它应该指向包含程序下一条指令的内存。

现在最大的困惑来了,我可以观察存储 $eip 的内存并一次观察多个单元,这可以在这个截图。然而,在使用 x/2x 的屏幕截图中,您可以看到内存中存储了两个值,并且大小都是 4 个字节?然后使用x/12,0x8048384中突然有4个字,而其他4个字0x8048394?

我似乎只是不明白存储在内存地址中的值如何根据我使用的单位而有所不同。我还认为每个内存地址应该只包含一个字节的信息?

如果您需要对问题进行任何澄清,请发帖,英语不是我的母语,我不确定我的解释是否正确。

感谢您提前的帮助

答案1

每个信息包含多少字节信息?

从一个字节push %ebp到几个字节的任意位置;指令没有固定长度。命令的输出例如 objdump -d a.out可以使指令长度更清晰(并且将显示指令是什么,这可能有用):

08048400 <main>:
 8048400:       55                      push   %ebp
 8048401:       89 e5                   mov    %esp,%ebp
 8048403:       83 ec 08                sub    $0x8,%esp
 8048406:       90                      nop
 8048407:       c7 45 fc 00 00 00 00    movl   $0x0,0xfffffffc(%ebp)
 804840e:       89 f6                   mov    %esi,%esi
 8048410:       83 7d fc 09             cmpl   $0x9,0xfffffffc(%ebp)

现在我很困惑,当使用 ir eip 时,我得到了这个寄存器的位置......

(gdb) disassemble main
Dump of assembler code for function main:
0x8048400 <main>:       push   %ebp
0x8048401 <main+1>:     mov    %esp,%ebp
0x8048403 <main+3>:     sub    $0x8,%esp
0x8048406 <main+6>:     nop
0x8048407 <main+7>:     movl   $0x0,0xfffffffc(%ebp)
0x804840e <main+14>:    mov    %esi,%esi
0x8048410 <main+16>:    cmpl   $0x9,0xfffffffc(%ebp)
...
(gdb) i r eip
eip            0x8048406        0x8048406

在我的版本中,该程序eip指向在 下停止程序nop的命令。b maingdb

现在最大的困惑来了,我可以观察内存 $eip存储在哪里

(gdb) x/10b $eip
0x8048406 <main+6>:     0x90    0xc7    0x45    0xfc    0x00    0x00    0x00    0x00
0x804840e <main+14>:    0x89    0xf6

回想一下上面的输出objdump

 8048406:       90                      nop
 8048407:       c7 45 fc 00 00 00 00    movl   $0x0,0xfffffffc(%ebp)
 804840e:       89 f6                   mov    %esi,%esi

10 字节的转储$eip显示了接下来的三个指令:90,然后是 c7 45 fc 00 00 00 00,然后是 89 f6。

我似乎只是不明白存储在内存地址中的值如何根据我使用的单位而有所不同。我还认为每个内存地址应该只包含一个字节的信息?

gdb允许通过不同的宽度或逻辑单元进行检查。我们可以说x/3i显示接下来的三个指令,例如:

(gdb) x/3i $eip
0x8048406 <main+6>:     nop
0x8048407 <main+7>:     movl   $0x0,0xfffffffc(%ebp)
0x804840e <main+14>:    mov    %esi,%esi

使用的最佳宽度选择将取决于具体情况。在 8 位系统上,您需要查看字节。应检查 64 位系统上的内存地址g是否为巨型或 8 字节。更改选区的宽度可能会改变数字gdb显示的数字,因为不同的宽度可能会导致不同的位模式,从而导致不同的数字。此外,CPU 的字节顺序可能会使事情变得复杂。

考虑:

#include <stdio.h>
char *pointer = "test";
int main(void) {
        printf("%s\n", pointer);
}

如果我们在检查时选择不同的宽度,pointer我们将从不同的位模式中得到不同的数字:

(gdb) p pointer
$1 = 0x8048488 "test"
(gdb) x/4c pointer
0x8048488 <_IO_stdin_used+4>:   116 't' 101 'e' 115 's' 116 't'
(gdb) x/4t pointer
0x8048488 <_IO_stdin_used+4>:   01110100        01100101        01110011        01110100
(gdb) x/t (int *)pointer
0x8048488 <_IO_stdin_used+4>:   01110100011100110110010101110100

另一个问题是英特尔系统是小尾数法(英特尔可能不认为这是一个问题)。因此,如果仔细观察的话,由于这种复杂性,位模式x/4t会略有不同。x/t (int *)我们可以指示gdb进入大端模式,

(gdb) set endian big
The target is assumed to be big endian
(gdb) x/t (int *)pointer
0x88840408:     Cannot access memory at address 0x88840408

但现在我们必须向后输入内存地址!该htonl 函数调用可以将 32 位小端值反转为大端值:

$ cfu 'printf("%d\n", htonl(0x88840408))'
134513800

然后gdb我们可以尝试该地址:

(gdb) x/t (int *)134513800
0x8048488 <_IO_stdin_used+4>:   01110100011001010111001101110100

现在,四个字符的位模式与大端形式匹配。将这些结果放入表格可能会有所帮助:

            --> big endian reads this way
              t        e        s        t
x/4t (char)   01110100 01100101 01110011 01110100
big endian    01110100 01100101 01110011 01110100
            <-- yaw siht sdaer naidne elttil dna
              t        s        e        t
little endian 01110100 01110011 01100101 01110100

test这是可以通过 来显示字符串的位模式的三种方式(多种方式中的一种) gdb。将显示不同的数字,具体取决于您告诉将输入分为多少位gdb以及系统的字节顺序,所有这些都来自相同的位模式。

相关内容