So you want to reverse-engineer an Android app (apk)

Maybe your favorite app is abandonware and it has a bug. Maybe you found a really nice social media Isolatr client, but unfortunately, it does absolutely nothing and you want to add its first feature and never share it with anyone. There are lots of reasons — good reasons — to reverse engineer Android apps. And it’s fun. But seriously: don’t infringe on anyone’s copyright. That’s just rude, and illegal.

So I’m going to show you how. To make matters simple, we’ll reverse engineer an app that’s incredibly simple. My own, relatively cheesy, GPS Share. These instructions are Linux, but within reason, you should be able to figure them out on any platform.

First, you want to assemble a toolbix of goodies. Go grab:

Using Android APK Tool to peek into APK file

Now then. I’ll download the apk file and unpack it with Android APK Tool.

$ wget -q http://kkinder.com/wp-content/uploads/2011/02/GPSShare-1.3.2.apk
$ apktool decode GPSShare-1.3.2.apk GPSShare
I: Baksmaling...
I: Loading resource table...
I: Decoding resources...
I: Loading resource table from file: /home/kkinder/apktool/framework/1.apk
I: Copying assets and libs...

It will have unpacked a nice little source tree.

$ tree
.
├── GPSShare
│   ├── AndroidManifest.xml
│   ├── apktool.yml
│   ├── res
│   │   ├── drawable-hdpi
│   │   │   └── icon.png
│   │   ├── drawable-ldpi
│   │   │   └── icon.png
│   │   ├── drawable-mdpi
│   │   │   └── icon.png
│   │   ├── layout
│   │   │   ├── about.xml
│   │   │   └── main.xml
│   │   ├── menu
│   │   │   └── mainmenu.xml
│   │   ├── values
│   │   │   ├── ids.xml
│   │   │   ├── public.xml
│   │   │   └── strings.xml
│   │   └── xml
│   │       └── preferences.xml
│   └── smali
│       └── com
│           └── kkinder
│               └── sharelocation
│                   ├── Preferences.smali
│                   ├── R$attr.smali
│                   ├── R$drawable.smali
│                   ├── R$id.smali
│                   ├── R$layout.smali
│                   ├── R$menu.smali
│                   ├── R.smali
│                   ├── R$string.smali
│                   ├── R$xml.smali
│                   ├── Sharelocation$1.smali
│                   ├── Sharelocation$2$1.smali
│                   ├── Sharelocation$2.smali
│                   ├── Sharelocation$3.smali
│                   └── Sharelocation.smali
└── GPSShare-1.3.2.apk
 
13 directories, 27 files

Notice that this created .smali files instead of Java files. Those are a direct output of the disassembly from Java virtual machine language and the most reliable way to tell, accurately, what the code does. That’s because unlike bytecode decompiled into Java code, there’s no guesswork about how to translate the vm instructions: it’s a direct mapping.

Smali is human-editable, human-readable, and you can learn more about it from the Smali project page.

From here, you can actually re-assmeble this into an APK:

kkinder@kkinder-laptop ~/gps-share-reverse> apktool build GPSShare GPSShare-rebuilt.apk
I: Checking whether sources has changed...
I: Smaling...
I: Checking whether resources has changed...
I: Building resources...
I: Building apk file...

Real Java (sort of)

For some reverse-engineering purposes, this alone might be enough, but I like to be able to read Java projects in Java source code, and for this purpose, JD-GUI is handy. First, convert the APK file into a Jar file, then run JD-GUI:

$ <path-to>/dex2jar.sh GPSShare-1.3.2.apk 
0 [main] INFO com.googlecode.dex2jar.v3.Main - version:0.0.7.9-SNAPSHOT
5 [main] INFO com.googlecode.dex2jar.v3.Main - dex2jar GPSShare-1.3.2.apk -> GPSShare-1.3.2.apk.dex2jar.jar
188 [main] INFO com.googlecode.dex2jar.v3.Main - Done.
$ jd-gui GPSShare-1.3.2.apk.dex2jar.jar

You’ll find that JD-GUI has, well, a handy GUI:

JD-GUI Screenshot

It isn’t perfection, but it’s Java. If you’re lucky, you’ll even get some original identifier names. A few things to note, however:

  • The “R” class is generated by the Android SDK, and shouldn’t be in the source tree.
  • Throughout, replaced references to R with absolute values.
  • JD-GUI expanded by activity subclass, Sharelocation, into Sharelocation, Sharelocation$1, Sharelocation$2, Sharelocation$2$1, and Sharelocation$3 (this, BTW, is also what Smali produced, but you can compile that back together just fine in APK Tool).

Getting to a workable project in Eclipse

Use the “Save All Sources” int JD-GUI to save a zip file with the kind of/sort of workable Java code. Unzip that file, and move it into our project directory:

$ unzip GPSShare-1.3.2.apk.dex2jar.src.zip 
Archive:  GPSShare-1.3.2.apk.dex2jar.src.zip
  inflating: com/kkinder/sharelocation/Preferences.java  
  inflating: com/kkinder/sharelocation/R.java  
  inflating: com/kkinder/sharelocation/Sharelocation$1.java  
  inflating: com/kkinder/sharelocation/Sharelocation$2$1.java  
  inflating: com/kkinder/sharelocation/Sharelocation$2.java  
  inflating: com/kkinder/sharelocation/Sharelocation$3.java  
  inflating: com/kkinder/sharelocation/Sharelocation.java  
$ mkdir GPSShare/src
$ mv com/ GPSShare/src

This is what the GPSShare directory should look like:

.
├── AndroidManifest.xml
├── apktool.yml
├── build
│   └── apk
│       ├── AndroidManifest.xml
│       ├── classes.dex
│       ├── res
│       │   ├── drawable-hdpi
│       │   │   └── icon.png
│       │   ├── drawable-ldpi
│       │   │   └── icon.png
│       │   ├── drawable-mdpi
│       │   │   └── icon.png
│       │   ├── layout
│       │   │   ├── about.xml
│       │   │   └── main.xml
│       │   ├── menu
│       │   │   └── mainmenu.xml
│       │   └── xml
│       │       └── preferences.xml
│       └── resources.arsc
├── res
│   ├── drawable-hdpi
│   │   └── icon.png
│   ├── drawable-ldpi
│   │   └── icon.png
│   ├── drawable-mdpi
│   │   └── icon.png
│   ├── layout
│   │   ├── about.xml
│   │   └── main.xml
│   ├── menu
│   │   └── mainmenu.xml
│   ├── values
│   │   ├── ids.xml
│   │   ├── public.xml
│   │   └── strings.xml
│   └── xml
│       └── preferences.xml
├── smali
│   └── com
│       └── kkinder
│           └── sharelocation
│               ├── Preferences.smali
│               ├── R$attr.smali
│               ├── R$drawable.smali
│               ├── R$id.smali
│               ├── R$layout.smali
│               ├── R$menu.smali
│               ├── R.smali
│               ├── R$string.smali
│               ├── R$xml.smali
│               ├── Sharelocation$1.smali
│               ├── Sharelocation$2$1.smali
│               ├── Sharelocation$2.smali
│               ├── Sharelocation$3.smali
│               └── Sharelocation.smali
└── src
    └── com
        └── kkinder
            └── sharelocation
                ├── Preferences.java
                ├── R.java
                ├── Sharelocation$1.java
                ├── Sharelocation$2$1.java
                ├── Sharelocation$2.java
                ├── Sharelocation$3.java
                └── Sharelocation.java
 
25 directories, 43 files

You should now have both the smali and Java sources in your project directory. Let’s fire it up in Eclipse. When you create a new Android Project, choose “create project from existing source” and select the GPSShare directory. You also want to enable the Google API’s.

Eclipse Project Screenshot

Now you can fix up the Java project. Fortunately, your res, assets, and AndroidManifest.xml are all there from the reverse engineered project, leaving the source code for you to sort out (lucky you).

Notice the R.java file. As you probably know, the SDK generates an R class for referencing static XML resources. You may be tempted to delete the R.class file, but it’s better to just set it aside somewhere outside of your project so you can reference it as you fix up your code. Having it around makes it easier to figure out how to reference R.whatever identifiers throughout your project. For example, take a look at Preferences.java:

package com.kkinder.sharelocation;
 
import android.os.Bundle;
import android.preference.PreferenceActivity;
 
public class Preferences extends PreferenceActivity
{
  protected void onCreate(Bundle paramBundle)
  {
    super.onCreate(paramBundle);
    addPreferencesFromResource(2130968576);
  }
}

Obviously, we’ll want to change addPreferencesFromResource to use an R.identifier instead of a numeric constant that depended on our now-deleted R class. Notice from R.class:

  public final class xml
  {
    public static final int preferences = 2130968576;
  }

Aha. So you can edit Preferences.java to reference that identifier by name instead of generated numeric value:

    addPreferencesFromResource(R.xml.preferences);

Use this process everywhere you find a numeric ID being passed as a resource identifier.

There are some other tweaks you have to make on a line-by-line basis. Methods that take booleans will have a 1 or 0 passed to them, and you need to make those true or false. Anonymous functions (in the form of Runnable classes) will appear expanded to while (True) loops that look truly awful.

But most noticeably, my own Sharelocation class is expanded out into several files. Those are all the anonymous classes in my project, sort of.

14 comments

  1. Kenny says:

    Interesting article! I have tested A to Z, but every time my device comes with error “Application Not Installed”. Finally got the answer, reason is android OS reject un-signed Apps. You can sign your APK while using signapk tool. Google it!!!

  2. Garth Miller says:

    Everything seems to work for me until I try to create a new android project in Eclipse. then Eclipse tells me: “Error: Case Variant Exists”. I think this means there is a problem with two things that have the same name except one uses an upper case letter and one uses a lower case letter. Is there any hope of solving this error?

  3. Dan says:

    Great article. How should the anonymous classes where they are expanded into several files be “put back together”?

    Would love an explanation if you have the time. I have a class which has been subdivided into several classes ending in $1, $2 etc. and now it does not run correctly on my phone.

  4. asdf says:

    I have followed the instructions closely, but when I open the decompiled Xperia Home.apk in Eclipse, it is riddled with errors. Some of them so obvious I can notice them without the help of Eclipse. For example functions starting with a return command.

  5. Johan Du Plessis says:

    I have gone throug the process described here and the project is in Eclipse. I have decompiled Samsung Note.apk. I have an error in an xml called styles.xml. The content is

    The “parent=” part is highlighted in eclipse and when I hover over it the error:
    Illegal resource reference: @*android resources are private and not always present

    How do I resolve this error

  6. George says:

    Hello and thanks for the tips.I decompiled successfully an apk and created a project in eclipse.
    But in the most classes (in src folder ) it has errors.The errors aren’t from R classes.
    For example:

    public class Movie
    implements Parcelable
    {
    public static final Parcelable.Creator CREATOR = new Parcelable.Creator()
    {
    public Movie createFromParcel(Parcel paramAnonymousParcel)
    {
    return new Movie(paramAnonymousParcel, null);
    }

    hits an error in “return new Movie(paramAnonymousParcel, null);” which says :

    The constructor Movie(Parcell,null) is undefined.

    OR

    public class MovieListPagerAdapter extends FragmentPagerAdapter -> “ragmentPagerAdapter cannot be resolved to a type”

    OR
    public MovieListPagerAdapter(Context paramContext, FragmentManager paramFragmentManager, ViewPager paramViewPager)
    {
    super(paramFragmentManager);
    Log.d(“MovieListPagerAdapter”, “MovieListPagerAdapter : setting pager’s onChangeListener, recording the view pager’s id”);
    this.fm = paramFragmentManager;
    this.context = paramContext;
    paramViewPager.setOnPageChangeListener(this);
    this.pager_id = paramViewPager.getId();
    }
    notifyDataSetChanged(); -> The method notifyDataSetChanged() is undefined for the type MovieListPagerAdapter

    Something misses here?

    Also , I noticed that in the apktool.yml file says android:minSdkVersion=”14″
    android:targetSdkVersion=”16″ (which I added to manifest)

    and there was smali->andoid->support->v4 and v13 .But I don’t use this folder in the prohect.

    I just added support library to the project in eclipse.
    Should I do sth more?

    Thanks!

  7. Geo says:

    Hello and thanks for the tips.
    I successfully decoded an apk but when I imported in eclipse I have some errors.The errors aren’t related with R class but sth like:

    public class Movie
    implements Parcelable
    {
    public static final Parcelable.Creator CREATOR = new Parcelable.Creator()
    {
    public Movie createFromParcel(Parcel paramAnonymousParcel)
    {
    return new Movie(paramAnonymousParcel, null);
    }

    gives error in return new Movie(paramAnonymousParcel, null);
    } -> “The constructor Movie(Parcel, null) is undefined”

    OR

    public class MovieDetailsPagerAdapter extends FragmentStatePagerAdapter -> FragmentStatePagerAdapter cannot be resolved to a type

    I noticed that in apktool.yml says android:minSdkVersion=”14″
    android:targetSdkVersion=”16″ which I added to manifest.

    Also , it has folders smali->android->support->v4 and v13

    I added support library in project in eclipse (which adds v4 I thin).Shoul I do sth more?Sth misses here?

    Thanks!

  8. ana says:

    One small problem: resources.arsc is not decoded!!!

    Decoded java without issue and stuck decoded into a class folder. XML decodes I find best with:

    cd [file location]
    apktool d -f [file locationapk file name] ./decoded (creates new folder)

    This method provides a complete XML decode.

    Need a method for the .arsc file. Please help!!!

Leave a Reply

Your email address will not be published. Required fields are marked *

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>