日期:2009-01-06  浏览次数:20614 次

什么是流程混淆,它的利与弊

现在继续来讲混淆,我们讲到那了?? 哦,流程混淆~~
流程混淆感觉和移形换位、乾坤大挪移有点象……好象已经说过……
为什么这么说呢?因为,流程混淆就是移来移去,达到让你看不懂流程的原理来进行的。


在此,我还要介绍一些其它的知识。由于NET的特性,所以,动态调试NET的全部过程几乎是不可能的,所以,静态分析成为了NET的首选。那么,对付静态分析最好的办法是什么呢?在远古的C时代就已经有这种方法了(混淆其实一点也不新鲜,都是旧技术换个名称而以),那时,这种技术叫作花指令。当然流程混淆和花指令还是有区别的,不过我想,基础的原理也算是差不多了。
什么是花指令?
好,我用汇编构建一段代码如下:
00410070 >/$ 8BEC MOV EBP,ESP
00410072 |. 6A 00 PUSH 0 ; /pModule = NULL
00410074 |. E8 310A0000 CALL <JMP.&KERNEL32.GetModuleHandleA> ; \GetModuleHandleA
00410079 |. A3 B0004200 MOV Dword PTR DS:[4200B0],EAX
0041007E |. A3 24004200 MOV Dword PTR DS:[420024],EAX
00410083 |. E8 160A0000 CALL <JMP.&KERNEL32.GetCommandLineA> ; [GetCommandLineA
00410088 90 NOP 《- 注意这里
00410089 90 NOP
0041008A 90 NOP
0041008B 90 /NOP
0041008C 90 NOP
0041008D 90 |NOP
0041008E 90 |NOP
0041008F 90 NOP
00410090 90 |NOP
00410091 90 NOP 《- 还有这里
00410092 |. C705 20004200 >|MOV Dword PTR DS:[420020],loaddll.00420>; ASCII "Missing DLL name"
0041009C |. E9 EB010000 |JMP loaddll.0041028C
004100A1 |> 3C 22 |CMP AL,22
004100A3 |.^75 E6 \JNZ SHORT loaddll.0041008B
004100A5 |> 8A06 /MOV AL,BYTE PTR DS:[ESI]
004100A7 |. 3C 20 |CMP AL,20
004100A9 |. 75 03 |JNZ SHORT loaddll.004100AE
004100AB |. 46 |INC ESI
004100AC |.^EB F7 \JMP SHORT loaddll.004100A5

修改了一下:
00410070 >/$ 8BEC MOV EBP,ESP
00410072 |. 6A 00 PUSH 0 ; /pModule = NULL
00410074 |. E8 310A0000 CALL <JMP.&KERNEL32.GetModuleHandleA> ; \GetModuleHandleA
00410079 |. A3 B0004200 MOV Dword PTR DS:[4200B0],EAX
0041007E |. A3 24004200 MOV Dword PTR DS:[420024],EAX
00410083 |. E8 160A0000 CALL <JMP.&KERNEL32.GetCommandLineA> ; [GetCommandLineA
00410088 EB 08 JMP SHORT loaddll.00410092 《- 注意 已经没有00410092
0041008A 2910 SUB Dword PTR DS:[EAX],EDX
0041008C F8 CLC
0041008D 60 PUSHAD
0041008E 99 CDQ
0041008F F8 CLC
00410090 E8 E8C70520 |CALL 2046C87D
00410095 0042 00 ADD BYTE PTR DS:[EDX],AL
00410098 |? 59 POP ECX
00410099 |? 0142 00 ADD Dword PTR DS:[EDX],EAX
0041009C |. E9 EB010000 |JMP loaddll.0041028C
004100A1 |> 3C 22 |CMP AL,22
004100A3 |.^75 E6 \JNZ SHORT loaddll.0041008B
004100A5 |> 8A06 /MOV AL,BYTE PTR DS:[ESI]
004100A7 |. 3C 20 |CMP AL,20
004100A9 |. 75 03 |JNZ SHORT loaddll.004100AE
004100AB |. 46 |INC ESI
004100AC |.^EB F7 \JMP SHORT loaddll.004100A5

这就叫做花指令,花指令就是利用跳转或其它的一些指令,并在这此指令中间制造一些无法看懂的代码,使反汇编出来的东西摸不着头脑,并且产生错误的句语(动态跟踪就不会受花指令的影响)从而达到混淆静态反汇编的功能。

那么流程混淆到底是什么呢?
原理基本上是一样,即把方法中的代码分为几段,并把每一段都错开,然后利用“跳转”语句连接原来的流程逻辑,并达到执行正确的目地。原理图如下表所示:
块编号 块代码
1 第一个功能
2 第二个功能
3 第三个功能
4 第四个功能

块编号 块代码 跳转
1 第一个功能 Jmp 2
4 第四个功能
3 第三个功能 Jmp 4
2 第二个功能 Jmp 3

基本流程混淆原理即是上表所示,总结就以下这么几个字:破坏原有程序结构,并利用Jmp语句接连原有流程。
基于上面原理所说,所以流程混淆是肯定会耗费资源的。而且,有些特殊的过程,可能在混淆后不能正常使用了,我以前就碰上一个,具体情况记不太清楚,不过第一次运行结果正确,第二次就不正确了,使用DBGCLR跟踪去看,发现第一次执行正常,而第二次则未执行。由于时间非常紧迫,所以未更深入的研究原因所在,当不混淆此方法后,一切正常。
流程混淆是目前各大厂商的混淆利器的最高境界,带有流程混淆的混淆器,基本售价都是上千美元,合人民币近万元。这么高的金额代价之下,它的强度是不是已经能够达到我们的需要了呢?呵呵。这个问题,我们在下一章里再来讨论吧。
附一段 IL 流程混淆前后的代码,明天拿它开刀。
C#源:
private string CreatePassword(char[] passwords,int arraylenghts,int lenghts)
{
int i;
Random RndNumber = new Random();
string return_value="";
for(i=0;i<=lenghts;i++)
{
return_value=return_value+passwords[(int)(RndNumber.NextDouble()*arraylenghts)];
}
return(return_value);
}
IL源:
.method private hidebysig instance string CreatePassword(char[] passwords, int32 arraylenghts, int32 lenghts) cil managed
{
// Code Size: 50 byte(s)
.maxstack 4
.locals (
int32 num1,
[mscorlib]System.Random random1,
string text1)
L_0000: newobj instance void [mscorlib]System.Random::.ctor()
L_0005: stloc.1
L_0006: ldstr ""
L_000b: stloc.2
L_0