You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Copy file name to clipboardExpand all lines: src/mobile-pentesting/android-app-pentesting/smali-changes.md
+76-1Lines changed: 76 additions & 1 deletion
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -13,6 +13,22 @@ Using **Visual Studio Code** and the [APKLab](https://github.com/APKLab/APKLab)
13
13
14
14
Another **script** that facilitates this task a lot is [**https://github.com/ax/apk.sh**](https://github.com/ax/apk.sh)
15
15
16
+
### Split APKs / App Bundles
17
+
18
+
Modern targets are commonly delivered as **split APKs** (`base.apk` + `split_config.*.apk`) instead of a single monolithic APK. If you patch only `base.apk`, resources or native libraries can go out of sync and the installation may fail.
19
+
20
+
Quick triage from a device:
21
+
22
+
```bash
23
+
adb shell pm path com.example.app
24
+
adb pull /data/app/.../base.apk
25
+
adb pull /data/app/.../split_config.arm64_v8a.apk
26
+
adb pull /data/app/.../split_config.en.apk
27
+
```
28
+
29
+
If the target is a split package, either rebuild the whole set or use tooling that **joins the APKs first**. [**apk.sh**](https://github.com/ax/apk.sh) is handy here because it can combine split APKs into a single patchable APK and fix public resource identifiers.\
30
+
For Frida/Objection-oriented repacking workflows, also check [Android Anti-Instrumentation & SSL Pinning Bypass](android-anti-instrumentation-and-ssl-pinning-bypass.md).
31
+
16
32
## Decompile the APK
17
33
18
34
Using APKTool you can access to the **smali code and resources**:
`jarsigner` still works for some quick tests, but for modern Android builds **`apksigner` is preferred** because it handles the newer APK signature schemes.
87
+
70
88
### Optimize new application
71
89
72
90
**zipalign** is an archive alignment tool that provides important optimisation to Android application (APK) files. [More information here](https://developer.android.com/studio/command-line/zipalign).
If the APK contains bundled native libraries (`lib/*.so`), Android now recommends using **`-P 16`** so the `.so` files are aligned for both 16 KiB and 4 KiB page-size devices:
98
+
99
+
```bash
100
+
zipalign -P 16 -f -v 4 infile.apk outfile.apk
101
+
```
102
+
79
103
### **Sign the new APK (again?)**
80
104
81
105
If you **prefer** to use [**apksigner**](https://developer.android.com/studio/command-line/) instead of jarsigner, **you should sing the apk** after applying **the optimization with** zipaling. BUT NOTICE THAT YOU ONLY HAVE TO **SIGN THE APPLCIATION ONCE** WITH jarsigner (before zipalign) OR WITH aspsigner (after zipaling).
@@ -84,6 +108,20 @@ If you **prefer** to use [**apksigner**](https://developer.android.com/studio/co
- If you **modify** an APK **after** signing it with `apksigner`, the signature is invalidated and you must sign it again.
123
+
-`apksigner verify --print-certs` is useful to confirm the rebuilt APK is installable and to inspect the certificate that the target will expose at runtime.
124
+
87
125
## Modifying Smali
88
126
89
127
For the following Hello World Java code:
@@ -149,6 +187,13 @@ goto :goto_6 #Always go to: :goto_6
149
187
150
188
### Bigger Changes
151
189
190
+
### Smali gotchas that usually break rebuilds
191
+
192
+
- Prefer increasing **`.locals`** when you only need temporary registers in the body of an existing method. Parameter registers (`p0`, `p1`...) are mapped to the **highest** registers of the method, so switching blindly to `.registers` often breaks argument layout.
193
+
-`move-result`, `move-result-wide`, and `move-result-object`**must appear immediately after** the matching `invoke-*`. Inserting logging or any other opcode between them makes the method invalid.
194
+
-`long` and `double` values are **wide** values and consume a **register pair**. If you reuse those registers later, remember that `v10` also occupies `v11`.
195
+
- If you need to pass many registers, or very high-numbered ones, use the `/range` variants such as `invoke-virtual/range`.
196
+
152
197
### Logging
153
198
154
199
```bash
@@ -168,6 +213,34 @@ Recommendations:
168
213
- The new variables should be the next numbers of the already declared variables (in this example should be _v10_ and _v11_, remember that it starts in v0).
169
214
- Change the code of the logging function and use _v10_ and _v11_ instead of _v5_ and _v1_.
170
215
216
+
### Patching common anti-tamper checks
217
+
218
+
When an app is repacked, one of the first things that may break is an in-app **signature / installer / integrity** check. Good strings to search in JADX or in the smali tree are:
219
+
220
+
-`GET_SIGNATURES`
221
+
-`GET_SIGNING_CERTIFICATES`
222
+
-`apkContentsSigners`
223
+
-`MessageDigest`
224
+
-`SHA-256`
225
+
-`Base64`
226
+
-`getInstallerPackageName`
227
+
-`com.android.vending`
228
+
229
+
Modern apps often call `PackageManager.getPackageInfo(..., GET_SIGNING_CERTIFICATES)`, hash the signer bytes with `MessageDigest`, and compare the result with a hardcoded constant. In practice, it is usually easier to patch the **final boolean / branch** than to rewrite all the signature-handling code.
230
+
231
+
Example patterns:
232
+
233
+
```smali
234
+
# Force a boolean result to "valid"
235
+
const/4 v0, 0x1
236
+
237
+
# Or invert the branch that sends execution to the tamper handler
238
+
if-eqz v0, :tamper_detected # original
239
+
if-nez v0, :tamper_detected # patched
240
+
```
241
+
242
+
If the verification code is noisy, look for the **last comparison** before the error dialog / `finish()` / `System.exit()` / telemetry call and patch there instead of touching the entire routine.
243
+
171
244
### Toasting
172
245
173
246
Remember to add 3 to the number of _.locals_ at the beginning of the function.
0 commit comments