ucontext
setjmp/longjmp的最终演变
- setjmp()/longjmp()
- sigsetjmp()/siglongjmp()
- getcontext()/makecontext()/setcontext()/swapcontext()
1. setjmp()/longjmp()
- 通过setjmp()设置一个保存点,可以通过longjmp()返回到那里
- 实现了函数间的goto
- 只要不从setjmp()的函数退出就有效
- 返回保存点时,esp寄存器会还原到设置保存点时的值。即只返回stack位置,而不是内容。调用setjmp()的函数一旦退出,stack本身可能与保存点设定时不同,即便还原也可能不能正常工作
- 正常工作的case:
- esp还原时,stack frame也还原到正常
- 非正常工作的case:
- funcA退出,funcC,funcD调用后,funcD内longjmp(),esp还原,这时候stack frame是funcC,不是原本的funcA
2. sigsetjmp()/siglongjmp()
也可以恢复signal stack信息
- 假如存在一个通过sigaction(2)指定signal stack的signal handler(例如SIG_TERM的handler运行中,不接受SIG_ALRM),当这个handler内通过longjmp()返回保存点时,它不接受SIG_ALRM返回。为避免这种情况,需要一种在longjmp()时还原signal stack信息的处理机制
3. getcontext()/makecontext()/setcontext()/swapcontext()
- 通过getcontext()设置保存点(context)
- 通过 makecontext()将context与函数相关联
- 通过setcontext()来返回到那个context
- 通过swapcontext()来返回到那个context,并且保存现在的context
ucontext
- 生成context
- 根据需要修改context中的信息
- 通过makecontext(),可以修改为通过特定函数带参数调用
- 可以直接重写context中的寄存器信息
- 之后通过setcontext()/swapcontext()进行切换
- 可以通过适当设置,从任意寄存器状况恢复
context的结构
- 作为参考,context结构大概是这样(Ubuntu x86_64)
- ss_sp是指向新stack的指针
- ss_size是它的size
- gregs[0~22]是用来保存通用寄存器及其值
- 从gregs[0]开始按顺序,R8, R9, R10, R11, R12, R13, R14, R15,RDI, RSI, RBP, RBX, RDX, RAX, RCX, RSP, RIP, EFL, CSGSFS(+pad),ERR, TRAPNO, OLDMASK, CR2
- __val是signal mask
ucontext与sigaction
context与sigaction也有关联
- https://linuxjm.osdn.jp/html/LDP_man-pages/man2/sigaction.2.html
- 在sa_flags指定SA_SIGINFO时(不是sa_handler),根据sa_sigaction的signum指定对应的signal handler函数。指定的函数,第一个参数是signal 编号,第二个是指向siginfo_t的指针,第三个参数是经过(void * 类型转换)的指向ucontext_t的指针
- 只要正确设置,成为signal handler的函数也可以接受context作为第三个参数
ucontext的总结
- 非常强大的机制
- 使用时,可以从任意函数/地址启动任意的寄存器状态
- 利用这个功能,用户可以自己实现类似线程的context切换
- 但是,context的结构非常复杂
- 需要注意,没有system call
DWARF
- 之前解释过malloc(-1)的行为
- 只是失败
- new(-1)时,会产生std::bad_alloc
- 产生异常,需要手动处理
- 实际上尝试大概是这样
- 可以使用try-catch来手动处理:
那么try-catch是怎么避免异常进行还原的?
- 使用IDA能够看到try catch,但不能确定范围
这是libgcc通过DWARF来确定处理的位置
DWARF
- 是作为调试信息,保存stack trace的机制
- .debug_frame section
- C++的try-catch实现也使用到
- .eh_frame_hdr section,.eh_frame section(CIE, FDE)
- 也用到了.gcc_except_table section(LSDA)
- libgcc中实现
- https://osdn.jp/projects/drdeamon64/wiki/DWARF%E3%83%95%E3%82%A1%E3%82%A4%E3%83%AB%E3%83%95%E3%82%A9%E3%83%BC%E3%83%9E%E3%83%83%E3%83%88
- https://refspecs.linuxfoundation.org/LSB_3.0.0/LSB-PDA/LSB-PDA/ehframechpt.html
- https://refspecs.linuxfoundation.org/LSB_3.0.0/LSB-PDA/LSB-PDA/ehframechpt.html
- 也存在单独的寄存器操作指令
- call frame信息
- CIE就像一个初期状态模板
- 也有readelf -wF不会显示的信息
- 是LSDA
- 可以使用katana
katana
相关论文及题目
- http://www.cs.dartmouth.edu/~sergey/battleaxe/hackito_2011_oakley_bratus.pdf
- http://www.cs.dartmouth.edu/~sergey/battleaxe/hackito_2011_oakley_bratus.pdf
- CodeGate2014 - pwn800 - membership
- https://www.bpak.org/blog/2014/02/codegate-2014-membership- 800pt-pwnable-write-up/
- Hack.lu 2014 - pwn500 - breakout