如何使用自定义类加载器防止代码被反编译破解

作者:微信小助手

发布时间:2023-07-28T15:54:01

点击蓝字之后,我们就是好朋友了啦

15



01

前言


最近和朋友聊天,他接了个外包项目,他问我有没有办法让自己的源码不被反编译破解,我就跟他说可以对代码进行混淆和加密。今天我们就来聊聊如何通过对代码进行加密实现代码防反编译,至于混淆因为可以直接利用proguard-maven-plugin进行配置实现,相对比较简单,就不在本文论述






02

代码防编译整体套路


1、编写加密工具类


@Slf4j
public class EncryptUtils {

    private static String secretKey = "test123456lyb-geek"+System.currentTimeMillis();

    private EncryptUtils(){}


    public static void encrypt(String classFileSrcPath,String classFileDestPath) {
        System.out.println(secretKey);
        FileInputStream fis = null;
        FileOutputStream fos = null;

        try {
            fis = new FileInputStream(classFileSrcPath);
            fos = new FileOutputStream(classFileDestPath);
            int len;
            String[] arrs = secretKey.split("lyb-geek");
            long key = Long.valueOf(arrs[1]);
            System.out.println("key:"+key);
            while((len = fis.read())!=-1){
              byte data = (byte)(len + key + secretKey.length());
              fos.write(data);
            }
        } catch (Exception e) {
           log.error("encrypt fail:"+e.getMessage(),e);
        }finally {
            if(fis != null){
                try {
                    fis.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }

            if(fos != null){
                try {
                    fos.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }

    }

}


2、对需要防止被反编译代码加密


public static void main(String[] args) {
        String classFileSrcPath = classFileSrcPath("UserServiceImpl");
        System.out.println("classFileSrcPath:--->"+classFileSrcPath);
        String classFileDestDir = ServiceGenerate.class.getClassLoader().getResource("META-INF/services/").getPath();
        System.out.println("classFileDestDir:--->"+classFileDestDir);
        String classFileDestPath = classFileDestDir + "com.github.lybgeek.user.service.impl.UserServiceImpl.lyb";
        EncryptUtils.encrypt(classFileSrcPath,classFileDestPath);
    }


3、对加密代码进行反编译验证

打开反编译工具jd-gui,把加密的代码拖入jd-gui


打不开,至少说明不能用jd-gui来反编译加密过的代码。

我们打开正常的编译的class文件,其内容形如下

从内容我们大概还是能看出一些东西,比如包名啥的。而打开加密后的文件,其内容如下

内容宛若天书


思考一:代码都被加密了,那jvm如何识别?


答案:既然有加密,自然可以通过解密来使用。那这个解密得存放在什么地方进行解密?

如果对类加载有一定了解的朋友,就会知道java的class文件是通过类加载器把class加载入jvm内存中,因此我们可以考虑把解密放在类加载器中。常用的类加载有启动类加载器、扩展类加载器、系统类加载。我们正常classpath路径下的类都是通过系统类加载器进行加载。而不巧这三个jdk提供的加载器没法满足我们的需求。因此我们只能自己实现我们的类加载器。其自定义加载器代码如下


@Slf4j
public class CustomClassLoader extends ClassLoader{

    /**
     * 授权码
     */

    private String secretKey;

    private String SECRETKEY_PREFIX = "lyb-geek";


    /**
     * class文件的根目录
     */

    private String classRootDir = "META-INF/services/";

    public CustomClassLoader(String secretKey) {
        this.secretKey = secretKey;
    }


    public String getClassRootDir() {
        return classRootDir;
    }

    public void setClassRootDir(String classRootDir) {
        this.classRootDir = classRootDir;
    }

    @Override
    protected Class<?> findClass(String name) throws ClassNotFoundException {

  &n