Android中使用Java开源库Javassist动态创建字节码的学习研究

研究该内容想要达到的目的:

通过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);
}
}

最后,使用DexClassLoad加载.dex文件。利用反射调用!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值