Thursday, March 3, 2022

How to decompile and repack Android App on ChromeOS (Part 2)

(1) This is the sequel to How to decompile android app (part 1)
And the demo apk can be download from here. TouchCalculator_1.0-debug.apk

(2) Part 1 is to demo the use of ChromeOS (devloper mode on) plus the decompile tools and the process of decode and edit some resource files then rebuild the apk using apktool.
If you have Windows machine, you can also download the required decompile tools as demo here.

(3) This part 2 is to continue to modify the logic in the Android App which involved the source code. I will use the JADX tools and Android Studio to edit the code.

cd ~/DecompileProjects
# Use jadx to decode the apk and export gradle project file and java source code
jadx --export-gradle TouchCalculator_1.0-debug.apk -d TouchCalculator2


(4) Then, launch Android Studio and open the project in ~/DecompileProjects/TouchCalculator2 and wait for the gradle to sync. If you try to Build -> Make Project (Ctrl F9), you will notice it has a lot of errors and these errors are mainly partial packages download from repo and many of them are not linkable. So we have to study the code, to see what are the actual code files needed for this project.

(5) Then create a new Android Studio project file with Empty Activity and with the following parameters.


(6) After creation of the new project, copy the decompiled java source code and resource files to the New Project as below:
shell script    Select all
cp ~/DecompileProjects/TouchCalculator2/app/src/main/java/com/pragmatouch/calculator/*.java ~/DecompileProjects/TouchCalculator3/app/src/main/java/com/pragmatouch/calculator/. cp ~/DecompileProjects/TouchCalculator2/app/src/main/res/drawable/k*.xml ~/DecompileProjects/TouchCalculator3/app/src/main/res/drawable/. cp ~/DecompileProjects/TouchCalculator2/app/src/main/res/drawable/stackview1.xml ~/DecompileProjects/TouchCalculator3/app/src/main/res/drawable/. cp ~/DecompileProjects/TouchCalculator2/app/src/main/res/drawable/inputview1.xml ~/DecompileProjects/TouchCalculator3/app/src/main/res/drawable/. cp ~/DecompileProjects/TouchCalculator2/app/src/main/res/drawable/appvertical1.xml ~/DecompileProjects/TouchCalculator3/app/src/main/res/drawable/. cp ~/DecompileProjects/TouchCalculator2/app/src/main/res/values/colors.xml ~/DecompileProjects/TouchCalculator3/app/src/main/res/values/. cp ~/DecompileProjects/TouchCalculator2/app/src/main/res/values/strings.xml ~/DecompileProjects/TouchCalculator3/app/src/main/res/values/. cp ~/DecompileProjects/TouchCalculator2/app/src/main/res/layout/activity_main.xml ~/DecompileProjects/TouchCalculator3/app/src/main/res/layout/ cp ~/DecompileProjects/TouchCalculator2/app/src/main/res/layout/calutor.xml ~/DecompileProjects/TouchCalculator3/app/src/main/res/layout/



(7) Then, there are several changes to the build.gradle settings. Target Sdk should be set to 30 in order to build as App Bundle format.
# Sdk should not use 32 and change to 30
   compileSdk 30
   targetSdk 30

# dependencies package change to lower version as below
   implementation 'androidx.appcompat:appcompat:1.3.0'
   implementation 'com.google.android.material:material:1.3.0'

For Release build type add
   debuggable false


(8) Then, we try to Build -> Make Project (Ctrl F9) again in the New Project and see if there are any errors to solve. For complex project, it is not possible to have a full project file to build after decompile. So there is a part 3 for this topic.

(9) In fact, the project code was from here, so I can find out the required project files easily. However, there are still some features of this app which have not been implemented yet and with the decompiled java source code, you can modify the decompiled code and rebuild it to apk and test again. ALternatively, you can copy the original source code from here and paste them to the relevant java source code files of your new project.

(10) Change the resource files as in Part 1 of this artcile and add the following lines in MainActivity.java to implement the PERCENT, SQRT and RRECIPROC functions. And then Run the app in Android Studio or rebuild the apk to test on real Android device.
diff results    Select all
# diff ./TouchCalculator2/app/src/main/java/com/pragmatouc h/calculator/MainActivity.java ./TouchCalculator3/app/src/main/java/com/pragmatouch/calculator/MainActivity.java 192a193,236 > case 16: // PERCENT > double userInputValue4 = tryParseUserInput(); > if (!Double.isNaN(userInputValue4)) { > if (Double.isNaN(this.memoryValue)) { > this.memoryValue = 0.0d; > } > this.memoryValue = (userInputValue4/100); > this.userInputText.setText(doubleToString(this.memoryValue)); > this.hasFinalResult = true; > return; > } > return; > case 17: // SQRT > // reject negative num > if (currentInputLen > 0 && currentInput.charAt(0) == '-') { > return; > } > double userInputValue5 = tryParseUserInput(); > if (!Double.isNaN(userInputValue5)) { > if (Double.isNaN(this.memoryValue)) { > this.memoryValue = 0.0d; > } > this.memoryValue = Math.sqrt(userInputValue5); > this.userInputText.setText(doubleToString(this.memoryValue)); > this.hasFinalResult = true; > return; > } > return; > case 18: // RECIPROC > // reject zero > if (currentInputLen == 1 && currentInput.charAt(0) == '0') { > return; > } > double userInputValue6 = tryParseUserInput(); > if (!Double.isNaN(userInputValue6)) { > if (Double.isNaN(this.memoryValue)) { > this.memoryValue = 0.0d; > } > this.memoryValue = (1/userInputValue6); > this.userInputText.setText(doubleToString(this.memoryValue)); > this.hasFinalResult = true; > return; > } > return; 276a321,332 > } > try { > $SwitchMap$com$pragmatouch$calculator$KeypadButton[KeypadButton.PERCENT.ordinal()] = 16; > } catch (NoSuchFieldError e16) { > } > try { > $SwitchMap$com$pragmatouch$calculator$KeypadButton[KeypadButton.SQRT.ordinal()] = 17; > } catch (NoSuchFieldError e17) { > } > try { > $SwitchMap$com$pragmatouch$calculator$KeypadButton[KeypadButton.RECIPROC.ordinal()] = 18; > } catch (NoSuchFieldError e18) {



There are still bugs here and please feel free to correct them plus any new enhancements of this calculator and Enjoy Android Studio Programming.



(11) When decompiling some apks that was compiled with old sdk or converting some old android projects to the at least using SDK 30, some conversion of libraries are needed as below:
In build.gradle, you should use androidx instead of old Android Support Libraries
     compileSdkVersion 30
    implementation 'androidx.appcompat:appcompat:1.3.0'
instead of 'com.android.support:appcompat-v7:28.0.0' or something like 'com.android.support:support-v13:26.0.2'
You also should use kotlin library if the decompiled project was converted from kotlin to Java
    implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version"

See here for reference. https://developer.android.com/jetpack/androidx

In gradle.properties, you should setup useAndroidX and enableJetifier
android.useAndroidX=true
android.enableJetifier=true

For converted/decompiled Java classes source, don't use old android support libraries, use androidx instead.
Full class-mapping here https://developer.android.com/jetpack/androidx/migrate/class-mappings
//import android.support.v4.app.NotificationCompat;
//import android.support.v7.app.AppCompatActivity;
//import android.support.v4.app.RemoteInput;
//import android.support.v4.app.Fragment;
import androidx.core.app.NotificationCompat;
import androidx.appcompat.app.AppCompatActivity;
import androidx.core.app.RemoteInput;
import androidx.fragment.app.Fragment;
import androidx.annotation.RequiresApi;
import android.annotation.TargetApi;
import android.annotation.SuppressLint;




(12) If you really want to support old devices and not using androidx, you should use SdkVersion 28 and compatible appcompat version as below:
      compileSdkVersion 28

    implementation 'com.android.support:appcompat-v7:28.0.0'

No comments: