[Android Software/Hacking General [Developers Only]] denversc:
This guide is a great example of how to hack a third-party, closed-source application using apktool and smali. The first goal is to fix an annoying UI lag bug in Words With Friends by inserting a call to Thread.yield() at the top of a tight loop before acquiring a global lock. The second goal is to turn off the advertisements that display after every move.

Table of Contents

1. Install Words With Friends from Google Play

2. Extract the APK file from device onto PC

3. Decompile the APK file

4. Edit Smali: add Thread.sleep() to animation loop

5. Edit Smail: remove addActivity() call for ads

6. Reassemble the APK file

7. Sign the Reassembled APK File

8. Zipalign the reassembled APK file

9. Uninstall Words With Friends from the device

10. Install the fixed Words With Friends APK to the device

Appendix A: Troubleshooting

1. Install Words With Friends from Google Play

Words With Friends is a free application provided by Zynga and is available for install from the Google Play store. Simply search for Words With Friends and install it. More information about the app is available at http://www.wordswithfriends.com.

2. Extract the APK file from device onto PC

In order to modify the Words With Friends application the APK file needs to be obtained. There are several different ways to extract an APK file from your device. The method outlined here is using Astro File Manager from the Google Play store.

Install Astro File Manager from the Google Play store

Launch Astro File Manager from home screen

Select "Application Backup" *

Checkmark "Words Free"

Click "Backup"

Copy the Words With Friends APK file from /sdcard/backups/apps to your PC

* If the screen does not have an "Application Backup" item but rather goes straight into the file browser, open the menu and select "Application Backup" from the there instead.

Astro File Manager will create the folder "backups/apps" on the SDCard if it does not exist and put the Words With Friends APK file in it. The file name may vary, and in my case it was "Words Free-4811.apk".

If you have root access to your device, it may be easier to simply copy the APK file from /data/app to /sdcard and then copy it off the device from the SDCard. To do this I like to use a program called "Terminal Emulator", which can be installed from the Google Play store. Then you can just run the "su" command followed by "cp /data/app/com.zynga.words-1.apk /sdcard" to copy the APK file to the SDCard.

3. Decompile the APK file

The next step is to decompile the Words With Friends APK file using "apktool". The apktool application is freely downloadable from http://code.google.com/p/android-apktool and requires the Android SDK and Java Development Kit (JDK) to be installed. The steps that follow assume that the directory with the "apktool" command is in the PATH environment variable and may be called without having to give the absolute path. I will not cover the steps to install the Android SDK nor apktool here, as there are numerous good instructions on the Internet. The apktool application works on Windows, Mac, and Linux. I will be using Ubuntu Linux 12.04 64-bit (not that it really matters!).

Open a terminal/command prompt into the directory where the Words With Friends APK file is located. In my case, the name of the APK file was "Words Free-4811.apk", but I have renamed it to "WordsWithFriends-original.apk" for simplicity.

Run the following command to decompile the APK. The "-d" command-line argument decompiles the application in "Debug mode". I am not entirely sure what this does, but many instructions recommend its usage. Since it has worked for me, I use it unconditionally. If you get any errors with this commoand, try removing "-d" to see if that works.

apktool decode -d WordsWithFriends-original.apk decoded​

This command will create a directory named "decoded" with the decompiled contents of the APK.

4. Edit Smali: add Thread.sleep() to animation loop

One of the bugs in Words With Friends is that there is a tight loop that spins, acquiring the graphics lock, checking if there are any animations to draw, then performing the animations if there are any. The problem is that sometimes this thread "thrashes" or "spins", causing the UI of the application to become temporarily unresponsive. This bug is especially noticable when a blank tile is played: after selecting the letter for the tile, the app appears to hang for several seconds. The fix for this is simple: put a call to Thread.yield() at the top of the loop before acquiring the graphics lock.

To fix this, open the file decompiled/smali/com/zynga/words/ui/game/as.smali in a text editor. Find the "run" method and then the line that follows that looks like:

iget-object v0, p0, Lcom/zynga/words/ui/game/as;->c:Landroid/view/SurfaceHolder;​

(for me this was at line 136). The two lines above it should be ":try_start_0" and ".line 2569" ("2569" may be different). Insert the following line *above* the ".line 2569" line:

invoke-static {}, Ljava/lang/Thread;->yield()V​

Save the changes to the file then exit.

5. Edit Smail: remove addActivity() call for ads

Do you ever get annoyed at the advertisements that are displayed after each move you make? Although this may not be legal, it is quite simple to stop those ads from displaying. All you need to do is remove the command that displays them.

To do this, open the file decompiled/smali/com/zynga/toybox/ads/f.smali in a text editor. Find the line with the text "startActivity" (for me this was line 181) and either delete it or add a '#' as the first character of the line (to turn it into a comment). Then, comment or delete the ".line" line immediately above the startActivity line. Save the changes to the file then exit.

6. Reassemble the APK file

Now we need to rebuild the APK file from the modified smali sources. To do this, run the following apktool command:

apktool build -d decompiled WordsWithFriends-unsigned.apk​

This should create the APK file WordsWithFriends-unsigned.apk in the current directory.

7. Sign the Reassembled APK File

Before the modified APK file can be loaded onto a device it must be signed. The instructions below are adapted from the official "Signing Your Applications" page at http://developer.android.com/tools/publishing/app-signing.html.

If you are already setup for APK signing, then this paragraph may be ignored. Otherwise, run the following command to create a code signing key:

keytool -genkey -keystore keystore -alias MyReleaseKey -keypass password -storepass password -keyalg RSA -keysize 2048 -validity 10000​

This will create a file named "keystore" in the current directory, protected by the password "password". Obviously, this is a terribly insecure password, so I wouldn't recommend using this key for anything else (unless you choose a better key). If you use different values for the -keystore, -alias, -keypass, or -storepass be sure to make the corresponding adjustments to the instructions that follow.

Run the following command to sign your APK file:

jarsigner -keystore keystore -storepass password -keypass password -sigalg MD5withRSA -digestalg SHA1 -signedjar WordsWithFriends-signed.apk WordsWithFriends-unsigned.apk MyReleaseKey​

Of course, replace the values of the -keystore, -storepass, -keypass, and MyReleaseKey with the corresponding values on your system.

This should create a file named WordsWithFriends-signed.apk in the current directory, which is the signed version of the modified application.

8. Zipalign the reassembled APK file

Although it is not strictly required, as a performance increase the signed APK file can be "zipaligned". Simply run the following command:

zipalign 4 WordsWithFriends-signed.apk WordsWithFriends.apk​

This will create a file named "WordsWithFriends.apk" in the current directory which is both signed and zipaligned. This is the "final" APK file that is ready to be installed.

9. Uninstall Words With Friends from the device

Since the modified APK file is signed with a different key than the one used to sign the APK that was installed from Google Play, the Android OS will refuse to load the modified APK. So before loading it Words With Friends must be uninstalled from the device. This can either be done from the device (eg. from the home screen or Settings -> Applications screen) or by connecting the device to the computer via the USB cable and running the following command:

adb uninstall com.zynga.words​

10. Install the fixed Words With Friends APK to the device

Finally, install the signed and (optionally) zipaligned APK to the device connecting the device to the PC via the USB cable and running the following command:

adb install WordsWithFriends.apk​

And you're done! Enjoy a better Words With Friends experience.

Appendix A: Troubleshooting

If you encounter any problems, check this section before reporting it.

A1. Cannot sign into Words With Friends using Facebook

It seems that sometimes after replacing the Words With Friends application from Google Play with the custom one that Facebook authentication does not work. Specifically, when the Words With Friends application starts up and prompts for either a Facebook account or email address to sign in, when Facebook is selected it briefly starts loading the Facebook page then returns to the Words With Friends application without having logged in.

This may be because the signature on the APK has changed. The workaround is to uninstall the Facebook application, log into Words With Friends with Facebook, then re-install the Facebook application from Google Play. This will cause Words With Friends to use the facebook.com web page to log in instead of the Facebook application that is installed locally, which does not check APK signatures.

A2. Words With Friends no longer updates from Google Play

Yep, that's unavoidable. To upgrade you will need to go through this entire process again, first uninstalling the modified Words With Friends and starting over with installing from Google Play.

A3. adb install fails with error "INSTALL_PARSE_FAILED_NO_CERTIFICATES"

If "adb install" fails with the error "INSTALL_PARSE_FAILED_NO_CERTIFICATES" then be sure that you are attempting to install the signed APK, as the OS will refuse to load unsigned APKs.

If you are using JDK 7 (as opposed to JDK 6 or 5), which can be tested by running "javac -version" then make sure to include "-sigalg MD5withRSA -digestalg SHA1" in the command-line arguments to jarsigner. The default signing algorithm changed in JDK 7, requiring that these arguments be explicitly specified (see http://developer.android.com/tools/publishing/app-signing.html#signapp for details)