关于调试:尝试从”开发的艺术”中了解示例char_array2.c

 2021-04-09 

Trying to understand example char_array2.c from “the art of exploitation”

好的,所以我真的想了解第二版"剥削的艺术"这个例子中发生了什么。我试图通过紧紧跟随本书中GDB的输出,来了解示例的确切情况。我最大的问题是最后一部分,我包括了整个内容,以便每个人都可以看到发生了什么。当然,我只有非常(非常)汇编代码的基础知识。我确实了解基本C。
在最后一部分中,作者说,程序的第二次运行与strcpy()指向的地址中的最后一次运行存在细微差别,我只是看不到它。

程序就是

1
2
3
4
5
6
7
8
9
10
#include<stdio.h>
#include<string.h>

int main() {

  char str_a[20];
  strcpy(str_a,"Hello, world!\
"
);
  printf(str_a);
  }

在使用必要的选项对其进行编译以对其进行调试之后,将其加载到
GDB并包括以下内容:

1
2
3
4
5
6
7
8
9
10
(gdb) break 6
Breakpoint 1 at 0x80483c4: file char_array2.c, line 6.
(gdb) break strcpy

Function"strcpy" not defined.
Make breakpoint pending on future shared library load? (y or [n]) y
Breakpoint 2 (strcpy) pending.
(gdb) break 8
Breakpoint 3 at 0x80483d7: file char_array2.c, line 8.
(gdb)

我对此没有任何问题,据我了解,
调试器只能使用用户定义的函数来执行此类操作。我也知道如何使用gcc选项解决此问题。
我也知道,当程序运行时,strcpy断点已解决。让我继续。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
(gdb) run
Starting program: /home/reader/booksrc/char_array2
Breakpoint 4 at 0xb7f076f4
Pending breakpoint"strcpy" resolved

Breakpoint 1, main() at char_array2.c:7
7   strcpy(str_a,"Hello, world!\
"
);
(gdb) i r eip
eip 0x80483c4   0x80483c4 <main+16>
(gdb) x/5i $eip
0x80483c4   <main+16>:  mov    DWORD PTR [esp+4],0x80484c4
0x80483cc   <main+24>:  lea    eax,[ebp-40]
0x80483cf   <main+27>:  mov    DWORD PTR [esp],eax
0x80483d2   <main+30>:  call   0x80482c4 <strcpy@plt>
0x80483d7   <main+35>:  lea    eax,[ebp-40]
(gdb) continue
Continuing.


Breakpoint 4, 0xb7f076f4 in strcpy () from /lib/tls/i686/cmov/libc.so.6
(gdb) i r eip
eip    0xb7f076f4    0xb7f076f4 <strcpy+4>
(gdb) x/5i $eip
0xb7f076f4 <strcpy+4>:  mov   esi,DWORD PTR [ebp+8]
0xb7f076f7 <strcpy+7>:  mov   eax,DWORD PTR [ebp+12]
0xb7f076fa <strcpy+10>: mov   ecx,esi
0xb7f076fc <strcpy+12>: sub   ecx,eax
0xb7f076fe <strcpy+14>: mov   edx,eax
(gdb) continue
Continuing.

Breakpoint 3, main () at char_array2.c:8
8
printf(str_a);
(gdb) i r eip
eip    0x80483d7    0x80483d7 <main+35>
(gdb) x/5i $eip
0x80483d7 <main+35>:   lea    eax,[ebp-40]
0x80483da <main+38>:   mov    DWORD PTR [esp],eax
0x80483dd <main+41>:   call   0x80482d4 <printf@plt>
0x80483e2 <main+46>:   leave
0x80483e3 <main+47>:   ret
(gdb)

这是程序的第二次运行,其中应该给strcpy的地址与其他地址不同。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
(gdb) run
The program being debugged has been started already.
Start it from the beginning? (y or n) y
Starting program: /home/reader/booksrc/char_array2
Error in re-setting breakpoint 4:
Function"strcpy" not defined.

Breakpoint 1, main () at char_array2.c:7
7
strcpy(str_a,"Hello, world!\
"
);
(gdb) bt
#0 main () at char_array2.c:7
(gdb) cont
Continuing.

Breakpoint 4, 0xb7f076f4 in strcpy () from /lib/tls/i686/cmov/libc.so.6
(gdb) bt
#0 0xb7f076f4 in strcpy () from /lib/tls/i686/cmov/libc.so.6
#1 0x080483d7 in main () at char_array2.c:7
(gdb) cont
Continuing.

Breakpoint 3, main () at char_array2.c:8
8
printf(str_a);
(gdb) bt
#0 main () at char_array2.c:8
(gdb)

有什么区别?我以为0xb7f076f4是strcpy的地址我错了吗?如果我是正确的话,在第二次运行中,所有内容均指示该地址为0xb7f076f4。

又是什么?我在本书前面的任何地方都找不到对此的解释。如果有人能够友好地从上至下向我解释这一点,那么我将不胜感激,以至于我不认识现实生活中的任何专家都可以为我提供帮助。我发现这些解释含糊不清,他像向5岁的孩子解释变量和循环一样,但是他留下了很多汇编代码供我们自己弄清楚,我在这方面还不是很成功。
任何帮助将不胜感激。


显然,gdb为调试的进程关闭了ASLR,以简化(会话到会话)调试。

来自https://sourceware.org/gdb/current/onlinedocs/gdb/Starting.html

1
2
3
4
5
6
7
set disable-randomization
set disable-randomization on
    This option (enabled by default in GDB) will turn off the native
    randomization of the virtual address space of the started program.
    This option is useful for multiple debugging sessions to make the
    execution better reproducible and memory addresses reusable across
    debugging sessions.

gdb.gdbinit文件中设置set disable-randomization off,然后重试。现在,每次运行二进制文件时,Libc应该会在不同的地址加载。

运行watch -n 1 cat /proc/self/maps也很高兴看到二进制文件和库是如何映射到"随机"地址的。


正如@Voo在上面的评论中所说,这本书可能涉及到ASLR(地址空间布局随机化),这是一种安全功能。它更改了每次执行使用地址空间的方式,因此您不必依赖总是在同一位置查找事物。

如果您没有在gdb中看到它,则意味着您已关闭ASLR。 gdb中的全局或本地。您可以在gdb提示符下使用cat /proc/sys/kernel/randomize_va_space检查前者,并使用show disable-randomization命令检查后者。