/**
* 用于保存字段原始名称和加密后的名称
* @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();
}
}
}