soot icon indicating copy to clipboard operation
soot copied to clipboard

RuntimeException(Trying to cast reference type to a primitive) when repacking android app.

Open am009 opened this issue 3 years ago • 0 comments

I'm trying instrumenting android apps. This feature is awesome. After digging into this exception, I think it's a bug that worth reporting.

Describe the bug APK file: native_leak.zip. (rename it to native_leak.apk) The related method is replace in android.support.constraint.solver.LinearEquation:

<android.support.constraint.solver.LinearEquation: void replace(android.support.constraint.solver.SolverVariable,android.support.constraint.solver.LinearEquation,java.util.ArrayList)>

Source produced by Jadx:

private void replace(SolverVariable solverVariable, LinearEquation linearEquation, ArrayList<EquationVariable> arrayList) {
    EquationVariable find = find(solverVariable, arrayList);
    if (find != null) {
        arrayList.remove(find);
        Amount amount = find.getAmount();
        ArrayList<EquationVariable> arrayList2 = linearEquation.mRightSide;
        int size = arrayList2.size();
        for (int i = 0; i < size; i++) {
            arrayList.add(new EquationVariable(amount, arrayList2.get(i)));
        }
    }
}

The important part is the int i in for loop. The following is the smali code in Jadx. It seems that p2(previously used to store 2nd argument) is reused as int i. But in Jimple, it's still recognized as a java.lang.Object. the const/4 p2, 0x0 (i=0) becomes $u5#9 = null;. i++becomes $u5#9 = $u5#9 + 1;.

Related smali code:

    .line 487
    iget-object p1, p2, Landroid/support/constraint/solver/LinearEquation;->mRightSide:Ljava/util/ArrayList;

    const/4 p2, 0x0

    .line 488
    invoke-virtual {p1}, Ljava/util/ArrayList;->size()I

    move-result v0

    :goto_14
    if-ge p2, v0, :cond_27

    .line 489
    invoke-virtual {p1, p2}, Ljava/util/ArrayList;->get(I)Ljava/lang/Object;

The full jimple for this method is:

private void replace(android.support.constraint.solver.SolverVariable, android.support.constraint.solver.LinearEquation, java.util.ArrayList)
{
    android.support.constraint.solver.LinearEquation this, $u5;
    java.util.ArrayList $u6, $u4#8;
    android.support.constraint.solver.Amount $u-1;
    android.support.constraint.solver.SolverVariable $u4;
    int $u-1#10, $i0;
    java.lang.Object $u5#9, $u-1#11;
    android.support.constraint.solver.EquationVariable $u2, $u1, $u0;

    this := @this: android.support.constraint.solver.LinearEquation;

    $u4 := @parameter0: android.support.constraint.solver.SolverVariable;

    $u5 := @parameter1: android.support.constraint.solver.LinearEquation;

    $u6 := @parameter2: java.util.ArrayList;

    $u2 = specialinvoke this.<android.support.constraint.solver.LinearEquation: android.support.constraint.solver.EquationVariable find(android.support.constraint.solver.SolverVariable,java.util.ArrayList)>($u4, $u6);

    if $u2 == null goto label2;

    virtualinvoke $u6.<java.util.ArrayList: boolean remove(java.lang.Object)>($u2);

    $u-1 = virtualinvoke $u2.<android.support.constraint.solver.EquationVariable: android.support.constraint.solver.Amount getAmount()>();

    $u4#8 = $u5.<android.support.constraint.solver.LinearEquation: java.util.ArrayList mRightSide>;

    $u5#9 = null;

    $u-1#10 = virtualinvoke $u4#8.<java.util.ArrayList: int size()>();

 label1:
    $i0 = (int) $u5#9;

    if $i0 >= $u-1#10 goto label2;

    $i0 = (int) $u5#9;

    $u-1#11 = virtualinvoke $u4#8.<java.util.ArrayList: java.lang.Object get(int)>($i0);

    $u1 = (android.support.constraint.solver.EquationVariable) $u-1#11;

    $u0 = new android.support.constraint.solver.EquationVariable;

    specialinvoke $u0.<android.support.constraint.solver.EquationVariable: void <init>(android.support.constraint.solver.Amount,android.support.constraint.solver.EquationVariable)>($u-1, $u1);

    virtualinvoke $u6.<java.util.ArrayList: boolean add(java.lang.Object)>($u0);

    $u5#9 = $u5#9 + 1;

    goto label1;

 label2:
    return;
}

The statement that results in the exception is $i0 = (int) $u5#9;. The exception is thown when calling DexPrinter.add() on the statement.

Input file github says it doesn't support apk file, so I changed the extension to zip. native_leak.zip and rename it to native_leak.apk

To reproduce After switching to 4.3.0-SNAPSHOT in my pom.xml, the issue still exist. The java code is basically APK to Jimple and back to APK. For the ease of debugging, I directly call DexPrinter.add with the class. Related java code:

public class APKRepacker {
    private static final Logger logger = LoggerFactory.getLogger(APKRepacker.class);
    // public boolean forceAndroidJar;
    public String apkFileLocation;
    public String platformDir;

    APKRepacker(String apkFileLocation, String platformDir) {
        this.apkFileLocation = apkFileLocation;
        this.platformDir = platformDir;
    }

    private String getClasspath() {
        String classpath = Scene.v().getAndroidJarPath(platformDir, apkFileLocation);
        logger.debug("soot classpath: " + classpath);
        return classpath;
    }

    public void initializeSoot() {
        logger.info("Initializing Soot...");

        G.reset();

        Options.v().set_no_bodies_for_excluded(true);
        Options.v().set_allow_phantom_refs(true);
        Options.v().set_output_format(Options.output_format_dex);
        Options.v().set_whole_program(true);
        Options.v().set_process_dir(Collections.singletonList(apkFileLocation));
        Options.v().set_android_jars(platformDir);
        Options.v().set_src_prec(Options.src_prec_apk_class_jimple);
        Options.v().set_keep_offset(true);
        Options.v().set_keep_line_number(true);
        Options.v().set_throw_analysis(Options.throw_analysis_dalvik);
        Options.v().set_process_multiple_dex(true);
        // Options.v().set_ignore_resolution_errors(true);

        // Set soot phase option if original names should be used
        Options.v().setPhaseOption("jb", "use-original-names:true");

        Options.v().set_soot_classpath(getClasspath());
        Main.v().autoSetOptions();

        // Load whatever we need
        logger.info("Loading dex files...");
        Scene.v().loadNecessaryClasses();

        // Make sure that we have valid Jimple bodies
        PackManager.v().getPack("wjpp").apply();
    }
    public static void outputAPK() {
        Options.v().set_output_format(Options.output_format_dex);
        SootClass c = Scene.v().getSootClassUnsafe("android.support.constraint.solver.LinearEquation");
        DexPrinter dexPrinter = new DexPrinter();
        dexPrinter.add(c);
        // PackManager.v().writeOutput();
    }
    public static void main(String[] args) {
        logger.info("args: " + args.toString());
        APKRepacker r = new APKRepacker(args[0], args[1]);
        r.initializeSoot();
        APKRepacker.outputAPK();
    }
}

Stacktrace

Exception in thread "main" soot.toDex.DexPrinterException: Error while processing method <android.support.constraint.solver.LinearEquation: void replace(android.support.constraint.solver.SolverVariable,android.support.constraint.solver.LinearEquation,java.util.ArrayList)>
        at soot.toDex.DexPrinter.toMethods(DexPrinter.java:1079)
        at soot.toDex.DexPrinter.addAsClassDefItem(DexPrinter.java:659)
        at soot.toDex.DexPrinter.add(DexPrinter.java:1686)
        at APKRepacker.outputAPK(APKRepacker.java:128)
        at APKRepacker.main(APKRepacker.java:137)
Caused by: java.lang.RuntimeException: Trying to cast reference type java.lang.Object to a primitive
        at soot.toDex.ExprVisitor.castPrimitive(ExprVisitor.java:684)
        at soot.toDex.ExprVisitor.caseCastExpr(ExprVisitor.java:639)
        at soot.jimple.internal.AbstractCastExpr.apply(AbstractCastExpr.java:136)
        at soot.toDex.StmtVisitor.caseAssignStmt(StmtVisitor.java:498)
        at soot.jimple.internal.JAssignStmt.apply(JAssignStmt.java:215)
        at soot.toDex.DexPrinter.toInstructions(DexPrinter.java:1560)
        at soot.toDex.DexPrinter.toMethodImplementation(DexPrinter.java:1192)
        at soot.toDex.DexPrinter.toMethods(DexPrinter.java:1077)
        at soot.toDex.DexPrinter.addAsClassDefItem(DexPrinter.java:659)
        at soot.toDex.DexPrinter.add(DexPrinter.java:1686)

am009 avatar Apr 20 '22 13:04 am009