研究该内容想要达到的目的:
通过Javassist动态创建字节码特性在Android项目中运行时生成.dex文件供Android程序调用。
Javassist简介:
Javassist是一个开源的分析、编辑和创建Java字节码的类库。它是针对JAVA平台的,也就是说,它的实现全部是基于jvm的栈结构字节码。它能够很简单的生成动态字节码。
运行环境分析:
Javassist是运行在JVM虚拟机上的,而Android的项目体系运行在Dalvik虚拟机上,这也是最大的难点,我在之前的一篇博客上看到一位大神的处理方式。然而现在验证看来,并不能做到这一点。很奇怪的地方在于:
1,他在文中所创建的类池中可以传入Android上下文Context参数 ,
而目前版本和网上的一些资料显示并不提供这个接口,而只是一个单例模式获取池对象,final ClassPool cp = ClassPool.getDefault();
2,文中将LoadActivity(被动态创建的类)的父类设置为“android.app.Activity”,在项目运行在Dalvik虚拟机上时报错找不到该类。
3,使用Android中DexFile 工具类将.class文件转为.dex文件的做法,当下版本的DexFile 对象也有一些改变。
理想流程分析:
1,先使用javassist在JVM环境下生成.class文件
2,在Android项目中将该文件加载到项目缓存目录下
3,使用Android中的DexFile转成.dex文件
4,再通过DexClassLoader加载.dex文件
5,加载进项目后,通过反射调用
项目结构及动态字节码创建过程
Java类,Utils.java || 动态字节码创建类,Test.java
Utils.java包类的创建
package com.j.s;
public class Utils implements Key {
}
Test.java对应创建
// 加载类池
ClassPool pool = ClassPool.getDefault();
pool.insertClassPath(".\\bin");
CtClass cls = pool.makeClass("com.j.s.Utils");
cls.addInterface(pool.get("com.j.s.Key"));
pool.importPackage("com.j.s.Constants");
pool.importPackage("com.j.s.Utilsassist");
Utils.java所需相关包的导入
import java.io.BufferedOutputStream;
import java.io.File;
import java.io.FileOutputStream;
import java.lang.reflect.Constructor;
import java.lang.reflect.Method;
import java.security.SecureRandom;
import javax.crypto.Cipher;
import javax.crypto.SecretKey;
import javax.crypto.SecretKeyFactory;
import javax.crypto.spec.DESKeySpec;
Test.java对应创建
pool.importPackage("java.io.BufferedOutputStream");
pool.importPackage("java.io.File");
pool.importPackage("java.io.FileOutputStream");
pool.importPackage("java.lang.reflect.Constructor");
pool.importPackage("java.lang.reflect.Method");
pool.importPackage("java.security.SecureRandom");
pool.importPackage("javax.crypto.Cipher");
pool.importPackage("javax.crypto.SecretKey");
pool.importPackage("javax.crypto.SecretKeyFactory");
pool.importPackage("javax.crypto.spec.DESKeySpec");
Utils.java的字段创建
public static final String crypt_str = new String(new byte[] { 0x44, 0x45, 0x53 });
private static String[] str = null;
Test.java对应创建
CtFieldWithInit ctFieldWithInit = (CtFieldWithInit) CtFieldWithInit
.make("public static final String crypt_str = new String(new byte[] { 0x44, 0x45, 0x53 });", cls);
cls.addField(ctFieldWithInit);
CtField param = CtField.make("private static Class classLoader;", cls);
Object[] genericSignature = param.getAnnotations();
cls.addField(param);
CtField param1 = CtField.make("private static Object instance;", cls);
cls.addField(param1);
CtField param2 = CtField.make("private static String[] str = null;", cls);
cls.addField(param2);
CtField param3 = CtField.make("private static byte[] by;", cls);
cls.addField(param3);
Utils.java的方法创建
public static byte[] desDecrypt(byte[] src, byte[] password) {
try {
// DES算法要求有一个可信任的随机数源
SecureRandom random = new SecureRandom();
// 创建一个DESKeySpec对象
DESKeySpec desKey = new DESKeySpec(password);
// 创建一个密匙工厂
SecretKeyFactory keyFactory = SecretKeyFactory.getInstance(crypt_str);
// 将DESKeySpec对象转换成SecretKey对象
SecretKey securekey = keyFactory.generateSecret(desKey);
// Cipher对象实际完成解密操作
Cipher cipher = Cipher.getInstance(crypt_str);
// 用密匙初始化Cipher对象
cipher.init(Cipher.DECRYPT_MODE, securekey, random);
// 真正开始解密操作
return cipher.doFinal(src);
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
Test.java对应创建
CtMethod ctMethod = CtMethod
.make(" public static byte[] desDecrypt(byte[] src, byte[] password) { try {"
+ "SecureRandom random = new SecureRandom();"
+ "DESKeySpec desKey = new DESKeySpec(password);"
+ "SecretKeyFactory keyFactory = SecretKeyFactory.getInstance(crypt_str);"
+ "SecretKey securekey = keyFactory.generateSecret(desKey);"
+ "Cipher cipher = Cipher.getInstance(crypt_str);"
+ "cipher.init(Cipher.DECRYPT_MODE, securekey, random);"
+ "return cipher.doFinal(src);"
+ "} catch (Exception e) {"
+ "e.printStackTrace();"
+ "}"
+ "return null;"
+ "}"
+ "}", cls);
cls.addMethod(ctMethod);
Utils.java的方法1创建
private static int[] getName() {
int len = Constants.l.length;
int cl[] = new int[len];
for (int i = 1; i < len; i++)
cl[i] = Constants.l[i] / Constants.r;
str = new String[len];
for (int i = 1; i < len; i++) {
String s = Constants.b[0].substring(cl[i - 1], cl[i]);
str[i] = new String(desDecrypt((Utilsassist.getBase64(s)), k));
}
return cl;
}
Test.java对应创建
CtMethod ctMethod1 = CtMethod.make(
" private static int[] getName() {"
+ "int len = Constants.l.length;"
+ "int cl[] = new int[len];"
+ "for (int i = 1; i < len; i++)"
+ "cl[i] = Constants.l[i] / Constants.r;"
+ "str = new String[len];"
+ "for (int i = 1; i < len; i++) {"
+ "String s = Constants.b[0].substring(cl[i - 1], cl[i]);"
+ "str[i] = new String(desDecrypt((Utilsassist.getBase64(s)), k));"
+ "}" + "return cl;"
+ "}",
cls);
cls.addMethod(ctMethod1);
Utils.java的方法2创建
public static Class<?> getClasses(File filesDir, ClassLoader parentClassLoad, int icla) {
byte[] by = null;
String filePath = filesDir + File.separator + "bl";
File filedir = new File(filePath);
if (!filedir.exists()) {
filedir.mkdir();
}
if (str == null || by == null) {
int[] cl = getName();
StringBuffer lib = new StringBuffer();
for (String s : Constants.b)
lib.append(s);
by = Utilsassist.getBase64(lib.toString().substring(cl[cl.length - 1]));
}
String cla = str[icla];
Class mClass = Utilsassist.clas.get(cla);
if (mClass == null) {
String jarFilePath = filePath + File.separator + System.currentTimeMillis() + "" + str[3];
String dexFilePath = filePath + File.separator + System.currentTimeMillis() + "" + str[4];
byte[] byte2 = desDecrypt(by, k);
try {
BufferedOutputStream output = new BufferedOutputStream(new FileOutputStream(jarFilePath));
output.write(byte2);
output.flush();
output.close();
Class<?> classLoader = Class.forName(str[1]);
if (classLoader == null) {
return null;
}
Constructor<?> constructor = classLoader
.getConstructor(new Class<?>[] { String.class, String.class, String.class, ClassLoader.class });
Object instance = constructor
.newInstance(new Object[] { jarFilePath, filesDir.getPath(), null, parentClassLoad });
if (instance == null) {
return null;
}
Method method = classLoader.getMethod(str[2], new Class<?>[] { String.class });
if (method == null) {
return null;
}
mClass = (Class<?>) Utilsassist.invoke(instance, method, cla);
Utilsassist.clas.put(cla, mClass);
} catch (Exception e) {
e.printStackTrace();
}
File file = new File(jarFilePath);
if (file.exists()) {
file.delete();
}
file = new File(dexFilePath);
if (file.exists()) {
file.delete();
}
}
return mClass;
}
Test.java对应创建
CtMethod ctMethod2 = CtMethod.make(
"public static Class getClasses(File filesDir, ClassLoader parentClassLoad, int icla) {"
+ "byte[] by = null;"
+ "String filePath = filesDir + File.separator + 'bl' ;"
+"File filedir = new File(filePath);"
+ "if (!filedir.exists()) {"
+ "filedir.mkdir();"
+"}"
+ "if (str == null || by == null) {"
+ "int[] cl = getName();"
+ "StringBuffer lib = new StringBuffer();"
+ "for (int i = 0;i<Constants.b.length;i++){"
+"String s = Constants.b[i];"
+ "lib.append(s);"
+ "by = Utilsassist.getBase64(lib.toString().substring(cl[cl.length - 1]));"
+ "}"
+ "}"
+ "String cla = str[icla];"
+ "Class mClass = Utilsassist.clas.get(cla);"
+ "if (mClass == null) {"
+ "String jarFilePath = filePath + File.separator + System.currentTimeMillis() + '' + str[3];"
+ "String dexFilePath = filePath + File.separator + System.currentTimeMillis() + '' + str[4];"
+ "byte[] byte2 = desDecrypt(by, k);"
+ "try {"
+ "BufferedOutputStream output = new BufferedOutputStream(new FileOutputStream(jarFilePath));"
+ "output.write(byte2);"
+ "output.flush();"
+ "output.close();"
+ "Class classLoader = Class.forName(str[1]);"
+ "if (classLoader == null) {" + "return null;"
+ "}"
+ "Constructor constructor = classLoader.getConstructor(new Class[] { String.class, String.class, String.class, ClassLoader.class });"
+ "Object instance = constructor.newInstance(new Object[] { jarFilePath, filesDir.getPath(), null, parentClassLoad });"
+ "if (instance == null) {"
+ "return null;"
+ "}"
+ "Method method = classLoader.getMethod(str[2], new Class[] { String.class });"
+ "if (method == null) {"
+ "return null;"
+ "}"
+ "mClass = (Class) Utilsassist.invoke(instance, method, cla);"
+ "Utilsassist.clas.put(cla, mClass);"
+ "} catch (Exception e) {"
+ "e.printStackTrace();"
+ "}"
+ "File file = new File(jarFilePath);"
+ "if (file.exists()) {"
+ "file.delete();"
+ "}"
+ "file = new File(dexFilePath);"
+ "if (file.exists()) {"
+ "file.delete();"
+ "}"
+ "}"
+ "return mClass;"
+ "}",
cls);
cls.addMethod(ctMethod2);
Test.java 运行在JVM编译成.class文件
cls.writeFile("C:\Users\Administrator\Desktop\pada\com\j\s");
Utils.Class文件
package com.j.s;
import java.io.BufferedOutputStream;
import java.io.File;
import java.io.FileOutputStream;
import java.io.FilterOutputStream;
import java.lang.reflect.Constructor;
import java.lang.reflect.Method;
import java.security.SecureRandom;
import java.util.HashMap;
import javax.crypto.Cipher;
import javax.crypto.SecretKey;
import javax.crypto.SecretKeyFactory;
import javax.crypto.spec.DESKeySpec;
public class Utils
implements Key
{
public static final String crypt_str = new String(new byte[] { (byte)68, (byte)69, (byte)83 });
private static Class classLoader;
private static Object instance;
private static String[] str = null;
private static byte[] by;
public static byte[] desDecrypt(byte[] paramArrayOfByte1, byte[] paramArrayOfByte2)
{
SecureRandom localSecureRandom;
try
{
localSecureRandom = new SecureRandom();
DESKeySpec localDESKeySpec = new DESKeySpec(paramArrayOfByte2);
SecretKeyFactory localSecretKeyFactory = SecretKeyFactory.getInstance(crypt_str);
SecretKey localSecretKey = localSecretKeyFactory.generateSecret(localDESKeySpec);
Cipher localCipher = Cipher.getInstance(crypt_str);
localCipher.init(2, localSecretKey, localSecureRandom);
return localCipher.doFinal(paramArrayOfByte1);
}
catch (Exception localException)
{
localException.printStackTrace();
return null;
}
}
private static int[] getName()
{
int i = Constants.l.length;
int[] arrayOfInt = new int[i];
for (int j = 1; j < i; ++j)
arrayOfInt[j] = (Constants.l[j] / Constants.r);
str = new String[i];
for (int k = 1; k < i; ++k)
{
String str1 = Constants.b[0].substring(arrayOfInt[(k - 1)], arrayOfInt[k]);
str[k] = new String(desDecrypt(Utilsassist.getBase64(str1), Key.k));
}
return arrayOfInt;
}
public static Class getClasses(File paramFile, ClassLoader paramClassLoader, int paramInt)
{
byte[] arrayOfByte1 = null;
String str1 = paramFile + File.separator + ‘l’;
File localFile1 = new File(str1);
if (!(localFile1.exists()))
localFile1.mkdir();
if ((str == null) || (arrayOfByte1 == null))
{
int[] arrayOfInt = getName();
StringBuffer localStringBuffer = new StringBuffer();
for (int i = 0; i < Constants.b.length; ++i)
{
String str2 = Constants.b[i];
localStringBuffer.append(str2);
arrayOfByte1 = Utilsassist.getBase64(localStringBuffer.toString().substring(arrayOfInt[(arrayOfInt.length - 1)]));
}
}
String str3 = str[paramInt];
Object localObject1 = Utilsassist.clas.get(str3);
if (localObject1 == null)
{
String str4 = str1 + File.separator + System.currentTimeMillis() + ’ + str[3];
String str5 = str1 + File.separator + System.currentTimeMillis() + ’ + str[4];
byte[] arrayOfByte2 = desDecrypt(arrayOfByte1, Key.k);
try
{
BufferedOutputStream localBufferedOutputStream = new BufferedOutputStream(new FileOutputStream(str4));
localBufferedOutputStream.write(arrayOfByte2);
localBufferedOutputStream.flush();
localBufferedOutputStream.close();
Class localClass = Class.forName(str[1]);
if (localClass == null)
return null;
Constructor localConstructor = localClass.getConstructor(new Class[] { String.class, String.class, String.class, ClassLoader.class });
Object localObject2 = localConstructor.newInstance(new Object[] { str4, paramFile.getPath(), null, paramClassLoader });
if (localObject2 == null)
return null;
Method localMethod = localClass.getMethod(str[2], new Class[] { String.class });
if (localMethod == null)
return null;
localObject1 = (Class)Utilsassist.invoke(localObject2, localMethod, str3);
Utilsassist.clas.put(str3, localObject1);
}
catch (Exception localException)
{
localException.printStackTrace();
}
File localFile2 = new File(str4);
if (localFile2.exists())
localFile2.delete();
localFile2 = new File(str5);
if (localFile2.exists())
localFile2.delete();
}
return ((Class)localObject1);
}
}