Heim  >  Artikel  >  System-Tutorial  >  Android-Linux-Kernelschicht Android-prozessübergreifende Kommunikation: Einführung in IPC, Binder und ServiceManager

Android-Linux-Kernelschicht Android-prozessübergreifende Kommunikation: Einführung in IPC, Binder und ServiceManager

PHPz
PHPzOriginal
2024-06-30 16:27:04758Durchsuche

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

Author Summit-Konto: windy_ll

Anwendung von ebpf in der Android-Sicherheit: In Kombination mit Binder zur Vervollständigung einer Verhaltensinspektions-Sandbox (Teil 1) 1. Kurze Einführung in IPC

IPC ist die Abkürzung für Inter-Process Communication, was Inter-Process-Communication oder Cross-Process-Communication bedeutet. Es bezieht sich auf den Prozess des Datenaustauschs zwischen zwei Prozessen.

Wann benötigt Android prozessübergreifende Kommunikation? Wenn Android Systemdienste anfordert, ist eine prozessübergreifende Kommunikation erforderlich, z. B. der Zugriff auf Adressbücher von Mobiltelefonen, das Abrufen des Standorts usw. Das Ziel dieses Artikels besteht darin, eine einfache Sandbox zu implementieren, um dieses Verhalten zu erfassen

2. Kurze Einführung in den Ordner

Binder ist eine Form der prozessübergreifenden Kommunikation in Android, die als spezifische Implementierungsmethode von IPC verstanden werden kann

3. Kurze Einführung in ServiceManager

ServiceManager ist ein äußerst wichtiger Systemdienst in Android. Wie Sie am Namen erkennen können, wird er zur Verwaltung von Systemdiensten verwendet

ServiceManager wird durch den Init-Prozess gestartet

ServiceManager ist für die folgenden Funktionen verantwortlich: Dienstregistrierung und -suche, Kommunikation zwischen Prozessen, Start und Aktivierung von Systemdiensten sowie Bereitstellung von Listeninstanzen von Systemdiensten

Der Binder-Treiber bestimmt die zugrunde liegenden Kommunikationsdetails, sodass der ServiceManager einer Navigation entspricht und der spezifischen Kommunikation mitteilt, wie sie gehen soll, wohin sie gelangen soll und so weiter

4. Kommunikationsanalyse 4.1 Client-Aufruf-JAVA-Layer-Analyse

Nehmen Sie die getConnectInfo-Funktion der WifiManager-Klasse (diese Funktion ruft WLAN-Informationen ab) als Beispiel für die Analyse

Strg+Linksklick, um die Referenz anzuzeigen. Sie können feststellen, dass die Funktion in der Klasse .wifi.WifiManager definiert ist, wie rechts gezeigt:

Wie Sie dem Bild oben entnehmen können, enthält der spezifische Code der getConnectInfo-Funktion nur einen Satz: thrownewRuntimeException("Stub!");, der uns mitteilt, dass diese Funktion durch dieselbe Klasse im ROM ersetzt und ausgeführt wird Die hier definierte Klasse ist für die Kompilierung erforderlich (PS: Als Referenz). Wir finden diese Klasse im Verzeichnis Frameworks/Base/Wifi/Java/Amdroid/Net/Wifi im Android-Quellcode und finden dann die spezifische Implementierung der Funktion. wie rechts abgebildet:

Sie können feststellen, dass diese Funktion die Funktion „getConnectionInfo“ von IWifiManager aufruft. Sie finden die Datei „IWifiManager.aidl“ im Verzeichnis „frameworks/base/wifi/java/amdroid/net/wifi“. rechts:

Hier muss ein Konzept vorgestellt werden: AIDL ist eine Socket-Sprache von Android, mit der der Socket von Android-Diensten verfügbar gemacht wird, um prozessübergreifende Funktionsaufrufe zu realisieren. AIDL generiert beim Kompilieren zwei Klassen, nämlich Stub und Proxy. Die Stub-Klasse ist die Manifestation der serverseitigen Darstellungsschicht, und der Proxy ist die Instanz, die der Android-Client über diese Entwurfsmuster von Proxy-Stub erhält

Schreiben Sie unten eine Hilfsdatei und generieren Sie dann den entsprechenden Java-Code, um zu sehen, wie der Aufruf implementiert wird. Zuerst suchen wir ein zufälliges Projekt in Android Studio und erstellen dann eine neue Hilfsdatei, wie rechts gezeigt:

Danach kann Build->MakeProbject generiert werden. Der generierte Pfad befindet sich in build/generated/aidl_source_output_dir/debug/out/package name, wie rechts gezeigt:

Wenn wir die generierte Java-Datei betrachten, können wir feststellen, dass die Proxy-Klasse bereits generiert wurde. In der Proxy-Klasse finden wir die von uns definierten Funktionen, wie im Bild rechts gezeigt:

Lassen Sie uns diese Funktion im Detail analysieren. Zuerst wird eine Parcel-Instanz über die Funktion „Abrufen“ generiert, und dann werden die Schreibreihenfunktionen von Parcel zum Schreiben aufgerufen. Obwohl es sich um einen Serialisierungsprozess handelt, wird dann die Transaktionsfunktion von IBinder aufgerufen und die Funktion verfolgt Die Java-Datei befindet sich im Verzeichnis „frameworks/base/core/java/android/os“, wie rechts gezeigt:

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

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

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

Es kann festgestellt werden, dass IBinder nur ein Socket-Linux-Löschbefehl ist, der die Transaktionsmethode definiert. Der erste Parametercode ist die Funktionsnummer in unserem Remote-Aufruf Die statischen Variablen in der Stub-Klasse, damit Sie analysieren können, welche Funktion aufgerufen wird. Die zweiten und dritten Parameter _data und _reply sind die übergebenen Parameter und der zurückgegebene Wert, bei denen es sich allesamt um serialisierte Daten handelt Parameterflags geben an, ob blockiert und auf das Ergebnis gewartet werden muss. 0 bedeutet Blockieren und Warten und 1 bedeutet sofortige Rückkehr. Nach einer globalen Suche können Sie feststellen, dass die BinderProxy-Klasse im selben Verzeichnis diesen Socket implementiert (PS: Es ist erwähnenswert, dass es im selben Verzeichnis auch eine Binder-Klasse gibt, die diesen Socket ebenfalls implementiert, die Binder-Klasse jedoch eine serverseitige Implementierung, keine Client-Implementierung), wie im Bild rechts gezeigt:

内核层是什么细胞_android linux内核层_内核层向上提供的标准接口Nachdem wir diese Funktion analysiert haben, können wir feststellen, dass sie sich schließlich in Richtung der transactNative-Funktion bewegt. Zu diesem Zeitpunkt ist die Analyse der Java-Schicht des IPC-Kommunikationsclients abgeschlossen

4.2 Analyse des Client-Aufrufs der nativen Ebene

Durchsuchen Sie die transactNative-Funktion global und Sie werden feststellen, dass die Funktion Informationen in der nativen Ebene registriert, wie rechts gezeigt:

内核层向上提供的标准接口_android linux内核层_内核层是什么细胞Verfolgen Sie die Funktion android_os_BinderProxy_transact. Sie können feststellen, dass die Funktion zuerst ein BpBinder-Objekt über getBPNativeData(env,obj)->mObject.get() abruft und dann die Transaktionsfunktion von BpBinder aufruft, wie rechts gezeigt:

内核层是什么细胞_内核层向上提供的标准接口_android linux内核层Folgen Sie weiter und Sie werden feststellen, dass Sie die Transaktionsfunktion von IPCThreadState eingegeben haben, wie rechts gezeigt:

Anschließend können Sie feststellen, dass zuerst die Funktion writeTransactionData aufgerufen wird, mit der die Struktur binder_transaction_data ausgefüllt und zum Senden an den Binder-Treiber vorbereitet wird. Anschließend wird waitForResponse aufgerufen, um auf die Rückgabe zu warten, wie rechts gezeigt:

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

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

Wenn wir der Funktion „waitForResponse“ folgen, können wir feststellen, dass das Wichtigste an dieser Funktion darin besteht, die Funktion „talkWithDriver“ aufzurufen. Bei der Analyse der Funktion „talkWithDriver“ können wir feststellen, dass ioctl schließlich aufgerufen wird, wie rechts gezeigt:

Bisher ist die Analyse der nativen Clientebene abgeschlossen

4.3 Kernel-Layer-Analyse (Binder-Treiber-Analyse)

An diesem PunktAndroid-Linux-Kernelschicht kann unser ebpf-Programm mit der Erfassung und Analyse des Datenformats beginnen

Wenn die Benutzerebene ioctl aufruft, wechselt sie in den Kernelstatus und gibt die Kernelfunktion binder_ioctl ein (ps: Der entsprechende Deskriptor befindet sich in binder.c im Quellcode des Kernelgeräts. Analysieren Sie die Funktion binder_ioctl.) Die Hauptfunktion dieser Funktion lautet: Um Daten zwischen den beiden Prozessen zu senden, lautet unser Kommunikationsdaten-ioctl-Befehl BINDER_WRITE_READ. Wenn dieser Befehl auftritt, wird die Funktion binder_ioctl_write_read aufgerufen, wie rechts gezeigt:

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

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

Nach der Funktion binder_ioctl_write_read können wir feststellen, dass die Funktion zuerst den Wert der Adresse, auf die der Parameter arg vom Typ unsignedlong zeigt, in die Struktur binder_write_read liest. Dies zeigt an, dass der übergebene Parameter der Zeiger ist, auf den der ioctl-Befehl BINDER_WRITE_READ lautet die binder_write_read-Struktur, wie rechts gezeigt:

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

Obwohl unsere Kernel-Zustandsanalyse hier abgeschlossen werden kann, haben wir bereits die gewünschten Daten beobachtet, nämlich die Struktur binder_write_read. Sie können sich die Definition dieser Struktur ansehen, wie unten gezeigt:

<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>

Diese Struktur wird verwendet, um die während der Kommunikation zwischen Prozessen übertragenen Daten zu beschreiben. Wenn wir das vom Client an den Server gesendete Kommunikationspaket lesen, müssen wir nur auf write_size, write_consumed und write_buffer achten Eine verknüpfte Liste. Diese Struktur wird in der nativen Schicht writeTransactionData gefüllt. Unter diesen sind die Datenstrukturen, auf die write_buffer und read_buffer verweisen

Im Allgemeinen übergibt der Treiber mehrere Befehls- und Adresskombinationen gleichzeitig, und wir müssen die Anweisungen finden, die der Struktur binder_transaction_data entsprechen. In der Header-Datei binder.h finden wir alle vom Treiber definierten Anweisungen (+). /refs /heads/android-mainline/include/uapi/linux/android/binder.h), wie rechts gezeigt:

内核层向上提供的标准接口_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检查等等。

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

-官方峰会

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

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

Das obige ist der detaillierte Inhalt vonAndroid-Linux-Kernelschicht Android-prozessübergreifende Kommunikation: Einführung in IPC, Binder und ServiceManager. Für weitere Informationen folgen Sie bitte anderen verwandten Artikeln auf der PHP chinesischen Website!

Stellungnahme:
Der Inhalt dieses Artikels wird freiwillig von Internetnutzern beigesteuert und das Urheberrecht liegt beim ursprünglichen Autor. Diese Website übernimmt keine entsprechende rechtliche Verantwortung. Wenn Sie Inhalte finden, bei denen der Verdacht eines Plagiats oder einer Rechtsverletzung besteht, wenden Sie sich bitte an admin@php.cn