Java代码加密与反编译(二):用加密算法DES修改classLoader实现对.class文件加密

Java代码加密与反编译(二):用加密算法DES修改classLoader实现对.class文件加密

二、利用加密算法DES实现java代码加密

传统的C/C++自动带有保护机制,但java不同,只要使用反编译工具,代码很容易被暴露,这里需要了解的就是Java的];

  • String algorithm = "DES";
  • // 生成密匙
  • SecureRandom sr = new SecureRandom();
  • KeyGenerator kg = KeyGenerator.getInstance(algorithm);
  • kg.init(sr);
  • SecretKey key = kg.generateKey();
  • // 把密匙数据保存到文件
  • Util.writeFile(keyFilename, key.getEncoded());
  • }
  • }
  • step2:加密待加密的.class文件。

    得到密匙之后,接下来就可以用它加密数据。除了解密的ClassLoader之外,一般还要有一个加密待发布应用的独立程序:

    EncryptClasses.java:

    1. import java.security.*;
    2. import javax.crypto.*;
    3. import javax.crypto.spec.*;
    4. public class EncryptClasses
    5. {
    6. static public void main(String args[]) throws Exception {
    7. String keyFilename = args[0];
    8. String algorithm = "DES";
    9. // 生成密匙
    10. SecureRandom sr = new SecureRandom();
    11. byte rawKey[] = Util.readFile(keyFilename);
    12. DESKeySpec dks = new DESKeySpec(rawKey);
    13. SecretKeyFactory keyFactory = SecretKeyFactory.getInstance( algorithm );
    14. SecretKey key = keyFactory.generateSecret(dks);
    15. // 创建用于实际加密操作的Cipher对象
    16. Cipher ecipher = Cipher.getInstance(algorithm);
    17. ecipher.init(Cipher.ENCRYPT_MODE, key, sr);
    18. // 加密命令行中指定的每一个类
    19. for (int i=1; i<args.length; ++i) {
    20. String filename = args[i];
    21. byte classData[] = Util.readFile(filename);  //读入类文件
    22. byte encryptedClassData[] = ecipher.doFinal(classData);  //加密
    23. Util.writeFile(filename, encryptedClassData);  // 保存加密后的内容
    24. System.out.println("Encrypted "+filename);
    25. }
    26. }
    27. }

    step3:加密待加密的.class文件。

    编译之后用命令行启动程序,下面是源码:

    DecryptStart.java:

    1. import java.io.*;
    2. import java.security.*;
    3. import java.lang.reflect.*;
    4. import javax.crypto.*;
    5. import javax.crypto.spec.*;
    6. public class DecryptStart extends ClassLoader
    7. {
    8. // 这些对象在构造函数中设置,以后loadClass()方法将利用它们解密类
    9. private SecretKey key;
    10. private Cipher cipher;
    11. // 构造函数:设置解密所需要的对象
    12. public DecryptStart(SecretKey key) throws GeneralSecurityException, IOException {
    13. this.key = key;
    14. String algorithm = "DES";
    15. SecureRandom sr = new SecureRandom();
    16. System.err.println("[DecryptStart: creating cipher]");
    17. cipher = Cipher.getInstance(algorithm);
    18. cipher.init(Cipher.DECRYPT_MODE, key, sr);
    19. }
    20. // main过程:在这里读入密匙,创建DecryptStart的实例,它就是定制ClassLoader。
    21. // 设置好ClassLoader以后,用它装入应用实例,
    22. // 最后,通过Java Reflection API调用应用实例的main方法
    23. public static void main(String args[]) throws Exception {
    24. String keyFilename = args[0];
    25. String appName = args[1];
    26. // 传递给应用本身的参数
    27. String realArgs[] = new String[args.length-2];
    28. System.arraycopy( args, 2, realArgs, 0, args.length-2 );
    29. // 读取密匙
    30. System.err.println( "[DecryptStart: reading key]" );
    31. byte rawKey[] = Util.readFile(keyFilename);
    32. DESKeySpec dks = new DESKeySpec(rawKey);
    33. SecretKeyFactory keyFactory = SecretKeyFactory.getInstance("DES");
    34. SecretKey key = keyFactory.generateSecret(dks);
    35. // 创建解密的ClassLoader
    36. DecryptStart dr = new DecryptStart(key);
    37. // 创建应用主类的一个实例,通过ClassLoader装入它
    38. System.err.println("[DecryptStart: loading "+appName+"]");
    39. Class clasz = dr.loadClass(appName);
    40. // 最后通过Reflection API调用应用实例
    41. // 的main()方法
    42. // 获取一个对main()的引用
    43. String proto[] = new String[1];
    44. Class mainArgs[] = { (new String[1]).getClass() };
    45. Method main = clasz.getMethod("main", mainArgs);
    46. // 创建一个包含main()方法参数的数组
    47. Object argsArray[] = { realArgs };
    48. System.err.println("[DecryptStart: running "+appName+".main()]");
    49. // 调用main()
    50. main.invoke(null, argsArray);
    51. }
    52. public Class loadClass(String name, boolean resolve) throws ClassNotFoundException {
    53. try {
    54. // 要创建的Class对象
    55. Class clasz = null;
    56. // 必需的步骤1:如果类已经在系统缓冲之中,不必再次装入它
    57. clasz = findLoadedClass(name);
    58. if (clasz != null)
    59. return clasz;
    60. // 下面是定制部分
    61. try{
    62. //读取经过加密的类文件
    63. byte classData[] = Util.readFile(name+".class");
    64. if(classData != null){
    65. byte decryptedClassData[] = cipher.doFinal(classData);  //解密
    66. clasz = defineClass( name, decryptedClassData, 0, decryptedClassData.length); // 再把它转换成一个类
    67. System.err.println( "[DecryptStart: decrypting class "+name+"]");
    68. }
    69. }catch(FileNotFoundException fnfe){
    70. }
    71. // 必需的步骤2:如果上面没有成功
    72. // 尝试用默认的ClassLoader装入它
    73. if (clasz == null)
    74. clasz = findSystemClass(name);
    75. // 必需的步骤3:如有必要,则装入相关的类
    76. if (resolve && clasz != null)
    77. resolveClass(clasz);
    78. return clasz;//把类返回给调用者
    79. } catch(IOException ie) {
    80. throw new ClassNotFoundException(ie.toString());
    81. } catch(GeneralSecurityException gse) {
    82. throw new ClassNotFoundException( gse.toString());
    83. }
    84. }
    85. }

    step4:运行加密程序。

    1.新建java项目,把上面三个.java程序和需要加密的程序.java都放在同一个src目录下,然后在eclipse里构建项目,把所有的.java文件在bin目录下生成.class文件。这里我需要加密jar包里有三个程序:SplitAddress.java、IPSeeker.java、IPEntity.java。

    Java代码加密与反编译(二):用加密算法DES修改classLoader实现对.class文件加密

    2.然后在命令行里,cd到bin目录下启动程序:

    1. java com.javacode.GenerateKeykey.data

    这样就在bin下生成了key.data文件。

    3.把bin\com\javacode\下的待加密的SplitAddress.class、IPSeeker.class、IPEntity.class拷贝到bin目录下。然后bin目录下命令行启动:

    1. java com.javacode.EncryptClasseskey.data SplitAddress.class IPSeeker.class IPEntity.class

    该命令把每个.class文件替换成其各自的加密版本。

    (注意:加密版本为bin目录下的.class文件,未加密版本为bin\com\javacode\下的.class文件)

    4.运行经过加密的应用。

    对于未经加密的应用(cd到bin\com\javacode\),正常执行方式如下:

    1. java IPSeeker arg0 arg1 arg2

    对于经过加密的应用(cd到bin\),则相应的运行方式为:

    1. java DecryptStart key.data IPSeeker arg0 arg1 arg2

    step5:验证

    (1) 未加密的可以直接用反编译工具jd-gui查看到源码:

    Java代码加密与反编译(二):用加密算法DES修改classLoader实现对.class文件加密

    (2) 经过加密的用反编译工具无法查看:

    Java代码加密与反编译(二):用加密算法DES修改classLoader实现对.class文件加密

    step6:一些说明

    虽然应用本身经过了加密,但启动程序DecryptStart没有加密。攻击者可以反编译启动程序并修改它,把解密后的类文件保存到磁盘。解决方法是对启动程序进行高质量模糊处理。或者可以采用直接编译成机器语言的代码,使得启动程序具有传统执行文件格式的安全性。

    另外大多数JVM本身并不安全,还可以修改JVM,从ClassLoader之外获取解密后的代码并保存到磁盘,从而绕过上述加密所做的一切工作。

    不过所有这些可能的攻击都有一个前提,这就是攻击者可以得到密匙。如果没有密匙,应用的安全性就完全取决于加密算法的安全性。

    上一篇:Java Base64加密、解密原理Java代码


    下一篇:Banner