Как я могу получить адрес памяти глобальной переменной, используя атаку форматирования строки в C?

Код здесь:

#include <stdio.h>

#define NUM 0x11a

int data = NUM;

int main(int argc, char * argv[])
{
    struct{
        unsigned long memoryAddress;
       char array[50];
    } locals;

    locals.memoryAddress= 2;

    scanf("%lx", &locals.memoryAddress);

    scanf("%49s", locals.array);
    printf(locals.array);

    data += 5;
    printf("\n%d\n", data);

    if(data != NUM + 0x5){
        printf("Print me!\n");
    }

    return 0;
}

Я должен получить "Print me!". Это атака строки формата, и я использую %n и gdb.

Итак, как я могу получить адрес памяти данных, чтобы перезаписать его?


person Gardas462    schedule 29.04.2020    source источник
comment
Может быть, это слишком много для меня, но я не понимаю, почему после присвоения NUM data и добавления 5 к data вы ожидаете, что data будет отличаться от NUM+5.   -  person Roberto Caboni    schedule 29.04.2020
comment
Связанный: stackoverflow.com/questions/31290850/   -  person RobertS supports Monica Cellio    schedule 29.04.2020
comment
Вы не можете получить адрес data. Либо вы это уже знаете, либо нет. Если ваша программа была скомпилирована с помощью -fno-pie -no-pie, вы можете проверить ее с помощью objdump или gdb (если вы компилируете с символами) перед запуском программы.   -  person Marco Bonelli    schedule 29.04.2020
comment
@RobertSsupportsMonicaCellio OP уже знает об этом, и это именно то, что они пытаются сделать, это не ошибка.   -  person Marco Bonelli    schedule 29.04.2020
comment
@MarcoBonelli Но почему вставка строки% n должна изменить содержимое несвязанной переменной?   -  person Roberto Caboni    schedule 29.04.2020
comment
@RobertoCaboni %n позволяет записывать количество символов, записанных на данный момент, в переменную. Если переменной нет или если вы используете точное смещение (например, %5$n), вы можете выбрать произвольный адрес для записи из стека (в этом случае OP хочет записать в data). Поскольку у вас есть контроль над строкой формата, вы можете упаковать произвольный адрес в саму строку формата, а затем найти соответствующее смещение, чтобы %n использовал этот адрес и писал то, что вы хотите, где хотите (комбинируя его с %NNNc, чтобы записать NNN символов перед строкой). %n попал).   -  person Marco Bonelli    schedule 29.04.2020
comment
@RobertoCaboni вот более подробное объяснение (первое попавшееся погуглить).   -  person Marco Bonelli    schedule 29.04.2020
comment
Должны ли мы предположить, что у строки locals.memoryAddress= 2; есть какое-то особое назначение. Я спрашиваю, потому что сразу за ним следует scanf("%lx", &locals.memoryAddress);, которое перезапишет предыдущее значение 2, хранящееся в locals.memoryAddress.   -  person ryyker    schedule 29.04.2020
comment
@MarcoBonelli Тем не менее, это просто UB: C18, 7.21.6.1/8 n - аргумент должен быть указателем на целое число со знаком, в которое записывается количество символов, записанных в выходной поток на данный момент этим вызовом fprintf. Ни один аргумент не преобразуется, но используется один.   -  person RobertS supports Monica Cellio    schedule 29.04.2020
comment
@RobertSsupportsMonicaCellio UB — это просто концепция, определенная стандартом. Как только вы определите точный компилятор и версию, и более того, когда у вас будет скомпилированная программа, поведение программы будет определено на 100%.   -  person Marco Bonelli    schedule 29.04.2020
comment
@MarcoBonelli Очень умно! Это дает понять, что я наивная душа.   -  person Roberto Caboni    schedule 29.04.2020
comment
@MarcoBonelli - _ просто концепция, определенная стандартом_, или, скорее, _ просто концепция_ that is not определенная стандартом. :)   -  person ryyker    schedule 29.04.2020
comment
@ryyker Я вижу, что ты там сделал, лол.   -  person Marco Bonelli    schedule 29.04.2020
comment
Пожалуйста, добавьте больше информации к вопросу. Какие именно команды вы использовали для компиляции кода? Это очень важно ответить на ваш вопрос. На какой платформе/ОС вы работаете? И, наконец, что вы пробовали, что не сработало? Покажите пример.   -  person Marco Bonelli    schedule 29.04.2020
comment
@MarcoBonelli Но на самом деле это требует, чтобы конкретная реализация определяла поведение, происходящее в этом случае. Предусмотрено ли это, например, в gcc/Linux в соответствии с использованием %n без аргумента при вызове printf()?   -  person RobertS supports Monica Cellio    schedule 29.04.2020
comment
@RobertSsupportsMonicaCellio В 32-битной версии Linux x86 printf() glibc предполагает, что параметры передаются в стеке, поэтому использование %n берет адрес из стека. Это безопасное предположение, и поведение на 100% детерминировано, поскольку вы можете посмотреть на скомпилированный код библиотеки. Функция вызывается правильно, и с точки зрения компилятора неопределенного поведения нет. Неопределенное поведение может произойти во время выполнения, но, как я уже сказал, вряд ли оно неопределенное.   -  person Marco Bonelli    schedule 29.04.2020
comment
Я должен устать, но где %n в опубликованном коде?   -  person Ôrel    schedule 29.04.2020
comment
@Ôrel должен быть введен пользователем в scanf("%49s", locals.array);   -  person Marco Bonelli    schedule 29.04.2020