Android使用ASM修改函数

目录

1be5325ba847dfd21bf39485cf48d4af.png

参考文章

Android 编译插桩(一):ASM

https://github.com/yxhuangCH/CSLearn/blob/master/android/Android%20%E7%BC%96%E8%AF%91%E6%8F%92%E6%A1%A9%EF%BC%88%E4%B8%80%EF%BC%89%EF%BC%9A%20ASM.md

Android 编译插桩(二):Gradle Transform

https://github.com/yxhuangCH/CSLearn/blob/master/android/Android%20%E7%BC%96%E8%AF%91%E6%8F%92%E6%A1%A9%EF%BC%88%E4%BA%8C%EF%BC%89%EF%BC%9A%20Gradle%20Transform.md


Android 编译插桩(三):Transform + ASM

https://github.com/yxhuangCH/CSLearn/blob/master/android/Android%20%E7%BC%96%E8%AF%91%E6%8F%92%E6%A1%A9%EF%BC%88%E4%B8%89%EF%BC%89%EF%BC%9ATransform%20%2B%20ASM.md


Java ASM系列:(028)修改已有的方法(删除-清空方法体)

https://blog.51cto.com/lsieun/2960338


我的代码是参考这些文章代码写的,主体是以前三篇文章的代码为基础

使用方法

1.编译使用插件

这里自定义了一个插件用来对字节码进行操作

f9582893bfc421254804c377eb4be29e.png

首先我们需要找到这个Gradle任务,双击进行编译打包

eb475b1c5e1c0d63df0c860888334905.png


打包成功后会生成如下目录

d2b375275438cd27a306215b76fe97e6.png

然后我们需要在项目的gradle文件中进行引用

ff841b6f5a0b8dd0d6e10207a802a0fc.png


然后在application的model下的gradle中应用插件

4133232aee71805a19a7a9bd0a6c5c8e.png

2.使用ASM清空特定方法体

这里在Activity中加了一个点击事件,这次是将点击事件的方法体进行清除

12e0d759a71660e23b80b1685e5615f6.png

这里我们在插件的MethodEmptyBodyVisitor中修改
首先在visitMethod函数中找到OnClickListener的onClick方法(通过判断函数签名,函数名等找到特定函数)

@Override
    public 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 type
        Type t = Type.getType(methodDesc);
        Type[] argumentTypes = t.getArgumentTypes();
        Type returnType = t.getReturnType();




        // (2) compute the size of local variable and operand stack
        boolean isStaticMethod = ((methodAccess & Opcodes.ACC_STATIC) != 0);
        int localSize = isStaticMethod ? 0 : 1;
        for (Type argType : argumentTypes) {
            localSize += argType.getSize();
        }
        int stackSize = returnType.getSize();


        // (3) method body
        mv.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文件中方法体已经清除了

dc344d6d6f1489565cc090933ab4f4a3.png

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 type
        Type t = Type.getType(methodDesc);
        Type[] argumentTypes = t.getArgumentTypes();
        Type returnType = t.getReturnType();




        // (2) compute the size of local variable and operand stack
        boolean isStaticMethod = ((methodAccess & Opcodes.ACC_STATIC) != 0);
        int localSize = isStaticMethod ? 0 : 1;
        for (Type argType : argumentTypes) {
            localSize += argType.getSize();
        }
        int stackSize = returnType.getSize();


        // (3) method body
        mv.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() {
            @Override
            public void onClick(View v) {
                Toast.makeText(MainActivity.this,"aaa",Toast.LENGTH_SHORT).show();
            }
        });

替换后的class如下

8e637c582547219b7ca67998e847b786.png

辅助工具

由于字节码的插桩具有一定难度,因此我们可以通过ASM Bytecode Viewer Support Kotlin这款插件来辅助

63cb27b60c9a4e4cf59ed49d286b45ad.png

我们安装完插件后可以在想要生成对应代码的原文件里右键选择ASM Bytecode Viewer来生成

08fadcc7d3f1328bc8d87bd96c09477d.png


生成的代码如下所示

38562b411db38d5aeb00087bfccf9de1.png

然后我们可以通过选择ASMified来查看ASM插桩的代码

5e5abfc2bca0cd302e8bdd2c5a6f504a.png

案例源码

https://gitee.com/itfitness/asm-demo



作者:itfitness
链接:https://www.jianshu.com/p/3d78de960354

关注我获取更多知识或者投稿

c2a1d190f566400ddc8c5341e7c49f2f.jpeg

114c9fd3671031bbe3f72f1333cb2574.jpeg

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值