上一篇文章中主要是一种花指令的思路

  1. 把当前的RIP的值pop到寄存器中,比如rax
  2. 调整rax的值,使之处于反汇编引擎的解析的某条指令中间,让反汇编引擎出错,但实际上可以正确跳转,解决方法是帮助IDA正确识别数据和代码

这篇文章主要是在子函数中劫持父函数的流程

使用的测试文件在附件中

栈上相关背景

在函数调用时,call xxx会将下一条指令的地址压栈,再前往要跳转的指令

当子函数调用结束,retn会将之前保存的指令地址置为RIP的值,也就是说,retn == pop rip

子函数调用时,一开始可能会把RBP压栈,并把RSP的值赋给RBP,在退出函数时进行逆操作

无论是哪种情况,可以发现,父函数的返回地址也只是栈上保存的一个数据而已,也没有保护机制:比如子函数只能把RSP的值控制在一定范围内

也就是说,我们可以在子函数中获得父函数的ret addr,将其pop到寄存器中,再修改这个寄存器的值,当子函数retn时,父函数的返回地址也就被劫持了

实例

伪造子函数

我们还是拿上一篇文章中的nothing-patched.exe做演示,在附件中

这是正常的原程序流程

最后一行的call sub_140001031是我改成nop后加上的

可以认为我们在这边伪造一个子函数

修改retaddr

此时rbx就是父函数的返回地址,而现在只是add rbx,0,并没有改变返回地址,只要把这个0改一下,就可以劫持流程了

可以看到,当前执行到retn,在堆栈窗口正好会回到...102E,它也就是原本call指令的下一条指令的地址

记为patched1,在附件中

如果这时我们将add rbx,0改成add rbx,46,那么就会跳转到源程序流程了

加花

我们通过动态调试,让返回地址加上0x19,也就是跳转到下一个jmp指令上,记作patched2

重新打开可以发现IDA已经识别错误了

这是因为在...1047处的jmp指令的前一个byte改成了如0xEB,这样就会让静态的反汇编引擎分析成错误jmp

上图为动态调试时的场景

同样的,F5的结果也已经飞了

去花

加上0xEB的这个技巧在上一篇文章已经说过了,去花也只是需要帮助IDA正确识别代码和数据等

补充

最后,因为本文只是介绍一下反-反汇编的小技巧,单一的技巧会显得比较单薄,但是如果把这些技巧组合起来,加上反调试等技术,会让代码变得很"难看",破解的难度也是指数级上升

点击收藏 | 1 关注 | 2 打赏
  • 动动手指,沙发就是你的了!
登录 后跟帖