基於ASM開發的一個關於class文件加密程序,可對整個jar進行加密且不影響資源文件

本插件利用ASM對class文件可修改功能而實現的下面展現一下實現的源代碼:
第一部分輔助代碼:
package com.test.encypt;
/**
* 用於保存字段原始名稱和加密後的名稱
* @author 陳雙
*
*/
public class Field {
private String oldName;
private String newName;
public Field()
{
 
}
public Field(String oldName,String newName)
{
  this.oldName=oldName;
  this.newName=newName;
}
public String getOldName() {
  return oldName;
}
public void setOldName(String oldName) {
  this.oldName = oldName;
}
public String getNewName() {
  return newName;
}
public void setNewName(String newName) {
  this.newName = newName;
}
}
package com.test.encypt;
/**
* 用於保存方法的原始名稱和加密名稱
* @author 陳雙
*
*/
public class Method {
private String oldName;
private String newName;
public Method()
{
 
}
public Method(String oldName,String newName)
{
  this.oldName=oldName;
  this.newName=newName;
}
public String getOldName() {
  return oldName;
}
public void setOldName(String oldName) {
  this.oldName = oldName;
}
public String getNewName() {
  return newName;
}
public void setNewName(String newName) {
  this.newName = newName;
}
}

第二部分核心代碼:

package com.test.encypt;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.jar.JarEntry;
import java.util.jar.JarOutputStream;

import org.objectweb.asm.ClassAdapter;
import org.objectweb.asm.ClassReader;
import org.objectweb.asm.ClassVisitor;
import org.objectweb.asm.ClassWriter;
import org.objectweb.asm.FieldVisitor;
import org.objectweb.asm.MethodAdapter;
import org.objectweb.asm.MethodVisitor;

/**
 * 對class文件中的字段全部加密(子類、父類、接口、父接口以及類的成員關係引用),字段對應的get、set方法加密,其他方法保留。
 * @author 陳雙
 * 目前應用範圍只限於一個jar的加密與所有未加密jar包應用,以後可能擴展到加密jar與加密jar相互應用
 * 操作步驟
 * 1.新建source(原目錄)將待加密jar包解壓到source下,
 * 2.新建dest(目標目錄)
 * 3.調用加密方法如:EncryptTool.startEncypt(new File("D:\\source"),new File("D:\\dest"), "test.jar", 150);其中test.jar是加密後的jar包名稱
 */
public class EncryptTool {
   private static char[] values;//數據字典
   private static Map<String,Map<String,String>> data=new HashMap<String,Map<String,String>>();//存放所有的變量
   private static  List<File> sourceList=new ArrayList<File>();//源class文件列表
   private static Map<String,File> mappingOfOld=new HashMap<String,File>();//源文件的映射,key是類的全路徑,value是File
   private static Map<String,File> mappingOfNew=new HashMap<String,File>();//新文件的映射,key是類的全路徑,value是File
   private static Map<String,String> mappingClass=new HashMap<String,String>();//子類與父類的映射,子類爲key,父類爲value
   private static int len;//加密長度
   public static void startEncypt(File sourceDir,File destDir,String destJarName,int length) throws IOException
   {
    len=length;
    List<File> newList=new ArrayList<File>();//新class文件列表
    List names=new ArrayList();//類文件的全路徑
    getAll(sourceDir,sourceList,newList,names,sourceDir.getPath(),destDir.getPath());
    for(int i=0;i<sourceList.size();i++)
    {
     File classFile=sourceList.get(i);
     File newClassFile=newList.get(i);
     encrypt(classFile,newClassFile,length);//開始加密
    }
    JarOutputStream out=new JarOutputStream(new FileOutputStream(new File(destDir.getPath()+"\\"+destJarName)));
  for(int i=0;i<newList.size();i++)
  {
    jar(out,(File)newList.get(i),(String)names.get(i));
  }
  deleteDir(destDir);
  out.close();
   }
   private static void jar(JarOutputStream out, File file, String base)
   throws IOException {
    out.putNextEntry(new JarEntry(base));
    FileInputStream in = new FileInputStream(file);
    byte[] buffer = new byte[1024];
    int count = in.read(buffer);
    while (count != -1) {
     out.write(buffer, 0, count);
     count = in.read(buffer);
    }
    in.close();
   }
   private static void deleteDir(File dir)
  {
   if(dir.isDirectory())
   {//判斷是否是目錄
    File[] files=dir.listFiles();
    for(File f:files)
    {
     deleteDir(f);
    }
    dir.delete();
   }
   else
   {//刪除文件
    if(!dir.getName().endsWith(".jar"))
    {
     dir.delete();
    }
   }
  }
   /**
    * 獲取所有的class文件
    * @param sourceDir源目錄
    * @param sourceList源class文件列表
    * @param newList新class文件列表
    * @param names Class文件的全路徑
    * @param sourceDirName源目錄名
    * @param newDirName新目錄名
    * @throws IOException
    */
   private static void getAll(File sourceDir,List sourceList,List newList,List names,String sourceDirName,String newDirName) throws IOException
   {
    if(sourceDir.isDirectory())
    {
     File[] files=sourceDir.listFiles();
     for(int i=0;i<files.length;i++)
     {
      File f=files[i];
      getAll(f,sourceList,newList,names,sourceDirName,newDirName);
     }
    }
    else
    {
     sourceList.add(sourceDir);
     //構建新的class文件
     File newClass=new File(newDirName+sourceDir.getPath().substring(sourceDirName.length()));
     String path=newClass.getPath();
     path=path.substring(0,path.lastIndexOf("\\"));
     File dirs=new File(path);
     dirs.mkdirs();
     newList.add(newClass);
     String dirName=sourceDirName;
     String temp=null;
     if(dirName!=null)
     {
      dirName=dirName.replace("\\", "/");
      temp=sourceDir.getPath().replace("\\", "/").substring(dirName.length()+1);
      names.add(temp);
     }
     if(sourceDir.getName().endsWith(".class"))
     {
      String newName=temp.substring(0,temp.lastIndexOf("."));
      mappingOfOld.put(newName, sourceDir);
      mappingOfNew.put(newName, newClass);
     }
  
    }
   }
   /**
    * 加密
    * @param classFile原class文件
    * @param newClassFile新class文件
    * @param length 密文長度
    * @throws IOException
    */
   private static void encrypt(File classFile,File newClassFile,int length) throws IOException
   {
    byte[] b=null;
    FileInputStream in=new FileInputStream(classFile);
    if(classFile.getName().endsWith(".class"))
    {
     ClassReader cr=new ClassReader(in);
     if(data.containsKey(cr.getClassName()))
     {//如果已經存在就退出
      return;
     }
     String[] interfaces=cr.getInterfaces();//接口
     if(interfaces!=null&&interfaces.length>0)
     {
      for(int i=0;i<interfaces.length;i++)
      {
       File interfaceFile=mappingOfOld.get(interfaces[i]);
       File interfaceNewFile=mappingOfNew.get(interfaces[i]);
       if(interfaceFile!=null&&interfaceNewFile!=null)
       {
        encrypt(interfaceFile, interfaceNewFile, length);//遞歸到頂層接口
       }
      }
     }
     String superName=cr.getSuperName();//超類
     if(!superName.equals("java/lang/Object"))
     {
      File superFile=mappingOfOld.get(superName);
      File superNewFile=mappingOfNew.get(superName);
      if(superFile!=null&&superNewFile!=null)
      {
       mappingClass.put(cr.getClassName(), superName);//添加子類與父類的映射
       encrypt(superFile, superNewFile, length);//遞歸到頂層超類
      }
     }
     ClassWriter cw=new ClassWriter(0);
     List fields=new ArrayList();//字段名列表
     List methods=new ArrayList();//方法名列表
     CommonClassAdapter classAdapter=new CommonClassAdapter(cw,fields,methods,length,cr.getClassName());
     cr.accept(classAdapter,0);//過濾
     cw.visitEnd();
     b=cw.toByteArray();
    }
    else
    {
     b=new byte[in.available()];
     in.read(b);
     in.close();
    }
    //輸出新class文件
    FileOutputStream out=new FileOutputStream(newClassFile);
    out.write(b);
    out.close();
   }
   /**
    * 從父類中查找字段或者方法,當且僅當子類使用this調用父類的字段或者方法時
    * @param owner 子類
    * @param name 字段或者方法舊名稱
    * @return 字段或者方法新名稱
    */
   private static String findSuperName(String owner,String name)
   {
    String superName=mappingClass.get(owner);
    if(superName!=null)
    {//父類存在
     Map<String,String> element=data.get(superName);
     String newName=element.get(name);//新名稱
     if(newName==null)
     {//新名稱爲空就繼續遞歸到頂層父類
      return findSuperName(superName, name);
     }
     else
     {
      return newName;
     }
    }
    return null;
   }
   /**
    * 查找父類的層級結構座標
    * @param name 子類名稱
    * @param index 默認索引
    * @return
    */
   private static int findSuperIndex(String name,int index)
   {
    String superName=mappingClass.get(name);
    if(superName!=null)
    {//父類存在
     return findSuperIndex(superName,++index);
    
    }
    return index;
   }
   static class CommonMethodAdapter extends MethodAdapter
   {
    private String name;//包名+類名
    private List fields;//字段名列表
    private List methods;//方法名列表
 public CommonMethodAdapter(MethodVisitor methodvisitor,List fields,List methods,String name) {
  super(methodvisitor);
  this.fields=fields;
  this.methods=methods;
  this.name=name;
  // TODO Auto-generated constructor stub
 }

 public void visitFieldInsn(int insn, String owner, String name, String desc) {
  // TODO Auto-generated method stub
  boolean flag=false;
  for(int i=0;i<this.fields.size();i++)
  {
   Field field=(Field)this.fields.get(i);
   if(field.getOldName().equals(name))
   {//替換被調用的字段名稱
    super.mv.visitFieldInsn(insn, owner, field.getNewName(), desc);
    flag=true;
    break;
   }
  }
  if(!owner.equals(this.name)&&data.containsKey(owner))
  {
   Map<String,String> element=data.get(owner);
   if(element!=null&&element.get(name)!=null)
   {
    super.mv.visitFieldInsn(insn, owner, element.get(name), desc);
    flag=true;
   }
  }
  else if(!owner.equals(this.name)&&!data.containsKey(owner))
  {//引用外部類
   File classFile=mappingOfOld.get(owner);
   File newClassFile=mappingOfNew.get(owner);
   if(classFile!=null&&newClassFile!=null)
   {
    try {
     encrypt(classFile, newClassFile, len);
     Map<String,String> element=data.get(owner);
     if(element!=null&&element.get(name)!=null)
     {
      super.mv.visitFieldInsn(insn, owner, element.get(name), desc);
      flag=true;
     }
    } catch (IOException e) {
     // TODO Auto-generated catch block
     e.printStackTrace();
    }
   }
  }
  if(!flag)
  {//其他類中的字段
   //引用父類
   String newName=EncryptTool.findSuperName(owner, name);
   if(newName!=null)
   {
    super.mv.visitFieldInsn(insn, owner, newName, desc);
   }
   else
   {
    super.mv.visitFieldInsn(insn, owner, name, desc);
   }
  }
 }
 public void visitMethodInsn(int insn, String owner, String name, String desc) {
  // TODO Auto-generated method stub
  boolean flag=false;
  for(int i=0;i<methods.size();i++)
  {
   Method method=(Method)this.methods.get(i);
   if(method.getOldName().equals(name))
   { //檢查get、set方法是否被調用並且替換被調用的get、set方法名稱
    super.mv.visitMethodInsn(insn, owner, method.getNewName(), desc);
    flag=true;
    break;
   }
  }
  if(!owner.equals(this.name)&&data.containsKey(owner))
  {
   Map<String,String> element=data.get(owner);
   if(element!=null&&element.get(name)!=null)
   {
    super.mv.visitMethodInsn(insn, owner,element.get(name), desc);
    flag=true;
   }
  }
  else if(!owner.equals(this.name)&&!data.containsKey(owner))
  {//引用外部類
   File classFile=mappingOfOld.get(owner);
   File newClassFile=mappingOfNew.get(owner);
   if(classFile!=null&&newClassFile!=null)
   {
    try {
     encrypt(classFile, newClassFile, len);
     Map<String,String> element=data.get(owner);
     if(element!=null&&element.get(name)!=null)
     {
      super.mv.visitMethodInsn(insn, owner,element.get(name), desc);
      flag=true;
     }
    } catch (IOException e) {
     // TODO Auto-generated catch block
     e.printStackTrace();
    }
   }
  }
  if(!flag)
  { //其他方法調用
   String newName=EncryptTool.findSuperName(owner, name);
   if(newName!=null)
   {
    super.mv.visitMethodInsn(insn, owner, newName, desc);
   }
   else
   {
    super.mv.visitMethodInsn(insn, owner, name, desc);
   }
  }
 }
 
   }
   static class CommonClassAdapter extends ClassAdapter
   {
 private String name;//包名+類名
    private List fields;//字段名列表
    private List methods;//方法名列表
    int index;//字段順序
    int length;//密文長度
 public CommonClassAdapter(ClassVisitor classvisitor,List fields,List methods,int length,String name) {
  super(classvisitor);
  this.fields=fields;
  this.methods=methods;
  this.length=length;
  this.index=0;
  this.name=name;
  // TODO Auto-generated constructor stub
 }
   
 public FieldVisitor visitField(int access, String name, String desc, String signature,
   Object value) {
  // TODO Auto-generated method stub
  String newName=EncryptTool.getNewName(index,length,this.name); //獲取新的字段名稱
  fields.add(new Field(name,newName));//將源字段名稱和新字段名稱保存到Field中
  Map element=null;//類的字段和方法名稱的全部信息
  //註冊字段名稱
  if(data.get(this.name)==null)
  {
      element=new HashMap<String,String>();
   element.put(name, newName);//原名稱爲key,新名稱爲value
   data.put(this.name, element);//類的完全名稱爲key,以類的字段名稱和方法名稱爲Map的對象進行存儲
  }
  else
  {
   element=data.get(this.name);
   element.put(name, newName);
  }
  index++;
  return super.cv.visitField(access, newName, desc, signature, value);
 }

 public MethodVisitor visitMethod(int access, String name, String desc,
   String signature, String[] exceptions) {
  // TODO Auto-generated method stub
  if(name.startsWith("set")||name.startsWith("get"))
  {//是否是get、set方法
   String str=name.substring(3).toLowerCase();
   String prefix=name.substring(0,3);
   for(int i=0;i<this.fields.size();i++)
   {
    Field field=(Field)this.fields.get(i);
    if(field.getOldName().equals(str))
    {//檢查set、get對應的字段 
     String newName=prefix+field.getNewName();
     methods.add(new Method(name,newName));
     Map element=null;//類的字段和方法名稱的全部信息
     //註冊字段名稱
     if(data.get(this.name)==null)
     {
         element=new HashMap<String,String>();
      element.put(name, newName);//原名稱爲key,新名稱爲value
      data.put(this.name, element);//類的完全名稱爲key,以類的字段名稱和方法名稱爲Map的對象進行存儲
     }
     else
     {
      element=data.get(this.name);
      element.put(name, newName);
     }
     //替換get、set方法名稱
     return new CommonMethodAdapter(super.cv.visitMethod(access, newName, desc, signature, exceptions),this.fields,this.methods,this.name);
    }
   }
  }
  if(data.get(this.name)==null)
  {
   data.put(this.name, new HashMap<String,String>());
  }
  return new CommonMethodAdapter(super.cv.visitMethod(access, name, desc, signature, exceptions),this.fields,this.methods,this.name);
 }
   
   }
   /**
    * 獲取字段對應密鑰
    * @param index字段順序
    * @param length密鑰長度
    * @return
    */
   public static String getNewName(int index,int length,String name)
   {
    int len=values.length;
    char[] content=new char[length];//密文內容
    int rs=findSuperIndex(name,0);//查找父類座標
    if(index>=len)
    { //如果字段順序長度大於密碼數據的長度
     int count=index/len;//求商
     int mod=index%len;//求餘數
     for(int i=0;i<count;i++)
     {//密文映射
      content[i]=values[i];
     }
     content[count]=values[mod];//密文映射
     for(int j=count+1;j<length;j++)
     {
      content[j]='0';//補0操作
     }
   
    }
    else
    {//如果字段順序長度小於密碼數據的長度
     content[0]=values[index];//密文映射
     for(int i=1;i<length;i++)
     {
      content[i]='0';//補0操作
     }
    }
    String temp=new String(content);
    if(rs>-1&&rs<10)
    {//0-9之間
     return temp.substring(0, length-7)+"super0"+rs;
    }
    else if(rs>=0&&rs<100)
    {//10-99
     return temp.substring(0, length-7)+"super"+rs;
    }
    return temp;
   }
   static
   {
    values=new char[]{'A','B','C','D','E','F','G','H','I','J','K','L','M',
               'N', 'O','P','Q','R','S','T','U','V','W','X','Y','Z',
               };
   }
}

第三部分測試代碼:

package com.test.encypt;
import java.io.File;
public class Test {
  /**
     * 用來加密class文件
      * 
      * @param args
      */ 
     public static void main(String[] args) { 
      try {
//把jar包放到source下解壓,一次只能放一個jar包,test.jar是dest目錄下加密之後的新的jar包名稱,運行完成之後dest中將會產生一個test.jar
//本程序必須依賴asm-all-3.0.jar
  EncryptTool.startEncypt(new File("D:\\source"), new File("D:\\dest"),"test.jar", 150) ;
   } catch (Exception e) {
    // TODO Auto-generated catch block
    e.printStackTrace();
   }
     }
   

第四部分運行效果展示:

package com.test.bean;
import java.lang.reflect.Method;
// Referenced classes of package com.test.bean:
//            InvocationHandler
public class Helloworld {
public Helloworld(InvocationHandler h) {
  A00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 = h;
}
public String getC00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000() {
  return C00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000;
}
public void setC00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000(
   String name) {
  C00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 = name;
}
public int getD00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000() {
  return D00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000;
}
public void setD00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000(
   int age) {
  D00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 = age;
}
public Object start(Object name1, Object name2, String name3) {
  Object array[] = new Object[3];
  array[0] = name1;
  array[1] = name2;
  setC00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000(name3);
  name3 = getC00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000();
  array[2] = name3;
  return A00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
    .invoke(this, "start", null, array);
}
public void end(Object name1, Object name2, String name3) {
  Object array[] = new Object[3];
  array[0] = name1;
  array[1] = name2;
  array[2] = name3;
  A00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
    .invoke(this, "start", null, array);
}
public char hello(int age, String name, String desc) {
  String s = "a";
  String s1 = "b";
  String s2 = "c" + s1;
  String s3 = s + s1;
  String str = new String("str");
  String s4 = str + "d";
  String s5 = str + s1;
  int i = 1;
  Object o[] = new Object[10];
  o[0] = name;
  o[1] = desc;
  o[2] = Integer.valueOf(age);
  o[3] = Short.valueOf((short) 1);
  o[4] = Byte.valueOf((byte) 1);
  o[5] = Character.valueOf('c');
  o[6] = Float.valueOf(1.0F);
  o[7] = Long.valueOf(12L);
  o[8] = Double.valueOf(122D);
  o[9] = Boolean.valueOf(false);
  return A00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
    .invoke(this, s, null, new Object[] { "11", "22" }).toString()
    .toCharArray()[0];
}
private InvocationHandler A00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000;
private static Method B00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000;
private String C00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000;
private int D00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000;
static {
  try {
   B00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 = Class
     .forName("com.test.bean.Student").getMethod("end",
       new Class[0]);
  } catch (Exception e) {
   e.printStackTrace();
  }
}
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章