
上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结构的缓冲区溢出都能达到这个目的。