diff --git a/module/src/main/cpp/binder/include/binder/IServiceManager.h b/module/src/main/cpp/binder/include/binder/IServiceManager.h new file mode 100644 index 0000000..119611e --- /dev/null +++ b/module/src/main/cpp/binder/include/binder/IServiceManager.h @@ -0,0 +1,163 @@ +/* + * Copyright (C) 2005 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once + +#include +#include +#include +#include +#include + +namespace android { + +// ---------------------------------------------------------------------- + +/** + * Service manager for C++ services. + * + * IInterface is only for legacy ABI compatibility + */ + class LIBBINDER_EXPORTED IServiceManager : public IInterface { + public: + // for ABI compatibility + virtual const String16 &getInterfaceDescriptor() const; + + IServiceManager(); + + virtual ~IServiceManager(); + + /** + * Must match values in IServiceManager.aidl + */ + /* Allows services to dump sections according to priorities. */ + static const int DUMP_FLAG_PRIORITY_CRITICAL = 1 << 0; + static const int DUMP_FLAG_PRIORITY_HIGH = 1 << 1; + static const int DUMP_FLAG_PRIORITY_NORMAL = 1 << 2; + /** + * Services are by default registered with a DEFAULT dump priority. DEFAULT priority has the + * same priority as NORMAL priority but the services are not called with dump priority + * arguments. + */ + static const int DUMP_FLAG_PRIORITY_DEFAULT = 1 << 3; + static const int DUMP_FLAG_PRIORITY_ALL = DUMP_FLAG_PRIORITY_CRITICAL | + DUMP_FLAG_PRIORITY_HIGH | + DUMP_FLAG_PRIORITY_NORMAL | + DUMP_FLAG_PRIORITY_DEFAULT; + static const int DUMP_FLAG_PROTO = 1 << 4; + + /** + * Retrieve an existing service, blocking for a few seconds if it doesn't yet exist. This + * does polling. A more efficient way to make sure you unblock as soon as the service is + * available is to use waitForService or to use service notifications. + * + * Warning: when using this API, typically, you should call it in a loop. It's dangerous to + * assume that nullptr could mean that the service is not available. The service could just + * be starting. Generally, whether a service exists, this information should be declared + * externally (for instance, an Android feature might imply the existence of a service, + * a system property, or in the case of services in the VINTF manifest, it can be checked + * with isDeclared). + */ + [[deprecated("this polls for 5s, prefer waitForService or checkService")]] + virtual sp getService(const String16 &name) const = 0; + + /** + * Retrieve an existing service, non-blocking. + */ + virtual sp checkService(const String16 &name) const = 0; + + /** + * Register a service. + */ + // NOLINTNEXTLINE(google-default-arguments) + virtual status_t addService(const String16 &name, const sp &service, + bool allowIsolated = false, + int dumpsysFlags = DUMP_FLAG_PRIORITY_DEFAULT) = 0; + + /** + * Return list of all existing services. + */ + // NOLINTNEXTLINE(google-default-arguments) + virtual Vector listServices(int dumpsysFlags = DUMP_FLAG_PRIORITY_ALL) = 0; + + /** + * Efficiently wait for a service. + * + * Returns nullptr only for permission problem or fatal error. + */ + virtual sp waitForService(const String16 &name) = 0; + + /** + * Check if a service is declared (e.g. VINTF manifest). + * + * If this returns true, waitForService should always be able to return the + * service. + */ + virtual bool isDeclared(const String16 &name) = 0; + + /** + * Get all instances of a service as declared in the VINTF manifest + */ + virtual Vector getDeclaredInstances(const String16 &interface) = 0; + + /** + * If this instance is updatable via an APEX, returns the APEX with which + * this can be updated. + */ + virtual std::optional updatableViaApex(const String16 &name) = 0; + + /** + * Returns all instances which are updatable via the APEX. Instance names are fully qualified + * like `pack.age.IFoo/default`. + */ + virtual Vector getUpdatableNames(const String16 &apexName) = 0; + + /** + * If this instance has declared remote connection information, returns + * the ConnectionInfo. + */ + struct ConnectionInfo { + std::string ipAddress; + unsigned int port; + }; + + virtual std::optional getConnectionInfo(const String16 &name) = 0; + + struct LocalRegistrationCallback : public virtual RefBase { + virtual void + onServiceRegistration(const String16 &instance, const sp &binder) = 0; + + virtual ~LocalRegistrationCallback() {} + }; + + virtual status_t registerForNotifications(const String16 &name, + const sp &callback) = 0; + + virtual status_t unregisterForNotifications(const String16 &name, + const sp &callback) = 0; + + struct ServiceDebugInfo { + std::string name; + int pid; + }; + + virtual std::vector getServiceDebugInfo() = 0; + }; + + LIBBINDER_EXPORTED sp defaultServiceManager(); + + LIBBINDER_EXPORTED void setDefaultServiceManager(const sp &sm); +} // namespace android \ No newline at end of file diff --git a/module/src/main/cpp/binder/include/utils/String16.h b/module/src/main/cpp/binder/include/utils/String16.h index d8073d4..14b80cd 100644 --- a/module/src/main/cpp/binder/include/utils/String16.h +++ b/module/src/main/cpp/binder/include/utils/String16.h @@ -1,6 +1,40 @@ -#pragma once +/* + * Copyright (C) 2005 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef ANDROID_STRING16_H +#define ANDROID_STRING16_H -// TODO namespace android { - class String16 {}; -} + class String16 { + public: + String16(); + + String16(const String16 &o); + + String16(String16 &&o) noexcept; + + explicit String16(const char *o); + + ~String16(); + + private: + const char16_t *mString; + }; + + +} // namespace android + +#endif // ANDROID_STRING16_H diff --git a/module/src/main/cpp/binder/stub_binder.cpp b/module/src/main/cpp/binder/stub_binder.cpp index 8b0502b..38e6c8b 100644 --- a/module/src/main/cpp/binder/stub_binder.cpp +++ b/module/src/main/cpp/binder/stub_binder.cpp @@ -4,6 +4,7 @@ #include "binder/IPCThreadState.h" #include "binder/Parcel.h" #include "binder/IInterface.h" +#include "binder/IServiceManager.h" namespace android { // IBinder.h @@ -148,4 +149,17 @@ namespace android { int32_t Parcel::readExceptionCode() const { return 0; } int Parcel::readFileDescriptor() const { return 0; } + + // IServiceManager.h + const String16 &IServiceManager::getInterfaceDescriptor() const { + return {}; + } + + IServiceManager::IServiceManager() {} + + IServiceManager::~IServiceManager() {} + + sp defaultServiceManager() { return nullptr; } + + void setDefaultServiceManager(const sp &sm) {} } diff --git a/module/src/main/cpp/binder/stub_utils.cpp b/module/src/main/cpp/binder/stub_utils.cpp index 8fe8684..cacf860 100644 --- a/module/src/main/cpp/binder/stub_utils.cpp +++ b/module/src/main/cpp/binder/stub_utils.cpp @@ -1,5 +1,6 @@ #include "utils/StrongPointer.h" #include "utils/RefBase.h" +#include "utils/String16.h" namespace android { void RefBase::incStrong(const void *id) const { @@ -49,4 +50,14 @@ namespace android { bool RefBase::weakref_type::attemptIncWeak(const void* id) { return false; } void sp_report_race() {} + + String16::String16() {} + + String16::String16(const String16 &o) {} + + String16::String16(String16 &&o) noexcept {} + + String16::String16(const char *o) {} + + String16::~String16() {} } diff --git a/module/src/main/cpp/binder_interceptor.cpp b/module/src/main/cpp/binder_interceptor.cpp index ddfc7f3..7423420 100644 --- a/module/src/main/cpp/binder_interceptor.cpp +++ b/module/src/main/cpp/binder_interceptor.cpp @@ -5,6 +5,7 @@ #include #include #include +#include #include #include @@ -63,7 +64,7 @@ CREATE_MEM_HOOK_STUB_ENTRY( LOGD("transact: binder=%p code=%d", thiz, code); if (IPCThreadState::self()->getCallingUid() == 0 && reply != nullptr && thiz != gBinderInterceptor) [[unlikely]] { - if (code == 0xdeadbeef) { + if (code == 0xadbeef) { LOGD("request binder interceptor"); reply->writeStrongBinder(gBinderInterceptor); return OK; @@ -268,6 +269,38 @@ bool hookBinder() { } 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/KeystoreInterceptor.kt b/service/src/main/java/io/github/a13e300/tricky_store/KeystoreInterceptor.kt index 9014084..f5bb9b0 100644 --- a/service/src/main/java/io/github/a13e300/tricky_store/KeystoreInterceptor.kt +++ b/service/src/main/java/io/github/a13e300/tricky_store/KeystoreInterceptor.kt @@ -88,12 +88,18 @@ object KeystoreInterceptor : BinderInterceptor() { return Skip } + private var tried = false + fun tryRunKeystoreInterceptor(): Boolean { Logger.i("trying to register keystore interceptor ...") val b = ServiceManager.getService("android.system.keystore2.IKeystoreService/default") ?: return false val bd = getBinderBackdoor(b) if (bd == null) { // no binder hook, try inject + if (tried) { + Logger.e("inject tried but still has no backdoor, exit") + exitProcess(1) + } Logger.i("trying to inject keystore ...") val p = Runtime.getRuntime().exec( arrayOf( @@ -108,6 +114,7 @@ object KeystoreInterceptor : BinderInterceptor() { Logger.e("failed to inject! daemon exit") exitProcess(1) } + tried = true return false } val ks = IKeystoreService.Stub.asInterface(b) 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 93f08e6..d5e1055 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 @@ -3,6 +3,7 @@ package io.github.a13e300.tricky_store.binder import android.os.Binder import android.os.IBinder import android.os.Parcel +import io.github.a13e300.tricky_store.Logger open class BinderInterceptor : Binder() { sealed class Result @@ -16,9 +17,14 @@ open class BinderInterceptor : Binder() { val data = Parcel.obtain() val reply = Parcel.obtain() try { - b.transact(0xdeadbeef.toInt(), data, reply, 0) + if (!b.transact(0xadbeef, data, reply, 0)) { + Logger.e("remote return false!") + return null + } + Logger.d("remote return true!") return reply.readStrongBinder() - } catch (ignored: Throwable) { + } catch (t: Throwable) { + Logger.e("failed to read binder", t) return null } finally { data.recycle()