著者サミットアカウント: Windy_ll
Android セキュリティにおける ebpf の適用: バインダーと組み合わせて動作検査サンドボックスを完成させる (パート 1) 1. IPC の簡単な紹介
IPCとはInter-Process Communicationの略で、プロセス間通信またはプロセス間通信のことを指します。
Android でクロスプロセス通信が必要になるのはどのような場合ですか? Android がシステム サービスをリクエストする場合、携帯電話のアドレス帳へのアクセス、位置情報の取得など、プロセスを越えた通信が必要になります。この記事の目的は、この動作をキャプチャするためのシンプルなサンドボックスを実装することです
2. バインダーの簡単な紹介
Binder は Android におけるクロスプロセス通信の形式であり、IPC の具体的な実装方法として理解できます
3. ServiceManager の簡単な紹介
ServiceManager は、その名前からわかるように、Android の非常に重要なシステム サービスです
。ServiceManagerはinitプロセスによって開始されます
ServiceManager は次の機能を担当します: サービスの登録と検索、プロセス間通信、システム サービスの起動と起動、システム サービスのリスト インスタンスの提供
バインダー ドライバーは基礎となる通信の詳細を決定するため、ServiceManager はナビゲーションに相当し、特定の通信に行き方や目的地などを伝えます
4. 通信分析 4.1 クライアント呼び出し Java レイヤー分析
WifiManager クラスの getConnectInfo 関数 (この関数は Wi-Fi 情報を取得します) を分析の例として取り上げます
Ctrl キーを押しながら左クリックしてリファレンスを表示すると、右側に示すように、関数が .wifi.WifiManager クラスで定義されていることがわかります。
上の図からわかるように、getConnectInfo 関数の特定のコードには、thrownewRuntimeException("Stub!"); という 1 つの文しかなく、この関数が rom 内の同じクラスによって置き換えられて実行されることがわかります。ここで定義されているものはコンパイルに必要です (追記: 参考)。Android ソース コードのディレクトリ Frameworks/base/wifi/java/amdroid/net/wifi でこのクラスを見つけて、関数の特定の実装を見つけることができます。右に示すように:この関数が IWifiManager の getConnectionInfo 関数を呼び出していることがわかります。IWifiManager.aidl ファイルは、下に示すように、aidl ディレクトリに定義されています。右:
ここで概念を紹介する必要があります --- AIDL は Android のソケット言語であり、Android サービスのソケットを公開してクロスプロセス関数呼び出しを実現するために使用されます。 AIDL はコンパイル時にスタブとプロキシという 2 つのクラスを生成します。スタブ クラスはサーバー側の表現層の表現であり、プロキシはこれらのプロキシ スタブの設計パターンを通じて Android が実装するインスタンスです。
以下の AIDL ファイルを作成し、対応する Java コードを生成して、呼び出しがどのように実装されるかを確認します。まず、Android Studio でランダムなプロジェクトを見つけて、右に示すように新しい AIDL ファイルを作成します。
その後、Build->MakeProbject を生成できます。生成されたパスは、右側に示すように、build/generated/aidl_source_output_dir/debug/out/package name にあります。
生成された Java ファイルを観察すると、右の図に示すように、Proxy クラス内に定義した関数が存在することがわかります。この関数を詳しく分析してみましょう。まず、取得関数によって Parcel インスタンスが生成され、次に Parcel の write シリーズ関数が呼び出され、書き込みが行われます。その後、IBinder の transact 関数が呼び出され、関数がトレースされます。 Java ファイルは、右に示すように、frameworks/base/core/java/android/os にあります。
IBinder は、transact メソッドを定義する単なるソケット Linux 削除コマンドであることがわかります。このメソッドには 4 つのパラメータがあります。最初のパラメータ コードは、この番号を受信した後、検索されます。 Stub クラスの静的変数なので、どの関数が呼び出されているかを解析できます。2 番目と 3 番目のパラメーター _data と _reply は、渡されるパラメーターと戻り値であり、すべてシリアル化されたデータですandroid linux カーネル層
、最後のものです。パラメータ フラグは、ブロックして結果を待つ必要があるかどうかを示します。0 はブロックして待機することを意味し、1 はすぐに戻ることを意味します。グローバル検索の後、同じディレクトリ内の BinderProxy クラスがこのソケットを実装していることがわかります (追記: 同じディレクトリ内にこのソケットを実装する Binder クラスもあることに注意してください。ただし、Binder クラスは右側の図に示すように、クライアント実装ではなくサーバー側の実装です:
この関数を分析すると、最終的にtransactNative関数に移行していることがわかります。この時点で、IPC通信クライアントのJava層の分析が完了しました。
4.2 ネイティブ層を呼び出すクライアントの解析transactNative 関数をグローバルに検索すると、右に示すように、関数がネイティブ層に情報を登録していることがわかります。
android_os_BinderProxy_transact 関数をトレースすると、右に示すように、この関数が最初に getBPNativeData(env,obj)->mObject.get() を通じて BpBinder オブジェクトを取得し、次に BpBinder の transact 関数を呼び出していることがわかります。
さらにフォローアップを続けると、右に示すように、IPCThreadState のトランザクション関数に入っていることがわかります。
続いて、右に示すように、writeTransactionData 関数が最初に呼び出されていることがわかります。この関数は、binder_transaction_data 構造体にデータを入力し、それをバインダー ドライバーに送信する準備をするために使用されます。次に、waitForResponse を呼び出して戻りを待ちます。
waitForResponse 関数に続いて、この関数で最も重要なことは talkWithDriver 関数を呼び出すことであることがわかります。 talkWithDriver 関数を分析すると、右に示すように、最後に ioctl が呼び出されることがわかります。
ここまででクライアントネイティブ層の分析は完了です4.3 カーネル層分析(バインダードライバー分析)
この時点で
Android Linuxカーネルレイヤー、ebpfプログラムはデータ形式のキャプチャと解析を開始できます
ユーザー層が ioctl を呼び出すと、カーネル状態に入り、binder_ioctl カーネル関数に入ります (追記: 対応する記述子は、カーネル デバイスのソース コードの binding.c にあります。binder_ioctl 関数を分析すると、この関数のメイン関数。2 つのプロセス間でデータを送信するために、通信データ ioctl コマンドは BINDER_WRITE_READ です。このコマンドが発生すると、右側に示すように、binder_ioctl_write_read 関数が呼び出されます。
binding_ioctl_write_read 関数に従うと、この関数は最初に unsignedlong 型の arg パラメーターによって示されるアドレスの値を binding_write_read 構造体に読み取ることがわかります。これは、ioctl コマンドが BINDER_WRITE_READ の場合、渡されるパラメーターは、 を指すポインターであることを示しています。右側に示すように、 binding_write_read 構造体:
カーネル状態の分析はここで終了できますが、必要なデータ、つまり、binder_write_read 構造体はすでに観察されています。以下に示すように、この構造体の定義を確認してください。
リーリーこの構造体は、クライアントからサーバーに送信される通信パケットを読み取る際に、write_size、write_consumed、write_buffer に注目するだけで済みます。このフィールドには、binder_transaction_data 構造体が含まれます。この構造体は、ネイティブ層の writeTransactionData 関数に埋め込まれます。このうち、write_buffer と read_buffer が指すデータ構造は次のとおりです。 一般的に、ドライバーは複数のコマンドとアドレスの組み合わせを一度に渡すため、バインダー.h ヘッダー ファイルで、ドライバーによって定義されたすべての命令を見つけることができる、binder_transaction_data 構造に対応する命令を見つける必要があります。 /refs /heads/android-mainline/include/uapi/linux/android/binder.h)、右に示すように:
可以发觉,BC_TRANSACTION和BC_REPLY指令都对应着binder_transaction_data结构体参数,但我们只须要顾客端发往驱动的数据包,所以我们只须要BC_TRANSACTION指令对应的参数即可
经过前面的剖析,我们找到了我们须要的核心数据---binder_transaction_data结构体,现今来看一下该结构体的定义,定义如下:
<em style="cursor: pointer;font-size: 12px"> 复制代码</em><em style="cursor: pointer;font-size: 12px"> 隐藏代码<br></em><code><span>struct</span> <span>binder_transaction_data</span> {<br> <span>union</span> {<br> __u32 handle;<br> <span>binder_uintptr_t</span> ptr;<br> } target; <span>/* 该事务的目标对象 */</span><br> <span>binder_uintptr_t</span> cookie; <span>/* 只有当事务是由Binder驱动传递给用户空间时,cookie才有意思,它的值是处理该事务的Server位于C++层的本地Binder对象 */</span><br> __u32 code; <span>/* 方法编号 */</span><br> __u32 flags;<br> <span>pid_t</span> sender_pid;<br> <span>uid_t</span> sender_euid;<br> <span>binder_size_t</span> data_size; <span>/* 数据长度 */</span><br> <span>binder_size_t</span> offsets_size; <span>/* 若包含对象,对象的数据大小 */</span><br> <span>union</span> {<br> <span>struct</span> {<br> <span>binder_uintptr_t</span> buffer; <span>/* 参数地址 */</span><br> <span>binder_uintptr_t</span> offsets; <span>/* 参数对象地址 */</span><br> } ptr;<br> __u8 buf[<span>8</span>];<br> } data; <span>/* 数据 */</span><br>};</code>
有了该数据结构linux安装,我们就可以晓得顾客端调用服务端的函数的函数、参数等数据了
五、实现疗效
PS:整套系统用于商业,就不做开源处理了,这儿只给出核心结构体复印的截图,就不再发后端的截图了
读取手机通信录(ps:这儿读取下来的数据采用了Toast复印的,所以将捕捉到的Toast相应的通讯包也一起复印了下来,下同):
获取地理位置:
获取wifi信息:
六、其他
里面当然我们只剖析到了发送部份,反过来,虽然我们也可以读取返回包甚至于更改返回包,就可用于对风控的对抗,比如在内核态中更改APP恳求的设备标示信息(其实前提是app走系统提供的驱动通讯),亦或则用于逆向的工作,比如过root检查等等。
这部份验证代码就暂不放下来了,感兴趣的可以自己实现一下
-官方峰会
公众号设置“星标”,您不会错过新的消息通知
如开放注册、精华文章和周边活动等公告
以上がAndroid Linux カーネル層 Android クロスプロセス通信: IPC、Binder、ServiceManager の概要の詳細内容です。詳細については、PHP 中国語 Web サイトの他の関連記事を参照してください。