转自:https://xz.aliyun.com/t/4721
上一篇文章中主要是一种花指令的思路
- 把当前的RIP的值pop到寄存器中,比如rax
- 调整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正确识别代码和数据等
补充
最后,因为本文只是介绍一下反-反汇编的小技巧,单一的技巧会显得比较单薄,但是如果把这些技巧组合起来,加上反调试等技术,会让代码变得很"难看",破解的难度也是指数级上升
|