文章目錄
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 functioncheck_mac_perms': service_manager.c:(.text+0xf8): undefined reference to
getpidcon’
service_manager.c:(.text+0x12c): undefined reference toselinux_check_access' service_manager.c:(.text+0x142): undefined reference to
freecon’
service_manager.o: In functioncheck_mac_perms_from_lookup': service_manager.c:(.text+0x20c): undefined reference to
selabel_lookup’
service_manager.c:(.text+0x248): undefined reference tofreecon' service_manager.o: In function
svcmgr_handler’:
service_manager.c:(.text+0x69a): undefined reference toselinux_status_updated' service_manager.c:(.text+0x6a4): undefined reference to
selinux_android_service_context_handle’
service_manager.c:(.text+0x6be): undefined reference toselabel_close' service_manager.o: In function
main’:
service_manager.c:(.text+0x8d6): undefined reference tois_selinux_enabled' service_manager.c:(.text+0x8e6): undefined reference to
selinux_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:/ #