帮助理解C堆栈

发布于 2024-08-06 11:13:15 字数 1710 浏览 4 评论 0原文

我正在尝试了解 C 中的低级内存管理器,尤其是堆栈。正如我所知,当调用函数时,返回地址被压入堆栈。然后局部变量位于之后。

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

#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

I am trying to understand low-level memory manager in C especially Stack. As I was told, when a function is called, a return address was push on a stack. Then local variables is located after that.

So I write a small program to investigate this. Here is my program:

#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() ;
}

Basically, it investigate all memory before and after the position x4 is located. which should well cover the position of the return address.

But I can't seems to find any byte that resembles the return address at all.

Here is my result:

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!!!

What am I missing here? Can someone please explain.

I am on Ubuntu 9.10 anyway.

Thanks in advance. :-D

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

与往事干杯 2024-08-13 11:13:15

您正在查看单个字符,然后将它们转换为 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));

You're looking at individual chars, then casting those to 32-bit integers, which is confusing you. The return address is in the least significant bytes of these four lines:

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

I.e. your return address is 0x08048b54.

Try this instead:

uint32_t *x;
x = (uint32_t *)&x4;
for(i = 0; i < 32; i++)
    printf("%02d: %08X : %08X\n", i, *(x + i), *(x - i));
你是暖光i 2024-08-13 11:13:15

在此行中:

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

x 指针是一个字符指针,在取消引用后,该指针将提升为带符号扩展的整数,这会导致所有输出值要么是 000000xx 要么是 FFFFFFxx,具体取决于位 7 的值相反

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

In this line:

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

the x pointer is a character pointer, which after dereferencing gets promoted to an integer with sign extension, which causes all your output values to be either 000000xx or FFFFFFxx depending on the value of bit 7.

Instead, what you probably want to do is use an int pointer to scan through the stack values, instead of a char pointer.

乜一 2024-08-13 11:13:15

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

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

更改:

 char *x = &x4;

TO:

 int *x = &i;

删除:

 x = &x4;

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

 int i;

新更改(更容易阅读,并且按照之前的操作(xi)是随机的/当前不在范围内的值):

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

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

在windbg中,获取调用、内存、线程和命令窗口,以便您可以看到它们全部。
用编译器编译你的C代码,我用的是MSVC(你可以免费试用),用“Visual Studio 20##命令提示符”和“cl /Zi your.c”进行编译。加载 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

A good idea if you want to know how this all works is to run your application in a debugger. It's really cut's the crap to figure out what's going on.

Any debugger is OK, however I know windbg best so here's some pointers on where to get started at a slightly modified version of your code, like Greg suggested, you need an int*.

CHANGE:

 char *x = &x4;

TO:

 int *x = &i;

DELETE:

 x = &x4;

MOVE(first variable, before x1):

 int i;

NEW CHANGE(much easeir to read, and doing as previously (x-i) is random/not currently in-scope values):

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

This change will show effectivly the stack-frame address and the value.

In windbg, get the calls, memory, threads and command windows up so you can see them all.
Compile your C code with a compiler, I used MSVC (you can get it for trial for free), compile with the "Visual Studio 20## Command Prompt" with "cl /Zi your.c". Load up Windbg (debugging tools for Windows), press Ctrl+E or use "open executable".

Load the following windows so you can see them all at one time, Calls, Locals, Memory, Threads and Command.

At the command window, put a breakpoint on your DoTestStack with "bu DoTestStack".

start debugging with "g" command.

After you get to the breakpoint, use "p" to single step, you should get the source popped up also, or you can watch the output of your application, after it's running in the for loop, go back to Windbg. Get the memory window up and set it to "Pointers and Symbols" for an address type in "i", it should have the debug information from the compile (/Zi) and will give you a listing of "Pointers and symbols" starting from a pointer to the address of i.

The output should be identical to the test code (after you do the changes I suggested), if you continue to hit p, you will see also that in the memory window you can observe the value for i changing per execution, however as the printf is now printing other values, you will simply see it at the original "0" on the output ;).

You can alternativly use the windbg command dds (note the value for i is 0xb as it's in the middle of the for loop)

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

This is identical (exept no symbol support) as the modified test code;

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
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文