Androidで他のアプリの関数をフックしてみた

# 初めに
・初記事になります。拙い文章ですが、暖かい目で見て頂けると幸いです。

# 用意するもの
・フッキング用ライブラリ
    今回はCydia SubstrateのMSHookFunctionを使用させて頂きました。

https://github.com/rweichler/substrate

・普通のAndroid端末

・ApkEditer

#注意
この記事は解説記事ではなく、あくまでやってみたよ～という紹介の記事になります。
ですので、自分なりの解釈と間違っている知識を披露している、説明を省いているなどの場合がありますが、ご了承ください。

# 方法
対象アプリに攻撃用ライブラリを埋め込んでフックする

# ①対象アプリを作る
・流石に他の方が作ったアプリをフックする訳には行かないので、今回は自分で対象のアプリを作ります。
![zMhEwVCTZ2tF5LW1644887857_1644888039.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1960856/8c250f6c-c879-8024-e3a9-af63ce598a33.png)
対象アプリのファイル構成はこんな感じです。


```java:MainActivity.java
package xxxxxx.vtable_victim;

import androidx.appcompat.app.AppCompatActivity;

import android.os.Bundle;
import android.widget.Button;
import android.widget.TextView;

public class MainActivity extends AppCompatActivity {

    // Used to load the 'native-lib' library on application startup.
    static {
        System.loadLibrary("native-lib");
    }

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        Start();//nativeメゾットの呼び出し
    }
    native void Start();//nativeメゾット
}
```
```cpp:native-lib.cpp
#include <jni.h>
#include <string>
#include <thread>
#include <pthread.h>
#include <unistd.h>
#include <dlfcn.h>
#define println(...) ((void)__android_log_print(ANDROID_LOG_DEBUG, "VTEST Victim", __VA_ARGS__))

struct Target {
    void TargetHello();
    void Update();
};
void Target::TargetHello()  {
    printB("TargetHello");
}
void Target::Update()  {
    printB("Update");
}
extern "C"
JNIEXPORT void JNICALL
Java_xxxxxx_vtable_1victim_MainActivity_Start(JNIEnv *env, jobject thiz) {

    Target target;
    target.TargetHello();//TargetHello呼び出し

}
```
javaとcppファイルもこんな感じでセットアップします。
# ②攻撃用アプリを作る
攻撃用アプリは新しいプロジェクトを作成してcppファイルだけセットアップします。

```cpp:attack-lib.cpp
#include <jni.h>
#include <string>
#include "Substrate/CydiaSubstrate.h"
#include "android/log.h"
#define println(...) ((void)__android_log_print(ANDROID_LOG_DEBUG, "VTEST Attacker", __VA_ARGS__))

__attribute__((constructor))//System.loadLibraryで読み込まれた時に起動
void OnLoad(){
    void* handle = dlopen("libnative-lib.so",RTLD_LAZY);//ライブラリのハンドル
    MSHookFunction(NULL,NULL,NULL);//↓
}
```
フック用ライブラリの関数、MSHookFunctionから見ていきます。

```cpp
void MSHookFunction(void * symbol,void * hook,void ** old);
```

引用元:http://www.cydiasubstrate.com/api/c/MSHookFunction/

・symbol
>The address of code to instrument with replacement code. This is normally, but need not be, a function.

・hook
>The address of an ABI-compatible replacement for the code at the address referenced by symbol.

・old
>	A pointer to a function pointer that will be filled in with a stub which may be used to call the original implementation. This can be NULL if you do not proceed to the original.

それぞれの引数の説明がありました。うーん...なんだこれ...
要約すると
>symbolはターゲットの関数のアドレスが入って
>hookの所にはターゲットと置き換えたい関数のアドレスが入って
>oldの所には置き換えられた元の関数ポインタへのポインタが入る

...のかな?
間違っていたらご指摘お願いいたします。m(__)m

ほんじゃこれに沿って...

```cpp:attack-lib.cpp
#include <jni.h>
#include <string>
#include "Substrate/CydiaSubstrate.h"
#include "android/log.h"
#define println(...) ((void)__android_log_print(ANDROID_LOG_DEBUG, "VTEST Attacker", __VA_ARGS__))

void (*Target_TargetHello_origin)(void*);//元の関数の関数ポインタ
void Target_TargetHello_hook(void* thiz){//置き換えたい処理
    println("Hook -> Target::TargetHello Called");
}

__attribute__((constructor))//System.loadLibraryで読み込まれた時に起動
void OnLoad(){
    void* handle = dlopen("libnative-lib.so",RTLD_LAZY);
    void* funcaddr = dlsym(handle,"_ZN6Target11TargetHelloEv");//フックする関数のアドレス

    MSHookFunction(funcaddr,(void*)Target_TargetHello_hook,(void**)Target_TargetHello_origin);
    dlclose(handle);
}
```
こんな感じになりました。
これをlibattack-lib.soにコンパイルします。
# ③早速フックしてみる
準備が出来たので早速フックしてみようと思います...が。
今回はInjectとか他のアプリからフックするみたいな方法ではなく、
**対象アプリに直接埋め込んでフックするという手法を取ります**。
何故なら、今回使用しているAndroidの端末はroot化していないからです。

さて、対象アプリに埋め込む作業ですが、次の二つの手順を踏んでいきます。

>❶コンパイルしたlibattack-lib.soを対象アプリlibフォルダに入れる

>❷System.loadLibraryでlibattack-lib.soを読み込ませる

❶は簡単にできますが、❷はjavaをsmailにデコンパイルして、それを編集しなければいけません。

❶からやっていきます。
といっても、簡単な作業ですので、ファイル構成だけ載せていきます。

```jav:ファイル構成
============APKの中身=============
assert
res
lib
  ┗armeabi-v7a
        ┣libnative-lib.so
        ┗libattack-lib.so ←移植
AndroidManifest.xml
...
=================================
```
libattack-lib.soを移植しましたら❷に行きます
❷はsmaliファイルを編集するのですが、少し注意点があります。
>・対象ライブラリが読み込まれてからフックする
・対象ライブラリ内でメゾットが読み込まれる前にフックする
・MainActivity.smaliに埋め込み用コードを挿入する

```java:埋め込み用コード
    const-string v0, "attack-lib" 

    invoke-static {v0}, Ljava/lang/System;->loadLibrary(Ljava/lang/String;)V
```
先ほどの注意点を踏まえて編集していきます。

上記埋め込み用コードですが、MainActivity.javaのsetContentViewメゾットの後に挿入します。
MainActivity.javaをsmaliに変換して....

```java:MainActivity.smali
.class public Lxxxxxx/vtable_victim/MainActivity;
.super Landroidx/appcompat/app/AppCompatActivity;
.source "MainActivity.java"


# direct methods
.method static constructor <clinit>()V
    .registers 1

    const-string v0, "native-lib"

    invoke-static {v0}, Ljava/lang/System;->loadLibrary(Ljava/lang/String;)V

    return-void
.end method

.method public constructor <init>()V
    .registers 1

    invoke-direct {p0}, Landroidx/appcompat/app/AppCompatActivity;-><init>()V

    return-void
.end method


# virtual methods
.method native Start()V
.end method

.method protected onCreate(Landroid/os/Bundle;)V
    .registers 3

    invoke-super {p0, p1}, Landroidx/appcompat/app/AppCompatActivity;->onCreate(Landroid/os/Bundle;)V

    const v0, 0x7f0b001c

    invoke-virtual {p0, v0}, Lxxxxxx/vtable_victim/MainActivity;->setContentView(I)V

    こ↑こ↓

    invoke-virtual {p0}, Lxxxxxx/vtable_victim/MainActivity;->Start()V

    return-void
.end method
```
こ↑こ↓と書かれている部分に挿入します。

```java:MainActivity.smali(挿入後onCreate)
.method protected onCreate(Landroid/os/Bundle;)V
    .registers 3

    invoke-super {p0, p1}, Landroidx/appcompat/app/AppCompatActivity;->onCreate(Landroid/os/Bundle;)V

    const v0, 0x7f0b001c

    invoke-virtual {p0, v0}, Lxxxxxx/vtable_victim/MainActivity;->setContentView(I)V

    const-string v0, "attack-lib" 

    invoke-static {v0}, Ljava/lang/System;->loadLibrary(Ljava/lang/String;)V

    invoke-virtual {p0}, Lxxxxxx/vtable_victim/MainActivity;->Start()V

    return-void
.end method
```
これでOKです。
そしたらApk EditerでApkを組み上げまして....
**完成です！**
# ④試運転
それでは起動して動作を確認してみます。
・フック前
![スクリーンショット 2022-02-16 114600.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1960856/b558d16f-8131-25d9-1a0d-c1997b5e9101.png)
・フック後![スクリーンショット 2022-02-16 114459.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1960856/31743179-74b8-43ff-4a35-c55f20393add.png)

これでフックが出来ました。おしまい。





。


ではなく、これには__重大な問題__が残っています。それは....

**元の関数が呼び出せてない**という問題です。
これでは元々あった処理が呼び出されず、危険です。
というわけで、元の関数を呼び出していきましょう。

# ⑤元の関数を呼び出してみる
MsHookFunctionの三番目の引数に入っていた謎の関数ポインタ君がカギです。
元の関数の関数ポインタを関数として呼び出すことによって、元の関数を呼び出すことが出来ます。

```cpp:attack-lib.cpp
#include <jni.h>
#include <string>
#include "Substrate/CydiaSubstrate.h"
#include "android/log.h"
#define println(...) ((void)__android_log_print(ANDROID_LOG_DEBUG, "VTEST Attacker", __VA_ARGS__))

void (*Target_TargetHello_origin)(void*);//元の関数の関数ポインタ
void Target_TargetHello_hook(void* thiz){//置き換えたい処理
    println("Hook -> Target::TargetHello Called");
    Target_TargetHello_origin(thiz);//元の関数を呼び出す
}

__attribute__((constructor))//System.loadLibraryで読み込まれた時に起動
void OnLoad(){
    void* handle = dlopen("libnative-lib.so",RTLD_LAZY);
    void* funcaddr = dlsym(handle,"_ZN6Target11TargetHelloEv");//フックする関数のアドレス

    MSHookFunction(funcaddr,(void*)Target_TargetHello_hook,(void**)Target_TargetHello_origin);
    dlclose(handle);
}
```
結果![スクリーンショット 2022-02-16 122316.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1960856/e1d861d9-5d87-7c99-09d6-594dbbb0d198.png)
これで無事に呼び出せました
# ⑥最後に
まだ試したいことや、もっと幅広くできることもありますので、また時間があれば書きたいと思います。
読みにくい私の記事を読んでいただいてありがとうございました。

それでは～