mirror of
https://github.com/kdrag0n/safetynet-fix.git
synced 2024-10-06 06:39:49 +00:00
6e35ac2b6c
This method is more portable, does not require a different executable for each Android version, and avoids breaking ROMs with heavy keystore customizations. It works by injecting a shared library into the keystore service and wrapping the Binder transaction handler in the generated AIDL interface. Because the actual attestKey implementation is statically linked into the keystore service executable, we can't hijack it directly without messy and error-prone code patching. Instead, we check each Binder transaction handled by the AIDL stub and hijack transactions with the TRANSACTION_attestKey code. In order to keep key attestation working for apps, we only block Google Play Services by checking the command line of the calling process through Binder. This is not infallible and can be spoofed, but it's much easier to do in C++ than looking up the calling UID's package name through PackageManagerService. There are no negative security implications as the only difference is a denied operation. We can't set LD_PRELOAD for the keystore service because Magisk modules start too late to modify its init.rc, so we inject the shim library as a dependency instead using patchelf and build it with the DF_1_GLOBAL ELF flag to make the dynamic linker prioritize it in symbol resolution. TRANSACTION_attestKey values and AIDL stub names by Android version: Android 10 - 11 (SDK 29-30): TX# 28, android::security::keystore::BnKeystoreService Android 9 (SDK 28 ): TX# 35, android::security::BnKeystoreService Android 7.0 - 8.1.0 (SDK 24-27): TX# 36, android::BnKeystoreService Fixes #3, #6, #7, #8, #10, and #12.
114 lines
3.8 KiB
C++
114 lines
3.8 KiB
C++
#define LOG_TAG "keystore"
|
|
|
|
#include <stdint.h>
|
|
#include <dlfcn.h>
|
|
#include <string>
|
|
#include <fstream>
|
|
#include <android/log.h>
|
|
#include <binder/Parcel.h>
|
|
#include <binder/IPCThreadState.h>
|
|
|
|
static android::status_t interceptTransaction(uint32_t code, uint32_t attestKey_code) {
|
|
// TRANSACTION_attestKey, varies by Android version
|
|
if (code == attestKey_code) {
|
|
pid_t pid = android::IPCThreadState::self()->getCallingPid();
|
|
std::string cmdline;
|
|
std::ifstream cmdlineFile("/proc/" + std::to_string(pid) + "/cmdline");
|
|
std::getline(cmdlineFile, cmdline);
|
|
cmdlineFile.close();
|
|
|
|
std::string gmsPrefix("com.google.android.gms");
|
|
if (!cmdline.compare(0, gmsPrefix.size(), gmsPrefix)) {
|
|
ALOGI("Blocking key attestation attempt from %s", cmdline.c_str());
|
|
return android::INVALID_OPERATION;
|
|
}
|
|
}
|
|
|
|
return android::NO_ERROR;
|
|
}
|
|
|
|
static void ensureSymbol(void** ptr, std::string realName) {
|
|
if (!*ptr) {
|
|
char* error;
|
|
void* symPtr = dlsym(RTLD_NEXT, realName.c_str());
|
|
if ((error = dlerror()) != NULL) {
|
|
ALOGE("%s", error);
|
|
exit(1);
|
|
}
|
|
|
|
*ptr = symPtr;
|
|
}
|
|
}
|
|
|
|
namespace android {
|
|
|
|
namespace security {
|
|
|
|
namespace keystore {
|
|
|
|
// Android 10 - 11 (SDK 29-30), TX 28
|
|
// libkeystore_aidl.so (android::security::keystore::BnKeystoreService)
|
|
class BnKeystoreService {
|
|
public:
|
|
android::status_t onTransact(uint32_t, const android::Parcel&, android::Parcel*, uint32_t);
|
|
};
|
|
|
|
typedef android::status_t (BnKeystoreService::*SecKsOnTransactFunc)(uint32_t, const android::Parcel&, android::Parcel*, uint32_t);
|
|
static SecKsOnTransactFunc real_onTransact = nullptr;
|
|
|
|
android::status_t BnKeystoreService::onTransact(uint32_t code, const android::Parcel& data, android::Parcel* reply, uint32_t flags) {
|
|
android::status_t ret = interceptTransaction(code, 28);
|
|
if (ret != android::NO_ERROR) {
|
|
return ret;
|
|
}
|
|
|
|
ensureSymbol(reinterpret_cast<void**>(&real_onTransact), "_ZN7android8security8keystore17BnKeystoreService10onTransactEjRKNS_6ParcelEPS3_j");
|
|
return (this->*real_onTransact)(code, data, reply, flags);
|
|
}
|
|
|
|
};
|
|
|
|
// Android 9 (SDK 28), TX 35
|
|
// libkeystore_aidl.so (android::security::BnKeystoreService)
|
|
class BnKeystoreService {
|
|
public:
|
|
android::status_t onTransact(uint32_t, const android::Parcel&, android::Parcel*, uint32_t);
|
|
};
|
|
|
|
typedef android::status_t (BnKeystoreService::*SecOnTransactFunc)(uint32_t, const android::Parcel&, android::Parcel*, uint32_t);
|
|
static SecOnTransactFunc real_onTransact = nullptr;
|
|
|
|
android::status_t BnKeystoreService::onTransact(uint32_t code, const android::Parcel& data, android::Parcel* reply, uint32_t flags) {
|
|
android::status_t ret = interceptTransaction(code, 35);
|
|
if (ret != android::NO_ERROR) {
|
|
return ret;
|
|
}
|
|
|
|
ensureSymbol(reinterpret_cast<void**>(&real_onTransact), "_ZN7android8security17BnKeystoreService10onTransactEjRKNS_6ParcelEPS2_j");
|
|
return (this->*real_onTransact)(code, data, reply, flags);
|
|
}
|
|
|
|
};
|
|
|
|
// Android 7.0 - 8.1.0 (SDK 24-27), TX 36
|
|
// libkeystore_binder.so (android::BnKeystoreService)
|
|
class BnKeystoreService {
|
|
public:
|
|
android::status_t onTransact(uint32_t, const android::Parcel&, android::Parcel*, uint32_t);
|
|
};
|
|
|
|
typedef android::status_t (BnKeystoreService::*AOnTransactFunc)(uint32_t, const android::Parcel&, android::Parcel*, uint32_t);
|
|
static AOnTransactFunc real_onTransact = nullptr;
|
|
|
|
android::status_t BnKeystoreService::onTransact(uint32_t code, const android::Parcel& data, android::Parcel* reply, uint32_t flags) {
|
|
android::status_t ret = interceptTransaction(code, 36);
|
|
if (ret != android::NO_ERROR) {
|
|
return ret;
|
|
}
|
|
|
|
ensureSymbol(reinterpret_cast<void**>(&real_onTransact), "_ZN7android17BnKeystoreService10onTransactEjRKNS_6ParcelEPS1_j");
|
|
return (this->*real_onTransact)(code, data, reply, flags);
|
|
}
|
|
|
|
};
|