首页
社区
课程
招聘
[原创]【Android】深入底层Binder拦截
2023-11-30 20:05 14590

[原创]【Android】深入底层Binder拦截

2023-11-30 20:05
14590

Android平台进程Binder通信的动态分析和拦截。

说明

Binder作为Android系统跨进程通信的核心机制。网上也有很多深度讲解该机制的文章,如:

这些文章和系统源码可以很好帮助我们理解Binder的实现原理和设计理念,为拦截做准备。借助Binder拦截可以我们可以扩展出那些能力呢:

  1. 虚拟化的能力,多年前就出现的应用免安装运行类产品如:VirtualApp/DroidPlugin/平行空间/双开大师/应用分身等。
  2. 测试验证的能力,通常为Framework层功能开发。
  3. 检测第三方SDK或模块系统服务调用访问情况(特别是敏感API调用)。
  4. 逆向分析应用底层服务接口调用实现。
  5. 第三方ROM扩展Framework服务。

现有方案

一直以来实时分析和拦截进程的Binder通信是通过Java层的AIDL接口代理来实现的。借助于Android系统Binder服务接口设计的规范,上层的接口均继承于IBinder

如一下为代理目标对象的所有的接口API的方法:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
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 {
        // TODO release fix proxy name
    }
    return null;
}

1、对于已经生成的Binder服务对象,在应用进程可参与实现逻辑之前就已经缓存了,我们需要找到并且进行替换(AMS、PMS、WMS等),如AMS在Android 8.0之后的缓存如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
// source code: http://aospxref.com/android-9.0.0_r61/xref/frameworks/base/core/java/android/app/ActivityManager.java
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;
                }
            };
}

因此我们需要找到并且替换它,如:

1
2
3
4
5
6
7
8
Object obj;
if (Build.VERSION.SDK_INT < 26) {// <= 7.0
    obj = ReflectUtils.getStaticFieldValue("android.app.ActivityManagerNative", "gDefault");
} else {// 8.0 <=
    obj = ReflectUtils.getStaticFieldValue("android.app.ActivityManager", "IActivityManagerSingleton");
}
Object inst = ReflectUtils.getFieldValue(obj, "mInstance");
ReflectUtils.setFieldValue(obj, "mInstance", createProxy(inst));

2、对于后续运行过程中才获取的Binder服务,则需要代理ServiceManager,源码如下:

1
2
3
4
5
6
7
8
// source code: http://aospxref.com/android-9.0.0_r61/xref/frameworks/base/core/java/android/os/ServiceManager.java
package android.os;
 
public final class ServiceManager {
    private static final String TAG = "ServiceManager";
 
    private static IServiceManager sServiceManager;
}

因此我们的代理如下:

1
2
3
4
5
6
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);
}

这样每次在第一次访问该服务时,就会调用IServiceManager中的getService的方法,而该方法已经被我们代理拦截,我们可以通过参数可以识别当前获取的是哪个服务,然后将获取的服务对象代理后在继续返回即可。

但是:这样的方案并不能拦截进程中所有的Binder服务。我们面临几大问题:

  1. 首先,Android源码越来越庞大,了解所有的服务工作量很大,因此有哪些服务已经被缓存排查非常困难。

  2. 其次,厂商越来越钟情于扩展自定义服务,这些服务不开源,识别和适配更加耗时。

  3. 再次,有一部分服务只有native实现,并不能通过Java层的接口代理进行拦截(如:Sensor/Audio/Video/Camera服务等)。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    // source code: http://aospxref.com/android-13.0.0_r3/xref/frameworks/av/camera/ICamera.cpp
     
    class BpCamera: public BpInterface<ICamera>
    {
    public:
        explicit BpCamera(const sp<IBinder>& impl)
            : BpInterface<ICamera>(impl)
        {
        }
       
        // start recording mode, must call setPreviewTarget first
        status_t startRecording()
        {
            ALOGV("startRecording");
            Parcel data, reply;
            data.writeInterfaceToken(ICamera::getInterfaceDescriptor());
            remote()->transact(START_RECORDING, data, &reply);
            return reply.readInt32();
        }
    }

新方案:基于底层拦截

原理

我们都知道Binder在应用进程运行原理如下图:

不管是Java层还是native层的接口调用,最后都会通过ioctl函数访问共享内存空间,达到跨进程访问数据交换的目的。因此我们只要拦截ioctl函数,即可完成对所有Binder通信数据的拦截。底层拦截有以下优势:

1)可以拦截所有的Binder通信。

2)底层拦截稳定,高兼容性。从Android 4.xAndroid 14,近10年的系统版本演进,涉及到Binder底层通信适配仅两次;一次是支持64位进程(当时需要同时兼容32位和64位进程访问Binder服务)。另一次是华为鸿蒙系统的诞生,华为ROMBinder通信协议中增加了新的标识字段。

要解决的问题

如何拦截

C/C++层的函数拦截,并不像Java层一样系统提供了较为稳定的代理工具,在这里不是我们本期讨论的重点,可以直接采用网上开源的Hook框架:

如何过滤

ioctl函数为系统底层设备访问函数,调用及其频繁,而Binder通信调用只是其中调用者之一,因此需要快速识别非Binder通信调用,不影响程序性能。

函数定义:

1
2
3
#include <sys/ioctl.h>
 
int ioctl(int fildes, unsigned long request, ...);

request的参数定义:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
// source code: http://aospxref.com/android-14.0.0_r2/xref/bionic/libc/kernel/uapi/linux/android/binder.h
#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)

对应的源码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
// source code: http://aospxref.com/android-14.0.0_r2/xref/frameworks/native/libs/binder/IPCThreadState.cpp
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) {
    // 实际Binder调用通信
    return ioctl(mProcess->mDriverFD, BINDER_WRITE_READ, &bwr);
}

快速过滤:

1
2
3
4
5
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);
    }
}
如何解析

目标源码:http://aospxref.com/android-14.0.0_r2/xref/frameworks/native/libs/binder

重点解析发送(即BC_TRANSACTIONBC_REPLY)和接收(即BR_TRANSACTIONBR_REPLY)的类型数据。

如何修改数据

修改数据分为以下几种:

1)修复调用时参数数据。

2)修复调用后返回的结果数据。

如果数据修复不改变当前数据的长度,只是内容的变化,则可以直接通过地址进行修改。否则需要创建新的内存进行修改后将新的数据地址设置到BINDER_WRITE_READ结构的buffer成员。此时处理好内存的释放问题。

3)直接拦截本次调用。

为了保障稳定性,不打断Binder的调用流程(通常这也是拦截和逆向方案保障稳定的最重要原则之一)。我们可以将目标函数code修改成父类处理的通用方法,然后通过修复调用的返回值即可完成拦截。

方案实现

数据解析

Binder调用数据结构如下:

解析bwr

bwrbinder_write_read,从源码中了解到ioctlBINDER_WRITE_READ类型的arg数据结构为:

1
2
3
4
5
6
7
8
9
10
11
struct binder_write_read {
  // 调用时传入的数据
     binder_size_t write_size;// call data
     binder_size_t write_consumed;// call data
     binder_uintptr_t write_buffer;// call data
   
    // 结果返回数据
     binder_size_t read_size;// recv data
     binder_size_t read_consumed;// recv data
     binder_uintptr_t read_buffer;// recv data
};

不管是传入还是返回的数据,都是一组BC命令或BR命令,也就是说一次调用上层会打包几个命令一起传递。因此我们需要通过循环来找到我们的命令。

1
2
3
4
5
6
7
8
9
10
void binder_find_for_bc(struct binder_write_read& bwr) {
    binder_uintptr_t cmds = bwr.write_buffer;
    binder_uintptr_t end = cmds + (binder_uintptr_t)bwr.write_size;
 
    binder_txn_st* txn = NULL;
    while (0 < cmds && cmds < end && !txn) {
        // 由于每次Binder通信数据量的限制,Binder设计每次调用有且仅包含一个有效的参数命令,因此只要找到即可,其他类型则直接跳过忽略
        cmds = binder_parse_cmds_bc(cmds, txn);
    }
}

dump数据如下:

1
2
3
4
5
6
7
8
9
write_buffer:0xb400007107d1d400, write_consumed:68, write_size:68
0000000000 63 40 40 14 00 00 00  00 00 00 00 00 00 00 00  .c@@............
0000001000 00 00 00 01 00 00 00  12 00 00 00 00 00 00 00  ................
0000002000 00 00 00 54 00 00 00  00 00 00 00 00 00 00 00  ....T...........
0000003000 00 00 00 00 4d 3a ac  70 00 00 b4 00 00 00 00  .....M:.p.......
0000004000 00 00 00                                       ....
BR_NOOP: 0x720c
BR_TRANSACTION_COMPLETE: 0x7206
BR_REPLY: 0
解析txn

txnbinder_transaction_data,Binder方法调用的方法参数信息定义如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
struct binder_transaction_data {
 union {
     __u32 handle;
     binder_uintptr_t ptr;
 } target;// 目标服务句柄,server端使用
 
 binder_uintptr_t cookie;// 缓存的Binder进行访问
 __u32 code;//方法编号
 
 __u32 flags;// 标识,如是否为 oneway
 __s32 sender_pid;
 __u32 sender_euid;
 binder_size_t data_size;// 数据长度
 binder_size_t offsets_size;// 若包含对象,则对象数据大小
   
 union {
     struct {
         binder_uintptr_t buffer;// Binder方法参数值地址
         binder_uintptr_t offsets;// Binder方法参数对象数据地址
     } ptr;
     __u8 buf[8];
 } data;
};

dumo数据如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
Trace   : target:       1   cookie:       0   code:      23   flags:   0x12(READ REPLY)
Trace   :    pid:       0      uid:       0   size:     196    offs:8
Trace   : 0000000000 00 00 80 ff ff ff ff  54 53 59 53 1c 00 00 00  ........TSYS....
Trace   : 0000001061 00 6e 00 64 00 72 00  6f 00 69 00 64 00 2e 00  a.n.d.r.o.i.d...
Trace   : 0000002061 00 70 00 70 00 2e 00  49 00 41 00 63 00 74 00  a.p.p...I.A.c.t.
Trace   : 0000003069 00 76 00 69 00 74 00  79 00 4d 00 61 00 6e 00  i.v.i.t.y.M.a.n.
Trace   : 0000004061 00 67 00 65 00 72 00  00 00 00 00 85 2a 62 73  a.g.e.r......*bs
Trace   : 0000005013 01 00 00 00 38 dd 2a  71 00 00 b4 00 05 e9 31  .....8.*q......1
Trace   : 0000006071 00 00 b4 01 00 00 0c  1a 00 00 00 63 00 6f 00  q...........c.o.
Trace   : 000000706d 00 2e 00 69 00 66 00  6d 00 61 00 2e 00 74 00  m...i.f.m.a...t.
Trace   : 0000008072 00 61 00 6e 00 73 00  65 00 63 00 2e 00 63 00  r.a.n.s.e.c...c.
Trace   : 000000906f 00 6e 00 74 00 61 00  69 00 6e 00 65 00 72 00  o.n.t.a.i.n.e.r.
Trace   : 000000a000 00 00 00 08 00 00 00  73 00 65 00 74 00 74 00  ........s.e.t.t.
Trace   : 000000b069 00 6e 00 67 00 73 00  00 00 00 00 00 00 00 00  i.n.g.s.........
Trace   : 000000c001 00 00 00                                       ....
Trace   : binder object offs:0x4c  type:0x73622a85  flags:0x113  ptr:0x2add3800  cookie:0x31e90500
解析服务名

Binder通信数据头如下,即可解析出目标服务名:

1
2
3
4
5
6
7
8
void find_server_name(const binder_txn_st* txn) {
        const int32_t* ptr = reinterpret_cast<const int32_t*>(txn->data.ptr.buffer);
      ++ ptr;// skip strict model
    if (29 <= sdkVersion()) ++ ptr;// 10.0 <=, skip flags(ff ff ff ff)
         
      int32_t nameLen = *ptr;
    const uint16_t* name16 = (const uint16_t*)(ptr+1);
}
解析方法名

Binder通信数据中标识该服务方法的参数是txn->codeAIDL定义类在编译后会为每个方法自动生成静态的方法。

如定义的Binder接口方法为:

1
2
3
4
interface IDemo {
  void test();
  void test2();
}

则编译后生成的类为:

1
2
3
4
5
6
7
class IDemo$Stub {
   void test();
   void test2();
    
  static final int TRANSACTION_test = 1;
  static final int TRANSACTION_test2 = 2;
}

因此我们可以通过反射的方式,找到服务名对应的类所有静态成员变量,然后找到与code值相等的成员即为此方法。

这里可能需要解决私有API的限制解除问题。

1
2
// 可直接使用工程工具类
TstClassPrinter.printStubByCodes("android.app.IActivityManager", 13, 16, 67);

日志输出如下:

解析数据

首先需要借助数据封装类Parcel

1
2
3
// souce code:
// http://aospxref.com/android-14.0.0_r2/xref/frameworks/native/include/binder/Parcel.h
// http://aospxref.com/android-14.0.0_r2/xref/frameworks/native/libs/binder/Parcel.cpp

借助该类可以解析一些比较简单的数据,快速的找到目标内容。而对于比较复杂的数据,如参数值为Intent,该参数类型嵌套了多层的Parcelable成员,因此在native层通过Parcel来解析,兼容性比较差。因此我们选择通过回调到Java层来解析,修改后再格式化为nativebuffer数据。这里需要处理好Javanative层的数据交换问题,以及回收。

native层:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
// 创建
jobject obtain(JNIEnv* env) {
    jclass jcls = env->FindClass("android/os/Parcel");
    jmethodID method = env->GetStaticMethodID(jcls, "obtain", "()Landroid/os/Parcel;");
    if (!method) return NULL;
 
    mParcelObj = env->CallStaticObjectMethod(jcls, method);
    if (!mParcelObj) return NULL;
 
    if (0 < mUparcel->dataSize()) {
        method = env->GetMethodID(sParcelClass, "setDataPosition", "(I)V");
        if (method) {
            unmarshall(env, mUparcel->data(), mUparcel->dataSize());
            env->CallVoidMethod(mParcelObj, method, mUparcel->dataPosition());
        }
    }
 
    return mParcelObj;
}
 
// 回收
void recycle(JNIEnv* env) {
    jclass jcls = env->FindClass("android/os/Parcel");
    jmethodID method = env->GetMethodID(jcls, "recycle", "()V");
    if (method) {
        env->CallVoidMethod(mParcelObj, method);
    }
    if (mParcelObj) {
        env->DeleteLocalRef(mParcelObj);
    }
    mParcelObj = NULL;
}

Java层:

1
2
3
4
5
6
7
8
9
10
11
public static void clearHttpLink(Parcel p/*IN*/, Parcel q/*OUT*/) {
    try {
        Intent ii = Intent.CREATOR.createFromParcel(pp);
        // TODO something ...
           
        // write new data
        q.appendFrom(p, p.dataPosition(), p.dataAvail());
    } catch (Throwable e) {
        e.printStackTrace();
    }
}

数据拦截

Binder的数据解析和打印不会改变原数据内容,因此相对简单,如果要对数据进行修改,则相对复杂一些。修复的数据需要替换原数据,因此需要进行如下操作。

1、数据替换。

txn中方法参数的数据指针指向新创建的数据区。

1
2
3
4
5
6
7
8
9
10
11
12
int binder_replace_txn_for_br(binder_txn_st *txn, ParcelEx* reply, binder_size_t _pos) {
    size_t size = reply->ipcDataSize();
    uint8_t* repData = (uint8_t*)malloc(size + txn->offsets_size);
    memcpy(repData, reply->data(), size);
    if (0 < txn->offsets_size) {
        binder_replace_objects(txn, repData, _pos, ((int)size) - ((int)(txn->data_size)));
    }
 
    txn->data.ptr.buffer = reinterpret_cast<uintptr_t>(repData);
    txn->data_size = size;
    return 0;
}

2、修正对象指针。

如果传入的参数包含Binder对象,如register方法的Observe。因此修复的数据可能导致偏移的地址前移或者后移,因此需要重新计算偏移,如:

1
2
3
4
5
6
7
8
9
10
11
void replaceObjects(binder_txn_st *txn, uint8_t* objData, binder_size_t _pos, int _off) {
    binder_size_t* offs = reinterpret_cast<binder_size_t*>(txn->data.ptr.offsets);
    unsigned count = txn->offsets_size / sizeof(binder_size_t);
 
    while (0 < count--) {
        if (0 != memcmp(objData + (int)(*offs), (uint8_t*)txn->data.ptr.buffer + (int)(*offs), sizeof(binder_size_t))) {
            *offs += _off;
        }
        ++ offs;
    }
}

3、内存释放。

需要保存原地址A和新的地址AA的映射关系到自定义的内存池中。

Binder通信命令出现BC_FREE_BUFFERBR_FREE_BUFFER时,则通过该命令要释放的AA地址,然后从内存池找到与之对应A的地址,并设置回去让上层继续释放,完成内存使用的闭环。

1
2
3
4
5
6
7
8
9
case BC_FREE_BUFFER:
{
    uintptr_t* buffPtr = (uintptr_t *)cmd;
    uintptr_t ptr = MemPool::detach(*buffPtr);
    if (__UNLIKELY(0 != ptr)) {
        *buffPtr = ptr;// set origin buffer
    }
    cmd += sizeof(uintptr_t);// move to next command
}   break;

附:

如果你有需要,可以直接使用我们已经封装好的SDK来实现相应的功能,该项目已经开源,链接地址如下:

Gitee

Github


[培训]内核驱动高级班,冲击BAT一流互联网大厂工作,每周日13:00-18:00直播授课

最后于 2023-12-1 09:42 被Tiony.bo编辑 ,原因:
收藏
免费 18
打赏
分享
最新回复 (23)
雪    币: 20493
活跃值: (30026)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
秋狝 2023-12-1 09:39
2
1
感谢分享
雪    币: 709
活跃值: (2385)
能力值: ( LV12,RANK:1010 )
在线值:
发帖
回帖
粉丝
sudami 25 2023-12-3 08:55
3
0
学习了
雪    币: 98
活跃值: (677)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
万里星河 2023-12-3 16:07
4
0
受教了
雪    币: 181
活跃值: (2868)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
huaerxiela 2023-12-4 13:18
5
0
学习了
雪    币: 715
活跃值: (214302)
能力值: ( LV4,RANK:40 )
在线值:
发帖
回帖
粉丝
shinratensei 1 2023-12-9 13:31
6
0
tql
雪    币: 375
活跃值: (3040)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
恋空 2023-12-11 15:43
7
0
mark
雪    币: 375
活跃值: (3040)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
恋空 2023-12-11 18:24
8
0
我的建议是直接在Java层hook transact函数
简单方便,一步到位。
雪    币: 345
活跃值: (566)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
test-belief 2023-12-14 14:22
9
0
学习了
雪    币: 275
活跃值: (506)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
Tiony.bo 2023-12-19 22:27
10
0
恋空 我的建议是直接在Java层hook transact函数 简单方便,一步到位。
正如文中所说,Java层的HOOK无法拦截native层的Binder通信
雪    币: 3078
活跃值: (13310)
能力值: ( LV9,RANK:230 )
在线值:
发帖
回帖
粉丝
珍惜Any 3 2023-12-27 10:09
11
0
其实我之前用的也是类似这种方案,但是发现很多native层改不全,很多都是服务都是在native层,最好的办法是我搞了个magisk模块,注入服务端,再导入framework.jar,和service.jar 直接在服务端hook,非常方便,还有一些ipc通讯,比如oaid这种,他是不走系统服务的,走的是小米的安全中心,还需要去额外hook小米安全中心。
拿到我们在服务端留的代理服务,直接和服务端交互就行了,服务端可以作为控制台 ,负责各种数据的分发和接受处理。这是最完美的架构。
雪    币: 1112
活跃值: (2785)
能力值: ( LV3,RANK:30 )
在线值:
发帖
回帖
粉丝
Amun 2023-12-27 10:20
12
0
# ls -al /sys/kernel/tracing/events/binder
total 0
drwxr-xr-x  35 root readtracefs 0 1970-01-01 08:00 .
drwxr-xr-x 128 root readtracefs 0 1970-01-01 08:00 ..
drwxr-xr-x   2 root readtracefs 0 1970-01-01 08:00 binder_alloc_lru_end
drwxr-xr-x   2 root readtracefs 0 1970-01-01 08:00 binder_alloc_lru_start
drwxr-xr-x   2 root readtracefs 0 1970-01-01 08:00 binder_alloc_page_end
drwxr-xr-x   2 root readtracefs 0 1970-01-01 08:00 binder_alloc_page_start
drwxr-xr-x   2 root readtracefs 0 1970-01-01 08:00 binder_command
drwxr-xr-x   2 root readtracefs 0 1970-01-01 08:00 binder_free_lru_end
drwxr-xr-x   2 root readtracefs 0 1970-01-01 08:00 binder_free_lru_start
drwxr-xr-x   2 root readtracefs 0 1970-01-01 08:00 binder_ioctl
drwxr-xr-x   2 root readtracefs 0 1970-01-01 08:00 binder_ioctl_done
drwxr-xr-x   2 root readtracefs 0 1970-01-01 08:00 binder_lock
drwxr-xr-x   2 root readtracefs 0 1970-01-01 08:00 binder_locked
drwxr-xr-x   2 root readtracefs 0 1970-01-01 08:00 binder_read_done
drwxr-xr-x   2 root readtracefs 0 1970-01-01 08:00 binder_return
drwxr-xr-x   2 root readtracefs 0 1970-01-01 08:00 binder_set_priority
drwxr-xr-x   2 root readtracefs 0 1970-01-01 08:00 binder_transaction
drwxr-xr-x   2 root readtracefs 0 1970-01-01 08:00 binder_transaction_alloc_buf
drwxr-xr-x   2 root readtracefs 0 1970-01-01 08:00 binder_transaction_buffer_release
drwxr-xr-x   2 root readtracefs 0 1970-01-01 08:00 binder_transaction_failed_buffer_release
drwxr-xr-x   2 root readtracefs 0 1970-01-01 08:00 binder_transaction_fd_recv
drwxr-xr-x   2 root readtracefs 0 1970-01-01 08:00 binder_transaction_fd_send
drwxr-xr-x   2 root readtracefs 0 1970-01-01 08:00 binder_transaction_node_to_ref
drwxr-xr-x   2 root readtracefs 0 1970-01-01 08:00 binder_transaction_received
drwxr-xr-x   2 root readtracefs 0 1970-01-01 08:00 binder_transaction_ref_to_node
drwxr-xr-x   2 root readtracefs 0 1970-01-01 08:00 binder_transaction_ref_to_ref
drwxr-xr-x   2 root readtracefs 0 1970-01-01 08:00 binder_transaction_update_buffer_release
drwxr-xr-x   2 root readtracefs 0 1970-01-01 08:00 binder_unlock
drwxr-xr-x   2 root readtracefs 0 1970-01-01 08:00 binder_unmap_kernel_end
drwxr-xr-x   2 root readtracefs 0 1970-01-01 08:00 binder_unmap_kernel_start
drwxr-xr-x   2 root readtracefs 0 1970-01-01 08:00 binder_unmap_user_end
drwxr-xr-x   2 root readtracefs 0 1970-01-01 08:00 binder_unmap_user_start
drwxr-xr-x   2 root readtracefs 0 1970-01-01 08:00 binder_update_page_range
drwxr-xr-x   2 root readtracefs 0 1970-01-01 08:00 binder_wait_for_work
drwxr-xr-x   2 root readtracefs 0 1970-01-01 08:00 binder_write_done
-rw-r--r--   1 root readtracefs 0 1970-01-01 08:00 enable
-rw-r--r--   1 root readtracefs 0 1970-01-01 08:00 filter

大哥用这个,劲大!

最后于 2023-12-27 10:23 被Amun编辑 ,原因:
雪    币: 1428
活跃值: (9802)
能力值: ( LV9,RANK:240 )
在线值:
发帖
回帖
粉丝
misskings 4 2023-12-27 16:12
13
0
跑通了。比较在意的是运用场景,第三方ROM扩展Framework服务 能展开说说吗。
雪    币: 275
活跃值: (506)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
Tiony.bo 2023-12-27 18:11
14
1
珍惜Any 其实我之前用的也是类似这种方案,但是发现很多native层改不全,很多都是服务都是在native层,最好的办法是我搞了个magisk模块,注入服务端,再导入framework.jar,和service ...
感谢您的关注,就像文中所说,在Server端拦截实现的确可以达到目的,但是只能采用开源的AOSP的ROM定制,或者ROOT系统,方案量,没办法通用,同样面临厂商定制的服务无法识别的问题。
雪    币: 275
活跃值: (506)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
Tiony.bo 2023-12-27 18:16
15
0
misskings 跑通了。比较在意的是运用场景,第三方ROM扩展Framework服务 能展开说说吗。
第三方ROM可以通过本框架拦截后找到对应的类和方法(仅Java层方法)。
适用场景并不太多,就像文中所说的,插件化方案的开发和适配,应用或SDK的服务端访问监测,免安装运行方案的开发和适配,应用运行时动态分析等。本方案的优势就是轻量,且兼容性好。
后面会上线应用重封装项目,免安装项目,都会用到,届时再和大家一起分享。
雪    币: 1428
活跃值: (9802)
能力值: ( LV9,RANK:240 )
在线值:
发帖
回帖
粉丝
misskings 4 2023-12-28 14:23
16
0
Tiony.bo 第三方ROM可以通过本框架拦截后找到对应的类和方法(仅Java层方法)。 适用场景并不太多,就像文中所说的,插件化方案的开发和适配,应用或SDK的服务端访问监测,免安装运行方案的开发和适配,应用运行 ...
好的。感觉simple输出的太过于简单。希望后期优化。感觉用的上。
雪    币: 3078
活跃值: (13310)
能力值: ( LV9,RANK:230 )
在线值:
发帖
回帖
粉丝
珍惜Any 3 2023-12-28 16:25
17
0
Tiony.bo 感谢您的关注,就像文中所说,在Server端拦截实现的确可以达到目的,但是只能采用开源的AOSP的ROM定制,或者ROOT系统,方案量,没办法通用,同样面临厂商定制的服务无法识别的问题。
不需要那么麻烦,用magisk模块,实现热插拔,编译个驱动内核注入就行了。
雪    币: 1112
活跃值: (2785)
能力值: ( LV3,RANK:30 )
在线值:
发帖
回帖
粉丝
Amun 2023-12-28 17:45
18
0

https://github.com/dxwu/BinderFilter
可以拿这个改改,整个 ko 驱动模块装上。

最后于 2023-12-28 17:57 被Amun编辑 ,原因:
雪    币: 275
活跃值: (506)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
Tiony.bo 2023-12-29 10:00
19
0
Amun https://github.com/dxwu/BinderFilter可以拿这个改改,整个&nbsp;ko&nbsp;驱动模块装上。
酷,已Star
雪    币: 275
活跃值: (506)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
Tiony.bo 2023-12-29 10:00
20
0
珍惜Any 不需要那么麻烦,用magisk模块,实现热插拔,编译个驱动内核注入就行了。
哈哈,支持
雪    币: 375
活跃值: (3040)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
恋空 2024-1-14 21:39
21
0
Amun #&nbsp;ls&nbsp;-al&nbsp;/sys/kernel/tracing/events/binder total&nbsp;0 drwxr-xr-x& ...
确实牛逼
雪    币: 19
活跃值: (563)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
hmlyn 2024-1-29 00:08
22
0
珍惜Any 其实我之前用的也是类似这种方案,但是发现很多native层改不全,很多都是服务都是在native层,最好的办法是我搞了个magisk模块,注入服务端,再导入framework.jar,和service ...
你好大牛,有已实现好的获取小米oaid吗?qq:876529930
雪    币: 507
活跃值: (1727)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
seeeseee 2024-1-29 09:46
23
0
这个的解析部分感觉更详细,就是适配工作量看起来挺大,可以参考参考

https://github.com/foundryzero/binder-trace
雪    币: 275
活跃值: (506)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
Tiony.bo 2024-2-17 10:12
24
0
seeeseee 这个的解析部分感觉更详细,就是适配工作量看起来挺大,可以参考参考 https://github.com/foundryzero/binder-trace
确实,非常详细,学习了
游客
登录 | 注册 方可回帖
返回