daemon-sec-cheatsheet

The cheatsheet vault for operators: AD, enumeration, exploitation, priv-esc, web, DFIR
git clone https://git.daemon-sec.xyz/daemon-sec-cheatsheet.git
Log | Files | Refs | README | LICENSE

smali-changes.md (15701B)


      1 ---
      2 title: "Smali - Decompiling/[Modifying]/Compiling"
      3 section: "Mobile"
      4 sectionSlug: "mobile-pentesting"
      5 sourcePath: "src/mobile-pentesting/android-app-pentesting/smali-changes.md"
      6 sourceUrl: "https://github.com/HackTricks-wiki/hacktricks/blob/188de82beb54e70956b2952367a0af91d26758b8/src/mobile-pentesting/android-app-pentesting/smali-changes.md"
      7 sha: "188de82beb54e70956b2952367a0af91d26758b8"
      8 isIndex: false
      9 modified: true
     10 license: "CC-BY-NC-4.0"
     11 ---
     12 
     13 # Smali - Decompiling/[Modifying]/Compiling
     14 
     15 Sometimes it is interesting to modify the application code to access hidden information for you (maybe well obfuscated passwords or flags). Then, it could be interesting to decompile the apk, modify the code and recompile it.
     16 
     17 **Opcodes reference:** [http://pallergabor.uw.hu/androidblog/dalvik_opcodes.html](http://pallergabor.uw.hu/androidblog/dalvik_opcodes.html)
     18 
     19 ## Fast Way
     20 
     21 Using **Visual Studio Code** and the [APKLab](https://github.com/APKLab/APKLab) extension, you can **automatically decompile**, modify, **recompile**, sign & install the application without executing any command.
     22 
     23 Another **script** that facilitates this task a lot is [**https://github.com/ax/apk.sh**](https://github.com/ax/apk.sh)<sup>[[6]](#references)</sup>
     24 
     25 ### Split APKs / App Bundles
     26 
     27 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.
     28 
     29 Quick triage from a device:
     30 
     31 ```bash
     32 adb shell pm path com.example.app
     33 adb pull /data/app/.../base.apk
     34 adb pull /data/app/.../split_config.arm64_v8a.apk
     35 adb pull /data/app/.../split_config.en.apk
     36 ```
     37 
     38 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.<sup>[[6]](#references)</sup>\
     39 For Frida/Objection-oriented repacking workflows, also check [Android Anti-Instrumentation & SSL Pinning Bypass](/hacktricks/mobile-pentesting/android-app-pentesting/android-anti-instrumentation-and-ssl-pinning-bypass).
     40 
     41 ## Decompile the APK
     42 
     43 Using APKTool you can access to the **smali code and resources**:
     44 
     45 ```bash
     46 apktool d APP.apk
     47 ```
     48 
     49 If **apktool** gives you any error, try[ installing the **latest version**](https://ibotpeaches.github.io/Apktool/install/)
     50 
     51 Some **interesting files you should look are**:
     52 
     53 - _res/values/strings.xml_ (and all xmls inside res/values/*)
     54 - _AndroidManifest.xml_
     55 - Any file with extension _.sqlite_ or _.db_
     56 
     57 If `apktool` has **problems decoding the application** take a look to [https://ibotpeaches.github.io/Apktool/documentation/#framework-files](https://ibotpeaches.github.io/Apktool/documentation/#framework-files) or try using the argument **`-r`** (Do not decode resources). Then, if the problem was in a resource and not in the source code, you won't have the problem (you won't also decompile the resources).
     58 
     59 ## Change smali code
     60 
     61 You can **change** **instructions**, change the **value** of some variables or **add** new instructions. I change the Smali code using [**VS Code**](https://code.visualstudio.com), you then install the **smalise extension** and the editor will tell you if any **instruction is incorrect**.\
     62 Some **examples** can be found here:
     63 
     64 - [Smali changes examples](/hacktricks/mobile-pentesting/android-app-pentesting/smali-changes)
     65 - [Google CTF 2018 - Shall We Play a Game?](/hacktricks/mobile-pentesting/android-app-pentesting/google-ctf-2018-shall-we-play-a-game)
     66 
     67 Or you can [**check below some Smali changes explained**](/hacktricks/mobile-pentesting/android-app-pentesting/smali-changes#modifying-smali).
     68 
     69 ## Recompile the APK
     70 
     71 After modifying the code you can **recompile** the code using:
     72 
     73 ```bash
     74 apktool b . #In the folder generated when you decompiled the application
     75 ```
     76 
     77 It will **compile** the new APK **inside** the _**dist**_ folder.
     78 
     79 If **apktool** throws an **error**, try[ installing the **latest version**](https://ibotpeaches.github.io/Apktool/install/)
     80 
     81 ### **Sign the new APK**
     82 
     83 Then, you need to **generate a key** (you will be asked for a password and for some information that you can fill randomly):
     84 
     85 ```bash
     86 keytool -genkey -v -keystore key.jks -keyalg RSA -keysize 2048 -validity 10000 -alias <your-alias>
     87 ```
     88 
     89 Finally, **sign** the new APK:
     90 
     91 ```bash
     92 jarsigner -keystore key.jks path/to/dist/* <your-alias>
     93 ```
     94 
     95 `jarsigner` still works for some quick tests, but for modern Android builds **`apksigner` is preferred** because it handles the newer APK signature schemes.<sup>[[4]](#references)</sup>
     96 
     97 ### Optimize new application
     98 
     99 **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).<sup>[[7]](#references)</sup>
    100 
    101 ```bash
    102 zipalign [-f] [-v] <alignment> infile.apk outfile.apk
    103 zipalign -v 4 infile.apk
    104 ```
    105 
    106 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:<sup>[[5]](#references)</sup>
    107 
    108 ```bash
    109 zipalign -P 16 -f -v 4 infile.apk outfile.apk
    110 ```
    111 
    112 ### **Sign the new APK (again?)**
    113 
    114 If you **prefer** to use [**apksigner**](https://developer.android.com/studio/command-line/) instead of `jarsigner`, **sign the APK** after applying the `zipalign` optimization. **Sign the application only once**: use `jarsigner` before `zipalign`, or `apksigner` after `zipalign`.
    115 
    116 ```bash
    117 apksigner sign --ks key.jks ./dist/mycompiled.apk
    118 ```
    119 
    120 A more practical modern flow is:
    121 
    122 ```bash
    123 apktool b . -o dist/app-unsigned.apk
    124 zipalign -P 16 -f -v 4 dist/app-unsigned.apk dist/app-aligned.apk
    125 apksigner sign --ks key.jks --out dist/app-signed.apk dist/app-aligned.apk
    126 apksigner verify --verbose --print-certs dist/app-signed.apk
    127 ```
    128 
    129 Important notes:
    130 
    131 - If you **modify** an APK **after** signing it with `apksigner`, the signature is invalidated and you must sign it again.
    132 - `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.
    133 
    134 ## Modifying Smali
    135 
    136 For the following Hello World Java code:
    137 
    138 ```java
    139 public static void printHelloWorld() {
    140     System.out.println("Hello World")
    141 }
    142 ```
    143 
    144 The Smali code would be:
    145 
    146 ```java
    147 .method public static printHelloWorld()V
    148     .registers 2
    149     sget-object v0, Ljava/lang/System;->out:Ljava/io/PrintStream;
    150     const-string v1, "Hello World"
    151     invoke-virtual {v0,v1}, Ljava/io/PrintStream;->println(Ljava/lang/String;)V
    152     return-void
    153 .end method
    154 ```
    155 
    156 The Smali instruction set is available [here](https://source.android.com/devices/tech/dalvik/dalvik-bytecode#instructions).
    157 
    158 ### Light Changes
    159 
    160 ### Modify initial values of a variable inside a function
    161 
    162 Some variables are defined at the beginning of the function using the opcode _const_, you can modify its values, or you can define new ones:
    163 
    164 ```bash
    165 #Number
    166 const v9, 0xf4240
    167 const/4 v8, 0x1
    168 #Strings
    169 const-string v5, "wins"
    170 ```
    171 
    172 ### Basic Operations
    173 
    174 ```bash
    175 #Math
    176 add-int/lit8 v0, v2, 0x1 #v2 + 0x1 and save it in v0
    177 mul-int v0,v2,0x2 #v2*0x2 and save in v0
    178 
    179 #Move the value of one object into another
    180 move v1,v2
    181 
    182 #Condtions
    183 if-ge #Greater or equals
    184 if-le #Less or equals
    185 if-eq #Equals
    186 
    187 #Get/Save attributes of an object
    188 iget v0, p0, Lcom/google/ctf/shallweplayagame/GameActivity;->o:I #Save this.o inside v0
    189 iput v0, p0, Lcom/google/ctf/shallweplayagame/GameActivity;->o:I #Save v0 inside this.o
    190 
    191 #goto
    192 :goto_6 #Declare this where you want to start a loop
    193 if-ne v0, v9, :goto_6 #If not equals, go to: :goto_6
    194 goto :goto_6 #Always go to: :goto_6
    195 ```
    196 
    197 ### Bigger Changes
    198 
    199 ### Smali gotchas that usually break rebuilds
    200 
    201 - 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.
    202 - `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.
    203 - `long` and `double` values are **wide** values and consume a **register pair**. If you reuse those registers later, remember that `v10` also occupies `v11`.
    204 - If you need to pass many registers, or very high-numbered ones, use the `/range` variants such as `invoke-virtual/range`.
    205 
    206 ### Logging
    207 
    208 ```bash
    209 #Log win: <number>
    210 iget v5, p0, Lcom/google/ctf/shallweplayagame/GameActivity;->o:I #Get this.o inside v5
    211 invoke-static {v5}, Ljava/lang/String;->valueOf(I)Ljava/lang/String; #Transform number to String
    212 move-result-object v1 #Move to v1
    213 const-string v5, "wins" #Save "win" inside v5
    214 invoke-static {v5, v1}, Landroid/util/Log;->d(Ljava/lang/String;Ljava/lang/String;)I #Logging "Wins: <num>"
    215 ```
    216 
    217 Recommendations:
    218 
    219 - If you are going to use declared variables inside the function (declared `v0`, `v1`, `v2`...) put these lines after the `.locals <number>` directive and before the existing variable instructions (for example, `const v0, 0x1`).
    220 - If you want to put the logging code in the middle of the code of a function:
    221   - Add 2 to the number of declared variables: Ex: from _.locals 10_ to _.locals 12_
    222   - 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).
    223   - Change the code of the logging function and use _v10_ and _v11_ instead of _v5_ and _v1_.
    224 
    225 ### Patching common anti-tamper checks
    226 
    227 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:
    228 
    229 - `GET_SIGNATURES`
    230 - `GET_SIGNING_CERTIFICATES`
    231 - `apkContentsSigners`
    232 - `MessageDigest`
    233 - `SHA-256`
    234 - `Base64`
    235 - `getInstallerPackageName`
    236 - `com.android.vending`
    237 
    238 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.
    239 
    240 Example patterns:
    241 
    242 ```text
    243 # Force a boolean result to "valid"
    244 const/4 v0, 0x1
    245 
    246 # Or invert the branch that sends execution to the tamper handler
    247 if-eqz v0, :tamper_detected   # original
    248 if-nez v0, :tamper_detected   # patched
    249 ```
    250 
    251 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.
    252 
    253 ### Toasting
    254 
    255 Remember to add 3 to the number of _.locals_ at the beginning of the function.
    256 
    257 This code is prepared to be inserted in the **middle of a function** (**change** the number of the **variables** as necessary). It will take the **value of this.o**, **transform** it to **String** and them **make** a **toast** with its value.
    258 
    259 ```bash
    260 const/4 v10, 0x1
    261 const/4 v11, 0x1
    262 const/4 v12, 0x1
    263 iget v10, p0, Lcom/google/ctf/shallweplayagame/GameActivity;->o:I
    264 invoke-static {v10}, Ljava/lang/String;->valueOf(I)Ljava/lang/String;
    265 move-result-object v11
    266 invoke-static {p0, v11, v12}, Landroid/widget/Toast;->makeText(Landroid/content/Context;Ljava/lang/CharSequence;I)Landroid/widget/Toast;
    267 move-result-object v12
    268 invoke-virtual {v12}, Landroid/widget/Toast;->show()V
    269 ```
    270 
    271 ### Loading a Native Library at Startup (System.loadLibrary)
    272 
    273 Sometimes you need to preload a native library so it initializes before other JNI libs (e.g., to enable process-local telemetry/logging).<sup>[[3]](#references)</sup> You can inject a call to `System.loadLibrary()` in a static initializer or early in `Application.onCreate()`. Example Smali for a static class initializer (`<clinit>`):
    274 
    275 ```text
    276 .class public Lcom/example/App;
    277 .super Landroid/app/Application;
    278 
    279 .method static constructor <clinit>()V
    280     .registers 1
    281     const-string v0, "sotap"         # library name without lib...so prefix
    282     invoke-static {v0}, Ljava/lang/System;->loadLibrary(Ljava/lang/String;)V
    283     return-void
    284 .end method
    285 ```
    286 
    287 Alternatively, place the same two instructions at the start of your Application.onCreate() to ensure the library loads as early as possible:
    288 
    289 ```text
    290 .method public onCreate()V
    291     .locals 1
    292     
    293     const-string v0, "sotap"
    294     invoke-static {v0}, Ljava/lang/System;->loadLibrary(Ljava/lang/String;)V
    295 
    296     invoke-super {p0}, Landroid/app/Application;->onCreate()V
    297     return-void
    298 .end method
    299 ```
    300 
    301 Notes:
    302 - Make sure the correct ABI variant of the library exists under `lib/<abi>/` (e.g., `arm64-v8a`/`armeabi-v7a`) to avoid `UnsatisfiedLinkError`.
    303 - Loading very early (class static initializer) guarantees the native logger can observe subsequent JNI activity.
    304 
    305 ## Smali Static Analysis / Rule-Based Hunting
    306 
    307 After decompiling with `apktool`, you can **scan Smali line-by-line** with regex rules to quickly spot anti-analysis logic (root/emulator checks) and likely hardcoded secrets. This is a **fast triage** technique: treat hits as leads that you must verify in surrounding Smali or reconstructed Java/Kotlin.<sup>[[1]](#references)[[2]](#references)</sup>
    308 
    309 Key ideas:
    310 - **Library filtering**: suppress or tag findings under common third-party namespaces so you focus on app-owned code paths.
    311 - **Context hints**: require suspicious strings to appear near the APIs that consume them (within the same method, within N lines).
    312 - **Confidence**: use simple levels (high/medium) to rank leads and reduce false positives.
    313 
    314 Example library prefixes to suppress by default:
    315 ```text
    316 Landroidx/
    317 Lkotlin/
    318 Lkotlinx/
    319 Lcom/google/
    320 Lcom/squareup/
    321 Lokhttp3/
    322 Lokio/
    323 Lretrofit2/
    324 ```
    325 
    326 Example detection rules (regex + context heuristics):
    327 ```json
    328 {
    329   "category": "root_check",
    330   "regex_patterns": [
    331     "(?i)invoke-static .*Runtime;->getRuntime\\(\\).*->exec\\(.*\\"(su|magisk|busybox)\\"",
    332     "(?i)const-string [vp0-9, ]+\\"(/system/xbin/su|/system/bin/su|/sbin/su)\\""
    333   ],
    334   "context_hint": "Only report when the same method also calls File;->exists/canExecute or Runtime;->exec."
    335 }
    336 ```
    337 
    338 Additional heuristics that work well in practice:
    339 - **Root package/path checks**: require nearby `PackageManager;->getPackageInfo` or `File;->exists` calls for strings like `com.topjohnwu.magisk` or `/data/local/tmp`.
    340 - **Emulator checks**: pair suspicious literals (e.g., `ro.kernel.qemu`, `generic`, `goldfish`) with nearby `Build.*` getters and string comparisons (`->equals`, `->contains`, `->startsWith`).
    341 - **Hardcoded secrets**: flag `const-string` only when a nearby `.field` or `move-result` identifier includes keywords like `password`, `token`, `api_key`. Explicitly ignore UI-only markers such as `AutofillType`, `InputType`, `EditorInfo`.
    342 
    343 Rule-driven scanners like PulseAPK Core implement this model to quickly surface anti-analysis logic and potential secrets in Smali.
    344 
    345 ## References
    346 - [1] [PulseAPK Core](https://github.com/deemoun/PulseAPK-Core)
    347 - [2] [PulseAPK Smali Detection Rules](https://github.com/deemoun/PulseAPK-Core/blob/main/APK_ANALYSIS_RULES.md)
    348 - [3] [SoTap: Lightweight in-app JNI (.so) behavior logger](https://github.com/RezaArbabBot/SoTap)
    349 - [4] [Android Developers: apksigner](https://developer.android.com/tools/apksigner)
    350 - [5] [Android Developers: zipalign](https://developer.android.com/tools/zipalign)
    351 - [6] [apk.sh](https://github.com/ax/apk.sh)
    352 - [7] [developer.android.com - Command Line - Zipalign](https://developer.android.com/studio/command-line/zipalign)