能力值:
( LV2,RANK:10 )
4 楼
代码是自己写的,binder通信协议确实是刚看,不太熟,就去实现一个程序解析binder_write_read结构,把代码贴出来吧,各位大虾帮忙看看,有什么问题,多多指教,或者有解析的现成代码给推荐一下也行,我就不在班门弄斧了,不胜感激!
这个是hook的ioctl代码:
// 欲接替ioctl的新函数地址,其中内部调用了老的ioctl
int new_ioctl (int __fd, unsigned long int __request, void * arg)
{
LOGD("New ioctl called!\n");
if ( __request == BINDER_WRITE_READ )
{
int dir = _IOC_DIR(__request); //根据命令获取传输方向
int type = _IOC_TYPE(__request); //根据命令获取类型
int nr = _IOC_NR(__request); //根据命令获取类型命令
int size = _IOC_SIZE(__request); //根据命令获取传输数据大小
// LOGD("new call to ioctl, dir:%d, type:%d, nr:%d, size:%d\n", dir, type, nr, size);
struct binder_write_read* tmp = (struct binder_write_read*) arg;
signed long write_size = tmp->write_size;
signed long read_size = tmp->read_size;
if(write_size > 0)//该命令将write_buffer中的数据写入到binder
{
LOGD("binder_write_read----->write size: %d,write_consumed :%d", tmp->write_size, tmp->write_consumed);
int already_got_size = 0;
unsigned long *pcmd = 0;
LOGD("=================write_buffer process start!");
while(already_got_size < write_size)//循环处理buffer中的每一个命令
{
pcmd = (unsigned long *)(tmp->write_buffer + already_got_size); //指针后移
int code = pcmd[0];
LOGD("pcmd: %x, already_got_size: %d", pcmd, already_got_size);
int dir = _IOC_DIR(code); //根据命令获取传输方向
int type = _IOC_TYPE(code); //根据命令获取类型
int nr = _IOC_NR(code); //根据命令获取类型命令
int size = _IOC_SIZE(code); //根据命令获取传输数据大小
LOGD("cmdcode:%d, dir:%d, type:%c, nr:%d, size:%d\n", code, dir, type, nr, size);
struct binder_transaction_data* pdata = (struct binder_transaction_data*)(&pcmd[1]);
switch (code)
{
case BC_TRANSACTION:
LOGD("pid: %d, BC_TRANSACTION, dir:%d, type:%c, nr:%d, size:%d\n", pdata->sender_pid, dir, type, nr, size);
parse_binder(pdata, 1);
break;
case BC_REPLY:
LOGD("pid: %d, BC_REPLY, dir:%d, type:%c, nr:%d, size:%d\n", pdata->sender_pid, dir, type, nr, size);
parse_binder(pdata, 1);
break;
default:
break;
}
already_got_size += (size+4);
}
LOGD("=================write_buffer process end!");
}
if(read_size > 0)//从binder中读取数据写入到read_buffer
{
LOGD("binder_write_read----->read size: %d, read_consumed: %d", tmp->read_size, tmp->read_consumed);
int already_got_size = 0;
unsigned long *pret = 0;
LOGD("=================read_buffer process start!");
while(already_got_size < read_size)//循环处理buffer中的每一个命令
{
pret = (unsigned long *)(tmp->read_buffer + already_got_size); //指针后移
int code = pret[0];
LOGD("pret: %x, already_got_size: %d", pret, already_got_size);
int dir = _IOC_DIR(code); //根据命令获取传输方向
int type = _IOC_TYPE(code); //根据命令获取类型
int nr = _IOC_NR(code); //根据命令获取类型命令
int size = _IOC_SIZE(code); //根据命令获取传输数据大小
LOGD("retcode:%d, dir:%d, type:%c, nr:%d, size:%d\n", code, dir, type, nr, size);
struct binder_transaction_data* pdata = (struct binder_transaction_data*)(&pret[1]);
switch (code)
{
case BR_TRANSACTION:
LOGD("pid: %d, BR_TRANSACTION, dir:%d, type:%c, nr:%d, size:%d\n", pdata->sender_pid, dir, type, nr, size);
parse_binder(pdata, 2);
break;
case BR_REPLY:
LOGD("pid: %d, BR_REPLY, dir:%d, type:%c, nr:%d, size:%d\n", pdata->sender_pid, dir, type, nr, size);
parse_binder(pdata, 2);
break;
default:
break;
}
already_got_size += (size+4);//数据内容加上命令码
}
LOGD("=================read_buffer process end!");
}
}
if (old_ioctl == -1)
{
LOGD("error\n");
return;
}
int res = (*old_ioctl)(__fd, __request, arg);
return res;
} 这个是解析binder_transaction_data的代码:
void parse_binder(struct binder_transaction_data* pdata, int type)
{
if(type == 1)//BC 系列命令,target.handle指定到需要访问的Binder对象
{
LOGD("binder_transaction_data----->reply to binder: %x, transaction code : %d\n", pdata->target.handle, pdata->code);
}
else //从Binder驱动里读回返回值时,需要指定返回的一段地址,target.ptr则会指向这个地址空间
{
LOGD("binder_transaction_data----->got from binder: %x, transaction code : %d\n", pdata->target.ptr, pdata->code);
}
if(pdata->data_size > 8)//通信数据量比较大,包含很多binder对象。
{
int n=0;
LOGD("binder_transaction_data size > 8");
for (;n<pdata->offsets_size/4; n++)//获取每一个Binder对象
{
LOGD("data_size: %d, buffer: %x, offsets: %x, offset_size: %d, offset %d:%x!",
pdata->data_size, pdata->data.ptr.buffer, pdata->data.ptr.offsets, pdata->offsets_size, n, ((int*)(pdata->data.ptr.offsets))[n]);
struct flat_binder_object* pbinder = (struct flat_binder_object*)((char*)pdata->data.ptr.buffer + ((int*)(pdata->data.ptr.offsets))[n]);
LOGD("got %d binder object in %x!", n, pbinder);
continue;
unsigned long type = pbinder->type;
switch (type)
{
case BINDER_TYPE_BINDER:
case BINDER_TYPE_WEAK_BINDER:
LOGD("flat_binder_object----->binder:%x, cookie:%x", pbinder->binder, pbinder->cookie);
break;
case BINDER_TYPE_HANDLE:
case BINDER_TYPE_WEAK_HANDLE:
LOGD("flat_binder_object----->handle:%x\n", pbinder->handle);
break;
default:
break;
}
}
}
else
{
LOGD("flat_binder_object----->binder data : %x %x\n", pdata->data.buf[0], pdata->data.buf[4]);
}
return;
}
能力值:
( LV2,RANK:10 )
7 楼
感谢回复,根据你的建议看了service_manager的代码,确实有不少帮助,谢谢提醒了!目前主要集中精力分析BR_TRANSACTION命令,刚得到了binder_transaction_data中pid,ptr,code等:
04-25 17:40:11.763: D/injectso(15244): pdata: 67b19d28, pid: 19598, BR_TRANSACTION, dir:2, type:r, nr:2, size:40
04-25 17:40:11.763: D/injectso(15244): binder_transaction_data----->from driver, ptr: 681583c0, service_com:681583a0, transaction code : 7
感觉里目标还有一些距离,你的回复增强了我继续下去的信心,特别感谢!
另外,请教你一下,我google了下,说cookie是service组件的地址,code是具体函数的序号,由这些信息是怎么得到程序的具体行为的,莫非要函数序号和函数的一一对应关系?是不是还有一些信息我还没有获取到?望指点迷津。
另:现在监控程序行为,感觉hook ioctl的方式太不方便了,解析的底层信息太多,有没有其他的方法,望不吝赐教!
能力值:
( LV2,RANK:10 )
8 楼
我们拦截ioctl的目的是拦截通过binder机制走的RPC(远程调用),远程调用的组成部分——rpc调用号(这是我自己的叫法),rpc参数和rpc返回值(分别叫msg和reply),这个你在service_manager中一定看到了;我们具体的拦截就是针对rpc调用号,分析其参数并伪造返回值。
不同的rpc有不同的rpc号和参数及返回值,这个需要针对具体接口(Ixxxx.java)具体实现。
现在这个技术我感觉也是有一定的先天不足,我目前项目中运用的也是这个技术,确实能做很多功能;不过下一步肯定是要改的。
能力值:
( LV2,RANK:10 )
11 楼
对请求rpc的Client来说,rpc号应该是相应的API,比如getLastKnownLocation,msg指的API对应的参数,reply是返回值?要分析这些东西需要知道对应的Service,比如对应于getLastKnownLocation的LocationService吧。截获的数据中有的应该是服务的Binder,根据这个Binder知道对应的是哪个服务,不知道对不对?
能力值:
( LV2,RANK:10 )
13 楼
以我目前的认识,基本正确,再解析binder_transaction_data的内容时,对于不同的服务有不同的处理,我现在就面临一个问题,对于得到的rpc号,怎么能找到其对应的API,因为这种对应关系是在Ixxxx.java中描述的,比如上面的getLastKnowLocation就对应于ILocationManager.java中的第17号调用(android 4.1.1):
static final int TRANSACTION_getLastKnownLocation = (android.os.IBinder.FIRST_CALL_TRANSACTION + 16);
而我们的注入代码是在native层运行的,将两者对应起来该如何实现,还要与版本无关(我猜想,不同的版本其API有所变化,对应的RPC号也可能变化,而不是简单的固定对应关系)。
能力值:
( LV2,RANK:10 )
15 楼
应该可以像楼上所说的,在Ixxx.java中找到对应关系,然后写死在解析函数中,我有一个疑问就是,怎么将拦截到得数据和对应的服务相对应,比如我拦截到了访问Location的数据包,我怎么知道它是访问Location的?
同做相关的毕设课题,快答辩了,软件还没做出来。。
能力值:
( LV2,RANK:10 )
20 楼
楼主,
struct flat_binder_object* pbinder = (struct flat_binder_object*)((char*)pdata->data.ptr.buffer + ((int*)(pdata->data.ptr.offsets))[n]);
这个是parcel对象吧,如果要解析,能否传到java上去解析呢?
能力值:
( LV2,RANK:10 )
22 楼
看到你说分析数据了,我就直奔主题了,我们分析的数据主要就是发给server进程如com.android.phone,system_server的请求数据(MSG)和这些进程返回的数据(REPLY)
在hook了这些进程的ioctl函数后,主要分析binder_write_read结构中read_buffer和write_buffer,并提取其中BR_TRANSCATION和BC_REPLY类型命令对应的详细数据,对应的是binder_transcation_data数据结构,这其中包含了你想要的所有数据。
其他的,你可以看看这个http://blog.csdn.net/chichoxian/article/details/20719443,其中的有效数据载荷你可以dump出来具体分析一下,会有不一样的效果!
希望能帮到你!
能力值:
( LV2,RANK:10 )
23 楼
[QUOTE=HuskarH;1293454]楼主,
struct flat_binder_object* pbinder = (struct flat_binder_object*)((char*)pdata->data.ptr.buffer + ((int*)(pdata->data.ptr.offsets))[n]);
这个是parce...[/QUOTE]
抱歉,这么久没有回复你,其实我也一直在研究如何将这些信息放在java层来解析的问题,但恕本人愚钝,除了能在java层获取出对应的API函数名称外,其他的进展不大,主要本人对java层的Binder机制知其然,不知其所依然,近期忙了点其他事也给耽误了。
下一步还想重点解析一下获取的API参数问题,有兴趣的话,一起研究啊!
能力值:
( LV2,RANK:10 )
24 楼
Hi,yxlpeter,非常谢谢你的回复,我已经做到了你说的这些步骤,获取到了binder_transcation_data,但是怎么判断其是哪个系统调用我现在还不清楚。
1. 我认真看了service_manager.c的源代码,按照他的步骤大概可以这样得到msg:
struct binder_txn *txn = (void *) &pret[1];
binder_dump_txn(txn);
struct binder_io msg;
bio_init_from_txn(&msg, txn);
然后根据msg的data中的数据判断其要使用的服务:
uint32_t strict_policy = bio_get_uint32(msg);
uint16_t *s;
s = bio_get_string16(msg, &len);
if ((len != (sizeof(svcmgr_id) / 2)) ||
memcmp(svcmgr_id, s, sizeof(svcmgr_id))) {
fprintf(stderr,"invalid id %s\n", str8(s));
return -1;
}
msg应该包含这样的字符串:“android.os.IServiceManager”,他也正是根据这个字符串判断是哪个服务的,不知道我这么想有没有错?因为我查到的一些资料里面都有说道binder_transcation_data里面包含服务的描述字符串(类似于“android.os.IServiceManager”),你发的那个链接里面在最后也说道了这个。
然而,按照上面的代码的会报错:storage size of 'msg' isn't known,应该是由于没有include正确的头文件,我代码的头文件有:
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <android/log.h>
#include <EGL/egl.h>
#include <GLES/gl.h>
#include <elf.h>
#include <fcntl.h>
#include <sys/mman.h>
#include <linux/binder.h>
不知道是不是少了什么?
2. 我注意到你和duoniduoni聊到rpc调用号,这个是怎么获得的,你们是根据这个来判断是哪个服务的吗?
3. 你说到有效数据载荷你可以dump出来具体分析一下,这个小弟不是很了解,能否稍微说明下,谢谢。
能力值:
( LV2,RANK:10 )
25 楼
1.嗯嗯,service_manager.c的源码确实帮助很大,当时其中的函数我没直接使用(我还没找到其中的方法
)。但是其中的分析步骤基本是可以借鉴的,binder_parse函数讲的最清楚,具体来讲,对于一个BR_TRANSCATION类型的msg,其后的binder_transcation_data结构体中code就是rpc号,这就直接回答了你第二个问题,其他的字段你看看其他资料估计一目了然,重点就是data.ptr.buufer字段,这其中就包含的服务组件的名称,比如:com.android.internal.telephony.ITelephony,具体结构你就具体分析吧!
2.关于dump,可以借鉴service_manager中binder.c中的binder_dump_txn函数及hexdump函数,打印出来后,就很清楚了。
3.另外,我再次感谢duoniduoni的帮助!