ASM在kotlin中的使用

1 篇文章 0 订阅

我们之前在Java中可以愉快的插桩,是因为我们熟悉了 gradle 的Transform 和Asm框架使用方法,掌握这两个,基本插桩就没问题了。但是还有一个重要的知识点,class文件结构和字节码指令。每次看到还是一头雾水,幸好As提供了一个Asm-bytecode-viewer插件,可以方便的帮我们生成asm的字节码代码:

 

不过,很不幸,这个插件对kotlin不那么友好。当你想用kotlin代码进行插桩,发现他会报错。

 

目前看应该是不支持kotlin转换。这。。。又要手动写字节码了?

 

暂时没有找到特别简单的办法,但还是可以做到转换的。

 

转换步骤:

1.实用硬代码方式写入,编译生成class,例如:如下代码:

object ApiService {
    fun getInstance(): Api {
        val retrofit = Retrofit.Builder()
            .baseUrl("https://wanandroid.com/")
            .client(getClient())
            .addConverterFactory(GsonConverterFactory.create())
            .build()
        return retrofit.create(Api::class.java)
    }

    private fun getClient(): OkHttpClient {
        return OkHttpClient.Builder()
//            .addInterceptor(MonitorInterceptor())
//            .eventListener(MonitorEventListener())
            .dns(MiDns())
            .build()
    }
//主要想看这一部分生成的asm字节码
    private fun test() {
        var builder = OkHttpClient.Builder()
        var intercepters = builder.interceptors()
        AsmOkHttpHelper.insertInterceptorToOkHttpClientBuilder(intercepters)
    }
}

使用ide build生成class:

 

这里用到一个工具:asm-all.jar 里面带了很多的功能,可以使用命令行将.class文件转化成asm代码,这里需要用到一个工具包asm-all-4.0.jar

https://download.csdn.net/download/qq_23992393/12724287

java -classpath "asm-all-4.0.jar" org.objectweb.asm.util.ASMifier /Users/hanlonglin/AndroidStudioProjects/yiduis/MiApm/MilianApm/app/build/tmp/kotlin-classes/debug/com/yidui/apm/tools/okhttp/api/ApiService.class > apiService.txt

 

这里输出到了 apiService.txt 中,查看:

package asm.com.yidui.apm.tools.okhttp.api;
import java.util.*;
import org.objectweb.asm.*;
import org.objectweb.asm.attrs.*;
public class ApiServiceDump implements Opcodes {

public static byte[] dump () throws Exception {

ClassWriter cw = new ClassWriter(0);
FieldVisitor fv;
MethodVisitor mv;
AnnotationVisitor av0;

cw.visit(V1_6, ACC_PUBLIC + ACC_FINAL + ACC_SUPER, "com/yidui/apm/tools/okhttp/api/ApiService", null, "java/lang/Object", null);

{
av0 = cw.visitAnnotation("Lkotlin/Metadata;", true);
av0.visit("mv", new int[] {1,1,15});
av0.visit("bv", new int[] {1,0,3});
av0.visit("k", new Integer(1));
{
AnnotationVisitor av1 = av0.visitArray("d1");
av1.visit(null, "\u0000 \n\u0002\u0018\u0002\n\u0002\u0010\u0000\n\u0002\u0008\u0002\n\u0002\u0018\u0002\n\u0002\u0008\u0002\n\u0002\u0018\u0002\n\u0000\n\u0002\u0010\u0002\n\u0000\u0008\u00c6\u0002\u0018\u00002\u00020\u0001B\u0007\u0008\u0002\u00a2\u0006\u0002\u0010\u0002J\u0008\u0010\u0005\u001a\u00020\u0004H\u0002J\u0006\u0010\u0006\u001a\u00020\u0007J\u0008\u0010\u0008\u001a\u00020\u0009H\u0002R\u000e\u0010\u0003\u001a\u00020\u0004X\u0082.\u00a2\u0006\u0002\n\u0000\u00a8\u0006\n");
av1.visitEnd();
}
{
AnnotationVisitor av1 = av0.visitArray("d2");
av1.visit(null, "Lcom/yidui/apm/tools/okhttp/api/ApiService;");
av1.visit(null, "");
av1.visit(null, "()V");
av1.visit(null, "client");
av1.visit(null, "Lokhttp3/OkHttpClient;");
av1.visit(null, "getClient");
av1.visit(null, "getInstance");
av1.visit(null, "Lcom/yidui/apm/tools/okhttp/api/Api;");
av1.visit(null, "test");
av1.visit(null, "");
av1.visit(null, "app_debug");
av1.visitEnd();
}
av0.visitEnd();
}
{
fv = cw.visitField(ACC_PRIVATE + ACC_STATIC, "client", "Lokhttp3/OkHttpClient;", null, null);
fv.visitEnd();
}
{
fv = cw.visitField(ACC_PUBLIC + ACC_FINAL + ACC_STATIC, "INSTANCE", "Lcom/yidui/apm/tools/okhttp/api/ApiService;", null, null);
fv.visitEnd();
}
{
mv = cw.visitMethod(ACC_PUBLIC + ACC_FINAL, "getInstance", "()Lcom/yidui/apm/tools/okhttp/api/Api;", null, null);
{
av0 = mv.visitAnnotation("Lorg/jetbrains/annotations/NotNull;", false);
av0.visitEnd();
}
mv.visitCode();
mv.visitTypeInsn(NEW, "retrofit2/Retrofit$Builder");
mv.visitInsn(DUP);
mv.visitMethodInsn(INVOKESPECIAL, "retrofit2/Retrofit$Builder", "<init>", "()V");
mv.visitLdcInsn("https://wanandroid.com/");
mv.visitMethodInsn(INVOKEVIRTUAL, "retrofit2/Retrofit$Builder", "baseUrl", "(Ljava/lang/String;)Lretrofit2/Retrofit$Builder;");
mv.visitVarInsn(ALOAD, 0);
mv.visitMethodInsn(INVOKESPECIAL, "com/yidui/apm/tools/okhttp/api/ApiService", "getClient", "()Lokhttp3/OkHttpClient;");
mv.visitMethodInsn(INVOKEVIRTUAL, "retrofit2/Retrofit$Builder", "client", "(Lokhttp3/OkHttpClient;)Lretrofit2/Retrofit$Builder;");
mv.visitMethodInsn(INVOKESTATIC, "retrofit2/converter/gson/GsonConverterFactory", "create", "()Lretrofit2/converter/gson/GsonConverterFactory;");
mv.visitTypeInsn(CHECKCAST, "retrofit2/Converter$Factory");
mv.visitMethodInsn(INVOKEVIRTUAL, "retrofit2/Retrofit$Builder", "addConverterFactory", "(Lretrofit2/Converter$Factory;)Lretrofit2/Retrofit$Builder;");
mv.visitMethodInsn(INVOKEVIRTUAL, "retrofit2/Retrofit$Builder", "build", "()Lretrofit2/Retrofit;");
mv.visitVarInsn(ASTORE, 1);
mv.visitVarInsn(ALOAD, 1);
mv.visitLdcInsn(Type.getType("Lcom/yidui/apm/tools/okhttp/api/Api;"));
mv.visitMethodInsn(INVOKEVIRTUAL, "retrofit2/Retrofit", "create", "(Ljava/lang/Class;)Ljava/lang/Object;");
mv.visitInsn(DUP);
mv.visitLdcInsn("retrofit.create(Api::class.java)");
mv.visitMethodInsn(INVOKESTATIC, "kotlin/jvm/internal/Intrinsics", "checkExpressionValueIsNotNull", "(Ljava/lang/Object;Ljava/lang/String;)V");
mv.visitTypeInsn(CHECKCAST, "com/yidui/apm/tools/okhttp/api/Api");
mv.visitInsn(ARETURN);
mv.visitMaxs(3, 2);
mv.visitEnd();
}
{
mv = cw.visitMethod(ACC_PRIVATE + ACC_FINAL, "getClient", "()Lokhttp3/OkHttpClient;", null, null);
mv.visitCode();
mv.visitFieldInsn(GETSTATIC, "com/yidui/apm/tools/okhttp/api/ApiService", "client", "Lokhttp3/OkHttpClient;");
mv.visitInsn(DUP);
Label l0 = new Label();
mv.visitJumpInsn(IFNONNULL, l0);
mv.visitLdcInsn("client");
mv.visitMethodInsn(INVOKESTATIC, "kotlin/jvm/internal/Intrinsics", "throwUninitializedPropertyAccessException", "(Ljava/lang/String;)V");
mv.visitLabel(l0);
mv.visitFrame(Opcodes.F_SAME1, 0, null, 1, new Object[] {"okhttp3/OkHttpClient"});
Label l1 = new Label();
mv.visitJumpInsn(IFNONNULL, l1);
mv.visitTypeInsn(NEW, "okhttp3/OkHttpClient$Builder");
mv.visitInsn(DUP);
mv.visitMethodInsn(INVOKESPECIAL, "okhttp3/OkHttpClient$Builder", "<init>", "()V");
mv.visitTypeInsn(NEW, "com/yidui/apm/tools/okhttp/api/MiDns");
mv.visitInsn(DUP);
mv.visitMethodInsn(INVOKESPECIAL, "com/yidui/apm/tools/okhttp/api/MiDns", "<init>", "()V");
mv.visitTypeInsn(CHECKCAST, "okhttp3/Dns");
mv.visitMethodInsn(INVOKEVIRTUAL, "okhttp3/OkHttpClient$Builder", "dns", "(Lokhttp3/Dns;)Lokhttp3/OkHttpClient$Builder;");
mv.visitMethodInsn(INVOKEVIRTUAL, "okhttp3/OkHttpClient$Builder", "build", "()Lokhttp3/OkHttpClient;");
mv.visitInsn(DUP);
mv.visitLdcInsn("OkHttpClient.Builder()\n/\u2026\n                .build()");
mv.visitMethodInsn(INVOKESTATIC, "kotlin/jvm/internal/Intrinsics", "checkExpressionValueIsNotNull", "(Ljava/lang/Object;Ljava/lang/String;)V");
mv.visitFieldInsn(PUTSTATIC, "com/yidui/apm/tools/okhttp/api/ApiService", "client", "Lokhttp3/OkHttpClient;");
mv.visitLabel(l1);
mv.visitFrame(Opcodes.F_SAME, 0, null, 0, null);
mv.visitFieldInsn(GETSTATIC, "com/yidui/apm/tools/okhttp/api/ApiService", "client", "Lokhttp3/OkHttpClient;");
mv.visitInsn(DUP);
Label l2 = new Label();
mv.visitJumpInsn(IFNONNULL, l2);
mv.visitLdcInsn("client");
mv.visitMethodInsn(INVOKESTATIC, "kotlin/jvm/internal/Intrinsics", "throwUninitializedPropertyAccessException", "(Ljava/lang/String;)V");
mv.visitLabel(l2);
mv.visitFrame(Opcodes.F_SAME1, 0, null, 1, new Object[] {"okhttp3/OkHttpClient"});
mv.visitInsn(ARETURN);
mv.visitMaxs(3, 1);
mv.visitEnd();
}
{
mv = cw.visitMethod(ACC_PRIVATE + ACC_FINAL, "test", "()V", null, null);
mv.visitCode();
mv.visitTypeInsn(NEW, "okhttp3/OkHttpClient$Builder");
mv.visitInsn(DUP);
mv.visitMethodInsn(INVOKESPECIAL, "okhttp3/OkHttpClient$Builder", "<init>", "()V");
mv.visitVarInsn(ASTORE, 1);
mv.visitVarInsn(ALOAD, 1);
mv.visitMethodInsn(INVOKEVIRTUAL, "okhttp3/OkHttpClient$Builder", "interceptors", "()Ljava/util/List;");
mv.visitVarInsn(ASTORE, 2);
mv.visitVarInsn(ALOAD, 2);
mv.visitInsn(DUP);
mv.visitLdcInsn("intercepters");
mv.visitMethodInsn(INVOKESTATIC, "kotlin/jvm/internal/Intrinsics", "checkExpressionValueIsNotNull", "(Ljava/lang/Object;Ljava/lang/String;)V");
mv.visitMethodInsn(INVOKESTATIC, "com/yidui/apm/apmtools/monitor/okhttp/AsmOkHttpHelper", "insertInterceptorToOkHttpClientBuilder", "(Ljava/util/List;)V");
mv.visitInsn(RETURN);
mv.visitMaxs(3, 3);
mv.visitEnd();
}
{
mv = cw.visitMethod(ACC_PRIVATE, "<init>", "()V", null, null);
mv.visitCode();
mv.visitVarInsn(ALOAD, 0);
mv.visitMethodInsn(INVOKESPECIAL, "java/lang/Object", "<init>", "()V");
mv.visitInsn(RETURN);
mv.visitMaxs(1, 1);
mv.visitEnd();
}
{
mv = cw.visitMethod(ACC_STATIC, "<clinit>", "()V", null, null);
mv.visitCode();
mv.visitTypeInsn(NEW, "com/yidui/apm/tools/okhttp/api/ApiService");
mv.visitInsn(DUP);
mv.visitMethodInsn(INVOKESPECIAL, "com/yidui/apm/tools/okhttp/api/ApiService", "<init>", "()V");
mv.visitVarInsn(ASTORE, 0);
mv.visitVarInsn(ALOAD, 0);
mv.visitFieldInsn(PUTSTATIC, "com/yidui/apm/tools/okhttp/api/ApiService", "INSTANCE", "Lcom/yidui/apm/tools/okhttp/api/ApiService;");
mv.visitInsn(RETURN);
mv.visitMaxs(2, 1);
mv.visitEnd();
}
cw.visitEnd();

return cw.toByteArray();
}
}

 

找到我们想要的那段代码:

mv = cw.visitMethod(ACC_PRIVATE + ACC_FINAL, "test", "()V", null, null);
mv.visitCode();
mv.visitTypeInsn(NEW, "okhttp3/OkHttpClient$Builder");
mv.visitInsn(DUP);
mv.visitMethodInsn(INVOKESPECIAL, "okhttp3/OkHttpClient$Builder", "<init>", "()V");
mv.visitVarInsn(ASTORE, 1);
mv.visitVarInsn(ALOAD, 1);
mv.visitMethodInsn(INVOKEVIRTUAL, "okhttp3/OkHttpClient$Builder", "interceptors", "()Ljava/util/List;");
mv.visitVarInsn(ASTORE, 2);
mv.visitVarInsn(ALOAD, 2);
mv.visitInsn(DUP);
mv.visitLdcInsn("intercepters");
mv.visitMethodInsn(INVOKESTATIC, "kotlin/jvm/internal/Intrinsics", "checkExpressionValueIsNotNull", "(Ljava/lang/Object;Ljava/lang/String;)V");
mv.visitMethodInsn(INVOKESTATIC, "com/yidui/apm/apmtools/monitor/okhttp/AsmOkHttpHelper", "insertInterceptorToOkHttpClientBuilder", "(Ljava/util/List;)V");
mv.visitInsn(RETURN);
mv.visitMaxs(3, 3);
mv.visitEnd();

 

得到了asm代码,再也不用死记字节码了。

 

还有,如果要查看字节码生成的对不对,使用AndroidStudio是没法查看kotlin生成的class的,需要借助:JD-Gui工具

 

https://download.csdn.net/download/qq_23992393/12724266

找到生成的class,注意这里需要去对应的transform中去找:

 

 

接下来在真实目录中将.class 文件拖拽到jd-gui中即可。

还有一种情况,你需要查看到class在jar包中,因为你要查看到是第三方库中的class,这时的步骤是:

解压apk,得到classes.dex---> d2j-dex-jar.sh classes.dex命令生成 classes.jar ---> 将jar包拖入jd-gui中查看。

 

上面就是在kotlin中进行插桩可能会遇到的阻碍和解决办法,希望能够帮助到你。

 

  • 3
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

龍林1102

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值