binder系統(一) 源碼分析以及編寫測試用例

android handler是什麼?

binder 源碼分析

binder 主要的功能是跨進程的通信
韋東山老師圖
IPC:
對於binder通信,有三個對象

A  B 通信,A 需要知道 B是否存在。 通過誰? 
源:A
目的B: A通過查詢, serviceManager 返回一個handler
數據: buf

RPC:
調用哪一個函數?
傳給他什麼參數? ipc的buff傳入
返回值? ipc的buff返回

binder 上層通信思路

韋東山老師手畫靈魂圖
\frameworks\native\cmds\servicemanager

【client】
	{
		1. open binder 驅動
		2. 獲取服務
			{
				向 serviceManager 查詢服務
				獲得一個 handler 
			}
		3. 向 handle 發送數據
	}
【server】
	{
		1. open binder驅動
		2. 註冊服務
				a. 向 serviceManager 發送服務的名字
		3. while(1)
			{休眠
				讀驅動
				解析數據
				調用對應函數
			}
	}
【serviceManager】
	{
		1. open binder驅動
		2. 告訴驅動, 它是 serviceManager
		3. while(1)
			{休眠
				讀驅動數據,
				解析數據
				調用  a. 註冊服務(在鏈表中記錄名字)   
				      b.1 獲取服務(鏈表中查詢)
				      b.2 返回 "server" 進程的handle
			}
	}

* 進程間通信,都是通過binder實現。 所以第一步都是打開
* 通過 底層的 binder 來交換信息(buff)

源碼驗證

\frameworks\native\cmds\servicemanager\service_manager.c
service_manager

main開始分析
a. binder_open
b. binder_become_context_manager
c. binder_loop(bs, svcmgr_handler); // { while  } // --<  svcmgr_handler 傳遞給binder.c 回調用
	c.1 ioctl(bs->fd, BINDER_WRITE_READ, &bwr)
	c.2 binder_parse(bs, 0, (uintptr_t) readbuf, bwr.read_consumed, func);
		解析
		處理  : svcmgr_handler   具體參看源碼 txn->code
				SVC_MGR_GET_SERVICE/SVC_MGR_CHECK_SERVICE : 獲取服務
				SVC_MGR_ADD_SERVICE : 註冊服務
		回覆

\frameworks\native\cmds\servicemanager\bctest.c
bctest

服務註冊過程
	a. binder_open
	b. binder_call(bs, &msg, &reply, target, SVC_MGR_ADD_SERVICE)
						msg -> 含有服務的名字
							  reply -> 返回的數據
									target -> #define BINDER_SERVICE_MANAGER  0U  // 目標是 serviceManager
							 			     code -> SVC_MGR_ADD_SERVICE -> 函數代碼
獲取服務的過程
	a. binder_open
	b. binder_call(bs, &msg, &reply, target, SVC_MGR_CHECK_SERVICE)
						msg -> 含有服務的名字
							  reply -> 返回的數據
									target -> #define BINDER_SERVICE_MANAGER  0U  // 目標是 serviceManager
							 			     code -> SVC_MGR_CHECK_SERVICE -> 函數代碼

binder_call 函數分析


函數原型:
call : 調用
應該是一個遠程調用

	向誰發送數據?		target
	調用哪個函數?		code
	提供的參數是什麼?	msg
	返回值是什麼?		reply
	int binder_call(struct binder_state *bs,
                struct binder_io *msg, struct binder_io *reply,
                uint32_t target, uint32_t code)

不知道可不可以讀取binder內存來寫個病毒

怎麼用binder_call?

① 構造數據 : 放在buf[…], 用 binder_io 來描述

struct binder_io
{
    char *data;            /* pointer to read/write from */
    binder_size_t *offs;   /* array of offsets */
    size_t data_avail;     /* bytes available in data buffer */
    size_t offs_avail;     /* entries available in offsets array */

    char *data0;           /* start of data buffer */
    binder_size_t *offs0;  /* start of offsets buffer */
    uint32_t flags;
    uint32_t unused;
};

👆 binder_io => binder_write_read 👇
② 調用 ioctl 來發送數據

res = ioctl(bs->fd, BINDER_WRITE_READ, &bwr);

struct binder_write_read bwr;
struct binder_write_read {
	binder_size_t		write_size;	/* bytes to write */
	binder_size_t		write_consumed;	/* bytes consumed by driver */
	binder_uintptr_t	write_buffer;
	binder_size_t		read_size;	/* bytes to read */
	binder_size_t		read_consumed;	/* bytes consumed by driver */
	binder_uintptr_t	read_buffer;
};

③ ioctl 也會收數據, 收到一個 binder_write_read
轉換爲 binder_io 再傳回去.

分析 bctest.c 使用binder_call

bctest.c

// 使用傳輸數據
uint32_t svcmgr_lookup(struct binder_state *bs, uint32_t target, const char *name)
{
    uint32_t handle;
    unsigned iodata[512/4];
    struct binder_io msg, reply;  // 構造

    bio_init(&msg, iodata, sizeof(iodata), 4);  // init 
    bio_put_uint32(&msg, 0);  // strict mode header // 👈 
    bio_put_string16_x(&msg, SVC_MGR_NAME); // 👈
    bio_put_string16_x(&msg, name);// 👆 有 put 就有 get

    if (binder_call(bs, &msg, &reply, target, SVC_MGR_CHECK_SERVICE))
        return 0;
}

int binder_call(struct binder_state *bs,
                struct binder_io *msg, struct binder_io *reply,
                uint32_t target, uint32_t code)
{
    int res;
    struct binder_write_read bwr;
    struct {
        uint32_t cmd;
        struct binder_transaction_data txn;
    } __attribute__((packed)) writebuf;
    unsigned readbuf[32];

    if (msg->flags & BIO_F_OVERFLOW) {
        fprintf(stderr,"binder: txn buffer overflow\n");
        goto fail;
    }

    writebuf.cmd = BC_TRANSACTION;
    writebuf.txn.target.handle = target;
    writebuf.txn.code = code;
    writebuf.txn.flags = 0;
    writebuf.txn.data_size = msg->data - msg->data0;
    writebuf.txn.offsets_size = ((char*) msg->offs) - ((char*) msg->offs0);
    writebuf.txn.data.ptr.buffer = (uintptr_t)msg->data0;
    writebuf.txn.data.ptr.offsets = (uintptr_t)msg->offs0;

    bwr.write_size = sizeof(writebuf);
    bwr.write_consumed = 0;
    bwr.write_buffer = (uintptr_t) &writebuf;

    hexdump(msg->data0, msg->data - msg->data0);
    for (;;) {
        bwr.read_size = sizeof(readbuf);
        bwr.read_consumed = 0;
        bwr.read_buffer = (uintptr_t) readbuf;

        res = ioctl(bs->fd, BINDER_WRITE_READ, &bwr);
}


根據 binder_io, target, code 三者構造
韋東山老師的圖

直接構造一個binder_io, 然後直接調用 binder_call 就可以了!

自己的代碼 怎麼寫?

1. client
	a. binder_open
	b. 獲得服務: handle
	c. 構造參數: binder_io  (未轉換的.)
	d. 調用 binder_call(handle, code, binder_io)
	e. 分析返回 binder_io , 取出返回值   👇
2. server 							  👇
	a. binder_open					  👇
	b. 註冊服務						  👇
	c. ioctl 讀取數據			  		  👇
	d. 解析數據				 		  👇
		binder_write_read->txn{ handle, code, 參數 }
			binder_write_read.readbuf->binder_transationdata{ code, 參數(構造成binder_io) }
	e. 根據code, 決定調用哪個函數, 從binder_io取出數據
	f. 再把返回值轉換爲 binder_io ,發送client

👇 還是韋老師的圖香在這裏插入圖片描述

編寫代碼cpp :

client 端 直接調用 binder_call 發送給 server 端。
client 端看來不是很複雜,這就是rpc。

編譯

查找編譯方法:

/home/zp/android-8.0.0_r15/frameworks/native/cmds/servicemanager
> mmm ./ showcommands
showcommands: 爲了看他的編譯命令是怎樣的,保存下來之後修改

比如找出來 service_manager.c

PWD=/proc/self/cwd prebuilts/clang/host/linux-x86/clang-3859424/bin/clang -c
-Iframeworks/native/cmds/servicemanager -mthumb -Os -fomit-frame-pointer -fno-strict-aliasing -fno-exceptions -Wno-multichar -ffunction-sections -fdata-sections -funwind-tables -fstack-protector-strong -Wa,–noexecstack -Werror=format-security -D_FORTIFY_SOURCE=2 -fno-short-enums -no-canonical-prefixes -DNDEBUG -g -Wstrict-aliasing=2 -DANDROID -fmessage-length=0 -W -Wall -Wno-unused -Winit-self -Wpointer-arith -DNDEBUG -UDEBUG -fdebug-prefix-map=/proc/self/cwd= -D__compiler_offsetof=__builtin_offsetof -Werror=int-conversion -Wno-reserved-id-macro -Wno-format-pedantic -Wno-unused-command-line-argument -fcolor-diagnostics -Wno-expansion-to-defined -fdebug-prefix-map=$PWD/= -Werror=return-type -Werror=non-virtual-dtor -Werror=address -Werror=sequence-point -Werror=date-time -nostdlibinc -msoft-float -march=armv7-a -mfloat-abi=softfp -mfpu=vfpv3-d16
-Iexternal/selinux/libselinux/include
-Iexternal/pcre/include
-Isystem/core/libpackagelistparser/include
-Isystem/core/liblog/include
-Isystem/core/libcutils/include
-Iexternal/libcxx/include
-Iexternal/libcxxabi/include
-Isystem/core/include
-Isystem/media/audio/include
-Ihardware/libhardware/include
-Ihardware/libhardware_legacy/include
-Ihardware/ril/include
-Ilibnativehelper/include
-Iframeworks/native/include
-Iframeworks/native/opengl/include
-isystem frameworks/av/include
-isystem bionic/libc/arch-arm/include
-isystem bionic/libc/include
-isystem bionic/libc/kernel/uapi
-isystem bionic/libc/kernel/uapi/asm-arm
-isystem bionic/libc/kernel/android/uapi
-Ilibnativehelper/include/nativehelper -Wall -Wextra -Werror -DVENDORSERVICEMANAGER=1 -DBINDER_IPC_32BIT=1 -target arm-linux-androideabi -Bprebuilts/gcc/linux-x86/arm/arm-linux-androideabi-4.9/arm-linux-androideabi/bin -DANDROID_STRICT -fpie -D_USING_LIBCXX -std=gnu99 -Werror=int-to-pointer-cast -Werror=pointer-to-int-cast -Werror=address-of-temporary -Werror=return-type -MD -MF out/soong/.intermediates/frameworks/native/cmds/servicemanager/vndservicemanager/android_arm_armv7-a_core/obj/frameworks/native/cmds/servicemanager/service_manager.o.d -o out/soong/.intermediates/frameworks/native/cmds/servicemanager/vndservicemanager/android_arm_armv7-a_core/obj/frameworks/native/cmds/servicemanager/service_manager.o frameworks/native/cmds/servicemanager/service_manager.c

👆 -I 的都是包含的目錄

編寫 makefile

APPS = service_manager test_client test_server 

all : ${APPS}

# $@ 目標         $^ 所有的依賴
service_manager : service_manager.o binder.o
	arm-linux-gcc -o $@ $^

test_client : test_client.o binder.o
	arm-linux-gcc -o $@ $^
test_server : test_server.o binder.o
	arm-linux-gcc -o $@ $^

# 所有的.o文件依賴於所有的.c文件
# $< 表示第一個依賴
%.o : %.c
	arm-linux-gcc -c -o $@ $< 

一個一個錯的解決

文件找不到 1
.service_manager.c:9:47: fatal error: private/android_filesystem_config.h: No such file or directory
這時候就看上面 showcommands 打印出來的 -isystem 所帶的參數

zp@ubuntu:~/android-8.0.0_r15$ find ./ -name “android_filesystem_config.h”
./system/core/libcutils/include/private/android_filesystem_config.h
./system/core/include/private/android_filesystem_config.h // 找到了在這裏 👈
./build/make/tools/fs_config/default/android_filesystem_config.h
./device/google/marlin/marlin/android_filesystem_config.h
./device/google/marlin/sailfish/android_filesystem_config.h

-Isystem/core/include // 👆 上面showcommands 打印出來的,果然有

解決:
將這個config文件拷貝到自身的目錄。 keep
修改makefile文件,最後一句加入

- arm-linux-gcc -c -o $@ $< 
+ arm-linux-gcc -i include -c -o $@ $< 

./bionic/libc/kernel/uapi/linux/android/binder.h
同上


一點點的 make 找錯誤

arm-linux-gnueabihf-gcc -o service_manager service_manager.o binder.o
service_manager.o: In function check_mac_perms': service_manager.c:(.text+0xf8): undefined reference togetpidcon’
service_manager.c:(.text+0x12c): undefined reference to selinux_check_access' service_manager.c:(.text+0x142): undefined reference tofreecon’
service_manager.o: In function check_mac_perms_from_lookup': service_manager.c:(.text+0x20c): undefined reference toselabel_lookup’
service_manager.c:(.text+0x248): undefined reference to freecon' service_manager.o: In functionsvcmgr_handler’:
service_manager.c:(.text+0x69a): undefined reference to selinux_status_updated' service_manager.c:(.text+0x6a4): undefined reference toselinux_android_service_context_handle’
service_manager.c:(.text+0x6be): undefined reference to selabel_close' service_manager.o: In functionmain’:
service_manager.c:(.text+0x8d6): undefined reference to is_selinux_enabled' service_manager.c:(.text+0x8e6): undefined reference toselinux_android_service_context_handle’
service_manager.c:(.text+0x93a): undefined reference to `getcon’
collect2: error: ld returned 1 exit status
Makefile:11: recipe for target ‘service_manager’ failed
make: *** [service_manager] Error 1

👆 不需要selinux的,我們就乾脆刪掉。

最終的目錄結構

zp@ubuntu:~/binder_test$ tree
.
|-- Android.bp
|-- Android.mk
|-- Makefile
|-- bctest.c
|-- binder.c
|-- binder.h
|-- binder.md
|-- binder.o
|-- include
|   |-- android_filesystem_capability.h
|   |-- linux
|   |   `-- binder.h
|   `-- private
|       `-- android_filesystem_config.h
|-- service_manager
|-- service_manager.c
|-- service_manager.o
|-- servicemanager.rc
|-- test_client
|-- test_client.c
|-- test_client.o
|-- test_server
|-- test_server.c
|-- test_server.h
|-- test_server.o
`-- vndservicemanager.rc

make 成功
第一次make git地址

因爲是android8.0編譯, 所以和韋東山老師的makefile不一樣:


APPS = service_manager test_client test_server 

all : ${APPS}

#	 $@ 目標   		<service_manager>      
# $^ 所有的依賴    	<service_manager.o binder.o>
service_manager : service_manager.o binder.o
	arm-linux-gnueabihf-g++ -fpermissive -static -o $@ $^ 

test_client : test_client.o binder.o
	arm-linux-gnueabihf-g++ -fpermissive -static -o $@ $^
test_server : test_server.o binder.o
	arm-linux-gnueabihf-g++ -fpermissive -static -o $@ $^

# 所有的.o文件依賴於所有的.c文件
# $< 表示第一個依賴
%.o : %.c
	arm-linux-gnueabihf-g++ -fpermissive -static -Iinclude -c -o $@ $< 

clean:
	rm $(APPS) -f; rm -rf *.o

# -fpermissive resolve "__unused"
# -static 

output:

255|sailfish:/data/local/tmp # ./test_server                                   
say hello : 0
 say hello : 1
 say hello : 2
 say hello : 3
 say hello : 4
 say hello to weidongshan : 0
 say hello to weidongshan : 1
 say hello to weidongshan : 2
 say hello to weidongshan : 3
 say hello to weidongshan : 4
 say hello to weidongshan : 5
------------------------------------------------------------
 sailfish:/ # /data/local/tmp/test_client                                       
Usage: 
/data/local/tmp/test_client hello
/data/local/tmp/test_client hello <name>
255|sailfish:/ # /data/local/tmp/test_client  hello                            
sailfish:/ # /data/local/tmp/test_client  hello                                
sailfish:/ # /data/local/tmp/test_client  hello                                
sailfish:/ # /data/local/tmp/test_client  hello                                
sailfish:/ # /data/local/tmp/test_client  hello                                
sailfish:/ # /data/local/tmp/test_client  hello weidongshan                    
Usage: 
/data/local/tmp/test_client hello
/data/local/tmp/test_client hello <name>
data/local/tmp/test_client  hello weidongshan                                 <
get ret to sayhello_to = 1
data/local/tmp/test_client  hello weidongshan                                 <
get ret to sayhello_to = 2
data/local/tmp/test_client  hello weidongshan                                 <
get ret to sayhello_to = 3
data/local/tmp/test_client  hello weidongshan                                 <
get ret to sayhello_to = 4
data/local/tmp/test_client  hello weidongshan                                 <
get ret to sayhello_to = 5
data/local/tmp/test_client  hello weidongshan                                 <
get ret to sayhello_to = 6
sailfish:/ # 

binder 調用的順序圖 (大致)

韋東山老師太強了

發佈了10 篇原創文章 · 獲贊 0 · 訪問量 2萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章