smaliをいじってapkを改変するメモ

最近apkをいじって機能を追加する機会があったのですが、また弄らないといけなくなった時のためのメモです。

自分用なんでミスってたら生暖かい目で見るかコメント下さい

ちょっと前にやった304SHのSIMロック解除もこういうことをして条件分岐を曲げています。

 

apk→smali

apktoolを使います。

中にbaksmaliが含まれており、中身がsmaliで出力されます。

また、smaliとclasses.dexは相互に変換できるため、いじる際はsmaliの段階で改変します。

Javaまで持っていくと可読性は上がりますが大抵コンパイルエラーが起きたりするのでおすすめしません。

 

smali→apk

同じくapktoolを使います。

ディレクトリ内のdistにapkが出力されます。

またインストールするためには署名が必要なのでjarsignerで署名します。

https://developer.android.com/studio/publish/app-signing#signing-manually

 

 

これでバイトコードとapkが行ったり来たりできるようになりました。次はapkを解析しつつsmaliに書き込んでいきます

 

まず、apkの解析ですが、jadxを使うと大変捗ります。

bin/jadx-guiを起動するとapkを求められるので読み込ませると驚くほどきれいなJavaコードで出力されます。

smaliの中に変数名が含まれている場合、わかっているものはすべてオリジナルの名前で表示されますが、一部zとかz1とかになります(引数など)

後記のsmaliに機能追加する際に書いた単純なコードですが、ほぼ完全な形で逆コンパイルできます。

Smaliについてちょっとだけ

このクソコードはsmaliではこのようになっています

smaliのオペコード一覧を見ながら見てほしいのですが、このようにレジスタに値を入れたり呼び出した結果を格納したり等を行っています。

  • .localsが使用しているレジスタの数です。変更してこれよりたくさんのレジスタを使うこともできます。
  • .lineがデバッガに表示される行数です。別に連番になっている必要はないです
  • const-string v2, “yyyy/MM/dd HH:mm:ss”
    • v2にString型の文字列を格納します。オブジェクトなのでレジスタは1つだけですが、long型などは隣のレジスタも使用します(例:v1を指定するとv1とv2が使われる)
  • invoke-virtual {v1, v0}, Ljava/text/SimpleDateFormat;->format(Ljava/util/Date;)Ljava/lang/String;
    • invoke-* 関数を呼び出します。他にもinvoke-privateやinvoke-interfaceなどがあります。
    • {v1,v0}→v1が自身(さっき作ったSimpleDateFormat)で、v2が引数(Ljava/util/Date;)です。
      • invoke-staticだけはInteger.parseInt(String)のようなインスタンスを作らなくていい物に使うため自身がありません。{}内はすべて引数です
    • 引数が必要ない関数は{}の中が1つの場合もあります→invoke-direct {v0}, Ljava/util/Date;-><init>()V
      • 逆に引数がたくさんある場合は増えます
    • ->format(Ljava/util/Date;)
      • SimpleDateFormatのformatを呼び出しています。引数はv0のdateです。
    • Ljava/lang/String;
      • 最後のこれは返り値を表しています。
      • voidだとV、intだとIなどいろいろあります(下のリンク参照)
  • move-result-object v2
    • 上で実行したものの返り値をv2に格納しています。型は上の最後に書いてあるやつ(ここならString)です。

 

MainActivity.smaliで例えると、MainActivity.smaliの他にMainActivity$1.smaliやMainActivity$Classname.smaliなどがある場合があります。

$1(ものによって増えます)の場合、深度を表しています。違いました。$が付くやつはその中でインターフェイスを実装したインスタンスを生産した場合にできるようです。例もRunnableのrun()実装したので生産されています。

たとえば

みたいなコードを書いたとき、funcはMainActivity.smaliに、run()はMainActivity$1に書かれます。

$Classnameの方はそのまんまで、中でClassnameというクラスを作った場合$Classnameに隔離されます。

$Classnameのほうも同様のルールで$1などが生産されます。

 

正直全然書ききれてないけどこんだけ見ると思い出せるだろうということで…

慣れが大事だと思うのでいろんなsmaliを見てみるといいです。またjadxの結果と照らし合わせてみることでアプリの構造もよくつかめると思います。

 

smali改変

で、ここまでの知識を組み合わせるとSmaliをいじってアプリの動きを変えることができます。

多分ネットに出回ってるクラック版のアプリなどはこういうことをしています。

simロック解除でifの判定を常にtrueにするぐらいならconstでレジスタをいじるだけなのですが、例えばString文字列の中にDateを入れたいみたいなとき、SimpleDateFormat.formatを使わなければいけません。

そういうときは感覚で書いてもいいのですが適当なアプリを書いてビルドしてsmaliにするのが楽です。(上のコードたちはそのためのものです)

こんな感じに、出力されたsmaliのレジスタを空いてるやつに変えて形をあわせながら入れていくだけでDateを入れることができます。

また、.lineが連番じゃなくていいのを利用して使われていない行数にしておくとlogcatでのデバッグがしやすくなります。

logcatを使うためにはアプリをdebuggableにしておかなければなりません。

AndroidManifest.xmlのapplicationに追記します。

<application android:allowBackup=”true” android:debuggable=”true”>
こんな感じでapkが改変できるよ、という備忘録でした。

コメントを残す

メールアドレスが公開されることはありません。 * が付いている欄は必須項目です