Soot 静态分析框架(五)Annotation 的实现

1. Annotation的实现

1.1 Java实现的Annotation

java 的自定义注解实现,通常我们会先自定义一个注解

import java.lang.annotation.ElementType;

import java.lang.annotation.*;

 

@Target({ElementType.TYPE,ElementType.FIELD, ElementType.METHOD, ElementType.PARAMETER,ElementType.TYPE_PARAMETER,ElementType.TYPE_USE})

@Retention(RetentionPolicy.RUNTIME

public @interface testAnnotation {

     boolean value() default false;

 }

反编译代码如下表,其本质就是构建一个接口testAnnotation,继承了java.lang.annotation.Annotation的接口,定义了一个抽象类 value,并且设置了AnnotationDefault: default_value: Z#10 Z#10是一个integer的值0,也就是booleanfalse

public interface testAnnotation extends java.lang.annotation.Annotation

  minor version: 0

  major version: 52

  flags: ACC_PUBLIC, ACC_INTERFACE, ACC_ABSTRACT, ACC_ANNOTATION

Constant pool:

   #1 = Class              #2             // pointto/testAnnotation

   #2 = Utf8               pointto/testAnnotation

   #3 = Class              #4             // java/lang/Object

   #4 = Utf8               java/lang/Object

   #5 = Class              #6             // java/lang/annotation/Annotation

   #6 = Utf8               java/lang/annotation/Annotation

   #7 = Utf8               value

   #8 = Utf8               ()Z

   #9 = Utf8               AnnotationDefault

  #10 = Integer            0

  #11 = Utf8               SourceFile

  #12 = Utf8               testAnnotation.java

  #13 = Utf8               RuntimeVisibleAnnotations

  #14 = Utf8               Ljava/lang/annotation/Target;

  #15 = Utf8               Ljava/lang/annotation/ElementType;

  #16 = Utf8               TYPE

  #17 = Utf8               FIELD

  #18 = Utf8               METHOD

  #19 = Utf8               PARAMETER

  #20 = Utf8               TYPE_PARAMETER

  #21 = Utf8               TYPE_USE

  #22 = Utf8               Ljava/lang/annotation/Retention;

  #23 = Utf8               Ljava/lang/annotation/RetentionPolicy;

  #24 = Utf8               RUNTIME

{

  public abstract boolean value();

    descriptor: ()Z

    flags: ACC_PUBLIC, ACC_ABSTRACT

    AnnotationDefault:

      default_value: Z#10}

SourceFile: "testAnnotation.java"

RuntimeVisibleAnnotations:

  0: #14(#7=[e#15.#16,e#15.#17,e#15.#18,e#15.#19,e#15.#20,e#15.#21])

  1: #22(#7=e#23.#24)

 

 

接着我们会去定义我们自己的实现的annotation, 可以定义在field,class,method, parameter,localvar里, 虽然注解在写代码的时候就已经确定了,但在实现的类里面并不会去在编译的时候去实现这个定义的接口,而是在字节码里标识了一下。

public void method();

    descriptor: (Lpointto/Container;Z)V

    flags: ACC_PUBLIC

    RuntimeVisibleAnnotations:

      0: #53(#54=Z#55)

    RuntimeVisibleParameterAnnotations:

      parameter 0:

        0: #53(#54=Z#55)

      parameter 1:

        0: #53(#54=Z#55)

code:

…….

      RuntimeVisibleTypeAnnotations:

        0: #53(#54=Z#55): LOCAL_VARIABLE, {start_pc=18, length=72, index=4}

    RuntimeVisibleTypeAnnotations:

      0: #53(#54=Z#55): METHOD_FORMAL_PARAMETER, param_index=0

      1: #53(#54=Z#55): METHOD_FORMAL_PARAMETER, param_index=1

 

定义了 RuntimeVisibleAnnotations, RuntimeVisibleParameterAnnotations 以及对应的RuntimeVisibleTypeAnnotations

 

LOCAL_VARIABLE 不同的是,就直接定义了

RuntimeVisibleTypeAnnotations:

        0: #53(#54=Z#55): LOCAL_VARIABLE, {start_pc=18, length=72, index=4}

并没有定义RuntimeVisibleAnnotations

当通过class, method来获取annotation的时候,JVM会通过AnnotationParser的方法来生成动态代理类

public static Annotation annotationForMap(final Class var0, final Map var1) {

    return (Annotation)AccessController.doPrivileged(new PrivilegedAction() {

        public Annotation run() {

            return (Annotation)Proxy.newProxyInstance(var0.getClassLoader(), new Class[]{var0}, new AnnotationInvocationHandler(var0, var1));

        }

    });

}

设置

System.setProperty("sun.misc.ProxyGenerator.saveGeneratedFiles", "true");

可以看到动态生成的类,生成路径为工作目录下的com.sun.proxy.Proxy1, Proxy2…

生成的类如下:

public final class $Proxy1 extends Proxy implements testAnnotation {

    private static Method m1;

    private static Method m2;

    private static Method m3;

    private static Method m4;

    private static Method m0;

 

    public $Proxy1(InvocationHandler var1) throws  {

        super(var1);

    }

 

    public final boolean equals(Object var1) throws  {

        try {

            return (Boolean)super.h.invoke(this, m1, new Object[]{var1});

        } catch (RuntimeException | Error var3) {

            throw var3;

        } catch (Throwable var4) {

            throw new UndeclaredThrowableException(var4);

        }

    }

 

    public final String toString() throws  {

        try {

            return (String)super.h.invoke(this, m2, (Object[])null);

        } catch (RuntimeException | Error var2) {

            throw var2;

        } catch (Throwable var3) {

            throw new UndeclaredThrowableException(var3);

        }

    }

 

    public final int count() throws  {

        try {

            return (Integer)super.h.invoke(this, m3, (Object[])null);

        } catch (RuntimeException | Error var2) {

            throw var2;

        } catch (Throwable var3) {

            throw new UndeclaredThrowableException(var3);

        }

    }

 

    public final Class annotationType() throws  {

        try {

            return (Class)super.h.invoke(this, m4, (Object[])null);

        } catch (RuntimeException | Error var2) {

            throw var2;

        } catch (Throwable var3) {

            throw new UndeclaredThrowableException(var3);

        }

    }

 

    public final int hashCode() throws  {

        try {

            return (Integer)super.h.invoke(this, m0, (Object[])null);

        } catch (RuntimeException | Error var2) {

            throw var2;

        } catch (Throwable var3) {

            throw new UndeclaredThrowableException(var3);

        }

    }

 

    static {

        try {

            m1 = Class.forName("java.lang.Object").getMethod("equals", Class.forName("java.lang.Object"));

            m2 = Class.forName("java.lang.Object").getMethod("toString");

            m3 = Class.forName("testAnnotation ").getMethod("value");

            m4 = Class.forName("testAnnotation ").getMethod("annotationType");

            m0 = Class.forName("java.lang.Object").getMethod("hashCode");

        } catch (NoSuchMethodException var2) {

            throw new NoSuchMethodError(var2.getMessage());

        } catch (ClassNotFoundException var3) {

            throw new NoClassDefFoundError(var3.getMessage());

        }

    }

}

 

调用AnnotationInvocationHandler的invoke方法来获取值,

在AnnotationInvocationHandler 里保留了

private final Map memberValues; Key是方法名,Value就是在你的类里定义的annotation的值

 

 

1.2  soot实现annotation 

Soot里对annotation里并没有和JVM一样生成的动态类,而是在SootClass, SootMethod, SootField里使用了Tag来封装annotation。

 

Soot 静态分析框架(五)Annotation 的实现_第1张图片

VisibilityAnnotationTag 代表AnnotationTag的可见属性,但实际上不可见的AnnotationTag soot依然使用VisibilityAnnotationTag来表示,而对函数的参数Annotation,soot 里使用VisibilityParameterAnnotationTag来表示,里面是方法的所有参数的Annotation,而每一个Annotation的值使用AnnotationElem来表示,不同的值的类型对应不同的AnnotationElem,比较特例的是boolean,byte,short, char, int 都是使用int来表示这和JVM比较类似,我们看到在前面JVM曾今定义boolean 定义成int 1或者0。

在class里定义的annotation会被加到SootClass里使用Tag的list来表示,在field定义的annotation会被加到SootField里的Tag的list

Method里面有Method, Parameter, Local Variable 三种类型的Annotation, 但实际SootMethod里只是封装了Method, Parameter这两种,Local Variable并没有封装,但语法树是能分析成Annotation,可能认为Local Variable会有很少的使用场景。

因为Soot使用objectweb 来遍历语法树,objectweb可以自定义Visit 的钩子函数,在分析过程语法树的过程种可以调用用户自定义的Visit,这样不需要在构建完语法树后再次遍历语法树来生成用户自己的分析逻辑,而是在分析过程就可以构建用户自己的逻辑提高效率,比如Soot的构建自己的SootClass, SootMethod, SootField,而Annotation在遍历语法树的时候就可以开始构建了。

结论:

1. Annotation并没有和JVM一样的方式通过构建动态代理类的方式来构建,这里实现和Lambda不同,只是在SootClass, SootMethod, SootField里构建了构建了AnnotationTag

2.  Annotation 不会生成Jimple 的IR语言,因为Annotation的值在写代码的时候就已经生成了,直接被保存在Tag里就可以了,只要标识一下声明就好了。

 

 

 

 

你可能感兴趣的:(Soot,静态分析)