现象:ArrayIndexOutOfBoundsException
- 经过反编译插桩后重打包的apk运行时报错,关键信息如下
java.lang.ArrayIndexOutOfBoundsException: src.length=4 srcPos=-1 dst.length=4 dstPos=0 length=1
at java.lang.System.arraycopy(Native Method)
at android.support.v4.util.SimpleArrayMap.put(Unknown Source)
定位问题
- 排除了插桩等其它环节的影响后,最终定位到问题出在使用dex2jar将dex转jar的时候,并在github上找到别人提交的issue: github.com/pxb1988/dex…,他帮忙定位出了问题的关键 “~运算符在反编译代码中消失了” ,接下来就是验证问题是否真的出在~运算符上。

- 使用反编译工具jadx查看经过dex2jar反编译前后代码差别,到这里就确定是dex2jar转换后丢失了~运算符导致了这个数组越界问题。
解决问题
- 在前面提到的issue中,我们了解到“~位补码运算符在旧的编译器中被编译成IXOR-1,而D8将其编译成NOT-INT,not-int不是Java字节码指令,所以它失败了”。查看旧的编译器中SimpleArrayMap源码,发现确实如此。
- 通过上述信息,在源码中搜索IXOR、NOT-INT、NOT关键词,一通乱改各种尝试后,最终定位到函数
com.googlecode.d2j.converter.IR2JConverter#reBuildE1Expression,修改时确实找不到not-int对应的字节码,那就按照旧编译器的规则来,用IXOR-1来修改,最终修改如下
com.googlecode.d2j.converter.IR2JConverter#reBuildE1Expression 修改后完整代码如下
private static void reBuildE1Expression(E1Expr e1, MethodVisitor asm) {
accept(e1.getOp(), asm);
switch (e1.vt) {
case STATIC_FIELD: {
FieldExpr fe = (FieldExpr) e1;
asm.visitFieldInsn(GETSTATIC, toInternal(fe.owner), fe.name, fe.type);
break;
}
case FIELD: {
FieldExpr fe = (FieldExpr) e1;
asm.visitFieldInsn(GETFIELD, toInternal(fe.owner), fe.name, fe.type);
break;
}
case NEW_ARRAY: {
TypeExpr te = (TypeExpr) e1;
switch (te.type.charAt(0)) {
case '[':
case 'L':
asm.visitTypeInsn(ANEWARRAY, toInternal(te.type));
break;
case 'Z':
asm.visitIntInsn(NEWARRAY, T_BOOLEAN);
break;
case 'B':
asm.visitIntInsn(NEWARRAY, T_BYTE);
break;
case 'S':
asm.visitIntInsn(NEWARRAY, T_SHORT);
break;
case 'C':
asm.visitIntInsn(NEWARRAY, T_CHAR);
break;
case 'I':
asm.visitIntInsn(NEWARRAY, T_INT);
break;
case 'F':
asm.visitIntInsn(NEWARRAY, T_FLOAT);
break;
case 'J':
asm.visitIntInsn(NEWARRAY, T_LONG);
break;
case 'D':
asm.visitIntInsn(NEWARRAY, T_DOUBLE);
break;
}
}
break;
case CHECK_CAST:
case INSTANCE_OF: {
TypeExpr te = (TypeExpr) e1;
asm.visitTypeInsn(e1.vt == VT.CHECK_CAST ? CHECKCAST : INSTANCEOF, toInternal(te.type));
}
break;
case CAST: {
CastExpr te = (CastExpr) e1;
cast2(e1.op.valueType, te.to, asm);
}
break;
case LENGTH:
asm.visitInsn(ARRAYLENGTH);
break;
case NEG:
asm.visitInsn(getOpcode(e1, INEG));
break;
case NOT: // fix issues
if (e1.getOp().valueType.equals("I")) {
asm.visitLdcInsn(-1);
asm.visitInsn(getOpcode(e1, IXOR));
} else if (e1.getOp().valueType.equals("J")) { // 不区分Long型的话在jar转回dex时会报错
asm.visitLdcInsn(-1L);
asm.visitInsn(getOpcode(e1, IXOR)); // 上面的-1控制为Long,这边需用IXOR,不能用LXOR,不知道为什么,否则转回dex会报错
}
break;
}
}
- 查看修改后的dex2jar将dex转为jar的代码