溢出的字节与我在 GDB 上看到的不同?

Overflowed bytes different than those I see on GDB?

我正在尝试 ProtoStar stack5 challenge。我知道解决方案(在写完之后),但我正在尝试想出一种不同的方法。

这是我们试图在其上执行 shellcode 的程序的源代码:

#include <stdlib.h>
#include <unistd.h>
#include <stdio.h>
#include <string.h>

int main(int argc, char **argv)
{
  char buffer[64];

  gets(buffer);
}

因此,为了查看寄存器中发生了什么,我执行以下操作:

(gdb) n
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
11      in stack5/stack5.c
(gdb) x/30x $esp
0xbffff750:     0xbffff760      0xb7ec6165      0xbffff768      0xb7eada75
0xbffff760:     0x41414141      0x41414141      0x41414141      0x41414141
0xbffff770:     0x41414141      0x41414141      0x41414141      0x41414141
0xbffff780:     0x41414141      0x41414141      0x41414141      0x41414141
0xbffff790:     0x41414141      0x41414141      0x41414141      0x41414141
0xbffff7a0:     0x41414141      0x41414141      0x41414141      0x41414141
0xbffff7b0:     0x41414141      0xbffff800      0xbffff85c      0xb7fe1848
0xbffff7c0:     0xbffff810      0xffffffff
(gdb) p $ebp
 = (void *) 0xbffff7a8
(gdb)

很好,我正在用 41414141 溢出 return 地址。正如预期的那样。现在,我要做的是将 return 地址更改为接下来的 4 个字节,这样

00xbffff7a8: |saved frame pointer| - | return address| - |shellcode part 1| - |...| - |shellcode part n|

然而,当我尝试写入 76 个“41”,然后是地址 0xbffff7a8 + 4(即 0xbffff7b0)时,它一直在写入错误的东西。这是我输入的内容:

41414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141b0f7ffbf

请注意,我们使用的是小端系统。

然而,当我输入这个(作为 ASCII)时,这是我在 $esp 和 $ebp 上看到的:

(gdb) n
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA°÷ÿ¿
11      in stack5/stack5.c
(gdb) x/30x $esp
0xbffff760:     0xbffff770      0xb7ec6165      0xbffff778      0xb7eada75
0xbffff770:     0x41414141      0x41414141      0x41414141      0x41414141
0xbffff780:     0x41414141      0x41414141      0x41414141      0x41414141
0xbffff790:     0x41414141      0x41414141      0x41414141      0x41414141
0xbffff7a0:     0x41414141      0x41414141      0x41414141      0xb7c3b0c2
0xbffff7b0:     0xbfc2bfc3      0xbffff800      0xbffff86c      0xb7fe1848
0xbffff7c0:     0xbffff820      0xffffffff ...
(gdb) p $ebp
 = (void *) 0xbffff7a8

如您所见,写入了 0xb7c3b0c2 而不是预期的 0xbffff7b0

有人知道这是为什么吗?

注意:我意识到我真正想要的地址是 0xbffff7ac,而不是 0xbffff7b0。我会解决这个问题,但这不会改变我遇到的问题。

所以我最终通过 LiveOverflow 在 LiveOverflow's subreddit and I was pointed towards the direction of this video 上发布了这个问题。

视频会比我解释得更好,但本质上,python2 和 python3 不会将十六进制打印成相同的 ascii。 Python3 插入额外字符,而 python2 打印原始十六进制字符串。

我强烈建议您观看视频,因为它解释得很深入。

@dsh 回答的另一个问题的答案也解释了它:

The byte-sequence C3 BE is the UTF-8 encoded representation of the character U+00FE.

Python 2 handles strings as a sequence of bytes rather than characters. So '\xfe' is a str object containing one byte.

In Python 3, strings are sequences of (Unicode) characters. So the code '\xfe' is a string containing one character. When you print the string, it must be encoded to bytes. Since your environment chose a default encoding of UTF-8, it was encoded accordingly.

How to solve this depends on your data. Is it bytes or characters? If bytes, then change the code to tell the interpreter: print(b'\xfe'). If it is characters, but you wanted a different encoding then encode the string accordingly: print( '\xfe'.encode('latin1') ).