深入分析GadgetInspector核心代码(二)

简介: 深入分析GadgetInspector核心代码

5.7 visitXxxInsn

根据opcode和操作数进行push和pop操作,模拟了JVM Frame中的OperandStack操作


5.7.1 visitInsn

访问0操作数指令

public void visitInsn(int opcode) {
   // 这些是临时变量,用于复制等指令
   Set<T> saved0, saved1, saved2, saved3;
   // 对模拟stack的size进行验证
   sanityCheck();
   switch(opcode) {
       // NOP跳过
       case Opcodes.NOP:
           break;
       // push null
       case Opcodes.ACONST_NULL:
       // push int
       case Opcodes.ICONST_M1:
       case Opcodes.ICONST_0:
       case Opcodes.ICONST_1:
       case Opcodes.ICONST_2:
       case Opcodes.ICONST_3:
       case Opcodes.ICONST_4:
       case Opcodes.ICONST_5:
       case Opcodes.FCONST_0:
       case Opcodes.FCONST_1:
       case Opcodes.FCONST_2:
           // 模拟进行一次push
           push();
           break;
       // long和double的size为2
       case Opcodes.LCONST_0:
       case Opcodes.LCONST_1:
       case Opcodes.DCONST_0:
       case Opcodes.DCONST_1:
           // size为2需要两次push
           push();
           push();
           break;
       // 这里是push各种类型的数组的索引对应的值
       // array,index -> value
       case Opcodes.IALOAD:
       case Opcodes.FALOAD:
       case Opcodes.AALOAD:
       case Opcodes.BALOAD:
       case Opcodes.CALOAD:
       case Opcodes.SALOAD:
           // 弹出数组引用和弹出index
           pop();
           pop();
           // push进去value
           push();
           break;
       // long和double的size为2
       case Opcodes.LALOAD:
       case Opcodes.DALOAD:
           // 弹出数组引用和弹出index
           pop();
           pop();
           // 两次push因为size为2
           push();
           push();
           break;
       // 弹出各种数组以及index和value
       case Opcodes.IASTORE:
       case Opcodes.FASTORE:
       case Opcodes.AASTORE:
       case Opcodes.BASTORE:
       case Opcodes.CASTORE:
       case Opcodes.SASTORE:
           // value
           pop();
           // index
           pop();
           // array
           pop();
           break;
       // 多pop一次因为size为2
       case Opcodes.LASTORE:
       case Opcodes.DASTORE:
           pop();
           pop();
           pop();
           pop();
           break;
       // 显而易见
       case Opcodes.POP:
           pop();
           break;
       // 显而易见
       case Opcodes.POP2:
           pop();
           pop();
           break;
       // 复制栈顶元素
       case Opcodes.DUP:
           push(get(0));
           break;
       // 复制栈顶插入到第2位
       case Opcodes.DUP_X1:
           saved0 = pop();
           saved1 = pop();
           push(saved0);
           push(saved1);
           push(saved0);
           break;
       // 复制栈顶插入到第3位
       case Opcodes.DUP_X2:
           saved0 = pop(); // a
           saved1 = pop(); // b
           saved2 = pop(); // c
           push(saved0); // a
           push(saved2); // c
           push(saved1); // b
           push(saved0); // a
           break;
       // 复制栈顶两个
       case Opcodes.DUP2:
           push(get(1));
           push(get(1));
           break;
       // 复制两个并向下插入
       // ..., value3 , value2 , value1 →
       // ..., value2 , value1 , value3 , value2 , value1
       case Opcodes.DUP2_X1:
           saved0 = pop();// value1
           saved1 = pop();// value2
           saved2 = pop();// value3
           push(saved1);// value2
           push(saved0);// value1
           push(saved2);// value3
           push(saved1);// value2
           push(saved0);// value1
           break;
       // 复制两个再向下两个插入
       // ..., value4 , value3 , value2 , value1 →
       // ..., value2 , value1 , value4 , value3 , value2 , value1
       case Opcodes.DUP2_X2:
           saved0 = pop();
           saved1 = pop();
           saved2 = pop();
           saved3 = pop();
           push(saved1);
           push(saved0);
           push(saved3);
           push(saved2);
           push(saved1);
           push(saved0);
           break;
       // 交换栈顶和第二个元素
       case Opcodes.SWAP:
           saved0 = pop();
           saved1 = pop();
           push(saved0);
           push(saved1);
           break;
       // 取栈顶两元素做完数学操作后push结果
       case Opcodes.IADD:
       case Opcodes.FADD:
       case Opcodes.ISUB:
       case Opcodes.FSUB:
       case Opcodes.IMUL:
       case Opcodes.FMUL:
       case Opcodes.IDIV:
       case Opcodes.FDIV:
       case Opcodes.IREM:
       case Opcodes.FREM:
           pop();
           pop();
           push();
           break;
       // long和size都乘2
       case Opcodes.LADD:
       case Opcodes.DADD:
       case Opcodes.LSUB:
       case Opcodes.DSUB:
       case Opcodes.LMUL:
       case Opcodes.DMUL:
       case Opcodes.LDIV:
       case Opcodes.DDIV:
       case Opcodes.LREM:
       case Opcodes.DREM:
           pop();
           pop();
           pop();
           pop();
           push();
           push();
           break;
       // 取出栈顶元素判断是否为int
       // 结果push回去
       case Opcodes.INEG:
       case Opcodes.FNEG:
           pop();
           push();
           break;
       // long和double都乘2
       case Opcodes.LNEG:
       case Opcodes.DNEG:
           pop();
           pop();
           push();
           push();
           break;
       // 取栈顶两个进行位运算
       // 结果push回去
       case Opcodes.ISHL:
       case Opcodes.ISHR:
       case Opcodes.IUSHR:
           pop();
           pop();
           push();
           break;
       // long和double的size为2
       // 操作数是int为1所以是3次pop
       case Opcodes.LSHL:
       case Opcodes.LSHR:
       case Opcodes.LUSHR:
           pop();
           pop();
           pop();
           push();
           push();
           break;
       // 取栈顶两个进行位运算
       // 结果push回去
       case Opcodes.IAND:
       case Opcodes.IOR:
       case Opcodes.IXOR:
           pop();
           pop();
           push();
           break;
       // long和double的size为2
       // long和long自己操作,所以是4次pop
       case Opcodes.LAND:
       case Opcodes.LOR:
       case Opcodes.LXOR:
           pop();
           pop();
           pop();
           pop();
           push();
           push();
           break;
       // 类型转换结果push回去
       case Opcodes.I2B:
       case Opcodes.I2C:
       case Opcodes.I2S:
       case Opcodes.I2F:
           pop();
           push();
           break;
       // 转long要2次push
       case Opcodes.I2L:
       case Opcodes.I2D:
           pop();
           push();
           push();
           break;
       // long转要2次pop
       case Opcodes.L2I:
       case Opcodes.L2F:
           pop();
           pop();
           push();
           break;
       // long转double各2次
       case Opcodes.D2L:
       case Opcodes.L2D:
           pop();
           pop();
           push();
           push();
           break;
       // float转int
       case Opcodes.F2I:
           pop();
           push();
           break;
       // long是2次
       case Opcodes.F2L:
       case Opcodes.F2D:
           pop();
           push();
           push();
           break;
       // double是2次
       case Opcodes.D2I:
       case Opcodes.D2F:
           pop();
           pop();
           push();
           break;
       // 比较栈顶两个long,结果push
       case Opcodes.LCMP:
           pop();
           pop();
           pop();
           pop();
           push();
           break;
       // 比较栈顶两个float,结果push
       case Opcodes.FCMPL:
       case Opcodes.FCMPG:
           pop();
           pop();
           push();
           break;
       // 比较栈顶两个double,结果push
       case Opcodes.DCMPL:
       case Opcodes.DCMPG:
           pop();
           pop();
           pop();
           pop();
           push();
           break;
       // return弹出一个
       case Opcodes.IRETURN:
       case Opcodes.FRETURN:
       case Opcodes.ARETURN:
           pop();
           break;
       // size为2
       case Opcodes.LRETURN:
       case Opcodes.DRETURN:
           pop();
           pop();
           break;
       // void没操作
       case Opcodes.RETURN:
           break;
       // 算数组长度
       case Opcodes.ARRAYLENGTH:
           pop();
           push();
           break;
       // 抛出异常类似return
       case Opcodes.ATHROW:
           pop();
           break;
       // 监视对象,弹出1个即可
       case Opcodes.MONITORENTER:
       case Opcodes.MONITOREXIT:
           pop();
           break;
       default:
           throw new IllegalStateException("Unsupported opcode: " + opcode);
  }
   // 传递
   super.visitInsn(opcode);
   // stack校验
   sanityCheck();
}

5.7.2 visitIntInsn

单int操作数指令

// 类似上文
public void visitIntInsn(int opcode, int operand) {
    switch(opcode) {
        // push一个值
        case Opcodes.BIPUSH:
        case Opcodes.SIPUSH:
            push();
            break;
        // 弹出一个size创建数组push回去引用
        case Opcodes.NEWARRAY:
            pop();
            push();
            break;
        default:
            throw new IllegalStateException("Unsupported opcode: " + opcode);
    }
    // 传递
    super.visitIntInsn(opcode, operand);
    // stack校验
    sanityCheck();
}


5.7.3 visitVarInsn

加载或存储局部变量值的指令

能够进行数据流动正式因为这一步从局部变量表获得或设置数据,而局部变量表数据是从5.4中获得,形成一个完整的流程

模拟JVM进行PUSH/POP操作

@Override
public void visitVarInsn(int opcode, int var) {
    // 同步到本地模拟local variables
    for (int i = savedVariableState.localVars.size(); i <= var; i++) {
        savedVariableState.localVars.add(new HashSet<T>());
    }
    // 临时变量
    Set<T> saved0;
    switch(opcode) {
        // push int/float
        case Opcodes.ILOAD:
        case Opcodes.FLOAD:
            push();
            break;
        // push long/double
        case Opcodes.LLOAD:
        case Opcodes.DLOAD:
            push();
            push();
            break;
        // push object
        case Opcodes.ALOAD:
            // 从局部变量表里push一个object
            push(savedVariableState.localVars.get(var));
            break;
        // pop int/float -> 加入局部变量表
        case Opcodes.ISTORE:
        case Opcodes.FSTORE:
            pop();
            savedVariableState.localVars.set(var, new HashSet<T>());
            break;
        // size为2
        case Opcodes.DSTORE:
        case Opcodes.LSTORE:
            pop();
            pop();
            savedVariableState.localVars.set(var, new HashSet<T>());
            break;
        // pop object -> 加入局部变量表
        case Opcodes.ASTORE:
            saved0 = pop();
            savedVariableState.localVars.set(var, saved0);
            break;
        // JSR相关,对stack和局部变量表没用影响
        case Opcodes.RET:
            break;
        default:
            throw new IllegalStateException("Unsupported opcode: " + opcode);
    }
    // 传递
    super.visitVarInsn(opcode, var);
    // stack校验
    sanityCheck();
}

5.7.4 visitTypeInsn

以类的内部名称作为参数的指令

@Override
public void visitTypeInsn(int opcode, String type) {
    switch(opcode) {
        // push object
        case Opcodes.NEW:
            push();
            break;
        // 弹出个size后push数组
        case Opcodes.ANEWARRAY:
            pop();
            push();
            break;
        // 检查类型,stack不变化
        case Opcodes.CHECKCAST:
            break;
        // 判断类是否一致
        // pop类引用,push结果
        case Opcodes.INSTANCEOF:
            pop();
            push();
            break;
        default:
            throw new IllegalStateException("Unsupported opcode: " + opcode);
    }
    // 传递
    super.visitTypeInsn(opcode, type);
    // stack校验
    sanityCheck();
}

5.7.5 visitFieldInsn

加载或存储对象字段值的指令

程序在这一步并没有过多的操作,只是简单的POP和PUSH

@Override
public void visitFieldInsn(int opcode, String owner, String name, String desc) {
    // size
    int typeSize = Type.getType(desc).getSize();
    switch (opcode) {
        // 获得static属性push进去
        case Opcodes.GETSTATIC:
            for (int i = 0; i < typeSize; i++) {
                push();
            }
            break;
        // 设置static属性pop出
        case Opcodes.PUTSTATIC:
            for (int i = 0; i < typeSize; i++) {
                pop();
            }
            break;
        // pop出对象
        // 从对象里获取的值push进去
        case Opcodes.GETFIELD:
            // ref
            pop();
            for (int i = 0; i < typeSize; i++) {
                // value
                push();
            }
            break;
        // pop出对象和值
        // 设置到对象里面(不影响stack)
        case Opcodes.PUTFIELD:
            for (int i = 0; i < typeSize; i++) {
                // value
                pop();
            }
            // ref
            pop();
            break;
        default:
            throw new IllegalStateException("Unsupported opcode: " + opcode);
    }
    // 传递
    super.visitFieldInsn(opcode, owner, name, desc);
    // stack校验
    sanityCheck();
}


5.7.6 visitMethodInsn

方法调用,比较核心的方法

根据方法调用需要的参数,在Stack中POP,这是对真实情况的模拟

如果是构造方法,那么argTaint第0位的this添加到污染

如果是void ObjectInputStream.defaultReadObject()不传参,这时候对象本身this就是污染,给当前局部变量表第0位设置污染(这种情况下这一步拿不到污染,在后续的数据流中得到污染)

如果目前的方法恰好匹配到白名单(很可能存在漏洞)那么白名单函数的参数位置设置到污染(其实白名单就是简化了分析,固定出了哪些类的哪些函数是存在漏洞的,它的第几个参数是可被污染的,如果匹配到白名单,直接设置该参数即可)

根据已有的passthroughDataflow得到与返回值有关的参数索引Set,加入污染(这一步是外部生成的,也就是Visit其他类的生成的,根据已有信息进行污点设置。参考8.7中的分析,调用链最末端的最优先被分析,因此调用到的方法必然已被visit分析过。由于Set的特性,并不会冲突,只是一次补充的效果)

如果当前类是集合类子类,认为集合中所有元素都是污染,这里不得到结果,只是设置污染,然后继续分析;如果返回对象或数组,认为返回是污染,这个结果是要并入PUSH的污染中的

最后把污染结果入栈,这模拟的就是执行完方法的PUSH返回值(代码第二次的PUSH是为了补位)


进一步的分析参考7.5


给出一个非STATIC方法调用的图

adc46536f78c2b9d719e71404e091d44_640_wx_fmt=png&wxfrom=5&wx_lazy=1&wx_co=1.png

@Override
public void visitMethodInsn(int opcode, String owner, String name, String desc, boolean itf) {
   // 方法信息,这里Handle是重新
   final MethodReference.Handle methodHandle = new MethodReference.Handle(
           new ClassReference.Handle(owner), name, desc);
   // 所有参数类型
   Type[] argTypes = Type.getArgumentTypes(desc);
   // 非静态调用
   if (opcode != Opcodes.INVOKESTATIC) {
       // 如果执行的非静态方法,则本地变量[0]=this
       // 这里获得的参数类型argTypes中不存在this,需要手动加
       Type[] extendedArgTypes = new Type[argTypes.length+1];
       System.arraycopy(argTypes, 0, extendedArgTypes, 1, argTypes.length);
       // 把this的type加到argTypes[0]
       extendedArgTypes[0] = Type.getObjectType(owner);
       argTypes = extendedArgTypes;
  }
   // 返回类型和size
   final Type returnType = Type.getReturnType(desc);
   final int retSize = returnType.getSize();
   switch (opcode) {
       // 四种方法调用
       case Opcodes.INVOKESTATIC:
       case Opcodes.INVOKEVIRTUAL:
       case Opcodes.INVOKESPECIAL:
       case Opcodes.INVOKEINTERFACE:
           // 构造污染参数集合,方法调用前先把操作数入栈
           final List<Set<T>> argTaint = new ArrayList<Set<T>>(argTypes.length);
           for (int i = 0; i < argTypes.length; i++) {
               // 占位
               argTaint.add(null);
          }
           for (int i = 0; i < argTypes.length; i++) {
               Type argType = argTypes[i];
               // 方法调用需要消耗掉这些参数,全部pop了
               if (argType.getSize() > 0) {
                   for (int j = 0; j < argType.getSize() - 1; j++) {
                       pop();
                  }
                   // 根据参数类型大小,从栈底获取入参,参数入栈是从右到左的
                   // 整体过程参考图片
                   argTaint.set(argTypes.length - 1 - i, pop());
              }
          }
           Set<T> resultTaint;
           // 构造
           if (name.equals("<init>")) {
               // 如果被调用的方法是构造方法,则直接通过this污染
               // 之前已经设置第0位是this
               resultTaint = argTaint.get(0);
          } else {
               resultTaint = new HashSet<>();
          }
           // void ObjectInputStream.defaultReadObject()
           if (owner.equals("java/io/ObjectInputStream") && name.equals("defaultReadObject") && desc.equals("()V")) {
               // this加入到局部变量表,污染与参数无关
               savedVariableState.localVars.get(0).addAll(argTaint.get(0));
          }
           // 这是一个很大的白名单,在下文给出
           for (Object[] passthrough : PASSTHROUGH_DATAFLOW) {
               // 恰好匹配到item
               if (passthrough[0].equals(owner) && passthrough[1].equals(name) && passthrough[2].equals(desc)) {
                   for (int i = 3; i < passthrough.length; i++) {
                       // 保存信息
                       resultTaint.addAll(argTaint.get((Integer)passthrough[i]));
                  }
              }
          }
           // 方法返回值与哪个参数有关系(见7,8)
           if (passthroughDataflow != null) {
               // 与哪个参数有关
               Set<Integer> passthroughArgs = passthroughDataflow.get(methodHandle);
               if (passthroughArgs != null) {
                   for (int arg : passthroughArgs) {
                       // 污点是第几个参数
                       resultTaint.addAll(argTaint.get(arg));
                  }
              }
          }
           // 如果对象实现java.util.Collection或java.util.Map
           // 则假定任何接受对象的方法都污染了collection
           // 假设任何返回对象的方法都返回集合的污点
           if (opcode != Opcodes.INVOKESTATIC && argTypes[0].getSort() == Type.OBJECT) {
               // 获取被调用函数的所有基类
               Set<ClassReference.Handle> parents = inheritanceMap.getSuperClasses(new ClassReference.Handle(argTypes[0].getClassName().replace('.', '/')));
               // 如果基类中存在collection或map
               if (parents != null && (parents.contains(new ClassReference.Handle("java/util/Collection")) ||
                       parents.contains(new ClassReference.Handle("java/util/Map")))) {
                   // 如果该类为集合类,则存储的所有元素都是污染
                   for (int i = 1; i < argTaint.size(); i++) {
                       argTaint.get(0).addAll(argTaint.get(i));
                  }
                   // 如果返回是对象或数组,认为this是污染
                   if (returnType.getSort() == Type.OBJECT || returnType.getSort() == Type.ARRAY) {
                       resultTaint.addAll(argTaint.get(0));
                  }
              }
          }
           // 如果有返回
           if (retSize > 0) {
               // 污染结果入栈
               push(resultTaint);
               // return的push从1开始,少1位
               for (int i = 1; i < retSize; i++) {
                   push();
              }
          }
           break;
       default:
           throw new IllegalStateException("Unsupported opcode: " + opcode);
  }
   // 传递
   super.visitMethodInsn(opcode, owner, name, desc, itf);
   // stack校验
   sanityCheck();
}

白名单

private static final Object[][] PASSTHROUGH_DATAFLOW = new Object[][] {
     { "java/lang/Object", "toString", "()Ljava/lang/String;", 0 },
     { "java/io/ObjectInputStream", "readObject", "()Ljava/lang/Object;", 0},
     { "java/io/ObjectInputStream", "readFields", "()Ljava/io/ObjectInputStream$GetField;", 0},
     ......
 }


5.7.7 visitInvokeDynamicInsn

动态调用方法

@Override
public void visitInvokeDynamicInsn(String name, String desc, Handle bsm, Object... bsmArgs) {
    // 参数总size
    int argsSize = 0;
    for (Type type : Type.getArgumentTypes(desc)) {
        argsSize += type.getSize();
    }
    // 返回size
    int retSize = Type.getReturnType(desc).getSize();
    // 方法调用需要pop参数
    for (int i = 0; i < argsSize; i++) {
        pop();
    }
    // 返回值需要push进去
    for (int i = 0; i < retSize; i++) {
        push();
    }
    // 传递
    super.visitInvokeDynamicInsn(name, desc, bsm, bsmArgs);
    // stack校验
    sanityCheck();
}

5.7.8 visitJumpInsn

跳到其他操作的操作

jump后应该处理Stack和局部变量表的问题

@Override
public void visitJumpInsn(int opcode, Label label) {
    switch (opcode) {
        // 取出栈顶元素判断
        case Opcodes.IFEQ:
        case Opcodes.IFNE:
        case Opcodes.IFLT:
        case Opcodes.IFGE:
        case Opcodes.IFGT:
        case Opcodes.IFLE:
        case Opcodes.IFNULL:
        case Opcodes.IFNONNULL:
            pop();
            break;
        // 取出栈顶两个元素判断
        case Opcodes.IF_ICMPEQ:
        case Opcodes.IF_ICMPNE:
        case Opcodes.IF_ICMPLT:
        case Opcodes.IF_ICMPGE:
        case Opcodes.IF_ICMPGT:
        case Opcodes.IF_ICMPLE:
        case Opcodes.IF_ACMPEQ:
        case Opcodes.IF_ACMPNE:
            pop();
            pop();
            break;
        // goto
        case Opcodes.GOTO:
            break;
        // 跳转子程序
        // push地址
        case Opcodes.JSR:
            push();
            super.visitJumpInsn(opcode, label);
            return;
        default:
            throw new IllegalStateException("Unsupported opcode: " + opcode);
    }
    // 合并goto后的stack和local variables
    mergeGotoState(label, savedVariableState);
    // 传递
    super.visitJumpInsn(opcode, label);
    // stack校验
    sanityCheck();
}
private void mergeGotoState(Label label, SavedVariableState savedVariableState) {
    if (gotoStates.containsKey(label)) {
        // goto需要合并stack和local variables
        SavedVariableState combinedState = new SavedVariableState(gotoStates.get(label));
        combinedState.combine(savedVariableState);
        // 出现过的label直接合并
        gotoStates.put(label, combinedState);
    } else {
        gotoStates.put(label, new SavedVariableState(savedVariableState));
    }
}


5.7.9 visitLabel

标签指定了紧接着它将被访问的指令

@Override
public void visitLabel(Label label) {
    // 如果跳转label已被初始化过
    if (gotoStates.containsKey(label)) {
        // 从已被初始化过的地方拿到stack和局部变量表信息
        savedVariableState = new SavedVariableState(gotoStates.get(label));
    }
    // 如果是异常label,类似return需要把异常push进去
    if (exceptionHandlerLabels.contains(label)) {
        // 只是占位
        push(new HashSet<T>());
    }
    // 传递
    super.visitLabel(label);
    // stack校验
    sanityCheck();
}

5.7.10 visitLdcInsn

常量池操作

@Override
public void visitLdcInsn(Object cst) {
    // push 常量池
    // size为2
    if (cst instanceof Long || cst instanceof Double) {
        push();
        push();
    } else {
        // size为1
        push();
    }
    // 传递
    super.visitLdcInsn(cst);
    // stack校验
    sanityCheck();
}

5.7.11 visitTableSwitchInsn

通过索引访问跳转表并跳转

@Override
public void visitTableSwitchInsn(int min, int max, Label dflt, Label... labels) {
    // pop index
    pop();
    // 根据跳转label合并状态
    mergeGotoState(dflt, savedVariableState);
    for (Label label : labels) {
        mergeGotoState(label, savedVariableState);
    }
    // 传递
    super.visitTableSwitchInsn(min, max, dflt, labels);
    // stack校验
    sanityCheck();
}


5.7.12 visitLookupSwitchInsn

通过键匹配和跳转访问跳转表

任何跳转都需要处理Stack和局部变量表

@Override
public void visitLookupSwitchInsn(Label dflt, int[] keys, Label[] labels) {
    // pop key
    pop();
    // 根据跳转label合并状态
    mergeGotoState(dflt, savedVariableState);
    for (Label label : labels) {
        mergeGotoState(label, savedVariableState);
    }
    // 传递
    super.visitLookupSwitchInsn(dflt, keys, labels);
    // stack校验
    sanityCheck();
}


5.7.13 visitMultiANewArrayInsn

创建新的多维数组

@Override
public void visitMultiANewArrayInsn(String desc, int dims) {
   // 每个维度有个size
   for (int i = 0; i < dims; i++) {
       pop();
  }
   // 创建完把引用push回去
   push();
   // 传递
   super.visitMultiANewArrayInsn(desc, dims);
   // stack校验
   sanityCheck();
}


5.7.14 visitOthers

这部分基本没有业务逻辑,也没有POP/PUSH操作

@Override
public AnnotationVisitor visitInsnAnnotation(int typeRef, TypePath typePath, String desc, boolean visible) {
   return super.visitInsnAnnotation(typeRef, typePath, desc, visible);
}
@Override
public void visitTryCatchBlock(Label start, Label end, Label handler, String type) {
   // 异常label保存
   exceptionHandlerLabels.add(handler);
   super.visitTryCatchBlock(start, end, handler, type);
}
@Override
public AnnotationVisitor visitTryCatchAnnotation(int typeRef, TypePath typePath, String desc, boolean visible) {
   return super.visitTryCatchAnnotation(typeRef, typePath, desc, visible);
}
@Override
public void visitMaxs(int maxStack, int maxLocals) {
   super.visitMaxs(maxStack, maxLocals);
}
@Override
public void visitEnd() {
   // visit完毕
   super.visitEnd();
}


相关文章
|
6月前
|
设计模式 前端开发 Java
【深入浅出Spring原理及实战】「夯实基础系列」360全方位渗透和探究SpringMVC的核心原理和运作机制(总体框架原理篇)
【深入浅出Spring原理及实战】「夯实基础系列」360全方位渗透和探究SpringMVC的核心原理和运作机制(总体框架原理篇)
69 0
|
4月前
|
运维 自然语言处理 监控
软件研发核心问题之在需求拆解过程中,“需要多少UI”的问题如何解决
软件研发核心问题之在需求拆解过程中,“需要多少UI”的问题如何解决
|
5月前
|
C++
C++核心技术要点《异常处理详解》
C++核心技术要点《try-throw-catch异常处理详解》
51 2
|
5月前
|
数据采集 存储 监控
构建高效爬虫系统:设计思路与案例分析
构建高效爬虫系统涉及关键模块如爬虫引擎、链接存储、内容处理器等,以及用户代理池、IP代理池等反反爬策略。评估项目复杂性考虑数据规模、网站结构、反爬虫机制等因素。案例分析展示了电子商务价格比较爬虫的设计,强调了系统模块化、错误处理和合规性的重要性。爬虫技术需要不断进化以应对复杂网络环境的挑战。
127 1
|
6月前
|
存储 算法 编译器
C++性能调优:从代码层面提升程序效率
本文探讨了C++程序性能调优的关键点:选择合适的数据结构和算法,例如用哈希表(如`std::unordered_map`)替换低效的数组或链表;减少不必要的内存分配和释放,利用智能指针和容器如`std::vector`自动管理内存;优化循环和条件语句,例如在循环外存储数组大小;利用编译器优化如`-O2`或`-O3`;以及使用性能分析工具如`gprof`、`callgrind`和`perf`识别并解决性能瓶颈。通过这些方法,可以有效提升C++程序的运行效率。
|
6月前
|
资源调度 供应链 监控
深入探究:ERP系统的核心模块解析
深入探究:ERP系统的核心模块解析
322 0
|
XML 开发框架 安全
C#学习核心知识总结
C#学习核心知识总结
53 1
每日一道面试题之软件体系结构的核心要素有哪些?各起什么作用?
每日一道面试题之软件体系结构的核心要素有哪些?各起什么作用?
电子游戏的核心原理
你小时候有没有玩过这样一种玩具:一块硬纸,一面画着一只鸟,一面画着一个笼子。硬纸下粘上一根细棒。用手来回转动细棒,让硬纸的两面快速交替出现,就会看见鸟被关在了笼子里。
|
安全 前端开发 Java
深入分析GadgetInspector核心代码(一)
深入分析GadgetInspector核心代码
244 1
深入分析GadgetInspector核心代码(一)