文章目录
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:/ #