目录
使用方法
1.编译使用插件
这里自定义了一个插件用来对字节码进行操作
首先我们需要找到这个Gradle任务,双击进行编译打包
打包成功后会生成如下目录
然后我们需要在项目的gradle文件中进行引用
然后在application的model下的gradle中应用插件
2.使用ASM清空特定方法体
这里在Activity中加了一个点击事件,这次是将点击事件的方法体进行清除
这里我们在插件的MethodEmptyBodyVisitor中修改
首先在visitMethod函数中找到OnClickListener的onClick方法(通过判断函数签名,函数名等找到特定函数)
@Overridepublic MethodVisitor visitMethod(int access, String name, String descriptor, String signature, String[] exceptions) {MethodVisitor mv = super.visitMethod(access, name, descriptor, signature, exceptions);String mInterfaceStr = "";if(mInterface != null && mInterface.length > 0){for(int i = 0 ; i < mInterface.length ; i++){mInterfaceStr += mInterface[i];}}if (mv != null && name.contains("onClick") && mInterfaceStr.contains("android/view/View$OnClickListener") && descriptor.contains("(Landroid/view/View;)V")) {boolean isAbstractMethod = (access & ACC_ABSTRACT) != 0;boolean isNativeMethod = (access & ACC_NATIVE) != 0;if (!isAbstractMethod && !isNativeMethod) {generateNewBody(mv, owner, access, name, descriptor,signature,exceptions);return null;}}return mv;}
然后我们在generateNewBody中进行处理
protected void generateNewBody(MethodVisitor mv, String owner, int methodAccess, String methodName, String methodDesc,String signature, String[] exceptions) {// (1) method argument types and return typeType t = Type.getType(methodDesc);Type[] argumentTypes = t.getArgumentTypes();Type returnType = t.getReturnType();// (2) compute the size of local variable and operand stackboolean isStaticMethod = ((methodAccess & Opcodes.ACC_STATIC) != 0);int localSize = isStaticMethod ? 0 : 1;for (Type argType : argumentTypes) {localSize += argType.getSize();}int stackSize = returnType.getSize();// (3) method bodymv.visitCode();if (returnType.getSort() == Type.VOID) {mv.visitInsn(RETURN);}else if (returnType.getSort() >= Type.BOOLEAN && returnType.getSort() <= Type.INT) {mv.visitInsn(ICONST_1);mv.visitInsn(IRETURN);}else if (returnType.getSort() == Type.LONG) {mv.visitInsn(LCONST_0);mv.visitInsn(LRETURN);}else if (returnType.getSort() == Type.FLOAT) {mv.visitInsn(FCONST_0);mv.visitInsn(FRETURN);}else if (returnType.getSort() == Type.DOUBLE) {mv.visitInsn(DCONST_0);mv.visitInsn(DRETURN);}else {mv.visitInsn(ACONST_NULL);mv.visitInsn(ARETURN);}mv.visitMaxs(stackSize, localSize);mv.visitEnd();}
可以看到编译后的class文件中方法体已经清除了
3.使用ASM替换特定方法体
我们修改generateNewBody方法为
protected void generateNewBody(MethodVisitor mv, String owner, int methodAccess, String methodName, String methodDesc,String signature, String[] exceptions) {// (1) method argument types and return typeType t = Type.getType(methodDesc);Type[] argumentTypes = t.getArgumentTypes();Type returnType = t.getReturnType();// (2) compute the size of local variable and operand stackboolean isStaticMethod = ((methodAccess & Opcodes.ACC_STATIC) != 0);int localSize = isStaticMethod ? 0 : 1;for (Type argType : argumentTypes) {localSize += argType.getSize();}int stackSize = returnType.getSize();// (3) method bodymv.visitCode();String mInterfaceStr = owner;if(exceptions != null && exceptions.length > 0){for(int i = 0 ; i < exceptions.length ; i++){mInterfaceStr += exceptions[i];}}//插入替换代码mv.visitVarInsn(ALOAD, 0);mv.visitFieldInsn(GETFIELD, "com/yxhuang/asm/MainActivity$1", "this$0", "Lcom/yxhuang/asm/MainActivity;");mv.visitMethodInsn(INVOKESTATIC, "com/yxhuang/asm/TTT", "test", "(Landroid/content/Context;)V", false);if (returnType.getSort() == Type.VOID) {mv.visitInsn(RETURN);}else if (returnType.getSort() >= Type.BOOLEAN && returnType.getSort() <= Type.INT) {mv.visitInsn(ICONST_1);mv.visitInsn(IRETURN);}else if (returnType.getSort() == Type.LONG) {mv.visitInsn(LCONST_0);mv.visitInsn(LRETURN);}else if (returnType.getSort() == Type.FLOAT) {mv.visitInsn(FCONST_0);mv.visitInsn(FRETURN);}else if (returnType.getSort() == Type.DOUBLE) {mv.visitInsn(DCONST_0);mv.visitInsn(DRETURN);}else {mv.visitInsn(ACONST_NULL);mv.visitInsn(ARETURN);}mv.visitMaxs(stackSize, localSize);mv.visitEnd();}
这是一段跳转其他Activity的代码,原代码如下
public class TTT {//跳转A页面public static void test(Context context){context.startActivity(new Intent(context,A.class));}
}
未被替换的代码如下
mTvHello.setOnClickListener(new View.OnClickListener() {@Overridepublic void onClick(View v) {Toast.makeText(MainActivity.this,"aaa",Toast.LENGTH_SHORT).show();}});
替换后的class如下
辅助工具
由于字节码的插桩具有一定难度,因此我们可以通过ASM Bytecode Viewer Support Kotlin这款插件来辅助
生成的代码如下所示
然后我们可以通过选择ASMified来查看ASM插桩的代码