Smali - デコンパイル/[変更]/コンパイル

Reading time: 12 minutes

tip

AWSハッキングを学び、実践する:HackTricks Training AWS Red Team Expert (ARTE)
GCPハッキングを学び、実践する:HackTricks Training GCP Red Team Expert (GRTE) Azureハッキングを学び、実践する:HackTricks Training Azure Red Team Expert (AzRTE)

HackTricksをサポートする

アプリケーションのコードを変更して隠された情報にアクセスすること(難読化されたパスワードやフラグなど)が有用な場合があります。その際、apk をデコンパイルしてコードを変更し、再コンパイルすることが有用です。

オペコード参照: http://pallergabor.uw.hu/androidblog/dalvik_opcodes.html

手早い方法

Using Visual Studio Code and the APKLab extension, you can automatically decompile, modify, recompile, sign & install the application without executing any command.

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

APK をデコンパイルする

APKTool を使用すると、smali コードとリソースにアクセスできます:

bash
apktool d APP.apk

もし apktool がエラーを返す場合は、 installing the latest version をインストールしてみてください。

調べるべき興味深いファイル:

  • res/values/strings.xml (および res/values/* 内のすべての xml)
  • AndroidManifest.xml
  • 拡張子が .sqlite または .db のファイル

apktoolアプリケーションのデコードに問題がある場合は、https://ibotpeaches.github.io/Apktool/documentation/#framework-files を参照するか、引数 -r(リソースをデコードしない)を試してください。問題がソースコードではなくリソース側にある場合、これで問題は発生しなくなります(リソースはデコンパイルされません)。

smali コードの変更

命令を変更したり、いくつかの変数のを変更したり、新しい命令を追加したりできます。私は Smali コードを VS Code で編集します。smalise extension をインストールすると、エディタが不正な命令を教えてくれます。
いくつかのは以下にあります:

または check below some Smali changes explained を参照してください。

APK を再コンパイルする

コードを変更した後、次のようにしてコードを再コンパイルできます:

bash
apktool b . #In the folder generated when you decompiled the application

これにより、新しい APK が dist フォルダの内部コンパイルされます。

もし apktoolエラーを出す場合は、最新バージョンをインストールしてみてください

新しい APK に署名する

次に、キーを生成する必要があります(パスワードやランダムに入力して構わないいくつかの情報を尋ねられます):

bash
keytool -genkey -v -keystore key.jks -keyalg RSA -keysize 2048 -validity 10000 -alias <your-alias>

最後に、署名して新しいAPKを作成します:

bash
jarsigner -keystore key.jks path/to/dist/* <your-alias>

新しいアプリケーションの最適化

zipalign は、Android application (APK) files に対して重要な最適化を提供するアーカイブ整列ツールです。 More information here.

bash
zipalign [-f] [-v] <alignment> infile.apk outfile.apk
zipalign -v 4 infile.apk

新しい APK に署名(また?)

もし jarsigner の代わりに apksigner を使うことを好むなら、zipaling による最適化を適用した後にAPK に署名するべきです。ただし、アプリケーションは jarsigner(zipalign の前)で、または aspsigner(zipaling の後)で一度だけ署名すればよいという点に注意してください。

bash
apksigner sign --ks key.jks ./dist/mycompiled.apk

Smaliの変更

次の Hello World Java コードの場合:

java
public static void printHelloWorld() {
System.out.println("Hello World")
}

Smali のコードは次のようになります:

java
.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 を使って定義されています。値を変更したり、新しいものを定義したりできます:

bash
#Number
const v9, 0xf4240
const/4 v8, 0x1
#Strings
const-string v5, "wins"

基本操作

bash
#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

大きな変更

ロギング

bash
#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>"

推奨事項:

  • 関数内で宣言済みの変数を使用する場合(宣言は v0,v1,v2...)、これらの行を .local と変数の宣言(const v0, 0x1)の間に入れてください。
  • 関数のコードの途中にロギングコードを置きたい場合:
  • 宣言済み変数の数に2を追加してください。例: .locals 10 から .locals 12
  • 新しい変数は既に宣言されている変数の次の番号にしてください(この例では v10v11 になります。v0 から始まることを忘れないでください)。
  • ロギング関数のコードを変更し、v10v11v5v1 の代わりに使用してください。

トースト表示

関数の先頭で .locals の数に3を追加することを忘れないでください。

このコードは 関数の途中 に挿入するために用意されています(必要に応じて 変数 の数を変更してください)。this.o の値を取得し、String に変換して、その値で toast を表示します。

bash
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() の呼び出しを static 初期化子または Application.onCreate() の早い段階に注入できます。static クラスイニシャライザ () の smali の例:

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() の先頭に置いて、ライブラリが可能な限り早くロードされるようにします:

smali
.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 activity を観測できることが保証されます。

参考

tip

AWSハッキングを学び、実践する:HackTricks Training AWS Red Team Expert (ARTE)
GCPハッキングを学び、実践する:HackTricks Training GCP Red Team Expert (GRTE) Azureハッキングを学び、実践する:HackTricks Training Azure Red Team Expert (AzRTE)

HackTricksをサポートする