A股上市公司传智教育(股票代码 003032)旗下技术交流社区北京昌平校区

本帖最后由 上海分校-小影 于 2018-5-22 09:42 编辑

类加载器加密class文件

需求介绍:
Java本身是开源的技术,那么只要拥有反编译工具和项目的.class文件就可以获取源代码内容。

那么如何在源代码级别增强安全性呢?

答:对.class文件做加密操作,让反编译工具无法反编译.class文件

那么如何开始做这个加密操作呢?

答:修改.class文件底层的字节数据

代码演示:
    /**
             * @throws Exception
             * 对Class文件进行加密
             */
            @Test
            public void test1() throws Exception {
                    FileInputStream in = new FileInputStream(new File("d:/class/MD5Utils.class"));
   
                    ByteArrayOutputStream out = new ByteArrayOutputStream();
                    byte[] buf = new byte[2048];
                    int len = 0;
                    byte mi = 1;
                    while ((len = in.read(buf)) != -1) {
                            for (int i = 0; i < buf.length; i++) {
                                    System.out.println("第一次:" + buf);
                                    buf = (byte) (buf + mi);
                                    System.out.println("第二次:" + buf);
                            }
                            out.write(buf, 0, len);
                    }
                    byte[] byteArray = out.toByteArray();
   
                    FileOutputStream fout = new FileOutputStream(new File("d:/clazz/MD5Utils.class"));
   
                    fout.write(byteArray, 0, byteArray.length);
            }

那么加密完成之后这个文件如何运行呢?

答:使用类加载器,读取加载文件,创建类对象,调用方法

    package cn.itcast.loader;
   
    import java.io.ByteArrayOutputStream;
    import java.io.File;
    import java.io.FileInputStream;
    import java.io.FileNotFoundException;
   
    public class MyClassLoader extends ClassLoader{
   
            @Override
            public Class findClass(String name)  {
                    
                    //自定义一个类加载器:加载指定位置的.class文件(d:/class)
                    String path = "d:/clazz";
                    try {
                            FileInputStream in = new FileInputStream(new File(path+"/"+name+".class"));
                            //根据获取到的数据,创建Class对象
                            //需要一个将数据转换为字节数组的输出流
                            ByteArrayOutputStream out = new ByteArrayOutputStream();
                           
                            int len = 0;
                            byte[] buf = new byte[1024];
                            while((len = in.read(buf)) != -1){
                                    
                                    out.write(buf, 0, len);
                            }
                            byte[] byteArray = out.toByteArray();
                            for (int i = 0; i < byteArray.length; i++) {
                                    byteArray = (byte) (byteArray - 1);
                            }
                           
                            return defineClass(name, byteArray, 0, byteArray.length);
                           
                    } catch (Exception e) {
                            e.printStackTrace();
                            try {
                                    return super.findClass(name);
                            } catch (ClassNotFoundException e1) {
                                    e1.printStackTrace();
                                    return null;
                            }
                    }
            }
    }

在加密的转换过程中byte数据底层加减可能出现超出数据范围的情况(例如:127+1),但是,我们的转换过程中,没有出现任何问题,这是为什么呢?
接下来我们一点点来分析下:

    /**
             * 问题:加密 加多少,解密,减多少——————》加密前是多少,加密后,数字也是多少?目前的效果,也是对的
             *
             * 问题:加法运算可能超出byte类型数据的范围,会导致加密解密失败,但是这里为什么没有失败?
             * 同学们的思路:
             *
             *    -127  1111 1111
             *  +   1   0000 0001
             *  ---------------------
             *         10000 0000
             * 最高丢弃       0000 0000 ————》  0
             *
             *      0   0000 0000
             *  -   1   0000 0001
             *  ------------------
             *     -1   1000 0001
             *     
             * 问题:数据经过加减之后,尽然可以,保持原样,那么计算底层到底是怎么计算的?
             *
             * 1 计算底层,所有都是加法运算
             *
             *      1 0000 0001
             *  -   1 0000 0001
             *  ----------------
             *  所有减法都是加法
             *  
             *      1 0000 0001
             *  +  -1 1000 0001
             *  -----------------
             *     -2 1000 0010
             * 错误的结果————按照普通人类的思路
             *
             * 按照机器思路做减法:
             *
             *          1 0000 0001
             *  +  -1 1000 0001
             *  -----------------
             *  获取当前数据(-1)的反码:0 ————》1  1————》0,最高位不动
             *  
             *          1 0000 0001
             *  +  -1 1111 1110
             *  -----------------
             *        1111 1111
             *  在反码计算结果的基础上加一(补码):
             *            1111 1111
             *  +   1 0000 0001
             *  ----------------
             *       10000 0000
             * byte数据最高位去掉:
             *                   0000 0000 ——————》 0
             *
             * 按照机器思路进行加密和解密
             *    -127  1111 1111
             *  +   1   0000 0001
             *  ---------------------
             *         10000 0000
             * byte数据最高位去掉:
             *                     0000 0000 ——————》 0
             * 以上是加密
             *
             * 以下解密:
             *          0000 0000
             *    +     1000 0001
             *    ----------------
             *   
             *  反码:  
             *          0000 0000
             *    +     1111 1110
             *    ----------------
             *          1111 1110
             *     
             *  补码:
             *                  1111 1110
             *    +     0000 0001
             *  -------------------
             *                  1111 1111
             *         
             * 总结:加密和解密不会导致,数据前后不一致,是因为,二进制减法计算的是使用了反码和补码的方式。
             * */
   
通过以上分析,我们发现其实在计算的过程中,计算机使用的是二进制的补码和反码进行运算,因此,没有数据超出范围的问题。

2 个回复

倒序浏览
我来占层楼啊   
回复 使用道具 举报
您需要登录后才可以回帖 登录 | 加入黑马