首頁 >系統教程 >Linux >android linux核心層 Android 跨進程通訊:IPC、Binder 與 ServiceManager 介紹

android linux核心層 Android 跨進程通訊:IPC、Binder 與 ServiceManager 介紹

PHPz
PHPz原創
2024-06-30 16:27:04805瀏覽

android linux内核层 Android 跨进程通信:IPC、Binder 与 ServiceManager 介绍

作者高峰會帳號:windy_ll

ebpf在Android安全上的應用:結合binder完成一個行為檢查沙箱(上篇)一、IPC簡單介紹

IPC是Inter-ProcessCommunication的簡寫,含意為進程間通訊或則跨進程通訊,是指兩個進程之間進行資料交換的過程。

Android在哪些時侯會有跨進程通訊的須要? Android在懇求系統服務的時侯會有跨進程通訊的需求,例如存取手機通訊錄、取得定位等等行為,本文的目標就是實現一個簡易的捕捉這種行為的沙箱

二、binder簡單介紹

Binder是Android中的一種跨進程通訊形式,可以理解為是IPC的一種具體實作方法

三、ServiceManager簡單介紹

ServiceManager是Android中一個及其重要的系統服務,從它的名稱上就可以曉得,它是用於管理系統服務的

ServiceManager由init進程啟動

ServiceManager負責了以下的一些功能:服務的註冊與尋找、進程間通訊、系統服務的啟動與喚起、提供系統服務的清單實例

binder驅動決定了底層的通訊詳情,這樣ServiceManager則相當於導航,告訴具體的通訊該如何走,達到那兒等

四、通訊剖析4.1顧客端調用JAVA層剖析

以WifiManager類別的getConnectInfo函數(此函數取得wifi資訊)為例進行剖析

ctrl+左鍵查看引用,可以發覺函數定義在.wifi.WifiManager類別中,如右圖所示:

從上圖可以看見,getConnectInfo函數具體程式碼只有一句thrownewRuntimeException("Stub!");,這告訴我們這個函數是由rom中相同的類別去取代執行,該函數在這被定義是編譯所須要(PS :可以參考),在android源碼中的目錄frameworks/base/wifi/java/amdroid/net/wifi下我們可以找到該類,之後找到該函數的具體實現,如右圖所示:

可以發覺該函數呼叫了IWifiManager的getConnectionInfo函數,在frameworks/base/wifi/java/amdroid/net/wifi目錄下可以找到IWifiManager.aidl文件,該aidl中定義了getConnectionInfo函數,如右圖所示:

這兒須要引入一個概念---AIDL,AIDL是android的一種插口語言,用於公開android服務的插口,藉此來實現跨進程的函數呼叫。 AIDL在編譯時會產生兩個類,即Stub和Proxy兩個類,Stub類是服務端具象層的彰顯,Proxy是客戶端獲取的實例,android透過proxy-stub這些設計模式實現了IPC

下邊寫一個aidl文件之後產生相應的java程式碼來瞧瞧是如何實現調用的,首先,我們在androidstudio中隨意找一個項目,之後新建一個aidl文件,如右圖所示:

内核层是什么细胞_android linux内核层_内核层向上提供的标准接口

之後Build->MakeProbject即可生成,生成的路徑坐落build/generated/aidl_source_output_dir/debug/out/包名,如右圖:

觀察生成後的java檔可發覺,Proxy類別早已生成,在Proxy類別中我們可以找到我們定義的函數,如右圖:

具體剖析一下該函數,首先透過obtain函數產生了一個Parcel實例,之後呼叫Parcel的write系列函數進行寫入,雖然就是一個序列化的過程,之後呼叫了IBinder的transact函數,追蹤剖析一下該函數,在目錄frameworks/base/core/java/android/os下可以找到該java文件,如右圖所示:

内核层向上提供的标准接口_内核层是什么细胞_android linux内核层

android linux内核层_内核层是什么细胞_内核层向上提供的标准接口

内核层向上提供的标准接口_android linux内核层_内核层是什么细胞

可以發覺,IBinder只是一個插口linux刪除指令,其中定義了transact方式,該方式有4個參數,第一個參數code在我們的遠端呼叫中為函數編號,服務端接受到這個編號後,會去找尋Stub類別中的靜態變量,因而解析出是呼叫哪個函數,第二個和第三個參數_data、_reply為傳入的參數和傳回的值,都是經過序列化後的資料android linux內核層,最後一個參數flags為指示是否須要阻塞等待結果,0為阻塞等待,1為立刻返回。

全域搜尋一下,可以發覺同目錄下的BinderProxy類實作了該插口(PS:值得注意的是,同目錄下邊還存在一個Binder類,也實現了該插口,但Binder類是服務端的實現,而不是顧客端的實作),如右圖:

内核层是什么细胞_android linux内核层_内核层向上提供的标准接口

剖析該函數,可以發覺最後邁向了transactNative函數,到此為止,進行IPC通訊客戶端java層早已剖析完畢

4.2顧客端呼叫Native層剖析

全域搜尋一下transactNative函數,可以發覺該函數在native層中註冊訊息,如右圖所示:

内核层向上提供的标准接口_android linux内核层_内核层是什么细胞

追蹤android_os_BinderProxy_transact函數,可以發覺函數先透過getBPNativeData(env,obj)->mObject.get()取得了一個BpBinder對象,之後呼叫了BpBinder的transact函數,如右圖所示:

内核层是什么细胞_内核层向上提供的标准接口_android linux内核层

繼續跟進下去,可以發覺步入了IPCThreadState的transact函數,如右圖所示:

接著跟進,可以發覺首先調用writeTransactionData函數,該函數作用為填充binder_transaction_data結構體,為發送到binder驅動做打算,之後調用waitForResponse等待返回,如右圖所示:

android linux内核层_内核层向上提供的标准接口_内核层是什么细胞

android linux内核层_内核层是什么细胞_内核层向上提供的标准接口

跟進waitForResponse函數,可以發覺該函數最重要的就是調用talkWithDriver函數,剖析一下talkWithDriver函數,可以發覺最終調用了ioctl,如右圖所示:

四處為止,顧客端native層剖析完畢

4.3內核層剖析(binder驅動剖析)

到這裡android linux核心層,我們的ebpf程式就可以開始捕捉之後解析資料格式了

當用戶層呼叫ioctl時,會步入內核態,步入binder_ioctl內核函數(ps:可在內核設備源碼中的binder.c找到相應的描述符),剖析一下binder_ioctl函數,可發覺該函數主要作用為在兩個進程之間首發數據,我們的通訊數據ioctl指令是BINDER_WRITE_READ,當遇見該指令的時侯,會呼叫binder_ioctl_write_read函數,如右圖所示:

内核层是什么细胞_android linux内核层_内核层向上提供的标准接口

内核层向上提供的标准接口_内核层是什么细胞_android linux内核层

跟進binder_ioctl_write_read函數,可以發覺,該函數首先將unsignedlong類型的arg參數指向的地址的值讀取到結構體binder_write_read中,說明當ioctl指令為BINDER_WRITE_READ時,傳遞進來的參數為指向結構的binder_wtl指令為BINDER_WRITE_READ時,傳遞進來的參數為指向結構的binder_wtlrite_read的表針,如右圖:

内核层是什么细胞_内核层向上提供的标准接口_android linux内核层

到這裡雖然我們內核態的剖析早已可以結束了,我們早已觀察到了我們想要的資料了,即binder_write_read結構體,可以看一下該結構體的定義,如下所示:

<em style="cursor: pointer;font-size: 12px"> 复制代码</em><em style="cursor: pointer;font-size: 12px"> 隐藏代码<br></em><code><span>struct</span> <span>binder_write_read</span> {<br>    <span>binder_size_t</span> write_size; <span>/* 写内容的数据总大小 */</span><br>    <span>binder_size_t</span> write_consumed; <span>/* 记录了从缓冲区读取写内容的大小 */</span><br>    <span>binder_uintptr_t</span> write_buffer; <span>/* 写内容的数据的虚拟地址 */</span><br>    <span>binder_size_t</span> read_size; <span>/* 读内容的数据总大小 */</span><br>    <span>binder_size_t</span> read_consumed; <span>/* 记录了从缓冲区读取读内容的大小 */</span><br>    <span>binder_uintptr_t</span> read_buffer; <span>/* 读内容的数据的虚拟地址 */</span><br>};</code>

這個結構體是拿來描述進程間通訊過程中所傳輸的數據,我們讀取從顧客端發送到服務端的通訊包只須要關注write_size、write_consumed、write_buffer,其中,write_buffer指向的是一個鍊錶,這個字段中就包含了binder_transaction_data結構體,這個結構體在native層writeTransactionData函數填充的,我們的目標通訊包就是這個結構體,其中,write_buffer和read_buffer指向的資料結構大致如下:

通常來說,驅動會一次傳遞多個命令和位址的組合,而我們要在其中找到binder_transaction_data結構體對應的指令,在binder.h頭檔中我們可以找到驅動定義的所有指令(+/refs /heads/android-mainline/include/uapi/linux/android/binder.h),如右圖:

内核层向上提供的标准接口_android linux内核层_内核层是什么细胞

可以发觉,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安装,我们就可以晓得顾客端调用服务端的函数的函数、参数等数据了

五、实现疗效

内核层是什么细胞_内核层向上提供的标准接口_android linux内核层

PS:整套系统用于商业,就不做开源处理了,这儿只给出核心结构体复印的截图,就不再发后端的截图了

读取手机通信录(ps:这儿读取下来的数据采用了Toast复印的,所以将捕捉到的Toast相应的通讯包也一起复印了下来,下同):

获取地理位置:

获取wifi信息:

六、其他

里面当然我们只剖析到了发送部份,反过来,虽然我们也可以读取返回包甚至于更改返回包,就可用于对风控的对抗,比如在内核态中更改APP恳求的设备标示信息(其实前提是app走系统提供的驱动通讯),亦或则用于逆向的工作,比如过root检查等等。

这部份验证代码就暂不放下来了,感兴趣的可以自己实现一下

-官方峰会

公众号设置“星标”,您不会错过新的消息通知

如开放注册、精华文章和周边活动等公告

以上是android linux核心層 Android 跨進程通訊:IPC、Binder 與 ServiceManager 介紹的詳細內容。更多資訊請關注PHP中文網其他相關文章!

陳述:
本文內容由網友自願投稿,版權歸原作者所有。本站不承擔相應的法律責任。如發現涉嫌抄襲或侵權的內容,請聯絡admin@php.cn