理论上来说,没有不能破解的程序,因为程序最终都要部署到目标机器上,在目标机器上运行,当然有的程序反编译后只能得到汇编源码,但有了汇编源码就还原了功能。反反编译的手段,主要是提高反编译的难度和门槛。
尤其像C#和java这类语言很容易反编译,因为这类语言编写的程序,都首先会被翻译成一种在.net或java虚拟机平台上运行的“中间语言”IL,只有在程序运行时,运行时编译器(JITter)才将IL代码编译成机器语言。这种IL以dll的形式存在,很容易通过反射、ILDASM和ILASM等方式将代码识别出来。为了保护这类源码,通常是通过混淆器的方式。
下面以C#源码为例,介绍下反反编译的手段。
方法一:在项目文件中增加SuppressIIdasm属性
该方法通过防止IIdasm.exe(MSIL的反编译程序)反编译源码,设置很简单,在属性的程序集配置文件中添加assembly:SuppressIldasm属性即可:
当项目增加了SuppressIldasm属性之后,ildasm.exe试图反编译代码时,会提示“受保护的模块 -- 无法进行反汇编”:
但是这种方法只对ildasm.exe工具有效果,ILSyp、Reflector等工具可以无视该属性,仍然可以反编译代码。
方法二:名称混淆
名称混淆的原理就是用不能直接猜出含义的变量名和函数名替换编译出来的程序包中具有明显语义信息的变量名和函数名。
通过原理我们可以知道:
- 混淆只改变变量名、函数名等,并不能改变程序逻辑,耐心的破解者仍然能够理解程序思路并尝试修改,但这类人不会太多,花费的代价也大;
- 重命名函数名或变量名可能导致程序异常,某些变量名和函数名不能改名,因此要将这些不能重命名的变量或函数名添加到屏蔽列表中。
混淆前:
public class Register // 一个注册类
{
public bool IsRegistered() // 判断是否已注册的方法
{
return true;
}
}
混淆后:
public class a // 一个注册类
{
public bool a() // 判断是否已注册的方法
{ return true; }
}
世上没有不能破解的程序,只有不值得破解的程序,当逆向工程的成本超过了收益,我们的软件资产才是安全的,名称混淆显然可以增加逆向工程的成本,但还不够。
方法三:流程混淆
流程混淆顾名思义就是打乱代码功能块的流程,目前流程混淆基本上是基于跳转实现的,程序跳转有三种方式:
1. goto跳转;
2.if-else跳转;
3.switch跳转。
混淆前:
混淆后:
从原理上看,做流程混淆要做这么几件事:
- 将源代码功能分成几块;
- 把功能块的顺序打乱;
- 用br.s对这几块的顺序进行连接,并保证到达原来的运行逻辑;
- 重新计算行号。
流程混淆结合名称混淆,可以大大提高反编译的难度,让破解者知难而退。