首先我们的代码打包编译之后会变成难以读懂的二进制字节码,并且变成.class文件。但是简单的APP编译出来之后可以被反编译,甚至你写的代码完完全全被暴露。你的代码被抄袭,被复制都是小事,重要的其实是你们APP的商业信息有可能被泄露!
下面是一个简单的例子被反编译的场景:
所以为了不让用户轻易的反编译出源代码文件,就要对.class文件进行加密,再通过特殊的加载类的方式解密,并将这个类加载到内存中。
首先说加密,加密无非就是把.class字节码文件进行一些变换,这里面就涉及密码学的知识了!加密的方式有很多种,要想提高保密性,可以考虑DES,AES,RSA。一旦加密算法的源码被公开,其实破解也就是很简单的事情了,所以建议大家还是用高安全性的密码系统,到时候及时的更换密钥。就能一定程度上增加破解难度。
有了加密算法,接下来就是加密一个字节码文件了:
private static File file = null; private static String path = null; // 读取已经编译好的正常的class字节码文件 public static void readClass(String filePath) throws Exception { file = new File(filePath); path = filePath; }
// 加密生成已加密的class字节码文件 public static void encrypt() throws Exception { FileInputStream fis = new FileInputStream(file); FileOutputStream fos = new FileOutputStream(path.substring(0, path.lastIndexOf(".class"))
- "附件.class"); byte[] b = new byte[1024]; int ch = 0; while ((ch = fis.read(b)) != -1) { // 变换b b=crypt(b, "encrypt"); fos.write(b, 0, ch); } }
这时候,调用readClass方法和encrypt方法,就能在原本XX.class文件的目录下生成XX附件.class。这时候,我们就使用这个副本的字节码文件,删除原来的,下次运行的时候再解密,这样就行了。
对类加密之后,需要用自己的方式把类再加载出来。正常的时候咱们写的类也需要被类加载器加载到内存中。所以,就涉及到类加载器的知识了:
系统默认三个类加载器,分别是:BootStrap,ExtClassLoader,AppClassLoader。那么这几个类加载器有什么区别呢?
首先类加载器有父子关系。BootStrap是爷爷(用C++编写,主要负责加载jre/lib/rt.jar),ExtClassLoader是爸爸(主要用于加载JRE/lib/ext/*.jar),AppClassLoader是儿子(用于负责加载ClassPath指定目录下的所有jar)。
所以我们一般写的class文件都是AppClassLoader加载的。那假如我们写了一个类,我们把这个类复制一份,放到ExtClassLoader目录下,那么类加载器会怎么加载呢?这就要提到类加载器的委托机制了。
类加载器的委托机制:当一个线程调用一个类的时候,首先用当前线程的类加载器去加载这个类,这个类加载器一开始不加载,会通知他的上一级类加载器去加载,等到了BootStrap加载器的时候,如果没有就再让调用的下级加载器去加载。如果都没有就报ClassNotFoundException异常。也可以直接指定类加载器去加载。
所以我们可以自己定义一个类加载器,让这个让这个类加载器去加载我们加密过的类。自定义类加载器需要继承ClassLoader类,并且重写findClass方法。
class MyClassLoader extends ClassLoader { private String path = null;
// 设置自定义类加载器的目录 public MyClassLoader(String path) { this.path = path; }
/*
- findClass和loadClass的区别?
*/
@Override
protected Class<?> findClass(String name) throws ClassNotFoundException {
try {
File f = new File(path, name.substring(name.lastIndexOf('.') + 1)
- ".class"); FileInputStream fis = new FileInputStream(f); ByteArrayOutputStream bos = new ByteArrayOutputStream(); int ch = 0; while ((ch = fis.read()) != -1) { bos.write(ch); } byte[] buf = bos.toByteArray(); //解密.class buf = Z1Encrypt.crypt(buf, "decrypt"); fis.close(); bos.close();