Android平台进程Binder通信的动态分析和拦截。
Binder
作为Android
系统跨进程通信的核心机制。网上也有很多深度讲解该机制的文章,如:
这些文章和系统源码可以很好帮助我们理解Binder的实现原理和设计理念,为拦截做准备。借助Binder拦截可以我们可以扩展出那些能力呢:
一直以来实时分析和拦截进程的Binder
通信是通过Java
层的AIDL
接口代理来实现的。借助于Android
系统Binder
服务接口设计的规范,上层的接口均继承于IBinder
。
如一下为代理目标对象的所有的接口API
的方法:
1、对于已经生成的Binder
服务对象,在应用进程可参与实现逻辑之前就已经缓存了,我们需要找到并且进行替换(AMS、PMS、WMS等
),如AMS
在Android 8.0之后的缓存如下:
因此我们需要找到并且替换它,如:
2、对于后续运行过程中才获取的Binder
服务,则需要代理ServiceManager
,源码如下:
因此我们的代理如下:
这样每次在第一次访问该服务时,就会调用IServiceManager
中的getService
的方法,而该方法已经被我们代理拦截,我们可以通过参数可以识别当前获取的是哪个服务,然后将获取的服务对象代理后在继续返回即可。
但是:
这样的方案并不能拦截进程中所有的Binder
服务。我们面临几大问题:
首先,Android源码越来越庞大,了解所有的服务工作量很大,因此有哪些服务已经被缓存排查非常困难。
其次,厂商越来越钟情于扩展自定义服务,这些服务不开源,识别和适配更加耗时。
再次,有一部分服务只有native
实现,并不能通过Java
层的接口代理进行拦截(如:Sensor/Audio/Video/Camera服务等
)。
我们都知道Binder
在应用进程运行原理如下图:
不管是Java
层还是native
层的接口调用,最后都会通过ioctl
函数访问共享内存空间,达到跨进程访问数据交换的目的。因此我们只要拦截ioctl
函数,即可完成对所有Binder
通信数据的拦截。底层拦截有以下优势:
1)可以拦截所有的Binder通信。
2)底层拦截稳定,高兼容性。从Android 4.x
至Android 14
,近10年的系统版本演进,涉及到Binder
底层通信适配仅两次;一次是支持64位进程(当时需要同时兼容32位和64位进程访问Binder
服务)。另一次是华为鸿蒙系统的诞生,华为ROM
在Binder
通信协议中增加了新的标识字段。
C/C++
层的函数拦截,并不像Java
层一样系统提供了较为稳定的代理工具,在这里不是我们本期讨论的重点,可以直接采用网上开源的Hook
框架:
ioctl
函数为系统底层设备访问函数,调用及其频繁,而Binder
通信调用只是其中调用者之一,因此需要快速识别非Binder
通信调用,不影响程序性能。
函数定义:
request
的参数定义:
对应的源码:
快速过滤:
目标源码:http://aospxref.com/android-14.0.0_r2/xref/frameworks/native/libs/binder
重点解析发送(即BC_TRANSACTION
和BC_REPLY
)和接收(即BR_TRANSACTION
和BR_REPLY
)的类型数据。
修改数据分为以下几种:
1)修复调用时参数数据。
2)修复调用后返回的结果数据。
如果数据修复不改变当前数据的长度,只是内容的变化,则可以直接通过地址进行修改。否则需要创建新的内存进行修改后将新的数据地址设置到BINDER_WRITE_READ
结构的buffer
成员。此时处理好内存的释放问题。
3)直接拦截本次调用。
为了保障稳定性,不打断Binder
的调用流程(通常这也是拦截和逆向方案保障稳定的最重要原则之一)。我们可以将目标函数code
修改成父类处理的通用方法,然后通过修复调用的返回值即可完成拦截。
Binder调用数据结构如下:
bwr
即binder_write_read
,从源码中了解到ioctl
的BINDER_WRITE_READ
类型的arg
数据结构为:
不管是传入还是返回的数据,都是一组BC命令或BR命令,也就是说一次调用上层会打包几个命令一起传递。因此我们需要通过循环来找到我们的命令。
dump
数据如下:
txn
即binder_transaction_data
,Binder方法调用的方法参数信息定义如下:
dumo
数据如下:
Binder
通信数据头如下,即可解析出目标服务名:
Binder
通信数据中标识该服务方法的参数是txn->code
。AIDL
定义类在编译后会为每个方法自动生成静态的方法。
如定义的Binder
接口方法为:
则编译后生成的类为:
因此我们可以通过反射的方式,找到服务名对应的类所有静态成员变量,然后找到与code
值相等的成员即为此方法。
这里可能需要解决私有API的限制解除问题。
日志输出如下:
首先需要借助数据封装类Parcel
。
借助该类可以解析一些比较简单的数据,快速的找到目标内容。而对于比较复杂的数据,如参数值为Intent
,该参数类型嵌套了多层的Parcelable
成员,因此在native
层通过Parcel
来解析,兼容性比较差。因此我们选择通过回调到Java层来解析,修改后再格式化为native
的buffer
数据。这里需要处理好Java
和native
层的数据交换问题,以及回收。
native
层:
Java
层:
Binder的数据解析和打印不会改变原数据内容,因此相对简单,如果要对数据进行修改,则相对复杂一些。修复的数据需要替换原数据,因此需要进行如下操作。
1、数据替换。
将txn
中方法参数的数据指针指向新创建的数据区。
2、修正对象指针。
如果传入的参数包含Binder对象,如register
方法的Observe
。因此修复的数据可能导致偏移的地址前移或者后移,因此需要重新计算偏移,如:
3、内存释放。
需要保存原地址A
和新的地址AA
的映射关系到自定义的内存池中。
当Binder
通信命令出现BC_FREE_BUFFER
和BR_FREE_BUFFER
时,则通过该命令要释放的AA
地址,然后从内存池找到与之对应A
的地址,并设置回去让上层继续释放,完成内存使用的闭环。
如果你有需要,可以直接使用我们已经封装好的SDK
来实现相应的功能,该项目已经开源,链接地址如下:
Gitee
Github
import
java.lang.reflect.InvocationHandler;
import
java.lang.reflect.InvocationTargetException;
import
java.lang.reflect.Proxy;
private
static
void
getInterface(Class<?> cls,
final
HashSet<Class<?>> ss) {
Class<?>[] ii;
do
{
ii = cls.getInterfaces();
for
(
final
Class<?> i : ii) {
if
(ss.add(i)) {
getInterface(i, ss);
}
}
cls = cls.getSuperclass();
}
while
(cls !=
null
);
}
private
static
Class<?>[] getInterfaces(Class<?> cls) {
final
HashSet<Class<?>> ss =
new
LinkedHashSet<Class<?>>();
getInterface(cls, ss);
if
(
0
< ss.size()) {
return
ss.toArray(
new
Class<?>[ss.size()]);
}
return
null
;
}
public
static
Object createProxy(Object org, InvocationHandler cb) {
try
{
Class<?> cls = org.getClass();
Class<?>[] cc = getInterfaces(cls);
return
Proxy.newProxyInstance(cls.getClassLoader(), cc, cb);
}
catch
(Throwable e) {
Logger.e(e);
}
finally
{
}
return
null
;
}
import
java.lang.reflect.InvocationHandler;
import
java.lang.reflect.InvocationTargetException;
import
java.lang.reflect.Proxy;
private
static
void
getInterface(Class<?> cls,
final
HashSet<Class<?>> ss) {
Class<?>[] ii;
do
{
ii = cls.getInterfaces();
for
(
final
Class<?> i : ii) {
if
(ss.add(i)) {
getInterface(i, ss);
}
}
cls = cls.getSuperclass();
}
while
(cls !=
null
);
}
private
static
Class<?>[] getInterfaces(Class<?> cls) {
final
HashSet<Class<?>> ss =
new
LinkedHashSet<Class<?>>();
getInterface(cls, ss);
if
(
0
< ss.size()) {
return
ss.toArray(
new
Class<?>[ss.size()]);
}
return
null
;
}
public
static
Object createProxy(Object org, InvocationHandler cb) {
try
{
Class<?> cls = org.getClass();
Class<?>[] cc = getInterfaces(cls);
return
Proxy.newProxyInstance(cls.getClassLoader(), cc, cb);
}
catch
(Throwable e) {
Logger.e(e);
}
finally
{
}
return
null
;
}
package
android.app;
public
class
ActivityManager {
public
static
IActivityManager getService() {
return
IActivityManagerSingleton.get();
}
private
static
final
Singleton<IActivityManager> IActivityManagerSingleton =
new
Singleton<IActivityManager>() {
@Override
protected
IActivityManager create() {
final
IBinder b = ServiceManager.getService(Context.ACTIVITY_SERVICE);
final
IActivityManager am = IActivityManager.Stub.asInterface(b);
return
am;
}
};
}
package
android.app;
public
class
ActivityManager {
public
static
IActivityManager getService() {
return
IActivityManagerSingleton.get();
}
private
static
final
Singleton<IActivityManager> IActivityManagerSingleton =
new
Singleton<IActivityManager>() {
@Override
protected
IActivityManager create() {
final
IBinder b = ServiceManager.getService(Context.ACTIVITY_SERVICE);
final
IActivityManager am = IActivityManager.Stub.asInterface(b);
return
am;
}
};
}
Object obj;
if
(Build.VERSION.SDK_INT <
26
) {
obj = ReflectUtils.getStaticFieldValue(
"android.app.ActivityManagerNative"
,
"gDefault"
);
}
else
{
obj = ReflectUtils.getStaticFieldValue(
"android.app.ActivityManager"
,
"IActivityManagerSingleton"
);
}
Object inst = ReflectUtils.getFieldValue(obj,
"mInstance"
);
ReflectUtils.setFieldValue(obj,
"mInstance"
, createProxy(inst));
Object obj;
if
(Build.VERSION.SDK_INT <
26
) {
obj = ReflectUtils.getStaticFieldValue(
"android.app.ActivityManagerNative"
,
"gDefault"
);
}
else
{
obj = ReflectUtils.getStaticFieldValue(
"android.app.ActivityManager"
,
"IActivityManagerSingleton"
);
}
Object inst = ReflectUtils.getFieldValue(obj,
"mInstance"
);
ReflectUtils.setFieldValue(obj,
"mInstance"
, createProxy(inst));
package
android.os;
public
final
class
ServiceManager {
private
static
final
String TAG =
"ServiceManager"
;
private
static
IServiceManager sServiceManager;
}
package
android.os;
public
final
class
ServiceManager {
private
static
final
String TAG =
"ServiceManager"
;
private
static
IServiceManager sServiceManager;
}
Class<?> cls = ReflectUtils.findClass(
"android.os.ServiceManager"
);
Object org = ReflectUtils.getStaticFieldValue(cls,
"sServiceManager"
);
Object pxy =
new
createProxy(org);
if
(
null
!= pxy) {
ReflectUtils.setStaticFieldValue(getGlobalClass(),
"sServiceManager"
, pxy);
}
Class<?> cls = ReflectUtils.findClass(
"android.os.ServiceManager"
);
Object org = ReflectUtils.getStaticFieldValue(cls,
"sServiceManager"
);
Object pxy =
new
createProxy(org);
if
(
null
!= pxy) {
ReflectUtils.setStaticFieldValue(getGlobalClass(),
"sServiceManager"
, pxy);
}
class
BpCamera:
public
BpInterface<ICamera>
{
public
:
explicit
BpCamera(
const
sp<IBinder>& impl)
: BpInterface<ICamera>(impl)
{
}
status_t startRecording()
{
ALOGV(
"startRecording"
);
Parcel data, reply;
data.writeInterfaceToken(ICamera::getInterfaceDescriptor());
remote()->transact(START_RECORDING, data, &reply);
return
reply.readInt32();
}
}
class
BpCamera:
public
BpInterface<ICamera>
{
public
:
explicit
BpCamera(
const
sp<IBinder>& impl)
: BpInterface<ICamera>(impl)
{
}
status_t startRecording()
{
ALOGV(
"startRecording"
);
Parcel data, reply;
data.writeInterfaceToken(ICamera::getInterfaceDescriptor());
remote()->transact(START_RECORDING, data, &reply);
return
reply.readInt32();
}
}
#include <sys/ioctl.h>
int
ioctl(
int
fildes, unsigned
long
request, ...);
#include <sys/ioctl.h>
int
ioctl(
int
fildes, unsigned
long
request, ...);
#define BINDER_WRITE_READ _IOWR('b', 1, struct binder_write_read)
#define BINDER_SET_IDLE_TIMEOUT _IOW('b', 3, __s64)
#define BINDER_SET_MAX_THREADS _IOW('b', 5, __u32)
#define BINDER_SET_IDLE_PRIORITY _IOW('b', 6, __s32)
#define BINDER_SET_CONTEXT_MGR _IOW('b', 7, __s32)
#define BINDER_THREAD_EXIT _IOW('b', 8, __s32)
#define BINDER_VERSION _IOWR('b', 9, struct binder_version)
#define BINDER_GET_NODE_DEBUG_INFO _IOWR('b', 11, struct binder_node_debug_info)
#define BINDER_GET_NODE_INFO_FOR_REF _IOWR('b', 12, struct binder_node_info_for_ref)
#define BINDER_SET_CONTEXT_MGR_EXT _IOW('b', 13, struct flat_binder_object)
#define BINDER_FREEZE _IOW('b', 14, struct binder_freeze_info)
#define BINDER_GET_FROZEN_INFO _IOWR('b', 15, struct binder_frozen_status_info)
#define BINDER_ENABLE_ONEWAY_SPAM_DETECTION _IOW('b', 16, __u32)
#define BINDER_GET_EXTENDED_ERROR _IOWR('b', 17, struct binder_extended_error)
#define BINDER_WRITE_READ _IOWR('b', 1, struct binder_write_read)
#define BINDER_SET_IDLE_TIMEOUT _IOW('b', 3, __s64)
#define BINDER_SET_MAX_THREADS _IOW('b', 5, __u32)
#define BINDER_SET_IDLE_PRIORITY _IOW('b', 6, __s32)
#define BINDER_SET_CONTEXT_MGR _IOW('b', 7, __s32)
#define BINDER_THREAD_EXIT _IOW('b', 8, __s32)
#define BINDER_VERSION _IOWR('b', 9, struct binder_version)
#define BINDER_GET_NODE_DEBUG_INFO _IOWR('b', 11, struct binder_node_debug_info)
#define BINDER_GET_NODE_INFO_FOR_REF _IOWR('b', 12, struct binder_node_info_for_ref)
#define BINDER_SET_CONTEXT_MGR_EXT _IOW('b', 13, struct flat_binder_object)
#define BINDER_FREEZE _IOW('b', 14, struct binder_freeze_info)
#define BINDER_GET_FROZEN_INFO _IOWR('b', 15, struct binder_frozen_status_info)
#define BINDER_ENABLE_ONEWAY_SPAM_DETECTION _IOW('b', 16, __u32)
#define BINDER_GET_EXTENDED_ERROR _IOWR('b', 17, struct binder_extended_error)
void
IPCThreadState::threadDestructor(
void
*st) {
ioctl(self->mProcess->mDriverFD, BINDER_THREAD_EXIT, 0);
}
status_t IPCThreadState::getProcessFreezeInfo(pid_t pid, uint32_t *sync_received, uint32_t *async_received) {
return
ioctl(self()->mProcess->mDriverFD, BINDER_GET_FROZEN_INFO, &info);
}
status_t IPCThreadState::freeze(pid_t pid,
bool
enable, uint32_t timeout_ms) {
return
ioctl(self()->mProcess->mDriverFD, BINDER_FREEZE, &info) < 0);
}
void
IPCThreadState::logExtendedError() {
ioctl(self()->mProcess->mDriverFD, BINDER_GET_EXTENDED_ERROR, &ee) < 0);
}
status_t IPCThreadState::talkWithDriver(
bool
doReceive) {
return
ioctl(mProcess->mDriverFD, BINDER_WRITE_READ, &bwr);
}
void
IPCThreadState::threadDestructor(
void
*st) {
ioctl(self->mProcess->mDriverFD, BINDER_THREAD_EXIT, 0);
}
status_t IPCThreadState::getProcessFreezeInfo(pid_t pid, uint32_t *sync_received, uint32_t *async_received) {
return
ioctl(self()->mProcess->mDriverFD, BINDER_GET_FROZEN_INFO, &info);
}
status_t IPCThreadState::freeze(pid_t pid,
bool
enable, uint32_t timeout_ms) {
return
ioctl(self()->mProcess->mDriverFD, BINDER_FREEZE, &info) < 0);
}
void
IPCThreadState::logExtendedError() {
ioctl(self()->mProcess->mDriverFD, BINDER_GET_EXTENDED_ERROR, &ee) < 0);
}
status_t IPCThreadState::talkWithDriver(
bool
doReceive) {
return
ioctl(mProcess->mDriverFD, BINDER_WRITE_READ, &bwr);
}
static
int
ioctl_hook(
int
fd,
int
cmd,
void
* arg) {
if
(cmd != BINDER_WRITE_READ || !arg || g_ioctl_disabled) {
return
g_ioctl_func(fd, cmd, arg);
}
}
static
int
ioctl_hook(
int
fd,
int
cmd,
void
* arg) {
if
(cmd != BINDER_WRITE_READ || !arg || g_ioctl_disabled) {
return
g_ioctl_func(fd, cmd, arg);
}
}
struct
binder_write_read {
binder_size_t write_size;
binder_size_t write_consumed;
binder_uintptr_t write_buffer;
binder_size_t read_size;
binder_size_t read_consumed;
binder_uintptr_t read_buffer;
};
struct
binder_write_read {
binder_size_t write_size;
binder_size_t write_consumed;
binder_uintptr_t write_buffer;
[培训]内核驱动高级班,冲击BAT一流互联网大厂工作,每周日13:00-18:00直播授课
最后于 2023-12-1 09:42
被Tiony.bo编辑
,原因: