/**
* 用於保存字段原始名稱和加密後的名稱
* @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;
}
}
/**
* 用於保存方法的原始名稱和加密名稱
* @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',
};
}
}
第三部分測試代碼:
/**
* 用來加密class文件
*
* @param args
*/
public static void main(String[] args) {
try {
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;
// InvocationHandler
A00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 = h;
}
return C00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000;
}
String name) {
C00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 = name;
}
return D00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000;
}
int age) {
D00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 = age;
}
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);
}
Object array[] = new Object[3];
array[0] = name1;
array[1] = name2;
array[2] = name3;
A00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
.invoke(this, "start", null, array);
}
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 static Method B00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000;
private String C00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000;
private int D00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000;
try {
B00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 = Class
.forName("com.test.bean.Student").getMethod("end",
new Class[0]);
} catch (Exception e) {
e.printStackTrace();
}
}
}