C和C++安全编码(原书第2版)
上QQ阅读APP看书,第一时间看更新

3.9 longjmp()函数

C标准定义了setjmp()宏、longjmp()函数,以及jmp_buf类型,它们可以用来绕过正常的函数调用和返回规则。

setjmp()宏为稍后将会调用的longjmp()函数保存其调用环境。longjmp()则恢复最后一次由setjmp()宏保存的调用环境。例3.13展示了longjmp()是如何将程序控制权返回到setjmp()调用点的。

例3.13 longjmp()函数使用示例


01  #include <setjmp.h>
02  jmp_buf buf;
03  void g(int n);
04  void h(int n);
05  int n = 6;
06
07  void f(void) {
08    setjmp(buf);
09    g(n);
10  }
11
12  void g(int n) {
13    h(n);
14  }
15
16  void h(int n){
17    longjmp(buf, 2);
18  }

例3.14展示了Linux中jmp_buf数据结构的实现及其相关定义。jmp_buf结构(第11~15行)包含3个域。调用环境存储于__jmp_buf(于第1行声明)中。__jmp_buf类型是一个包含了6个元素的整数数组。#define语句指明每个数组元素中保存的值。例如,基址指针(Base Pointer,BP)存储于__jmp_buf[3]中,而程序计数器(Program Counter,PC)则存储于__jmp_buf[5]中。

例3.14 jmp_buf结构的Linux实现


01  typedef int __jmp_buf[6];
02
03  #define JB_BX 0
04  #define JB_SI 1
05  #define JB_DI 2
06  #define JB_BP 3
07  #define JB_SP 4
08  #define JB_PC 5
09  define JB_SIZE 24
10
11  typedef struct __jmp_buf_tag {
12    __jmp_buf __jmpbuf;
13    int __mask_was_saved;
14    __sigset_t __saved_mask;
15  } jmp_buf[1];

例3.15展示了Linux上为longjmp()产生的汇编指令。第2行的movl指令恢复BP,第3行的movl指令恢复栈指针(SP),第4行则将程序控制权转移到保存的PC。

例3.15 在Linux上为longjmp()生成的汇编指令


1  movl i, %eax /* 
返回 i */ 
2  movl env.__jmpbuf[JB_BP], %ebp 
3  movl env.__jmpbuf[JB_SP], %esp 
4  jmp (env.__jmpbuf[JB_PC]) 

可以通过将jmp_buf缓冲区中PC的值覆写为外壳代码的起始地址的方法来利用longjmp()函数。任意内存写或者直接针对jmp_buf结构的缓冲区溢出都能达到这个目的。