9.3.2 流程混淆
流程混淆是指打乱方法的流程,让反编译软件无法将代码正确的反编译为高级语言。流程混淆即可保护代码又可增加破解者分析代码的难度。
目前流程混淆基本上都是基于跳转实现的,我们在程序中使用的跳转有如下三种方式:
1) goto跳转;
2) if-else跳转
3) switch跳转。
.NET的流程混淆和传统windows应用程序的流程混淆本质上是有区别的。传统的流程混淆的目的是防止反汇编,而.NET流程混淆的目的是防止反编译。传统的流程混淆是基于汇编指令进行的,操作层次较低,可更改堆栈调用,可操作方式较多;.NET流程混淆是基于IL指令进行的,操作层次较高,不可触及堆栈调用,可操作方式较少。
下面我们针对代码清单9-9和代码清单9-10的示例程序,来实践流程混淆的不同方式。
代码清单9-9 测试流程混淆代码
class Program
{
static void Main(string[] args)
{
string h = "hello";
if (h == "hell0")
{
OutString();
}
else
{
Console.WriteLine("There is no hello!");
}
}
public static void OutString()
{
Console.WriteLine("hello");
}
}
现在编译项目,然后使用ILDasm导出改程序的IL代码(只截取Class IL代码部分),如代码清单9-10所示。
代码清单9-10 代码清单9-9的IL代码(Class IL代码部分)
// =============== CLASS MEMBERS DECLARATION ===================
.class private auto ansi beforefieldinit FlowObufscation.Program
extends [mscorlib]System.Object
{
.method private hidebysig static void Main(string[] args) cil managed
{
.entrypoint
// 代码大小 49 (0x31)
.maxstack 2
.locals init ([0] string h,
[1] bool CS$4$0000)
IL_0000: nop
IL_0001: ldstr "hello"
IL_0006: stloc.0
IL_0007: ldloc.0
IL_0008: ldstr "hell0"
IL_000d: call bool [mscorlib]System.String::op_Equality(string, string)
IL_0012: ldc.i4.0
IL_0013: ceq
IL_0015: stloc.1
IL_0016: ldloc.1
IL_0017: brtrue.s IL_0023
IL_0019: nop
IL_001a: call void FlowObufscation.Program::OutString()
IL_001f: nop
IL_0020: nop
IL_0021: br.s IL_0030
IL_0023: nop
IL_0024: ldstr "There is no hello!"
IL_0029: call void [mscorlib]System.Console::WriteLine(string)
IL_002e: nop
IL_002f: nop
IL_0030: ret
} // end of method Program::Main
.method public hidebysig static void OutString() cil managed
{
// 代码大小 13 (0xd)
.maxstack 8
IL_0000: nop
IL_0001: ldstr "hello"
IL_0006: call void [mscorlib]System.Console::WriteLine(string)
IL_000b: nop
IL_000c: ret
} // end of method Program::OutString
.method public hidebysig specialname rtspecialname
instance void .ctor() cil managed
{
// 代码大小 7 (0x7)
.maxstack 8
IL_0000: ldarg.0
IL_0001: call instance void [mscorlib]System.Object::.ctor()
IL_0006: ret
} // end of method Program::.ctor
} // end of class FlowObufscation.Program
// =============================================================
// *********** 反汇编完成 ***********************
// 警告: 创建了 Win32 资源文件 D:\FlowObufscation.res
代码清单9-10为代码清单9-9中整个Program类对应的IL代码。下面结合这两段代码进行流程混淆的实践。
q 代码块易位
Main方法内,代码从IL_0000一直到IL_0030,下面我们将这段代码分成三段,如代码清单9-11所示。
代码清单9-11 把Main方法分成三段
.method private hidebysig static void Main(string[] args) cil managed
{
//第一段开始
.entrypoint
// 代码大小 49 (0x31)
.maxstack 2
.locals init ([0] string h,
[1] bool CS$4$0000)
IL_0000: nop
IL_0001: ldstr "hello"
IL_0006: stloc.0
IL_0007: ldloc.0
IL_0008: ldstr "hell0"
IL_000d: call bool [mscorlib]System.String::op_Equality(string, string)
//第一段结束
//第二段开始
IL_0012: ldc.i4.0
IL_0013: ceq
IL_0015: stloc.1
IL_0016: ldloc.1
IL_0017: brtrue.s IL_0023
IL_0019: nop
IL_001a: call void FlowObufscation.Program::OutString()
IL_001f: nop
IL_0020: nop
IL_0021: br.s IL_0030
IL_0023: nop
IL_0024: ldstr "There is no hello!"
//第二段结束
//第三段开始
IL_0029: call void [mscorlib]System.Console::WriteLine(string)
IL_002e: nop
IL_002f: nop
IL_0030: ret
//第三段结束
} // end of method Program::Main
如代码清单9-11,我将Main方法的IL代码分成了三段,分段并没有什么依据,完全是随意的。下面我们将这三段代码重新组合,按照第一段、第三段、第二段的顺序排列,然后在第一段的结尾加上跳转语句“br IL_0012”,在第二段代码的后面加上跳转语句“br IL_0029”。修改之后的代码如代码清单9-12所示。
代码清单9-12重新排列的Main方法
.method private hidebysig static void Main(string[] args) cil managed
{
//第一段开始
.entrypoint
// 代码大小 49 (0x31)
.maxstack 2
.locals init ([0] string h,
[1] bool CS$4$0000)
IL_0000: nop
IL_0001: ldstr "hello"
IL_0006: stloc.0
IL_0007: ldloc.0
IL_0008: ldstr "hell0"
IL_000d: call bool [mscorlib]System.String::op_Equality(string, string)
br IL_0012
//第一段结束
//第三段开始
IL_0029: call void [mscorlib]System.Console::WriteLine(string)
IL_002e: nop
IL_002f: nop
IL_0030: ret
//第三段结束
//第二段开始
IL_0012: ldc.i4.0
IL_0013: ceq
IL_0015: stloc.1
IL_0016: ldloc.1
IL_0017: brtrue.s IL_0023
IL_0019: nop
IL_001a: call void FlowObufscation.Program::OutString()
IL_001f: nop
IL_0020: nop
IL_0021: br.s IL_0030
IL_0023: nop
IL_0024: ldstr "There is no hello!"
br IL_0029
//第二段结束
} // end of method Program::Main