Невозможно понять код использования строки форматирования
Я столкнулся с этим кодом, показывающим использование строки строки при чтении этой статьи.
#include <stdio.h>
int main(void)
{
char secret[]="hack.se is lame";
char buffer[512];
char target[512];
printf("secret = %pn",&secret);
fgets(buffer,512,stdin);
snprintf(target,512,buffer);
printf("%s",target);
}
Выполнение его с помощью следующего ввода
[[email protected]]$ ./a.out
secret = 0xbffffc68
AAAA%x %x %x %x %x %x %x //Input given
AAAA4013fe20 0 0 0 41414141 33313034 30326566
- [[email protected]]$
Я до сих пор понимаю, что последовательность %x
будет продолжать печатать значения по адресам выше текущего %esp
(я предполагаю, что стек растет вниз к нижнему адресу).
То, что я не могу понять, - это введенный ввод сохраняется в массиве buffer
, который не может быть меньше 512 байт от текущего %esp
. Итак, как вывод может содержать 41414141
(шестнадцатеричное представление AAAA
) сразу после 4 %x
, то есть чуть выше 4 адресов текущего %esp
. Я тоже старался уставиться на код сборки, но я думаю, что не мог следить за манипуляциями с строками в стеке.
Ответы
Ответ 1
При входе в snprintf
в стеке есть следующее:
0xbfd257d0: 0xxxxxxxxx 0xxxxxxxxx 0xxxxxxxxx 0x080484d5
0xbfd257e0: 0xbfd25800 0x00000200 0xbfd25a00 0x00000000
0xbfd257f0: 0x00000000 0x00000000 0x00000000 0x00000000
0xbfd25800: 0x00000000 0x00000040 0xb7f22f2c 0x00000000
0xbfd25810: 0x00000000 0x00000000 0x00000000 0x00000000
0xbfd25800 -> target (initially 0x00000000 0x00000040 ...)
... -> garbage
0xbfd257e8 -> pointer to buffer
0xbfd257e4 -> 512
0xbfd257e0 -> pointer to target
0xbfd257df -> return address
target
получает замену с результатом snprintf
до того, как snprintf
будет использовать свои слова в качестве аргументов: сначала он пишет "AAAA" (0x41414141) в 0xbfd25800, а затем "% x" считывает значение в 0xbfd257ec и записывает его на 0xbfd25804,..., тогда "% x" считывает значение в 0xbfd25800 (0x41414141) и записывает его на 0xbfd25814,...
Ответ 2
Прежде всего, посмотрим на стек после вызова snprintf():
Reading symbols from /home/blackbear/a.out...done.
(gdb) run
Starting program: /home/blackbear/a.out
secret = 0xbffff40c
ABCDEF%x %x %x %x %x %x %x
Breakpoint 1, main () at prova.c:13
13 printf("%s",target);
(gdb) x/20x $esp
0xbfffeff0: 0xbffff00c 0x00000200 0xbffff20c 0x00155d7c
0xbffff000: 0x00155d7c 0x000000f0 0x000000f0 0x44434241
0xbffff010: 0x35314645 0x63376435 0x35353120 0x20633764
0xbffff020: 0x66203066 0x34342030 0x32343334 0x33203134
0xbffff030: 0x34313335 0x20353436 0x37333336 0x35333436
(gdb)
Мы можем видеть на 0xbffff00c строку, уже отформатированную, поэтому sprintf() написал там. Мы также можем видеть в 0xbfffeff0 последний аргумент для snprintf(): адрес цели, который на самом деле равен 0xbffff00c.
Поэтому я могу вывести, что строки сохраняются от конца до начала выделенного места в стеке, так как мы также можем увидеть добавление strcpy():
[email protected]:~$ cat prova.c
#include <stdio.h>
#include <string.h>
int main(void)
{
char secret[]="hack.se is lame";
char buffer[512];
char target[512];
printf("secret = %p\n", &secret);
strcpy(target, "ABCDEF");
fgets(buffer,512,stdin);
snprintf(target,512,buffer);
printf("%s",target);
}
[email protected]:~$ gcc prova.c -g
prova.c: In function ‘main’:
prova.c:14: warning: format not a string literal and no format arguments
prova.c:14: warning: format not a string literal and no format arguments
[email protected]:~$ gdb ./a.out -q
Reading symbols from /home/blackbear/a.out...done.
(gdb) break 13
Breakpoint 1 at 0x8048580: file prova.c, line 13.
(gdb) run
Starting program: /home/blackbear/a.out
secret = 0xbffff40c
Breakpoint 1, main () at prova.c:13
13 fgets(buffer,512,stdin);
(gdb) x/10x $esp
0xbfffeff0: 0xbffff00c 0x080486bd 0x00000007 0x00155d7c
0xbffff000: 0x00155d7c 0x000000f0 0x000000f0 0x44434241
0xbffff010: 0x00004645 0x00000004
(gdb)
Что это! В заключение мы нашли строку там, потому что строки хранятся в стеке обратным образом, а начало (или конец?) Цели находится около esp.