iBotPeaches / Apktool

A tool for reverse engineering Android apk files
https://apktool.org/
Apache License 2.0
19.99k stars 3.57k forks source link

APKTool 2.0.2 - ClassCastException ResStringValue -> ResAttr #1060

Closed kvandermast closed 8 years ago

kvandermast commented 8 years ago

We are using a MobileIron Store to distribute our enterprise apps to the employees. We just released a new version and the service team is unable to upload the APK to the Store. I can trace it back to MobileIron decompile the APK (God knows why).

When I try to decompile the apk via command line, it throws the ClassCastException:

$ ./apktool d ~/Desktop/APK-Playground/fancyapp.apk 
I: Using Apktool 2.0.2 on fancyapp.apk
I: Loading resource table...
I: Decoding AndroidManifest.xml with resources...
I: Loading resource table from file: /Users/ecas/Library/apktool/framework/1.apk
Exception in thread "main" java.lang.ClassCastException: brut.androlib.res.data.value.ResStringValue cannot be cast to brut.androlib.res.data.value.ResAttr
    at brut.androlib.res.decoder.ResAttrDecoder.decode(ResAttrDecoder.java:38)
    at brut.androlib.res.decoder.AXmlResourceParser.getAttributeValue(AXmlResourceParser.java:369)
    at org.xmlpull.v1.wrapper.classic.XmlPullParserDelegate.getAttributeValue(XmlPullParserDelegate.java:69)
    at org.xmlpull.v1.wrapper.classic.StaticXmlSerializerWrapper.writeStartTag(StaticXmlSerializerWrapper.java:267)
    at org.xmlpull.v1.wrapper.classic.StaticXmlSerializerWrapper.event(StaticXmlSerializerWrapper.java:211)
    at brut.androlib.res.decoder.XmlPullStreamDecoder$1.event(XmlPullStreamDecoder.java:83)
    at brut.androlib.res.decoder.XmlPullStreamDecoder.decode(XmlPullStreamDecoder.java:141)
    at brut.androlib.res.decoder.XmlPullStreamDecoder.decodeManifest(XmlPullStreamDecoder.java:153)
    at brut.androlib.res.decoder.ResFileDecoder.decodeManifest(ResFileDecoder.java:140)
    at brut.androlib.res.AndrolibResources.decodeManifestWithResources(AndrolibResources.java:199)
    at brut.androlib.Androlib.decodeManifestWithResources(Androlib.java:140)
    at brut.androlib.ApkDecoder.decode(ApkDecoder.java:100)
    at brut.apktool.Main.cmdDecode(Main.java:165)
    at brut.apktool.Main.main(Main.java:81)

Environment:

s$ java -version
java version "1.8.0_31"
Java(TM) SE Runtime Environment (build 1.8.0_31-b13)
Java HotSpot(TM) 64-Bit Server VM (build 25.31-b07, mixed mode)

Android build tools:

Let me know if you need more information.

Regards, Kris

CunningLogic commented 8 years ago

Can you share this apk?

kvandermast commented 8 years ago

Hmm, I'm afraid not (NDA and other legal restrictions). I'll try to wiz-up an app which I can share (using the same config, but without the code).

CunningLogic commented 8 years ago

If you can whip up one with the same error, ill take a look

kvandermast commented 8 years ago

Well, the good news is that the Android app I tried to whip-up doesn't have this problem. I've taken the liberty of downloading the APKTool sources and followed the stacktrace to where the exception is thrown.

I updated the code to:

public class ResAttrDecoder {
    public String decode(int type, int value, String rawValue, int attrResId)
            throws AndrolibException {
        ResScalarValue resValue = mCurrentPackage.getValueFactory().factory(
                type, value, rawValue);

        String decoded = null;
        if (attrResId > 0) {
            try {
                ResTable resTable = getCurrentPackage().getResTable();

                ResResource o = resTable.getResSpec(attrResId).getDefaultResource();

                try {
                    ResAttr attr = (ResAttr) getCurrentPackage().getResTable()
                            .getResSpec(attrResId).getDefaultResource().getValue();

                    decoded = attr.convertToResXmlFormat(resValue);
                } catch (ClassCastException e) {
                    System.out.println("DECODING ---------------------------------------------");
                    System.out.println("ResValue : " + resValue.toString());
                    System.out.println("Fullname: " + resTable.getResSpec(attrResId).getFullName());
                    System.out.println("Trying to decode RawValue: " + rawValue);

                    ResStringValue val = (ResStringValue) o.getValue();

                    System.out.println("ResStringValue: " + val.toString());
                    System.out.println("Value contains : " + val.encodeAsResXmlValue());

                    decoded = val.encodeAsResXmlValue();

                }

            } catch (UndefinedResObject ex) {
                // ignored
            }
        }

        return decoded != null ? decoded : resValue.encodeAsResXmlAttr();
    }
...

And I get this as output:

DECODING ---------------------------------------------
ResValue : ResScalarValue{mType='bool', mRawValue='null'}
Fullname: android:attr/private_resource_pad22
Trying to decode RawValue: null
ResStringValue: ResScalarValue{mType='string', mRawValue='padding'}
Value contains : padding

It also gets this error after parsing the "application" tag of the manifest. Strangely enough, I have no values or attributes defined as "padding", nor the private_resource_pad22.

Could it be related to any of the libraries which are included?

    compile 'com.android.support:support-v4:23.0.1'
    compile 'com.android.support:appcompat-v7:23.0.1'
    compile 'com.android.support:design:23.0.1'
    compile 'com.google.android.gms:play-services:7.8.0'
    compile 'com.google.android.gms:play-services-gcm:7.8.0'
    compile 'com.google.android.gms:play-services-wearable:7.8.0'
    compile 'com.jakewharton:butterknife:6.1.0'
    compile 'com.squareup.okhttp:okhttp:2.4.0'
    compile 'javax.inject:javax.inject:1'
    compile 'javax.annotation:javax.annotation-api:1.2'
    apt 'com.google.dagger:dagger-compiler:2.0'
    compile 'com.google.dagger:dagger:2.0'
    provided 'org.glassfish:javax.annotation:10.0-b28'
iBotPeaches commented 8 years ago

Yeah, I see the problem. Attributes should not be decoded as a ResStringValue as that is not part of the ResAttr. I could probably handle the conversion from ResStringValue to ResAttr, or catch the ClassCast exception and treat it as the raw ResValue, in which case will decode it via resValue.encodeAsResXmlAttr(), which will call encodeAsXmlValue and thus return the string of the attribute.

What you could do to help is dump the attrResId and then search aapt with the apk via aapt d --values resources name_of_private.apk | grep 'resource id'. Then remove the values/NDA stuff to let me see what aapt is reporting the value as before I go patching something incorrectly.

kvandermast commented 8 years ago

Dumped the (int) value, which is 16844011. The grep didn't return any results though (not even for its hex-variant)...

...
                } catch (ClassCastException e) {
                    System.out.println("DECODING ---------------------------------------------");
                    System.out.println("AttrResId: " + attrResId);
                    System.out.println(">>> " + o.getResSpec().getId().toString());
...

Output:

DECODING ---------------------------------------------
AttrResId: 16844011
>>> 0x010104eb

Grep result:

$ ./build-tools/23.0.1/aapt d --values resources ~/private.apk | grep -i 16844011
(no output)
$ ./build-tools/23.0.1/aapt d --values resources ~/private.apk | grep -i 0x010104eb
(no output)
iBotPeaches commented 8 years ago

^ Give that commit a shot.

kvandermast commented 8 years ago

Works like a charm! Thanks :+1:

jeetjshah commented 8 years ago

@kvandermast Has the service team been able to resolve the issue with uploading the to the MobileIron MDM? Our team is experiencing a similar issue at the moment.

kvandermast commented 8 years ago

@jeetjshah the issue has been escalated to MobileIron, whom recognized the problem and are preparing a patch release to fix the issue. A release date has not been communicated. But tests on the development environment indicated that the problem was fixed with a new version of the tool.

carl-wallace commented 8 years ago

I ran into this issue too. I tried versions of apktool until I got the same error as Mobile Iron (2.0.0-RC4). I finally noticed that I was failing on the same resource identifier as noted above. Googling this value got a hit on the android:fullBackupContent attribute that I had defined in the application element in AndroidManifest.xml (see http://developer.android.com/reference/android/R.attr.html). Removing that value from the manifest allows apktool 2.0.0RC4 to work and I can load it into Mobile Iron.

iBotPeaches commented 8 years ago

A lot of bugs are happening related to casting types. The problem stems from styles.xml becoming a more robust file with support for booleans/references/parents/ids/strings/etc. These all have to be mapped in a format that we can write back out to the user.

The current fixes of simply identifying types and reacting is hacky but will work in short term. The issue this was reported for is fixed. If there are other apks that experience ClassCastExceptions from either Strings or Styles then open another bug with the apk referencing this one.