黑马程序员技术交流社区

标题: 用类加载器将代码加密的3个步骤 [打印本页]

作者: 浅木头    时间: 2015-1-26 19:54
标题: 用类加载器将代码加密的3个步骤
代码1

package day02;

import java.util.Date;

public class ClassLoaderTest {

  /**
       * @param args
    * 如何对类加载器加密?
     * 1,三个类,一个类加载器的类,一个运行类,一个附件类(输出语句的)
      * 2,在工程下新建一个目录,准备放新的附件类
  * 3,将附件类放入这个目录——在MyClassLoader中右键Run As,选择Other...
       * 先选择主要运行类为MyClassLoader,然后输入两个传入的参数
     * 第一个参数是附件类的class文件所在路径,在bin文件夹里,获取其绝对路径
         * 这里有个技巧,就是将MyClassLoader.class拖到运行对话框,就可得到。
     * 第二个参数是刚才在工程下新建的目录名字,如lib。
      * 点击Apply和确定
     * 4,左键选中lib,按下F5刷新,应该会出来这个新的附件class文件,这个类就是已经加密的类
        * 5,正常的效果是在运行类中运行附件类,应该能输出语句,但要让它不能,操作如下:
        *      a,找出旧的没加密的附件类class文件完整路径,复制
       *  b,在MyClassLoader下Run As:
      *  先选择主要的类为MyClassLoader
         *  再设置参数,第一个是刚才复制的旧的文件路径,第二个是lib
         *  这样就表示,如果运行,是可以正常的,因为附件类class文件是没加密的
   * 6,如果想看到异常现象,就将已加密的class文件覆盖掉bin目录下的class文件,再运行就报错:
     * Exception in thread "main" java.lang.ClassFormatError: Truncated class file
    * 7,如果要功能完善,步骤如下:
        *              MyClassLoader类继承ClassLoader,覆盖findClass方法(protected),其中包含解密算法
                    在运行类中加载原来存放已加密的附件类的目录下的附件类,这样就等于解密了
                      再创建新附件类的实例,运行即可
   *
        *
        *
        *如果把附件类删除,则可能需要重启MyEclipse,重新导入父类类加载器
    *父类类加载器只能加载带包名的类
         * @throws Exception
     */
      public static void main(String[] args) throws Exception {
                // TODO Auto-generated method stub

                //System.out.println(new ClassLoaderAttachment().toString());
            
         //用自定义的类加载器加载放于mylib01目录下的ClassLoaderAttachment类,生成后已经解密,因为再次运行了一次加密算法
           //如果loadClass的目录写成day02.ClassLoaderAttachment则无法读取
               //只有写成ClassLoaderAttachment才可以读取
         //因为父类只能读取day02目录下的附件类,我们的目的就是不让父类读到,
            //从而可以用自己自定义的类加载器。所以,如果传入父类的目录,就无法使用自定义的类加载器了。
           
         /**
               * 知识点:对于这个例子来说,父类会查找/practice.javaEnhance/src/day02
              * 目录下的所有jar包和目录,也就是classPath,一般的类,父类也就是AppClassLoader。
           * 所以会查找这个。而我们只要保证这个classPath下没有,就可以用自定义
          * 的类加载器了。自定义的类加载器可以加载指定目录。指定的目录就是
                * new MyClassLoader("mylib01")里的mylib01。里面只有一个文件。
                *
               */
              Class clazz = new MyClassLoader("mylib01").loadClass("ClassLoaderAttachment");
           //创建一个新附件类的实例
            Date d1 = (Date)clazz.newInstance();
             //打印信息
           System.out.println(d1);
         
}

}




代码2

package day02;


import java.util.Date;

public class ClassLoaderAttachment extends Date {

    /**
       * @param args
    */
      public static void main(String[] args) {
         // TODO Auto-generated method stub

        }
        
public String toString(){
                return "Welcome,my friend!";
     }

}



代码3

package day02;

import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;

//继承类加载器
public class MyClassLoader extends ClassLoader{

     /**
       * 自定义一个加载器,带有加密功能
        * @param args
    * @throws IOException
   */
      public static void main(String[] args) throws IOException {
              /*// TODO Auto-generated method stub
             //获取ClassLoaderTest01类的对应的类加载器的名字
                System.out.println(MyClassLoader.class.getClassLoader().getClass()
                               .getName());
             //获取System类的对应的类加载器,因为特殊,所以不能获取名字
                System.out.println(System.class.getClassLoader());
               //将ClassLoaderTest01对应的类加载器记录下来
          ClassLoader classLoader = MyClassLoader.class.getClassLoader();
         
         //倒序打印类加载器的层次(对于classLoader来说)
           while(classLoader!=null){
                        System.out.println(classLoader.getClass().getName());
                    classLoader = classLoader.getParent();
           }
                System.out.println(classLoader);*/
               
         /**
               * *——————————————————————————————————
            * * ——————————————————————————————————
           * * ——————————————————————————————————
           * * ——————————————————————————————————
         
         */
               //正常情况下,可以打印出来ClassLoaderAttachment的覆盖的toString方法
                //但需要让ClassLoaderAttachment的方法打印不出来,则需要继续操作
              
         //定义源路径(包括文件名)
           String srcPath = args[0];
                //定义目标目录(不包括文件名)
         String destDir = args[1];
                //将源文件的完整名称传入输入流
         FileInputStream fis = new FileInputStream(srcPath);
              //获取目标文件的文件名:用源文件路径的最后一个下划线往后切出来子串
               String destFileName = srcPath.substring(srcPath.lastIndexOf("\\")+1);
            //获取目标文件的路径:用目录和下划线加上获取的文件名
              String destPath = destDir+"\\"+destFileName;
             //用输出流输出
         FileOutputStream fos = new FileOutputStream(destPath);
           fis.close();
             fos.close();
            
         
}
        
//定义加密方法,异或
      private static void cypher(InputStream ips,OutputStream ops) throws Exception{
           int b = -1;
              while((b = ips.read())!=-1){
                     ops.write(b ^ 0xff);//异或一下,解密也是这个密钥
              }      
}
        
@Override
        //重写findClass方法,如果继承类加载器则必须重写
    protected Class<?> findClass(String name) throws ClassNotFoundException {
          // TODO Auto-generated method stub
               //获取class文件的完整文件路径
               String classFileName = classDir+"\\"+".class";
           
         try {
                    //输入流
                    FileInputStream fis = new FileInputStream(classFileName);
                        //输出流用字节数组输出
                     ByteArrayOutputStream bos = new ByteArrayOutputStream();
                 //再用一次加密算法,相当于解密
                 cypher(fis,bos);
                 //关闭输入流
                  fis.close();
                     //新建缓冲区
                  byte[] bytes = bos.toByteArray();
                        //将字节数组组合成一个class文件返回,用defineClass方法
                     return defineClass(bytes, 0, bytes.length);
              } catch (Exception e) {
                  // TODO Auto-generated catch block
                       e.printStackTrace();
             }
               
         
         
         
         return super.findClass(name);
    }
        private String classDir;
public MyClassLoader(){
         
}
        public MyClassLoader(String classDir) {
          super();
         this.classDir = classDir;
        }
        


}





作者: 温晓慧    时间: 2015-1-26 21:18
这个好,这一部分听得真的是很难理解,谢谢分享
作者: 浅木头    时间: 2015-1-27 13:20
温晓慧 发表于 2015-1-26 21:18
这个好,这一部分听得真的是很难理解,谢谢分享

谢谢支持,我也是刚学完没多长时间,如果有什么错误还望指教~




欢迎光临 黑马程序员技术交流社区 (http://bbs.itheima.com/) 黑马程序员IT技术论坛 X3.2