たくさんの自由帳

Apktool を使ってアプリを解体して組み立てる

投稿日 : | 0 日前

文字数(だいたい) : 8382

どうもこんばんわ。
adbコマンドでアプリインストールするの、面倒ですよね。特に複数端末が接続されている時。

Android Studioが使える場合のみですが、
物理端末の画面ミラーリング画面へapkを投げ込むとインストール出来ます、

step1

step2

step3

本題

アプリを作り直せるApktoolを使いました。動いた!
老舗ツールですが、今でも動いて感動。

というわけで、今回は自分用にApktoolで、APKを解体・組み立てまでの流れを残しておこうと思います。

これは、アプリ(APK)を解体して中身を書き換えて、もう一度APKに組み立て直すツール。
逆コンパイルだけでなく、そこからAPKをもう一度作ることが出来る。

リソースは容易に書き換えできて、
AndroidManifestから怪しい権限を消したり、strings.xmlを自分の言語にローカライズしたり、して、その後APKを作る。とか。

JavaのコードはSmaliになっているので、すごい微調整とかならすぐ出来るかもですが、
大きい変更は厳しそう雰囲気。。

難読化等されてると厳しいらしい。

環境

なまえあたい
ぱそこんWindows 10 Pro

ひつようなもの

  • Android Studio
    • まじでAPK直すだけで、Android開発一ミリも興味ないから入れたくない
      • って場合は、WSL2とかにインストールすれば良いはずです
      • 削除するのも面倒だし分からなくもない
    • ちなみにAndroid Studioが必要というかは
      • adbapksignerコマンドを叩きたいからで、別に開発するわけではない
  • Java
    • Android Studioを入れると付いてきますが、JAVA_HOMEがなんとかとか言われたら...
    • Android Studioを入れたときに入ってくるjbrのパスを環境変数にすれば良いはず
  • テキストエディタ
    • VSCodeなど、メモ帳で頑張っても良いはず?
      • 上手く行かなかったらVSCodeでも

ながれ

  • APKファイルを取り出す
  • Apktoolに渡して解体
  • 中身を書き換える
  • Apktoolで組み立てAPKの作成
  • 署名する
  • adb installでインストール

犠牲になる?アプリ

NewRadioSupporter
自分のだから大丈夫だし、そもそも自己満足のためだから誰にも言われんやろ...

必要なものを揃える

Apktoolはここから。
.batファイルは今回使わず、javaコマンドを毎回叩くことにします(なぜ...)
ダウンロードしたら、適当に新しいフォルダを作ってそこに入れてください。

Install Guide | Apktool

A minimum of Java 8 is required to run Apktool.

https://apktool.org/docs/install

Android Studioも使います。
先述の通り必要なのは一緒についてくるadbとかなので、起動することはないです、、、
とりあえずインストールを進めてください

Android Studio とアプリツールのダウンロード - Android デベロッパー  |  Android Developers

Android Studio provides app builders with an integrated development environment (IDE) optimized for Android apps. Download Android Studio today.

https://developer.android.com/studio?hl=ja

あとJavaが必要ですが、Android Studio入れていれば付いてくるはずなので。
JAVA_HOMEの環境変数の設定だけやっておいて1!!

APK を取り出す

adbコマンドで取り出す方法もあると思いますが、ファイルマネージャーによってはAPKを取り出す機能があるので今回はそれを使います。

apk

これをパソコンに転送~
apktoolのところに置きました。名前も短くしておくと、ターミナル操作が楽かも?

explorer

apktool を使って解体

.batを経由しないので、javaから書いていきます。
以下のコマンドを叩くと、APKが解体されフォルダに中身が展開されます。

java -jar .\apktool_2.11.1.jar d {APKのパス}

terminal

別のアプリとしてインストールする

AndroidアプリはapplicationId (packageName)で識別しています。
同じ名前のアプリでも、applicationIdが違えば別のアプリです。

既にapplicationIdのが同じアプリがインストールされている場合、アプリの更新としてインストールされます。
そのさい、アップデートの提供元が本物の開発者かどうか判断のため、署名を確認します。
この署名は、開発元しか持っていないため、別人が同じapplicationIdで更新することは出来ません。

なので、applicationIdを変更して別アプリとしてインストールするか、本家のアプリを削除するか。の2択です。
今回は前者のapplicationIdを変えて、もう一個同じアプリを入れることにします。

本家のアプリを消す場合はこの手順はスキップできます。
あと、よく分からないという場合も、本家を削除してスキップしたほうが良いかなと思います。

アプリが展開されたフォルダを開いて、AndroidManifest,xmlを、VSCodeか何かで開きます。

vscode

そしたらpackage属性を探してください。
この行のことです。2行目とかにあるはずです。

<manifest xmlns:android="http://schemas.android.com/apk/res/android" android:compileSdkVersion="36" android:compileSdkVersionCodename="16" package="io.github.takusan23.newradiosupporter" platformBuildVersionCode="36" platformBuildVersionName="16">

このpackage="io.github.takusan23.newradiosupporter"を、なにか被らない(ユニークな)別の文字にする必要があります。
適当に後ろに.originalとかつけておきますか。

package="io.github.takusan23.newradiosupporter.original"

他の箇所

同様に、変更する必要がある箇所一覧です

(あれば)

この2つの、android:name属性をapplicationIdの時のように被らないよう書き換えてください。
そして同じ値にする必要があります。

<permission android:name="io.github.takusan23.newradiosupporter.DYNAMIC_RECEIVER_NOT_EXPORTED_PERMISSION" android:protectionLevel="signature"/>
<uses-permission android:name="io.github.takusan23.newradiosupporter.DYNAMIC_RECEIVER_NOT_EXPORTED_PERMISSION"/>

<permission android:name="io.github.takusan23.newradiosupporter.original.DYNAMIC_RECEIVER_NOT_EXPORTED_PERMISSION" android:protectionLevel="signature"/>
<uses-permission android:name="io.github.takusan23.newradiosupporter.original.DYNAMIC_RECEIVER_NOT_EXPORTED_PERMISSION"/>

(あれば)

ContentProviderも一意である必要があります。
android:authoritiesの値を、applicationIdの時のように被らないよう書き換えてください。

<provider android:authorities="io.github.takusan23.newradiosupporter.androidx-startup" android:exported="false" android:name="androidx.startup.InitializationProvider">

<provider android:authorities="io.github.takusan23.newradiosupporter.original.androidx-startup" android:exported="false" android:name="androidx.startup.InitializationProvider">

リソースを編集してみる

手始めに、ホーム画面に表示されるアプリの名前を書き換えてみましょう。
同様にAndroidManifestを開きます

ホーム画面のアイコンを押したときに開くActivity (画面)の探し方ですが、
<intent-filter>android.intent.category.LAUNCHERがあるものが、ランチャーアプリで表示されるActivityになります。
まあ、後ははMainActivityって名前のActivityなら十中八九それです。

見つけたら、android:label属性を探します。今回は@string/app_nameでした。
@string/で始まってますね!!!これは文字列リソースなので、resフォルダを開きます。

そしたら、values-{言語コード}のフォルダが見つかると思います、日本語なのでjaですね。それを開きます。
最後、!strings.xmlを開きます!!
どうでしょう?見覚えある文字列、ありますか?

見覚えある

ランチャーアプリに表示される名前は@string/app_nameでした、ので、name="app_name"を探します。
一番上でしたね。(非開発者向け:Android Studioが最初に作るのでそれはそう)

<string name="app_name">NewRadioSupporter</string>

これを書き換えてみます。5G チェッカーみたいな?

<string name="app_name">5Gチェッカー</string>

出来たら保存です!

APK を組み立てる

apktoolにあるフォルダに戻って、ターミナル(コマンドプロンプト等)を開き、今度はbオプション!

java -jar .\apktool_2.11.1.jar b {展開されたフォルダの名前}

ビルド

成功すると、展開されたフォルダのdistフォルダにAPKがあるはずです。

alignment

これがないとインストールに失敗するのでやります。
zipalignコマンドですが、これはAndroid Studioをインストールすると付いてきます。
パスはここ:C:\Users\{ユーザー名}\AppData\Local\Android\Sdk\build-tools\{最新バージョン}\

パスが分かったところで、以下のコマンドを叩きます。
APKはさっき作ったやつ(dist)です。

{build-toolsのパス}\{最新バージョン}\zipalign.exe -p -f 4 {distにあるapkのパス} aligned.apk

aligned.apkが出来ていれば成功です!

aligned

署名

多分署名しないとインストール出来ません、
署名には署名ファイルが必要です。自分のものを使う、自分で作る、また、もしかすると開発中(デバッグ用)の署名が使えるかもしれません。

もう持っている場合は署名コマンドまでスキップ。

デバッグ用の署名ファイルを使う

すいません、今回は自分の署名ファイルを使うので、これが使えるかは見れていません。
署名ファイルはホームディレクトリの.androidフォルダのdebug.keystoreだと思います。

デバッグ

署名ファイルを用意

コマンドで作る場合は調べてみてください。
Android Studioで作る場合、適当に何かしらプロジェクトを作ります。なんでも良いです。

new project

そしたら、メニューバーからBuildGenerate Signed Bundle/APKへ進み、

menubar

apk

APKでもAABでもどっちでも良いので次に進み、Create newを押します。これを埋めれば、APKに署名するための証明書が完成です。
パスワードは署名コマンドを叩く際に聞かれるので覚えておいてください。

sign

APK に署名

署名ファイルが出来たら、以下のコマンドを叩きます。
今回もbuild-toolsにあるやつを使います。

{apksigner.bat のパス} sign --ks {署名ファイルのパス} --v2-signing-enabled true --ks-key-alias {key alias の値} --ks-pass pass:{key store pass の値} {aligned した APK のパス}

インストール

ここまで出来るとaligned.apkがインストールできるはずです。
以下のコマンドでインストール!!

install

端末にAPKを転送して、ファイルマネージャーで入れようとするとなんか失敗する時があったので、
adbが確実かも?

名前変わってる!!!

数字だと一番上で草

りねーむ

Java のコードも編集したい

Smaliと戦うか、、、
値を書き換えるくらいなら、その値で検索をかければいいので、今回はこれを試します。

探す

例えば、こんな感じに文字列リテラルの中のURLを書き換えてみようと思います。

/** プライバシーポリシーを開く */
private fun openPrivacyPolicy(context: Context) {
    val url = "https://takusan.negitoro.dev/pages/new_radio_supporter_privacy_policy/".toUri()
    context.startActivity(Intent(Intent.ACTION_VIEW, url))
}

そもそもこの関数をどうやって見つけるんだって話ですが、
GUIAPK逆コンパイル+ソース閲覧ツールjadxを使っています。

このアプリで、それっぽい処理を探し、(今回の例ではURLの文字列検索)
クラス、関数を探します。

jadx

該当の Smali ファイル

クラス名と同じ名前でSmaliファイルがあるはずです。
smaliで始まるフォルダを探してみてください。

Javaのパッケージと同じやつが見つかるはずなんですよね。
package

それっぽいファイルを見つけたら、同様にVSCode何かで開いて、探します。
関数の中で使ってる関数とかで検索をかけていく

書き換えてみる

対応する処理はここでした。
文字列リテラルがあると探すの楽でいいですね。

.method private static final openPrivacyPolicy(Landroid/content/Context;)V
    .locals 3

    .line 67
    const-string v0, "https://takusan.negitoro.dev/pages/new_radio_supporter_privacy_policy/"

    .line 168
    invoke-static {v0}, Landroid/net/Uri;->parse(Ljava/lang/String;)Landroid/net/Uri;

    move-result-object v0

    .line 68
    new-instance v1, Landroid/content/Intent;

    const-string v2, "android.intent.action.VIEW"

    invoke-direct {v1, v2, v0}, Landroid/content/Intent;-><init>(Ljava/lang/String;Landroid/net/Uri;)V

    invoke-virtual {p0, v1}, Landroid/content/Context;->startActivity(Landroid/content/Intent;)V

    return-void
.end method

この"https://github.com/takusan23/NewRadioSupporter"を書き換えて、ApktoolAPKを作り、実際にURLが書き換わっているか見てみます。
適当にこのブログのトップページに飛ばすようにしてみた。

const-string v0, "https://takusan.negitoro.dev/"

あとはこれでもう一回ApktoolAPKを作り、alignment署名インストールの手順で良いはずです。

事件簿

INSTALL_FAILED_DUPLICATE_PERMISSION: Package ... attempting to redeclare permission ...DYNAMIC_RECEIVER_NOT_EXPORTED_PERMISSION already owned by ...

androidx.appcompatライブラリ(?)が勝手にDYNAMIC_RECEIVER_NOT_EXPORTED_PERMISSION権限を作って自分に付与している、
同じ名前で権限を作ると怒られるので、名前変更。
別のアプリとしてインストールする の項目読んでください。

Failure [INSTALL_FAILED_CONFLICTING_PROVIDER: Scanning Failed.: Can't install because provider name ...androidx-startup (in package ...) is already used by ...]

これも同じ名前問題です。
別のアプリとしてインストールする の項目読んでください。

Failure [INSTALL_PARSE_FAILED_NO_CERTIFICATES: Failed to collect certificates from tmp/base.apk: Attempt to get length of null array]

署名してない。署名してからインストールです。
署名 の項目読んでください。

[INSTALL_FAILED_INVALID_APK: Failed to extract native libraries, res=-2]

この次と同じ

-124: Failed parse during installPackageLI: Targeting R+ (version 30 and above) requires the resources.arsc of installed APKs to be stored uncompressed and aligned on a 4-byte boundary

alignment の項目読んでください;;

[INSTALL_PARSE_FAILED_NO_CERTIFICATES: Scanning Failed.: No signature found in package of version 2 or newer for package ...]

jarsinger ではなく apksinger を使ってください。
詳しくは 署名 の項目読んでください。

おわり。
お疲れ様でした 88888