-
-
[分享][分享][建议]ctf 中常见混淆与反混淆学习 0x01
-
发表于:
2024-7-19 23:05
2227
-
[分享][分享][建议]ctf 中常见混淆与反混淆学习 0x01
ctf 中常见混淆与反混淆学习 1(
概念:
- 阻止逆向分析,在源代码和工具分析处理出的中间代码产生混淆
混淆种类:
也可按源码和机器码层面来进行分类:
源码级:
机器码:
在ctf中常见的是花指令,ollvm,自解密,mov混淆
花指令
花指令一般干扰静态分析层面,扰乱汇编代码,让ida反汇编失败
这里说一下在反汇编的过程中,数据与代码的区分问题
在程序中,字节码可能表示指令,也可能表示数据,不同字节码包含的字节数不同,有单字节指令,也有多字节指令。如果首字节是多字节指令,反汇编在确定第一个指令,也就是操作码之后,就会确定该指令是包含多少字节码的指令,然后将这些字节码转化为汇编指令
反汇编算法
- 线性扫描法(如OD):从入口点或是代码开头,逐条语句进行反汇编,以一条指令的结束作为下一条指令的开始。这样的实现很容易被干扰
列如:
1 2 3 4 | jmp label1
db 0xe8 ; 这个字节在跳转后不执行,但线性反汇编器会错误地将其当作指令的一部分
label1:
nop ; 实际CPU从这里开始运行
|
在上面的代码中,当 jmp label1
被执行后,CPU 实际上会跳转到 label1
执行 nop
指令。
然而,线性反汇编器会继续从 0xe8
处分析,误将数据字节当作指令进行反汇编,导致大量指令反汇编错误。
- 递归下降(如ida):对代码的执行路径进行分析,当解码出分支指令后,把这个分支指令的地址记录下来,并反汇编各个分支中的指令,可以有效避免将数据作为指令解析,但也会被干扰
列如:
1 2 3 4 5 | jz label1
jnz label1
db 0xe8 ; 干扰字节
label1:
nop ; 正常指令
|
由于jz和jnz都存在理论上的连续向下执行分支,所以ida仍然会优先反汇编干扰字节,导致反汇编出错,也就是jz和jnz一起使用,产生了jmp一样的效果。
花指令原理
向正常的程序里面嵌入数据块或者指令,达到反汇编器分析失败的目的。
常见花指令分类:
- jz,jnz:用两条相反的条件跳转语句,使条件跳转变为跳转
- call:用pop的方式来清除cal的压栈,使栈平衡。从而用call实现jmp,让ida认为cal的目标地址为函数起始地址,导致函数创建错误
下面是几种的花指令的实战:(采用的AT&T语法)
1 2 3 4 5 6 | __asm__ (
"xor %eax, %eax\n"
"jz 1f\n"
".byte 0xe8\n"
"1:\n"
);
|
1 2 3 4 5 6 | __asm__ (
"xor %eax, %eax\n"
"jz 1f\n"
".byte 0xeb\n"
"1:\n"
); #这一种花指令,对od来说是无效的,因为od是递归扫描
|
但可以创建一个指向无效的数据地址,来迷惑od
1 2 3 4 5 6 7 8 9 | __asm__ (
"xor %eax, %eax\n"
"jz 1f\n"
"jnz 2f\n"
"2:\n"
".byte 0xEB\n"
"1:\n"
);
|
还有一类push的,大量使用可以让程序四分五裂,但这类花指令的使用时要涉及retn,需要保证esp栈顶指针值不紊乱,故在ctf中不是很常见,这里不多加讨论
花指令解决
对于不影响程序运行的花指令,nop即可。
而有一类花指令,对于程序起连结作用的,可以改为其他工具可识别指令
SMC 代码自修改
原理
- 在执行某一段代码时,程序会对自身的该段代码进行自修改,只有在修改后的代码才是可汇编、可执行的。
- 在程序未对该段代码进行修改之前, 在静态分析状态下, 均是不可读的字节码,IDA之类的反汇编器无法识别程序的正常逻辑。
SMC的实现是需要对目标内存进行修改的,.text一般是没有写权限的。那么就需要拥有修改目标内存的权限:
在linux系统中,可以通过mprotect函数修改目标内存的权限
在Windows系统中,VirtualProtect函数实现内存权限的修改
因此也可以观察是否有这俩个函数来判断是否进行了SMC。
smc实操
- 先写出可运行的程序,包括加密逻辑。
- 得到可执行程序后,借助ida、OD等逆向工具,导出待加密代码段或待加密函数的字节码。
- 通过设定好的SMC加密算法,加密上一步骤导出字节码,得到字节码密文。
- 在可运行程序的源代码中添加字节码密文,并在调用SMC加密函数前,使用SMC加密字节码的逆算法解密代码段,然后通过函数指针的方式进行调用。
如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 | #include <stdio.h>
char enc_bytecode[] = {0xEB, 0x0E, 0x5E, 0xC6, 0x46, 0x01, 0x65, 0xC6, 0x46, 0x02, 0x66, 0xB0, 0x01, 0xB4, 0x4C, 0xCD, 0x21, 0xB4, 0x4C, 0xCD, 0x21};
int main() {
int (*Check)( char *, char *);
char reg1[] = "test1" ;
char reg2[] = "test2" ;
for ( int i = 0; i < sizeof (enc_bytecode); i++) {
enc_bytecode[i] ^= 0x12;
}
Check = ( int (*)( char *, char *))&enc_bytecode;
Check(reg1, reg2);
return 0;
}
|
smc处理
静态分析时可以ida python直接patch,也可以动态调试让程序自解密再dump
[培训]内核驱动高级班,冲击BAT一流互联网大厂工作,每周日13:00-18:00直播授课