这个累加载器(EncryptedClassLoader)有两个基本的操作在给定的类路径下加密一系列Class文件并且运行一个先前加密的程序加密后的文件很简单有一些极讨厌的各个字节的位组成(当然XOR运算符不可能被加密这只是一个范例请多多包涵)
通过EncryptedClassLoader来加载类需要注意一些问题我实现的是继承自URLClassLoader并且重载了loadClass()和defineClass()两个方法来实现自己的两个功能一个是专心于JAVA 类加载器的委托规则并且在系统类加载器做之前先加载一个经加密过的类;二是在执行defineClass()之前立即调用crypt()方法否则会执行URLClassLoaderfindClass()
执行下面的语句
>javac d bin src/*java src/my/secret/code/*java
我把Mainclass和MySecretClassclass进行了加密
>java cp bin EncryptedClassLoader encrypt bin Main my
deMySecretClass
encrypted [Mainclass]
encrypted [my\secret\code\MySecretClassclass]
现在原先编译的class文件已经被加密后的文件所替代了如果我想运行原始类文件需要使用EncryptedClassLoader来操作
>java cp bin Main
Exception in thread main javalangClassFormatError:
Main (Illegal constant pool type)
at javalangClassLoaderdefineClass(Native Method)
at javalangClassLoaderdefineClass(ClassLoaderjava:)
at javasecuritySecureClassLoaderdefineClass
(SecureClassLoaderjava:)
at URLClassLoaderdefineClass(URLClassLoaderjava:)
at URLClassLoaderaccess$(URLClassLoaderjava:)
at URLClassLoader$run(URLClassLoaderjava:)
at javasecurityAccessControllerdoPrivileged(Native Method)
at URLClassLoaderfindClass(URLClassLoaderjava:)
at javalangClassLoaderloadClass(ClassLoaderjava:)
at sunmiscLauncher$AppClassLoaderloadClass(Launcherjava:)
at javalangClassLoaderloadClass(ClassLoaderjava:)
at javalangClassLoaderloadClassInternal(ClassLoaderjava:)
>java cp bin EncryptedClassLoader run bin Main
decrypted [Main]
decrypted [deMySecretClass]
secret result =
现在可以确信采用任何反编译工具对加密后的Class文件都不会起作用的
现在添加一个可靠的密码保护机制把它打包成本地可执行文件并且使其对外收费这样子可以吗?当然不能这样了
ClassLoaderdefineClass():必然经过的接口
所有的类加载器必须经过明确地API把类定义传递到JVM里这就需要javalangClassLoaderdefineClass()方法了类加载器的API有多个这个方法的重载但是所有的方法都会调用defineClass(String byte[] int int ProtectionDomain)这是一个在经过一些简单验证后放入到JVM里的最终的方法如果你想建立一个新的Class文件的话这对于理解每个类加载器都会不可避免的调用该方法是很重要的
你只能在方法defineClass()里把一些单调的字节数组生成Class对象并且我们猜测这些字节数组文件会包含一些文档格式化(查看class文件格式规范welldocument.d format)的未加密的class定义通过拦截对该方法的所有调用可以很简单的破坏这种加密模式并且很方便的反编译你感兴趣的Class文件
做这种拦截并不困难实际上破坏自己建立的保护模式比用工具更加迅速的首先我取得基于JSDK的javalangClassLoader源文件并修改defineClass(String byte[] int int ProtectionDomain)方法在里面加入其他的类正如下面
c = defineClass(name b off len protectionDomain);
//Intercept classes defined by the system loader and its children:
if (isAncestor (getSystemClassLoader ()getParent ()))
{
// Choose your own dump location here [use an absolute pathname]:
final File parentDir = new File (c:/TEMP/classes/);
File dump = new File (parentDir
namereplace ( FileseparatorChar) + [ +
getClass ()getName () + @ +LongtoHexString
(SystemidentityHashCode (this)) + ]class);
dumpgetParentFile ()mkdirs ();
FileOutputStream out = null;
try
{
out = new FileOutputStream (dump);
outwrite (b off len);
}
catch (IOException ioe)
{
ioeprintStackTrace (Systemout);
}
finally
{
if (out != null) try { outclose (); }
catch (Exception ignore) {}
}
}
注意if里的语句可以过滤系统类加载器及其子类加载器同样在defineClass()方法可以正常工作的情况下才能载入类很难以相信不只有一个类加载器实例加载一个类可通过在文件名堆里面加入类加载器标志我还是最终把这一问题给解决了)
最后一步是用包含javalangClassLoader类的可执行文件临时替换由JRE使用的文件rtjar你也可以使用Xbootclasspath/p选项
我再一次运行加密的程序并恢复了所有的未加密的文件这么说可以很容易的把class文件正确的反编译我先声明我并没有用EncryptedClassLoader类的内部机制来完成此壮举的
在这里注意一点假如我没去使用一个系统类我可以使用别的方法比如自定义一个JVMPI代理来处理JVMPI_EVENT_CLASS_LOAD_HOOK事件
学习小结
我希望你能对本文有所兴趣你必须认识到得很重要的一点是在购买市面上任何反编译工具前要三思而行除非JVM体系结构进行改革以支持class字节码在本地能进行译码转换你才会更好的从传统的困惑中走出来上演一场字节码的改革浪潮!当然也有其他的更有效的方法对类加载进行调试尽可能地得到类加载的轨迹是很有价值的特别是在类加载时你去捕获异常情况下使用因此JAVA的诞生可能纯粹是为了开源项目当然其他一些体系结构(如NET)也正在倾向于反编译目前我就说说这种思想了