Android 代碼注入的記錄

前言

最近因爲工作需要,需要使用代碼注入的功能,這裏簡單介紹下代碼注入的流程和心得。

  • 這篇文章主要是包含了我收集的一些有用的東西,沒有其他的一些具體分享。

前置知識

  • Android的代碼注入需要對Class的一些指令比較熟悉,纔可以完成基本的操作。

Class文件的結構

Class文件的結構大概如下所示,其中*表示0個或者多個。

  • 摘錄自:ASM4-Guide
    • Core API / Classes / Structure / Overview

在這裏插入圖片描述

類字段 對應屬性字段
Modifiers, name, super class, interfaces
Constant pool: numeric, string and type constants
Source file name(optional)
Enclosing class reference
Annotation*
Attribute*
Inner class* Name
Field* Modifiers, name, type
Annotation*
Attribute*
Method* Modifiers, name, return and parameter typers
Annotation*
Attribute*
Compiled code

Java基本類型與Class類型

  • 摘錄自:ASM4-Guide
    • Core API / Classes / Structure / Overview
Class字段 含義
B 基本類型byte
C 基本類型char
D 基本類型double
F 基本類型float
I 基本類型int
J 基本類型long
S 基本類型short
Z 基本類型boolean
V 特殊類型void
L 對象類型,以分號結尾,如Ljava/lang/Object;
[Ljava/lang/String; 數組類型,每一位使用一個前置的[字符來表示

Java方法聲明與Class聲明

  • 摘錄自:ASM4-Guide

    • Core API / Classes / Structure / Overview
  • 括號中的是參數聲明,括號後緊跟着的是返回類型。

Class字段 Java方法聲明
(IF)V void m(int i, float f)
(Ljava/lang/Object;)I int m(Object o)
(ILjava/lang/String;)[I int[] m(int i, String s);
([I)Ljava/lang/Object; Object m(int[] i);

Class修飾符

修飾符 ID 說明
ACC_PUBLIC 0X0001 public類型
ACC_FINAL 0X0010 聲明爲final,只有類可以設置
ACC_SUPER 0X0020 使用invokespecial字節碼指令的新語意,invokespecial指令的語意在JDK1.0.2發生過改變,爲了區別這條指令使用哪種語意,JDK1.0.2之後編譯出來的類都爲真
ACC_INTERFACE 0X0200 接口
ACC_ABSTRACT 0X0400 abstract類型,對於接口或者抽象類來說,此標誌值爲真,其他類爲假
ACC_SYNTHETIC 0X1000 這個類並非由用戶代碼產生
ACC_ANNOTATION 0X2000 註解
ACC_ENUM 0X4000 枚舉

Class常量池類型

常量池的類型 說明
CONSTANT_Utf8_info tag標誌位爲1, UTF-8編碼的字符串
CONSTANT_Integer_info tag標誌位爲3, 整形字面量
CONSTANT_Float_info tag標誌位爲4, 浮點型字面量
CONSTANT_Long_info tag標誌位爲5, 長整形字面量
CONSTANT_Double_info tag標誌位爲6, 雙精度字面量
CONSTANT_Class_info tag標誌位爲7, 類或接口的符號引用
CONSTANT_String_info tag標誌位爲8,字符串類型的字面量
CONSTANT_Fieldref_info tag標誌位爲9, 字段的符號引用
CONSTANT_Methodref_info tag標誌位爲10,類中方法的符號引用
CONSTANT_InterfaceMethodref_info tag標誌位爲11, 接口中方法的符號引用
CONSTANT_NameAndType_info tag 標誌位爲12,字段和方法的名稱以及類型的符號引用
CONSTANT_Method-Handle_info tag標誌位爲15,方法句柄
CONSTANT_Method-Type_info tag標誌位爲16,方法類型
CONSTANT_Invoke-Dynamic_info tag標誌位爲18,動態方法調用點

ASM的文檔

如果啃不動英文的文檔,可以看這個博主的譯文

IBM Developer社區的文章也是非常的不錯的,可以作爲入門用。

ASM的整體架構

ASM Core API調用流程圖

  • ASM的架構分析和原理介紹詳見:Whyn的文章ASM 簡介,下圖源於該文章。

在這裏插入圖片描述

實踐

直接使用Sample的例子對照着改就好了,O(∩_∩)O

實踐 地址
添加變量 AddFieldAdapter.java
添加方法 AddMethodAdapter.java
刪除變量 RemoveFieldAdapter.java
刪除方法 RemoveMethodAdapter.java
添加耗時統計 AddTimerAdapter,還有這部分也是:Other AddTimerAdapter
添加註解 AddAnnotationAdapter.java
刪除註解 RemoveAnnotationAdapter.java
使用Transformer添加變量 AddFieldTransformer.java
使用Transformer添加方法 AddMethodTransformer.java
使用Transformer刪除變量 RemoveFieldTransformer.java
使用Transformer刪除方法 RemoveMethodTransformer.java

文檔

MethodVisitor

org.objectweb.asm.MethodVisitor

  • ASM提供的接口和Java class的指令一致,可以對照着Class指令找對應的方法。
參數 說明 解釋
invokevirtual Invoke instance method; dispatch based on class 執行一般實例方法,創建完實例對象後,obj.method()調用的
invokespecial Invoke instance method; special handling for superclass, private, and instance initialization method invocations 實例初始化方法(構造函數)、父類的方法(super.method()方式調用)、私有方法
invokeinterface Invoke interface method 執行接口方法
invokestatic Invoke a class (static) method 執行靜態方法
invokedynamic Invoke dynamic method jdk1.7新增,執行動態方法,不需要在編譯時確定

Sample & QA

增加註解

注入靜態變量

  • 關於靜態變量的注入只有基本類型可以注入。否則會拋出Unexpected static-value typeException,比如Unexpected static-value type java.lang.Class
  • 代碼摘錄自:JarClassFileReader.java
private DexValue getStaticValue(Object value, DexType type) {
  if (value == null) {
    return null;
  }
  DexItemFactory factory = parent.application.getFactory();
  if (type == factory.booleanType) {
    int i = (Integer) value;
    assert 0 <= i && i <= 1;
    return DexValueBoolean.create(i == 1);
  }
  if (type == factory.byteType) {
    return DexValueByte.create(((Integer) value).byteValue());
  }
  if (type == factory.shortType) {
    return DexValueShort.create(((Integer) value).shortValue());
  }
  if (type == factory.charType) {
    return DexValueChar.create((char) ((Integer) value).intValue());
  }
  if (type == factory.intType) {
    return DexValueInt.create((Integer) value);
  }
  if (type == factory.floatType) {
    return DexValueFloat.create((Float) value);
  }
  if (type == factory.longType) {
    return DexValueLong.create((Long) value);
  }
  if (type == factory.doubleType) {
    return DexValueDouble.create((Double) value);
  }
  if (type == factory.stringType) {
    return new DexValueString(factory.createString((String) value));
  }
  throw new Unreachable("Unexpected static-value type " + type);
}

附錄

字節碼工具

ASM


Gradle

其他

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章