上一篇文章介绍了x64架构的程序通过patch让IDA无法正确识别参数的小技巧,本文补充一下x86架构中这个技巧的运用
平级函数的修改
源码、编译
测试程序源码如下:
vuln
只是命名习惯了...并不是指漏洞函数
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
int func(int a) {
int num = a;
printf("%d",a);
getchar();
getchar();
return 1;
}
int func_wrapper(int b) {
func(b+1);
int num = b;
printf("%d",num);
getchar();
getchar();
getchar();
return 2;
}
int vuln(int num){
printf("%d",num);
return 0xff;
}
int main(int argc, char** argv) {
func_wrapper(1);
vuln(2);
return 0;
}
程序没有什么特别的,注意getchar()
因为没有参数,就是用来nop
掉然后开始表演的,也不会影响堆栈平衡
编译:gcc -m32 main.c -o test
编译过后IDA打开,基本是这样
因为没有开优化,所以esp
的加加减减没有被合并
patch
因为x64
是fastcall
,前几个参数用寄存器传参;而x86
则全是用栈传参
原先vuln
函数显示是这样的(这两个就是"平级函数")
目的是让它显示成vuln(v3)
这种形式,以达到一些干扰的效果
我们关注这段汇编
func_wrapper
的参数是1
,被push
到栈中
而vuln
函数调用之前先收回了原先参数1
的栈,又压入了自己的参数2
我们可以把vuln
的参数改成1
,比如这样
去掉了一个
push 2
,对应的,多sub esp,4
保存patch版本,重新打开
显示已经变成了这样
在gdb-peda
中调试一下,原先可以显示出Guessed arguments
现在虽然栈上的参数被我们改成了1
,但也只是改了一个数值,而Guessed arguments
已经显示错误了
子函数改父函数
很显然刚刚的技巧在动态调试时会暴露无遗,IDA F5
虽然没有成功识别参数,但是也给出了程序的大致逻辑,这次我们用一些更复杂的方法来混淆干扰
源码、编译
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
int func() {
getchar();
getchar();
getchar();
return 1;
}
int func_wrapper(int num) {
printf("%d",num);
func();
printf("%d",num);
return 2;
}
int main(int argc, char** argv) {
func_wrapper(1);
return 0;
}
编译:gcc -m32 main.c -o test
我们的目标是让func_wrapper
中的两次printf("%d",num);
输出不同的值
patch1
关键的patch
在func()
里,预留的3个getchar()
就是nop
后留下空间表演的
patch2
我们调试一下,看看nop
时的esp
的值,再根据func_wrapper
的参数在栈上的地址,算出偏移
断在printf("%d",num);
参数入栈前,发现0xffffcfd0
处为参数
跟进,进入func()
的一堆nop
中
此时esp
的值0xffffcfb0
为
算出esp
距离父函数的参数偏移为0x20
patch3
我们在func
中修改如下
中间改掉的三行就是把父函数printf
的参数改掉了
原先运行会输出两个1
,但是由于我们把第二次输出的参数改成了2,输出如下
中途调的时候有好多小问题...
此时,IDA打开,func
函数已经无法F5
了
在func_wrapper
中也看不出我们在func
里做了什么手脚
总结
原理不难理解,实际上手作为花指令单靠patch
显然效率太低也容易被发现
上次
x64
的技巧,我夜影大哥给了一个C语言内联汇编在复杂函数中悄咪咪内联传参的建议,之后会试一下
另外,考虑递归函数,如果也在内层把外层的参数改了,可能会让人疯掉...没有敢去尝试
比如用一个随机数函数,加上内联汇编,符合某条件就改外层参数,栈回溯时会非常复杂