Smali - Decompiling/[Modifying]/Compiling
Tip
AWS Hackingを学び、実践する:
HackTricks Training AWS Red Team Expert (ARTE)
GCP Hackingを学び、実践する:HackTricks Training GCP Red Team Expert (GRTE)
Az Hackingを学び、実践する:HackTricks Training Azure Red Team Expert (AzRTE)
HackTricks Trainingの全カタログ を閲覧して、評価トラック(ARTA/GRTA/AzRTA)と Linux Hacking Expert (LHE) を確認してください。
HackTricksをサポート
- subscription plans を確認してください!
- 💬 Discord group、telegram group に参加し、X/Twitterで @hacktricks_live をフォローするか、LinkedIn page と YouTube channel を確認してください。
- HackTricks と HackTricks Cloud の github repos に PR を送信して hacking tricks を共有してください。
アプリケーションのコードを変更して、隠された情報(例えば巧妙に難読化されたパスワードや flags)にアクセスすることが有用な場合があります。その場合、apkを逆コンパイルしてコードを修正し、再コンパイルすることが有効です。
Opcodes reference: http://pallergabor.uw.hu/androidblog/dalvik_opcodes.html
簡単な方法
Visual Studio Code と APKLab 拡張機能を使用すると、コマンドを実行することなくアプリを 自動で逆コンパイル、修正、再コンパイル、署名およびインストールできます。
この作業を大幅に容易にする別のスクリプトは https://github.com/ax/apk.sh です。
Split APKs / App Bundles
最近のターゲットは単一のモノリシックなAPKではなく、split APKs(base.apk + split_config.*.apk)として配布されることが一般的です。base.apkのみをパッチすると、リソースやネイティブライブラリが同期しなくなり、インストールが失敗することがあります。
デバイス上での簡易トリアージ:
adb shell pm path com.example.app
adb pull /data/app/.../base.apk
adb pull /data/app/.../split_config.arm64_v8a.apk
adb pull /data/app/.../split_config.en.apk
ターゲットがスプリットパッケージの場合、セット全体を再ビルドするか、先に APK を結合するツールを使用してください。apk.sh はここで便利で、split APKs を単一のパッチ可能な APK に結合し、公開リソース識別子を修正できます。
Frida/Objection を使ったリパッキングのワークフローの場合は、Android Anti-Instrumentation & SSL Pinning Bypass も参照してください。
APK をデコンパイルする
APKTool を使うと、smali code and resources にアクセスできます:
apktool d APP.apk
If apktool がエラーを出す場合は、 installing the latest version を試してください。
以下は確認すべき興味深いファイルです:
- res/values/strings.xml (and all xmls inside res/values/*)
- AndroidManifest.xml
- Any file with extension .sqlite or .db
If apktool has problems decoding the application take a look to https://ibotpeaches.github.io/Apktool/documentation/#framework-files or try using the argument -r (リソースをデコードしない). 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).
Change smali code
命令をchangeしたり、いくつかの変数のvalueを変更したり、新しい命令をaddすることができます。私は Smali コードを VS Code を使って変更し、smalise extension をインストールすると、エディタが命令に誤りがあれば教えてくれます。
Some examples can be found here:
Or you can check below some Smali changes explained.
Recompile the APK
コードを変更した後、次のコマンドでコードをrecompileできます:
apktool b . #In the folder generated when you decompiled the application
新しい APK は dist フォルダの中に compileされます。
もし apktool が error を出す場合は、 installing the latest version を試してください
新しい APK に署名する
次に、鍵を生成する必要があります(パスワードといくつかの情報を尋ねられますが、これらはランダムに入力して構いません):
keytool -genkey -v -keystore key.jks -keyalg RSA -keysize 2048 -validity 10000 -alias <your-alias>
最後に、新しい APK に 署名 します:
jarsigner -keystore key.jks path/to/dist/* <your-alias>
jarsigner は簡易的なテストではまだ有効ですが、最新の Android ビルドでは新しい APK 署名スキームを扱えるため apksigner が推奨されます。
新しいアプリケーションの最適化
zipalign はアーカイブの整列ツールで、Android アプリケーション (APK) ファイルに対して重要な最適化を提供します。 More information here.
zipalign [-f] [-v] <alignment> infile.apk outfile.apk
zipalign -v 4 infile.apk
APK がバンドルされたネイティブライブラリ (lib/*.so) を含む場合、Android は現在 -P 16 の使用を推奨しています。これにより .so ファイルが 16 KiB と 4 KiB のページサイズの両方に対して整列されます:
zipalign -P 16 -f -v 4 infile.apk outfile.apk
新しい APK に署名する(また?)
もし jarsigner の代わりに apksigner を 好む なら、zipaling による 最適化を 適用した後で apk に署名するべきです。
しかし、注意してください:jarsigner(zipalign の前)または aspsigner(zipaling の後)で、アプリケーションを一度だけ署名すればよい。
apksigner sign --ks key.jks ./dist/mycompiled.apk
より実用的な現代のフローは次のとおりです:
apktool b . -o dist/app-unsigned.apk
zipalign -P 16 -f -v 4 dist/app-unsigned.apk dist/app-aligned.apk
apksigner sign --ks key.jks --out dist/app-signed.apk dist/app-aligned.apk
apksigner verify --verbose --print-certs dist/app-signed.apk
重要な注意点:
apksignerで署名した後にAPKを変更すると、署名は無効になり、再度署名する必要があります。apksigner verify --print-certsは、再構築したAPKがインストール可能かを確認し、ターゲットがランタイムで公開する証明書を検査するのに役立ちます。
Smaliの変更
以下の Hello World Java コード:
public static void printHelloWorld() {
System.out.println("Hello World")
}
Smali code は次のようになります:
.method public static printHelloWorld()V
.registers 2
sget-object v0, Ljava/lang/System;->out:Ljava/io/PrintStream;
const-string v1, "Hello World"
invoke-virtual {v0,v1}, Ljava/io/PrintStream;->println(Ljava/lang/String;)V
return-void
.end method
Smaliの命令セットはhereで参照できます。
軽微な変更
関数内の変数の初期値を変更する
一部の変数は関数の先頭で opcode const を使用して定義されています。値を変更したり、新しい変数を定義したりできます:
#Number
const v9, 0xf4240
const/4 v8, 0x1
#Strings
const-string v5, "wins"
基本操作
#Math
add-int/lit8 v0, v2, 0x1 #v2 + 0x1 and save it in v0
mul-int v0,v2,0x2 #v2*0x2 and save in v0
#Move the value of one object into another
move v1,v2
#Condtions
if-ge #Greater or equals
if-le #Less or equals
if-eq #Equals
#Get/Save attributes of an object
iget v0, p0, Lcom/google/ctf/shallweplayagame/GameActivity;->o:I #Save this.o inside v0
iput v0, p0, Lcom/google/ctf/shallweplayagame/GameActivity;->o:I #Save v0 inside this.o
#goto
:goto_6 #Declare this where you want to start a loop
if-ne v0, v9, :goto_6 #If not equals, go to: :goto_6
goto :goto_6 #Always go to: :goto_6
大きな変更
Smaliで通常リビルドを壊す注意点
- Prefer increasing
.localswhen 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.registersoften breaks argument layout. move-result,move-result-wide, andmove-result-objectmust appear immediately after the matchinginvoke-*. Inserting logging or any other opcode between them makes the method invalid.longanddoublevalues are wide values and consume a register pair. If you reuse those registers later, remember thatv10also occupiesv11.- If you need to pass many registers, or very high-numbered ones, use the
/rangevariants such asinvoke-virtual/range.
Logging
#Log win: <number>
iget v5, p0, Lcom/google/ctf/shallweplayagame/GameActivity;->o:I #Get this.o inside v5
invoke-static {v5}, Ljava/lang/String;->valueOf(I)Ljava/lang/String; #Transform number to String
move-result-object v1 #Move to v1
const-string v5, "wins" #Save "win" inside v5
invoke-static {v5, v1}, Landroid/util/Log;->d(Ljava/lang/String;Ljava/lang/String;)I #Logging "Wins: <num>"
Recommendations:
- 関数内で宣言済みの変数を使用する場合(declared v0,v1,v2…)は、これらの行を .local
と変数の宣言(const v0, 0x1)の間に配置してください。 - 関数のコードの途中にログ用のコードを入れたい場合:
- 宣言済み変数の数に2を追加します。例: .locals 10 から .locals 12 へ
- 新しい変数は既に宣言されている変数の次の番号にしてください(この例では v10 と v11 になります。v0 から始まることを忘れないでください)。
- logging 関数のコードを変更し、v10 と v11 を v5 と v1 の代わりに使用してください。
一般的な anti-tamper チェックのパッチ適用
アプリを repacked すると、最初に壊れる可能性が高いものの1つはアプリ内の signature / installer / integrity チェックです。JADX や smali ツリーで検索する良い文字列は次の通りです:
GET_SIGNATURESGET_SIGNING_CERTIFICATESapkContentsSignersMessageDigestSHA-256Base64getInstallerPackageNamecom.android.vending
最近のアプリはしばしば PackageManager.getPackageInfo(..., GET_SIGNING_CERTIFICATES) を呼び出し、signer bytes を MessageDigest でハッシュして、その結果をハードコードされた定数と比較します。実際には、署名処理全体を書き直すよりも、final boolean / branch をパッチする方が簡単なことが多いです。
Example patterns:
# Force a boolean result to "valid"
const/4 v0, 0x1
# Or invert the branch that sends execution to the tamper handler
if-eqz v0, :tamper_detected # original
if-nez v0, :tamper_detected # patched
検証コードがノイジーな場合は、エラーダイアログ / finish() / System.exit() / テレメトリ呼び出しの直前にある最後の比較を探し、ルーチン全体を触る代わりにそこでパッチしてください。
トースト表示
関数の先頭にある .locals の数に 3 を加えることを忘れないでください。
このコードは関数の途中に挿入するよう用意されています(必要に応じて変数の数を変更してください)。これはthis.o の値を取り、変換してStringにし、その値でtoastを表示します。
const/4 v10, 0x1
const/4 v11, 0x1
const/4 v12, 0x1
iget v10, p0, Lcom/google/ctf/shallweplayagame/GameActivity;->o:I
invoke-static {v10}, Ljava/lang/String;->valueOf(I)Ljava/lang/String;
move-result-object v11
invoke-static {p0, v11, v12}, Landroid/widget/Toast;->makeText(Landroid/content/Context;Ljava/lang/CharSequence;I)Landroid/widget/Toast;
move-result-object v12
invoke-virtual {v12}, Landroid/widget/Toast;->show()V
スタートアップ時にネイティブライブラリを読み込む (System.loadLibrary)
場合によっては、他の JNI ライブラリより先に初期化されるようにネイティブライブラリをプリロードする必要があります(例: プロセスローカルの telemetry/logging を有効にするため)。System.loadLibrary() の呼び出しを静的初期化子や Application.onCreate() の早い段階に注入できます。静的クラス初期化子 (
.class public Lcom/example/App;
.super Landroid/app/Application;
.method static constructor <clinit>()V
.registers 1
const-string v0, "sotap" # library name without lib...so prefix
invoke-static {v0}, Ljava/lang/System;->loadLibrary(Ljava/lang/String;)V
return-void
.end method
あるいは、同じ2つの指示を Application.onCreate() の先頭に配置して、ライブラリが可能な限り早く読み込まれるようにしてください:
.method public onCreate()V
.locals 1
const-string v0, "sotap"
invoke-static {v0}, Ljava/lang/System;->loadLibrary(Ljava/lang/String;)V
invoke-super {p0}, Landroid/app/Application;->onCreate()V
return-void
.end method
注意:
- ライブラリの正しい ABI バリアントが lib/
/(例: arm64-v8a/armeabi-v7a)配下に存在することを確認して、UnsatisfiedLinkError を回避してください。 - 非常に早い段階(class static initializer)でロードすると、native logger がその後の JNI 活動を観測できることが保証されます。
Smali Static Analysis / Rule-Based Hunting
apktool でデコンパイルした後、regex ルールで Smali を行単位でスキャンして、anti-analysis logic(root/emulator checks)やハードコーディングされている可能性が高いシークレットを素早く検出できます。これは 高速なトリアージ 手法であり、ヒットは周辺の Smali や再構築した Java/Kotlin で検証すべきリードとして扱ってください。
Key ideas:
- Library filtering: 一般的なサードパーティのネームスペース配下の検出結果を抑制またはタグ付けして、アプリ所有のコードパスに集中できるようにする。
- Context hints: 疑わしい文字列がそれらを利用する API の近く(同じメソッド内、N 行以内など)に出現することを要求する。
- Confidence: 単純なレベル(high/medium)を使ってリードをランク付けし、誤検出を減らす。
Example library prefixes to suppress by default:
Landroidx/
Lkotlin/
Lkotlinx/
Lcom/google/
Lcom/squareup/
Lokhttp3/
Lokio/
Lretrofit2/
検出ルールの例 (regex + コンテキストヒューリスティック):
{
"category": "root_check",
"regex_patterns": [
"(?i)invoke-static .*Runtime;->getRuntime\\(\\).*->exec\\(.*\\"(su|magisk|busybox)\\"",
"(?i)const-string [vp0-9, ]+\\"(/system/xbin/su|/system/bin/su|/sbin/su)\\""
],
"context_hint": "Only report when the same method also calls File;->exists/canExecute or Runtime;->exec."
}
実務で有効な追加ヒューリスティクス:
- Root package/path checks:
com.topjohnwu.magiskや/data/local/tmpのような文字列に対して、近傍にPackageManager;->getPackageInfoやFile;->exists呼び出しがあることを要求する。 - Emulator checks: 疑わしいリテラル(例:
ro.kernel.qemu,generic,goldfish)を、近傍のBuild.*ゲッターや文字列比較(->equals,->contains,->startsWith)と組み合わせる。 - Hardcoded secrets: 近傍の
.fieldやmove-result識別子にpassword、token、api_keyのようなキーワードが含まれる場合にのみconst-stringをフラグ付けする。AutofillType、InputType、EditorInfoのような UI 専用マーカーは明示的に無視する。
Rule-driven scanners like PulseAPK Core implement this model to quickly surface anti-analysis logic and potential secrets in Smali.
参考資料
- PulseAPK Core
- PulseAPK Smali Detection Rules
- SoTap: Lightweight in-app JNI (.so) behavior logger – github.com/RezaArbabBot/SoTap
- Android Developers: apksigner and zipalign
- apk.sh: github.com/ax/apk.sh
Tip
AWS Hackingを学び、実践する:
HackTricks Training AWS Red Team Expert (ARTE)
GCP Hackingを学び、実践する:HackTricks Training GCP Red Team Expert (GRTE)
Az Hackingを学び、実践する:HackTricks Training Azure Red Team Expert (AzRTE)
HackTricks Trainingの全カタログ を閲覧して、評価トラック(ARTA/GRTA/AzRTA)と Linux Hacking Expert (LHE) を確認してください。
HackTricksをサポート
- subscription plans を確認してください!
- 💬 Discord group、telegram group に参加し、X/Twitterで @hacktricks_live をフォローするか、LinkedIn page と YouTube channel を確認してください。
- HackTricks と HackTricks Cloud の github repos に PR を送信して hacking tricks を共有してください。


