千家信息网

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

发表于:2025-11-12 作者:千家信息网编辑
千家信息网最后更新 2025年11月12日,本篇内容主要讲解"怎么使用自定义类加载器防止代码被反编译破解",感兴趣的朋友不妨来看看。本文介绍的方法操作简单快捷,实用性强。下面就让小编来带大家学习"怎么使用自定义类加载器防止代码被反编译破解"吧!
千家信息网最后更新 2025年11月12日怎么使用自定义类加载器防止代码被反编译破解

本篇内容主要讲解"怎么使用自定义类加载器防止代码被反编译破解",感兴趣的朋友不妨来看看。本文介绍的方法操作简单快捷,实用性强。下面就让小编来带大家学习"怎么使用自定义类加载器防止代码被反编译破解"吧!

代码防编译整体套路

1、编写加密工具类

@Slf4jpublic 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提供的加载器没法满足我们的需求。因此我们只能自己实现我们的类加载器。其自定义加载器代码如下

@Slf4jpublic 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 {        Class clz = findLoadedClass(name);        //先查询有没有加载过这个类。如果已经加载,则直接返回加载好的类。如果没有,则加载新的类。        if(clz != null){            return clz;        }else{            ClassLoader parent = this.getParent();            clz = getaClass(name, clz, parent);            if(clz != null){                return clz;            }else{                clz = getaClass(name);            }        }        return clz;    }    private Class getaClass(String name) throws ClassNotFoundException {        Class clz;        byte[] classData = getClassData(name);        if(classData == null){            throw new ClassNotFoundException();        }else{            clz = defineClass(name, classData, 0,classData.length);        }        return clz;    }    private Class getaClass(String name, Class clz, ClassLoader parent) {        try {            //委派给父类加载            clz = parent.loadClass(name);        } catch (Exception e) {           //log.warn("parent load class fail:"+ e.getMessage(),e);        }        return clz;    }    private byte[] getClassData(String classname){        if(StringUtils.isEmpty(secretKey) || !secretKey.contains(SECRETKEY_PREFIX) || secretKey.split(SECRETKEY_PREFIX).length != 2){            throw new RuntimeException("secretKey is illegal");        }        String path = CustomClassLoader.class.getClassLoader().getResource("META-INF/services/").getPath() +"/"+ classname+".lyb";        InputStream is = null;        ByteArrayOutputStream bas = null;        try{            is  = new FileInputStream(path);            bas = new ByteArrayOutputStream();            int len;            //解密            String[] arrs = secretKey.split(SECRETKEY_PREFIX);            long key = Long.valueOf(arrs[1]);          //  System.out.println("key:"+key);            while((len = is.read())!=-1){                byte data = (byte)(len - key - secretKey.length());                bas.write(data);            }            return bas.toByteArray();        }catch(Exception e){            e.printStackTrace();            return null;        }finally{            try {                if(is!=null){                    is.close();                }            } catch (IOException e) {                log.error("encrypt fail:"+e.getMessage(),e);            }            try {                if(bas!=null){                    bas.close();                }            } catch (IOException e) {                e.printStackTrace();            }        }    }}

通过如下方式进行调用

 public static void main(String[] args) throws Exception{        CustomClassLoader customClassLoader = new CustomClassLoader("test123456lyb-geek1603895713759");        Class clz = customClassLoader.loadClass("com.github.lybgeek.user.service.impl.UserServiceImpl");        if(clz != null){            Method method = clz.getMethod("list", User.class);            method.invoke(clz.newInstance(),new User());        }    }

思考二:通过自定义加载器加载过的类如何整合进行spring?

答案: 通过spring提供的扩展点进行ioc容器注入

1、编写bean定义,并注册注册bean定义

@Componentpublic class ServiceBeanFactoryPostProcessor implements BeanFactoryPostProcessor {    @Override    public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {        Object secretKey = YmlUtils.getValue("lyb-geek.secretKey");        if(ObjectUtils.isEmpty(secretKey)){            throw new RuntimeException("secretKey can not be null,you maybe need to config in application.yml with key lyb-geek.secretKey");        }        registerBean(beanFactory,secretKey.toString());//        setClassLoader(beanFactory,secretKey.toString());    }    /**     * 如果项目中引入了>spring-boot-devtools,则默认加载器为org.springframework.boot.devtools.restart.classloader.RestartClassLoader     * 此时如果使用自定加载器,则需把bean的类加载器变更为AppClassLoader     * @param beanFactory     */    private void setClassLoader(ConfigurableListableBeanFactory beanFactory,String secretKey) {        CustomClassLoader customClassLoader = new CustomClassLoader(secretKey);        beanFactory.setBeanClassLoader(customClassLoader.getParent());    }    private void registerBean(ConfigurableListableBeanFactory beanFactory,String secretKey){        DefaultListableBeanFactory defaultListableBeanFactory = (DefaultListableBeanFactory) beanFactory;        BeanDefinitionBuilder builder = BeanDefinitionBuilder.genericBeanDefinition(UserService.class);        GenericBeanDefinition definition = (GenericBeanDefinition) builder.getRawBeanDefinition();        definition.getPropertyValues().add("serviceClz",UserService.class);        definition.getPropertyValues().add("serviceImplClzName", "com.github.lybgeek.user.service.impl.UserServiceImpl");        definition.getPropertyValues().add("secretKey", secretKey);        definition.setBeanClass(ServiceFactoryBean.class);        definition.setAutowireMode(GenericBeanDefinition.AUTOWIRE_BY_TYPE);        String beanId = StringUtils.uncapitalize(UserService.class.getSimpleName());        defaultListableBeanFactory.registerBeanDefinition(beanId, definition);    }}

2、如果是接口注入,还需通过FactoryBean进行狸猫换太子

@Data@AllArgsConstructor@NoArgsConstructorpublic class ServiceFactoryBean  implements FactoryBean , ApplicationContextAware, InitializingBean {    private ApplicationContext applicationContext;    private Class serviceClz;    private String serviceImplClzName;    private String secretKey;    private Object targetObj;    @Override    public T getObject() throws Exception {        return (T) targetObj;    }    @Override    public Class getObjectType() {        return serviceClz;    }    @Override    public void afterPropertiesSet() throws Exception {        targetObj = ServiceFactory.create(secretKey,serviceImplClzName,applicationContext);    }    @Override    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {        this.applicationContext = applicationContext;    }}

3、验证是否整合成功

验证示例代码

@RestController@RequestMapping("/user")public class UserController {    @Autowired    private UserService userService;    @PostMapping(value = "/save")    public User save(User user){                User newUser = userService.save(user);                return newUser;    }    }

能够正常输出,说明整合成功

到此,相信大家对"怎么使用自定义类加载器防止代码被反编译破解"有了更深的了解,不妨来实际操作一番吧!这里是网站,更多相关内容可以进入相关频道进行查询,关注我们,继续学习!

0