1

我试图了解 C 中的低级内存管理器,尤其是 Stack。有人告诉我,当调用函数时,返回地址被压入堆栈。然后局部变量位于之后。

所以我写了一个小程序来调查这个。这是我的程序:

#include <stdio.h>

void TestStack();

void DoTestStack() {
    char x1 = 1;
    char x2 = 2;
    char x3 = 3;
    char x4 = 4;
    char *x = &x4;

    printf("TestStack: %08X\n", (&TestStack));
    printf("\n");

    int i;
    x = &x4;
    for(i = 0; i < 32; i++)
        printf("%02d: %08X : %08X\n", i, *(x + i), *(x - i));

    printf("\n");
    printf("x1: %02X\n", x1);
    printf("x2: %02X\n", x2);
    printf("x3: %02X\n", x3);

    printf("DONE!!!\n");
}

void TestStack() {
    DoTestStack();
}

void main() {
    TestStack() ;
}

基本上,它会调查 x4 所在位置之前和之后的所有内存。这应该很好地涵盖了退货地址的位置。

但我似乎根本找不到任何类似于返回地址的字节。

这是我的结果:

TestStack: 08048B49

00: 00000004 : 00000004
01: 00000003 : FFFFFFBF
02: 00000002 : FFFFFFAC
03: 00000001 : FFFFFFED
04: 00000004 : 0000001C
05: FFFFFFC3 : 00000000
06: FFFFFFB9 : 00000000
07: 00000000 : 00000000
08: FFFFFFF4 : 00000008
09: FFFFFFBF : 00000000
10: FFFFFFB9 : FFFFFF90
11: 00000000 : FFFFFFBD
12: 00000038 : 00000020
13: FFFFFFED : 00000000
14: FFFFFFAC : 00000000
15: FFFFFFBF : 00000000
16: 00000054 : 00000000
17: FFFFFF8B : 00000000
18: 00000004 : FFFFFFFF
19: 00000008 : 00000000
20: 00000045 : 00000008
21: 00000073 : 00000000
22: FFFFFFA7 : 00000000
23: 00000000 : 00000000
24: 00000020 : 00000017
25: FFFFFFBD : 00000008
26: FFFFFF90 : 00000004
27: 00000000 : FFFFFF8C
28: 00000048 : FFFFFFCF
29: FFFFFFED : 00000008
30: FFFFFFAC : 00000004
31: FFFFFFBF : FFFFFF8A

x1: 01
x2: 02
x3: 03
DONE!!!

我在这里想念什么?有人可以解释一下。

无论如何,我在 Ubuntu 9.10 上。

提前致谢。:-D

4

3 回答 3

6

您正在查看单个字符,然后将它们转换为 32 位整数,这让您感到困惑。返回地址位于这四行的最低有效字节中:

16: 00000054 : 00000000
17: FFFFFF8B : 00000000
18: 00000004 : FFFFFFFF
19: 00000008 : 00000000

即你的返回地址是0x08048b54。

试试这个:

uint32_t *x;
x = (uint32_t *)&x4;
for(i = 0; i < 32; i++)
    printf("%02d: %08X : %08X\n", i, *(x + i), *(x - i));
于 2009-10-03T10:58:15.050 回答
4

在这一行:

    printf("%02d: %08X : %08X\n", i, *(x + i), *(x - i));

x指针是一个字符指针,在取消引用后被提升为一个带符号扩展的整数,这会导致所有输出值都是 000000xx 或 FFFFFFxx,具体取决于第 7 位的值。

相反,您可能想要做的是使用int指针而不是指针来扫描堆栈值char

于 2009-10-03T10:55:40.280 回答
1

如果您想知道这一切是如何工作的,一个好主意是在调试器中运行您的应用程序。弄清楚发生了什么真的很糟糕。

任何调试器都可以,但是我最了解windbg,所以这里有一些关于从稍微修改过的代码版本开始的指针,就像 Greg 建议的那样,你需要一个 int*。

改变:

 char *x = &x4;

到:

 int *x = &i;

删除:

 x = &x4;

移动(第一个变量,在 x1 之前):

 int i;

新变化(更容易阅读,并且像以前一样做(xi)是随机/当前不在范围内的值):

printf("%02d: %08x : %08x\n", i, x + i, *(x + i));   

此更改将有效地显示堆栈帧地址和值。

在 windbg 中,打开调用、内存、线程和命令窗口,以便您可以看到它们。使用编译器编译您的 C 代码,我使用 MSVC(您可以免费试用),使用带有“cl /Zi your.c”的“Visual Studio 20## 命令提示符”进行编译。加载 Windbg(Windows 调试工具),按 Ctrl+E 或使用“打开可执行文件”。

加载以下窗口,以便您可以一次查看它们,调用、本地、内存、线程和命令。

在命令窗口中,使用“bu DoTestStack”在 DoTestStack 上放置一个断点。

使用“g”命令开始调试。

到达断点后,使用“p”单步执行,您应该也会弹出源代码,或者您可以查看应用程序的输出,在 for 循环中运行后,返回 Windbg。启动内存窗口并将其设置为“i”中地址类型的“指针和符号”,它应该具有来自编译 (/Zi) 的调试信息,并会为您提供从以下开始的“指针和符号”列表指向 i 的地址的指针。

输出应该与测试代码相同(在您执行我建议的更改之后),如果您继续点击 p,您还将看到在内存窗口中您可以观察到每次执行时 i 的值发生变化,但是作为 printf现在正在打印其他值,您只会在输出的原始“0”处看到它;)。

您可以交替使用 windbg 命令dds (注意 i 的值是 0xb,因为它位于 for 循环的中间)

0:000> dds i
0018ff24  0000000b
0018ff28  02586bf9
0018ff2c  0018ff24
0018ff30  0018ff38
0018ff34  00401118 a!TestStack+0x8 [c:\temp\a.c @ 33]
0018ff38  0018ff40
0018ff3c  00401128 a!main+0x8 [c:\temp\a.c @ 35]
0018ff40  0018ff88
0018ff44  00401435 a!__tmainCRTStartup+0xf8 [f:\dd\vctools\crt_bld\self_x86\crt\src\crt0.c @ 257]
0018ff48  00000001
0018ff4c  003c1e48

这与修改后的测试代码相同(不支持符号除外);

00: 0018ff24 : 00000000
01: 0018ff28 : 02586bf9
02: 0018ff2c : 0018ff24
03: 0018ff30 : 0018ff38
04: 0018ff34 : 00401118
05: 0018ff38 : 0018ff40
06: 0018ff3c : 00401128
07: 0018ff40 : 0018ff88
08: 0018ff44 : 00401435
09: 0018ff48 : 00000001
10: 0018ff4c : 003c1e48
于 2009-10-03T11:54:41.487 回答