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をサポート

アプリケーションのコードを変更して、隠された情報(例えば巧妙に難読化されたパスワードや flags)にアクセスすることが有用な場合があります。その場合、apkを逆コンパイルしてコードを修正し、再コンパイルすることが有効です。

Opcodes reference: http://pallergabor.uw.hu/androidblog/dalvik_opcodes.html

簡単な方法

Visual Studio CodeAPKLab 拡張機能を使用すると、コマンドを実行することなくアプリを 自動で逆コンパイル、修正、再コンパイル、署名およびインストールできます。

この作業を大幅に容易にする別のスクリプトhttps://github.com/ax/apk.sh です。

Split APKs / App Bundles

最近のターゲットは単一のモノリシックなAPKではなく、split APKsbase.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されます。

もし apktoolerror を出す場合は、 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 .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.
  • 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.
  • long and double values are wide values and consume a register pair. If you reuse those registers later, remember that v10 also occupies v11.
  • If you need to pass many registers, or very high-numbered ones, use the /range variants such as invoke-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
  • 新しい変数は既に宣言されている変数の次の番号にしてください(この例では v10v11 になります。v0 から始まることを忘れないでください)。
  • logging 関数のコードを変更し、v10v11v5v1 の代わりに使用してください。

一般的な anti-tamper チェックのパッチ適用

アプリを repacked すると、最初に壊れる可能性が高いものの1つはアプリ内の signature / installer / integrity チェックです。JADX や smali ツリーで検索する良い文字列は次の通りです:

  • GET_SIGNATURES
  • GET_SIGNING_CERTIFICATES
  • apkContentsSigners
  • MessageDigest
  • SHA-256
  • Base64
  • getInstallerPackageName
  • com.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() の早い段階に注入できます。静的クラス初期化子 () の smali の例:

.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;->getPackageInfoFile;->exists 呼び出しがあることを要求する。
  • Emulator checks: 疑わしいリテラル(例: ro.kernel.qemu, generic, goldfish)を、近傍の Build.* ゲッターや文字列比較(->equals, ->contains, ->startsWith)と組み合わせる。
  • Hardcoded secrets: 近傍の .fieldmove-result 識別子に passwordtokenapi_key のようなキーワードが含まれる場合にのみ const-string をフラグ付けする。AutofillTypeInputTypeEditorInfo のような UI 専用マーカーは明示的に無視する。

Rule-driven scanners like PulseAPK Core implement this model to quickly surface anti-analysis logic and potential secrets in Smali.

参考資料

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をサポート