new way to hook

This commit is contained in:
5ec1cff
2024-07-17 22:58:26 +08:00
parent ebdaf87061
commit 5c9fe31ced
3 changed files with 382 additions and 65 deletions

View File

@@ -0,0 +1,239 @@
/*
* This file is auto-generated. Modifications will be lost.
*
* See https://android.googlesource.com/platform/bionic/+/master/libc/kernel/
* for more information.
*/
#ifndef _UAPI_LINUX_BINDER_H
#define _UAPI_LINUX_BINDER_H
#include <linux/types.h>
#include <linux/ioctl.h>
#define B_PACK_CHARS(c1, c2, c3, c4) ((((c1) << 24)) | (((c2) << 16)) | (((c3) << 8)) | (c4))
#define B_TYPE_LARGE 0x85
enum {
BINDER_TYPE_BINDER = B_PACK_CHARS('s', 'b', '*', B_TYPE_LARGE),
BINDER_TYPE_WEAK_BINDER = B_PACK_CHARS('w', 'b', '*', B_TYPE_LARGE),
BINDER_TYPE_HANDLE = B_PACK_CHARS('s', 'h', '*', B_TYPE_LARGE),
BINDER_TYPE_WEAK_HANDLE = B_PACK_CHARS('w', 'h', '*', B_TYPE_LARGE),
BINDER_TYPE_FD = B_PACK_CHARS('f', 'd', '*', B_TYPE_LARGE),
BINDER_TYPE_FDA = B_PACK_CHARS('f', 'd', 'a', B_TYPE_LARGE),
BINDER_TYPE_PTR = B_PACK_CHARS('p', 't', '*', B_TYPE_LARGE),
};
enum flat_binder_object_shifts {
FLAT_BINDER_FLAG_SCHED_POLICY_SHIFT = 9,
};
enum flat_binder_object_flags {
FLAT_BINDER_FLAG_PRIORITY_MASK = 0xff,
FLAT_BINDER_FLAG_ACCEPTS_FDS = 0x100,
FLAT_BINDER_FLAG_SCHED_POLICY_MASK = 3U << FLAT_BINDER_FLAG_SCHED_POLICY_SHIFT,
FLAT_BINDER_FLAG_INHERIT_RT = 0x800,
FLAT_BINDER_FLAG_TXN_SECURITY_CTX = 0x1000,
};
#ifdef BINDER_IPC_32BIT
typedef __u32 binder_size_t;
typedef __u32 binder_uintptr_t;
#else
typedef __u64 binder_size_t;
typedef __u64 binder_uintptr_t;
#endif
struct binder_object_header {
__u32 type;
};
struct flat_binder_object {
struct binder_object_header hdr;
__u32 flags;
union {
binder_uintptr_t binder;
__u32 handle;
};
binder_uintptr_t cookie;
};
struct binder_fd_object {
struct binder_object_header hdr;
__u32 pad_flags;
union {
binder_uintptr_t pad_binder;
__u32 fd;
};
binder_uintptr_t cookie;
};
struct binder_buffer_object {
struct binder_object_header hdr;
__u32 flags;
binder_uintptr_t buffer;
binder_size_t length;
binder_size_t parent;
binder_size_t parent_offset;
};
enum {
BINDER_BUFFER_FLAG_HAS_PARENT = 0x01,
};
struct binder_fd_array_object {
struct binder_object_header hdr;
__u32 pad;
binder_size_t num_fds;
binder_size_t parent;
binder_size_t parent_offset;
};
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_version {
__s32 protocol_version;
};
#ifdef BINDER_IPC_32BIT
#define BINDER_CURRENT_PROTOCOL_VERSION 7
#else
#define BINDER_CURRENT_PROTOCOL_VERSION 8
#endif
struct binder_node_debug_info {
binder_uintptr_t ptr;
binder_uintptr_t cookie;
__u32 has_strong_ref;
__u32 has_weak_ref;
};
struct binder_node_info_for_ref {
__u32 handle;
__u32 strong_count;
__u32 weak_count;
__u32 reserved1;
__u32 reserved2;
__u32 reserved3;
};
struct binder_freeze_info {
__u32 pid;
__u32 enable;
__u32 timeout_ms;
};
struct binder_frozen_status_info {
__u32 pid;
__u32 sync_recv;
__u32 async_recv;
};
struct binder_extended_error {
__u32 id;
__u32 command;
__s32 param;
};
enum {
BINDER_WRITE_READ = _IOWR('b', 1, struct binder_write_read),
BINDER_SET_IDLE_TIMEOUT = _IOW('b', 3, __s64),
BINDER_SET_MAX_THREADS = _IOW('b', 5, __u32),
BINDER_SET_IDLE_PRIORITY = _IOW('b', 6, __s32),
BINDER_SET_CONTEXT_MGR = _IOW('b', 7, __s32),
BINDER_THREAD_EXIT = _IOW('b', 8, __s32),
BINDER_VERSION = _IOWR('b', 9, struct binder_version),
BINDER_GET_NODE_DEBUG_INFO = _IOWR('b', 11, struct binder_node_debug_info),
BINDER_GET_NODE_INFO_FOR_REF = _IOWR('b', 12, struct binder_node_info_for_ref),
BINDER_SET_CONTEXT_MGR_EXT = _IOW('b', 13, struct flat_binder_object),
BINDER_FREEZE = _IOW('b', 14, struct binder_freeze_info),
BINDER_GET_FROZEN_INFO = _IOWR('b', 15, struct binder_frozen_status_info),
BINDER_ENABLE_ONEWAY_SPAM_DETECTION = _IOW('b', 16, __u32),
BINDER_GET_EXTENDED_ERROR = _IOWR('b', 17, struct binder_extended_error),
};
enum transaction_flags {
TF_ONE_WAY = 0x01,
TF_ROOT_OBJECT = 0x04,
TF_STATUS_CODE = 0x08,
TF_ACCEPT_FDS = 0x10,
TF_CLEAR_BUF = 0x20,
TF_UPDATE_TXN = 0x40,
};
struct binder_transaction_data {
union {
__u32 handle;
binder_uintptr_t ptr;
} target;
binder_uintptr_t cookie;
__u32 code;
__u32 flags;
__kernel_pid_t sender_pid;
__kernel_uid32_t sender_euid;
binder_size_t data_size;
binder_size_t offsets_size;
union {
struct {
binder_uintptr_t buffer;
binder_uintptr_t offsets;
} ptr;
__u8 buf[8];
} data;
};
struct binder_transaction_data_secctx {
struct binder_transaction_data transaction_data;
binder_uintptr_t secctx;
};
struct binder_transaction_data_sg {
struct binder_transaction_data transaction_data;
binder_size_t buffers_size;
};
struct binder_ptr_cookie {
binder_uintptr_t ptr;
binder_uintptr_t cookie;
};
struct binder_handle_cookie {
__u32 handle;
binder_uintptr_t cookie;
} __attribute__((__packed__));
struct binder_pri_desc {
__s32 priority;
__u32 desc;
};
struct binder_pri_ptr_cookie {
__s32 priority;
binder_uintptr_t ptr;
binder_uintptr_t cookie;
};
enum binder_driver_return_protocol {
BR_ERROR = _IOR('r', 0, __s32),
BR_OK = _IO('r', 1),
BR_TRANSACTION_SEC_CTX = _IOR('r', 2, struct binder_transaction_data_secctx),
BR_TRANSACTION = _IOR('r', 2, struct binder_transaction_data),
BR_REPLY = _IOR('r', 3, struct binder_transaction_data),
BR_ACQUIRE_RESULT = _IOR('r', 4, __s32),
BR_DEAD_REPLY = _IO('r', 5),
BR_TRANSACTION_COMPLETE = _IO('r', 6),
BR_INCREFS = _IOR('r', 7, struct binder_ptr_cookie),
BR_ACQUIRE = _IOR('r', 8, struct binder_ptr_cookie),
BR_RELEASE = _IOR('r', 9, struct binder_ptr_cookie),
BR_DECREFS = _IOR('r', 10, struct binder_ptr_cookie),
BR_ATTEMPT_ACQUIRE = _IOR('r', 11, struct binder_pri_ptr_cookie),
BR_NOOP = _IO('r', 12),
BR_SPAWN_LOOPER = _IO('r', 13),
BR_FINISHED = _IO('r', 14),
BR_DEAD_BINDER = _IOR('r', 15, binder_uintptr_t),
BR_CLEAR_DEATH_NOTIFICATION_DONE = _IOR('r', 16, binder_uintptr_t),
BR_FAILED_REPLY = _IO('r', 17),
BR_FROZEN_REPLY = _IO('r', 18),
BR_ONEWAY_SPAM_SUSPECT = _IO('r', 19),
BR_TRANSACTION_PENDING_FROZEN = _IO('r', 20),
};
enum binder_driver_command_protocol {
BC_TRANSACTION = _IOW('c', 0, struct binder_transaction_data),
BC_REPLY = _IOW('c', 1, struct binder_transaction_data),
BC_ACQUIRE_RESULT = _IOW('c', 2, __s32),
BC_FREE_BUFFER = _IOW('c', 3, binder_uintptr_t),
BC_INCREFS = _IOW('c', 4, __u32),
BC_ACQUIRE = _IOW('c', 5, __u32),
BC_RELEASE = _IOW('c', 6, __u32),
BC_DECREFS = _IOW('c', 7, __u32),
BC_INCREFS_DONE = _IOW('c', 8, struct binder_ptr_cookie),
BC_ACQUIRE_DONE = _IOW('c', 9, struct binder_ptr_cookie),
BC_ATTEMPT_ACQUIRE = _IOW('c', 10, struct binder_pri_desc),
BC_REGISTER_LOOPER = _IO('c', 11),
BC_ENTER_LOOPER = _IO('c', 12),
BC_EXIT_LOOPER = _IO('c', 13),
BC_REQUEST_DEATH_NOTIFICATION = _IOW('c', 14, struct binder_handle_cookie),
BC_CLEAR_DEATH_NOTIFICATION = _IOW('c', 15, struct binder_handle_cookie),
BC_DEAD_BINDER_DONE = _IOW('c', 16, binder_uintptr_t),
BC_TRANSACTION_SG = _IOW('c', 17, struct binder_transaction_data_sg),
BC_REPLY_SG = _IOW('c', 18, struct binder_transaction_data_sg),
};
#endif

View File

@@ -6,11 +6,14 @@
#include <utils/StrongPointer.h>
#include <binder/Common.h>
#include <binder/IServiceManager.h>
#include <sys/ioctl.h>
#include "kernel/binder.h"
#include <utility>
#include <map>
#include <shared_mutex>
#include <vector>
#include <queue>
#include "logging.hpp"
#include "dobby.h"
@@ -50,36 +53,140 @@ public:
status_t onTransact(uint32_t code, const android::Parcel &data, android::Parcel *reply,
uint32_t flags) override;
bool handleIntercept(BBinder *thiz, uint32_t code, const Parcel &data, Parcel *reply,
bool handleIntercept(sp<BBinder> target, uint32_t code, const Parcel &data, Parcel *reply,
uint32_t flags, status_t &result);
bool needIntercept(const wp<BBinder>& target);
};
static sp<BinderInterceptor> gBinderInterceptor = nullptr;
CREATE_MEM_HOOK_STUB_ENTRY(
"_ZN7android7BBinder8transactEjRKNS_6ParcelEPS1_j",
status_t, BBinder_Transact,
(BBinder * thiz, uint32_t code, const Parcel &data, Parcel *reply, uint32_t flags),
{
LOGD("transact: binder=%p code=%d", thiz, code);
if (IPCThreadState::self()->getCallingUid() == 0 && reply != nullptr &&
thiz != gBinderInterceptor) [[unlikely]] {
if (code == 0xadbeef) {
LOGD("request binder interceptor");
reply->writeStrongBinder(gBinderInterceptor);
return OK;
struct thread_transaction_info {
uint32_t code;
wp<BBinder> target;
};
thread_local std::queue<thread_transaction_info> ttis;
class BinderStub : public BBinder {
status_t onTransact(uint32_t code, const android::Parcel &data, android::Parcel *reply, uint32_t flags) override {
LOGD("BinderStub %d", code);
if (!ttis.empty()) {
auto tti = ttis.front();
ttis.pop();
if (tti.target == nullptr && tti.code == 0xdeadbeef && reply) {
LOGD("backdoor requested!");
reply->writeStrongBinder(gBinderInterceptor);
return OK;
} else if (tti.target != nullptr) {
LOGD("intercepting");
auto p = tti.target.promote();
if (p) {
LOGD("calling interceptor");
status_t result;
if (!gBinderInterceptor->handleIntercept(p, tti.code, data, reply, flags,
result)) {
LOGD("calling orig");
result = p->transact(tti.code, data, reply, flags);
}
return result;
} else {
LOGE("promote failed");
}
}
status_t result;
if (gBinderInterceptor->handleIntercept(thiz, code, data, reply,
flags, result)) {
LOGD("transact intercepted: binder=%p code=%d result=%d", thiz, code, result);
return result;
}
return UNKNOWN_TRANSACTION;
}
};
static sp<BinderStub> gBinderStub = nullptr;
int (*old_ioctl)(int fd, int request, ...) = nullptr;
int new_ioctl(int fd, int request, ...) {
va_list list;
va_start(list, request);
auto arg = va_arg(list, void*);
va_end(list);
auto result = old_ioctl(fd, request, arg);
// TODO: check fd
if (result >= 0 && request == BINDER_WRITE_READ) {
auto &bwr = *(struct binder_write_read*) arg;
LOGD("read buffer %p size %zu consumed %zu", bwr.read_buffer, bwr.read_size,
bwr.read_consumed);
if (bwr.read_buffer != 0 && bwr.read_size != 0 && bwr.read_consumed > sizeof(int32_t)) {
auto ptr = bwr.read_buffer;
auto consumed = bwr.read_consumed;
while (consumed > 0) {
consumed -= sizeof(uint32_t);
if (consumed < 0) {
LOGE("consumed < 0");
break;
}
auto cmd = *(uint32_t *) ptr;
ptr += sizeof(uint32_t);
auto sz = _IOC_SIZE(cmd);
LOGD("ioctl cmd %d sz %d", cmd, sz);
consumed -= sz;
if (consumed < 0) {
LOGE("consumed < 0");
break;
}
if (cmd == BR_TRANSACTION_SEC_CTX || cmd == BR_TRANSACTION) {
binder_transaction_data_secctx *tr_secctx = nullptr;
binder_transaction_data *tr = nullptr;
if (cmd == BR_TRANSACTION_SEC_CTX) {
LOGD("cmd is BR_TRANSACTION_SEC_CTX");
tr_secctx = (binder_transaction_data_secctx *) ptr;
tr = &tr_secctx->transaction_data;
} else {
LOGD("cmd is BR_TRANSACTION");
tr = (binder_transaction_data *) ptr;
}
if (tr != nullptr) {
auto wt = tr->target.ptr;
if (wt != 0) {
bool need_intercept = false;
thread_transaction_info tti{};
if (tr->code == 0xdeadbeef && tr->sender_euid == 0) {
tti.code = 0xdeadbeef;
tti.target = nullptr;
need_intercept = true;
} else if (reinterpret_cast<RefBase::weakref_type *>(wt)->attemptIncStrong(
nullptr)) {
auto b = (BBinder *) tr->cookie;
auto wb = wp<BBinder>::fromExisting(b);
if (gBinderInterceptor->needIntercept(wb)) {
tti.code = tr->code;
tti.target = wb;
need_intercept = true;
LOGD("intercept code=%d target=%p", tr->code, b);
}
b->decStrong(nullptr);
}
if (need_intercept) {
LOGD("add intercept item!");
tr->target.ptr = (uintptr_t) gBinderStub->getWeakRefs();
tr->cookie = (uintptr_t) gBinderStub.get();
tr->code = 0xdeadbeef;
ttis.push(tti);
}
}
} else {
LOGE("no transaction data found!");
}
}
ptr += sz;
}
result = backup(thiz, code, data, reply, flags);
LOGD("transact: binder=%p code=%d result=%d", thiz, code, result);
return result;
});
}
}
return result;
}
bool BinderInterceptor::needIntercept(const wp<BBinder> &target) {
ReadGuard g{lock};
return items.find(target) != items.end();
}
status_t
BinderInterceptor::onTransact(uint32_t code, const android::Parcel &data, android::Parcel *reply,
@@ -182,20 +289,21 @@ public:
};
bool
BinderInterceptor::handleIntercept(BBinder *thiz, uint32_t code, const Parcel &data, Parcel *reply,
BinderInterceptor::handleIntercept(sp<BBinder> target, uint32_t code, const Parcel &data, Parcel *reply,
uint32_t flags, status_t &result) {
#define CHECK(expr) ({ auto __result = (expr); if (__result != OK) { LOGE(#expr " = %d", __result); return false; } })
sp<IBinder> interceptor;
{
ReadGuard rg{lock};
wp<IBinder> target = wp<IBinder>::fromExisting(thiz);
auto it = items.find(target);
if (it == items.end()) return false;
if (it == items.end()) {
LOGE("no intercept item found!");
return false;
}
interceptor = it->second.interceptor;
}
LOGD("intercept on binder %p code %d flags %d (reply=%s)", thiz, code, flags,
LOGD("intercept on binder %p code %d flags %d (reply=%s)", target.get(), code, flags,
reply ? "true" : "false");
sp<IBinder> target = sp<IBinder>::fromExisting(thiz);
Parcel tmpData, tmpReply, realData;
CHECK(tmpData.writeStrongBinder(target));
CHECK(tmpData.writeUint32(code));
@@ -223,7 +331,7 @@ BinderInterceptor::handleIntercept(BBinder *thiz, uint32_t code, const Parcel &d
} else {
CHECK(realData.appendFrom(&data, 0, data.dataSize()));
}
result = BBinder_Transact.backup(thiz, code, realData, reply, flags);
result = target->transact(code, realData, reply, flags);
tmpData.freeData();
tmpReply.freeData();
@@ -258,49 +366,19 @@ BinderInterceptor::handleIntercept(BBinder *thiz, uint32_t code, const Parcel &d
}
bool hookBinder() {
HookHandler handler{ElfInfo::getElfInfoForName("libbinder.so")};
if (!handler.isValid()) {
ElfImg img{ElfInfo::getElfInfoForName("libc.so")};
if (!img.isValid()) {
LOGE("libbinder not found!");
return false;
}
if (!hook_helper::HookSym(handler, BBinder_Transact)) {
gBinderInterceptor = sp<BinderInterceptor>::make();
gBinderStub = sp<BinderStub>::make();
auto ioctlAddr = img.getSymbAddress("ioctl");
if (DobbyHook(ioctlAddr, (dobby_dummy_func_t) new_ioctl, (dobby_dummy_func_t*) &old_ioctl) != 0) {
LOGE("hook failed!");
return false;
}
LOGI("hook success!");
gBinderInterceptor = sp<BinderInterceptor>::make();
auto transactSym = handler.get_symbol("_ZN7android7BBinder8transactEjRKNS_6ParcelEPS1_j");
auto &img = handler.img;
auto [vtSym, vtSize] = img.getSymInfo("_ZTVN7android7BBinderE");
auto sm = defaultServiceManager();
if (sm == nullptr) {
LOGE("service manager is null!");
return false;
} else {
int transactPos = -1;
auto svc = sm->checkService(String16("android.system.keystore2.IKeystoreService/default"));
if (svc != nullptr) {
for (int i = 0; i < vtSize / sizeof(uintptr_t); i++) {
auto val = *((uintptr_t *) vtSym + i);
auto name = img.findSymbolNameForAddr(val);
LOGI("vtable %i: %p %s", i, val, name.c_str());
if (val == (uintptr_t) transactSym) {
transactPos = i - 3;
LOGI("transact pos %d", transactPos);
}
}
if (transactPos >= 0) {
auto svcTransactAddr = *(*reinterpret_cast<void ***>(svc.get()) + transactPos);
LOGI("transact of svc %p: %p", svc.get(), svcTransactAddr);
} else {
LOGE("transactPos not found!");
return false;
}
} else {
LOGE("IKeystoreService not found!");
return false;
}
}
return true;
}

View File

@@ -17,7 +17,7 @@ open class BinderInterceptor : Binder() {
val data = Parcel.obtain()
val reply = Parcel.obtain()
try {
if (!b.transact(0xadbeef, data, reply, 0)) {
if (!b.transact(0xdeadbeef.toInt(), data, reply, 0)) {
Logger.e("remote return false!")
return null
}