首页
社区
课程
招聘
[求助]关于HOOK libbinder.so 中ioctl函数拦截软件行为的问题
发表于: 2014-4-23 10:58 30858

[求助]关于HOOK libbinder.so 中ioctl函数拦截软件行为的问题

2014-4-23 10:58
30858
小弟最近做软件行为分析的毕业课题,在看雪学习了不少注入的技术,已经基本实现了IOCTL函数的替换和拦截,对其中传输的binder_write_read数据结构进行了基本分析,但是得到的信息都不太理解,按照数据结构解析了如下数据:

04-23 10:53:27.469: D/injectso(514): New ioctl called!
04-23 10:53:27.469: D/injectso(514): binder_write_read----->write size: 44
04-23 10:53:27.469: D/injectso(514): pid: 17, BC_TRANSACTION, dir:1, type:99, nr:0, size:40
04-23 10:53:27.469: D/injectso(514): binder_transaction_data----->target binder: 40286300, transaction code : 0
04-23 10:53:27.469: D/injectso(514): flat_binder_object----->binder data : 4 78

但是看到这些,如同看天书了

请论坛里的大虾,给我一些指点,如何通过这些信息,获取通话、短信、摄像头操作等行为,不胜感激!

[课程]Android-CTF解题方法汇总!

收藏
免费 0
支持
分享
最新回复 (38)
雪    币: 159
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
2
没人回答吗?自己先顶一下!
2014-4-24 07:26
0
雪    币: 181
活跃值: (45)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
3
你这是直接跑别人的代码吧~~~~~~~~~~~~~

学习下binder通信协议。
2014-4-24 10:30
0
雪    币: 159
活跃值: (10)
能力值: ( 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;
}
2014-4-25 08:42
0
雪    币: 459
活跃值: (349)
能力值: ( LV8,RANK:120 )
在线值:
发帖
回帖
粉丝
5
求楼主的学校,哪里教行为分析的
2014-4-25 09:02
0
雪    币: 181
活跃值: (45)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
6
代码没仔细看,但是思路没问题。

解析binder协议这块,你可以看android源码中service_manager的代码,里面有完整的对binder协议(RETURN协议)的解析,可以省很多事情。

一般都是分析RETURN协议,拦截Service。

希望可以帮到你。
2014-4-25 13:40
0
雪    币: 159
活跃值: (10)
能力值: ( 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的方式太不方便了,解析的底层信息太多,有没有其他的方法,望不吝赐教!
2014-4-25 17:51
0
雪    币: 181
活跃值: (45)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
8
我们拦截ioctl的目的是拦截通过binder机制走的RPC(远程调用),远程调用的组成部分——rpc调用号(这是我自己的叫法),rpc参数和rpc返回值(分别叫msg和reply),这个你在service_manager中一定看到了;我们具体的拦截就是针对rpc调用号,分析其参数并伪造返回值。

不同的rpc有不同的rpc号和参数及返回值,这个需要针对具体接口(Ixxxx.java)具体实现。

现在这个技术我感觉也是有一定的先天不足,我目前项目中运用的也是这个技术,确实能做很多功能;不过下一步肯定是要改的。
2014-4-28 09:09
0
雪    币: 159
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
9
感谢duoniduoni,确实明白了不少,下一步重点研究一下获取rpc号、msg、reply的问题,还要看看一些接口的具体实现,实现拦截。你已经做了类似的项目,太厉害了!下一步有什么问题还得多向你请教啊!
2014-4-29 07:59
0
雪    币: 1
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
10
楼主可否留个qq,详细交流下,小弟最近也在学android安全方面的知识。向与您讨论下。
2014-5-1 16:45
0
雪    币: 19
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
11
对请求rpc的Client来说,rpc号应该是相应的API,比如getLastKnownLocation,msg指的API对应的参数,reply是返回值?要分析这些东西需要知道对应的Service,比如对应于getLastKnownLocation的LocationService吧。截获的数据中有的应该是服务的Binder,根据这个Binder知道对应的是哪个服务,不知道对不对?
2014-5-2 15:47
0
雪    币: 159
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
12
谢谢关注了,我也是刚开始学习,都不好意思出来班门弄斧!要是你需要,给你一些建议,我研究这方面的路线是:注入---》hook---》binder机制---》分析和拦截。其实hook后还可以有其他的方式,我还没研究。这些可以在论坛里学习,多向古河、螳螂一号等大牛请教,还有duoniduoni,希望能帮到你!

另外:我以后要是有一些进展或问题,还会发帖,可以再一起交流!
2014-5-4 08:45
0
雪    币: 159
活跃值: (10)
能力值: ( 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号也可能变化,而不是简单的固定对应关系)。
2014-5-4 09:24
0
雪    币: 181
活跃值: (45)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
14
不客气。

rpc号什么的,你去找找I***.java文件,看一下就明白了。
2014-5-7 14:25
0
雪    币: 19
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
15
应该可以像楼上所说的,在Ixxx.java中找到对应关系,然后写死在解析函数中,我有一个疑问就是,怎么将拦截到得数据和对应的服务相对应,比如我拦截到了访问Location的数据包,我怎么知道它是访问Location的?

同做相关的毕设课题,快答辩了,软件还没做出来。。
2014-5-7 14:36
0
雪    币: 19
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
16
楼主有什么突破了么,求共享啊
2014-5-16 09:30
0
雪    币: 39
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
17
你好,有没有什么进展,pdata->data.ptr.buffer 这个能不能解析呢?
2014-6-13 19:09
0
雪    币: 159
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
18
谢谢关注了,最近一直在闭门研究,基本上有点眉目,推荐看一下这个:http://blog.csdn.net/chichoxian/article/details/20719443

有机会在一起讨论啊!
2014-6-16 10:01
0
雪    币: 159
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
19
同回@HuskarH!
2014-6-16 10:02
0
雪    币: 39
活跃值: (10)
能力值: ( 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上去解析呢?
2014-6-16 10:11
0
雪    币: 89
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
21
楼主,你好,你现在解决了这个问题没?我现在在做一个类似的项目,在分析数据这里也卡住了,你是怎么获取到msg和reply的?
2014-7-28 16:39
0
雪    币: 159
活跃值: (10)
能力值: ( 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出来具体分析一下,会有不一样的效果!

希望能帮到你!
2014-7-29 15:03
0
雪    币: 159
活跃值: (10)
能力值: ( 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参数问题,有兴趣的话,一起研究啊!
2014-7-29 15:07
0
雪    币: 89
活跃值: (10)
能力值: ( 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出来具体分析一下,这个小弟不是很了解,能否稍微说明下,谢谢。
2014-7-29 20:39
0
雪    币: 159
活跃值: (10)
能力值: ( 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的帮助!
2014-7-30 09:18
0
游客
登录 | 注册 方可回帖
返回
//