Помогите разобраться в C Stack - PullRequest
1 голос
/ 03 октября 2009

Я пытаюсь понять менеджер памяти низкого уровня в 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

Ответы [ 3 ]

6 голосов
/ 03 октября 2009

Вы смотрите на отдельные символы, а затем приводите их к 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));
4 голосов
/ 03 октября 2009

В этой строке:

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

указатель x является символьным указателем, который после разыменования преобразуется в целое число с расширением знака, в результате чего все ваши выходные значения равны либо 000000xx, либо FFFFFFxx, в зависимости от значения бита 7.

Вместо этого вы, вероятно, захотите использовать указатель int для сканирования значений стека вместо указателя char.

1 голос
/ 03 октября 2009

Хорошая идея, если вы хотите знать, как все это работает, - запускать ваше приложение в отладчике. Это действительно дерьмо, чтобы понять, что происходит.

С любым отладчиком все в порядке, но я лучше знаю windbg, так что вот несколько советов о том, с чего начать, когда немного измененная версия вашего кода, как предложил Грег, вам нужна int *.

CHANGE:

 char *x = &x4;

TO:

 int *x = &i;

DELETE:

 x = &x4;

MOVE (первая переменная, перед x1):

 int i;

НОВОЕ ИЗМЕНЕНИЕ (намного проще читать и делать как раньше (x-i) - это случайные / не входящие в объем значения):

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

Это изменение будет эффективно отображать адрес кадра стека и значение.

В windbg откройте окна вызовов, памяти, потоков и команд, чтобы вы могли видеть их все. Скомпилируйте ваш код C с помощью компилятора, я использовал MSVC (вы можете получить его для ознакомления бесплатно), скомпилируйте с помощью «Visual Studio 20 ## Command Prompt» с помощью «cl / Zi your.c». Загрузите Windbg (средства отладки для Windows), нажмите Ctrl + E или используйте «открыть исполняемый файл».

Загрузите следующие окна, чтобы вы могли видеть их все одновременно, Звонки, Местные жители, Память, Потоки и Команды.

В командном окне установите точку останова на DoTestStack с помощью «bu DoTestStack».

начать отладку командой "g".

После того, как вы дойдете до точки останова, используйте «p» для одного шага, вы также должны получить всплывающее окно с исходным кодом, или вы можете наблюдать за выводом вашего приложения, после того, как оно запустится в цикле for, вернитесь к Windbg. Откройте окно памяти и установите для него «Указатели и символы» для типа адреса в «i», оно должно иметь отладочную информацию из компиляции (/ Zi) и выдаст вам список «Указателей и символов», начиная с указатель на адрес я.

Выходные данные должны быть идентичны тестовому коду (после внесения предложенных мной изменений), если вы продолжите нажимать 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
...