From 5c9fe31ced0a3e9b0486c47b61cbcbf14488a9d0 Mon Sep 17 00:00:00 2001 From: 5ec1cff Date: Wed, 17 Jul 2024 22:58:26 +0800 Subject: [PATCH] new way to hook --- .../main/cpp/binder/include/kernel/binder.h | 239 ++++++++++++++++++ module/src/main/cpp/binder_interceptor.cpp | 206 ++++++++++----- .../tricky_store/binder/BinderInterceptor.kt | 2 +- 3 files changed, 382 insertions(+), 65 deletions(-) create mode 100644 module/src/main/cpp/binder/include/kernel/binder.h diff --git a/module/src/main/cpp/binder/include/kernel/binder.h b/module/src/main/cpp/binder/include/kernel/binder.h new file mode 100644 index 0000000..97cdb1a --- /dev/null +++ b/module/src/main/cpp/binder/include/kernel/binder.h @@ -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 +#include + +#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 diff --git a/module/src/main/cpp/binder_interceptor.cpp b/module/src/main/cpp/binder_interceptor.cpp index 7423420..505f482 100644 --- a/module/src/main/cpp/binder_interceptor.cpp +++ b/module/src/main/cpp/binder_interceptor.cpp @@ -6,11 +6,14 @@ #include #include #include +#include +#include "kernel/binder.h" #include #include #include #include +#include #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 target, uint32_t code, const Parcel &data, Parcel *reply, uint32_t flags, status_t &result); + + bool needIntercept(const wp& target); }; static sp 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 target; +}; + +thread_local std::queue 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 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(wt)->attemptIncStrong( + nullptr)) { + auto b = (BBinder *) tr->cookie; + auto wb = wp::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 &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 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 interceptor; { ReadGuard rg{lock}; - wp target = wp::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 target = sp::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::make(); + gBinderStub = sp::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::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(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; } diff --git a/service/src/main/java/io/github/a13e300/tricky_store/binder/BinderInterceptor.kt b/service/src/main/java/io/github/a13e300/tricky_store/binder/BinderInterceptor.kt index d5e1055..680bc38 100644 --- a/service/src/main/java/io/github/a13e300/tricky_store/binder/BinderInterceptor.kt +++ b/service/src/main/java/io/github/a13e300/tricky_store/binder/BinderInterceptor.kt @@ -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 }