This commit is contained in:
chiteroman 2024-06-20 23:59:48 +02:00
parent ecd3b82869
commit 3649341939
12 changed files with 305 additions and 223 deletions

View File

@ -16,8 +16,8 @@ android {
applicationId = "es.chiteroman.playintegrityfix"
minSdk = 26
targetSdk = 34
versionCode = 16000
versionName = "v16.0"
versionCode = 16300
versionName = "v16.3"
multiDexEnabled = false
packaging {
@ -94,7 +94,7 @@ tasks.register("copyFiles") {
val dexFile = project.layout.buildDirectory.get().asFile.resolve("intermediates/dex/release/minifyReleaseWithR8/classes.dex")
val soDir = project.layout.buildDirectory.get().asFile.resolve("intermediates/stripped_native_libs/release/stripReleaseDebugSymbols/out/lib")
// dexFile.copyTo(moduleFolder.resolve("classes.dex"), overwrite = true)
dexFile.copyTo(moduleFolder.resolve("classes.dex"), overwrite = true)
soDir.walk().filter { it.isFile && it.extension == "so" }.forEach { soFile ->
val abiFolder = soFile.parentFile.name

View File

@ -1,3 +1,4 @@
-keep class es.chiteroman.playintegrityfix.EntryPoint {public <methods>;}
-keep class es.chiteroman.playintegrityfix.CustomProvider
-keep class es.chiteroman.playintegrityfix.CustomKeyStoreSpi
-keep class es.chiteroman.playintegrityfix.KeyboxUtils

View File

@ -1,20 +1,24 @@
#include <android/log.h>
#include <sys/system_properties.h>
#include <unistd.h>
#include <fstream>
#include "dobby.h"
#include "json.hpp"
#include "zygisk.hpp"
#include "dex.h"
#define LOGD(...) __android_log_print(ANDROID_LOG_DEBUG, "PIF", __VA_ARGS__)
#define DEX_PATH "/data/adb/modules/playintegrityfix/classes.dex"
#define PIF_JSON "/data/adb/pif.json"
#define PIF_JSON_DEFAULT "/data/adb/modules/playintegrityfix/pif.json"
static std::string DEVICE_INITIAL_SDK_INT, SECURITY_PATCH, ID;
#define KEYBOX_JSON "/data/adb/keybox.xml"
static inline ssize_t xread(int fd, void *buffer, size_t count) {
#define KEYBOX_JSON_DEFAULT "/data/adb/modules/playintegrityfix/keybox.xml"
static ssize_t xread(int fd, void *buffer, size_t count) {
ssize_t total = 0;
char *buf = (char *) buffer;
while (count > 0) {
@ -27,7 +31,7 @@ static inline ssize_t xread(int fd, void *buffer, size_t count) {
return total;
}
static inline ssize_t xwrite(int fd, void *buffer, size_t count) {
static ssize_t xwrite(int fd, void *buffer, size_t count) {
ssize_t total = 0;
char *buf = (char *) buffer;
while (count > 0) {
@ -40,6 +44,8 @@ static inline ssize_t xwrite(int fd, void *buffer, size_t count) {
return total;
}
static std::string DEVICE_INITIAL_SDK_INT;
typedef void (*T_Callback)(void *, const char *, const char *, uint32_t);
static T_Callback o_callback = nullptr;
@ -50,17 +56,8 @@ static void modify_callback(void *cookie, const char *name, const char *value, u
std::string_view prop(name);
if (prop.ends_with("api_level") && !DEVICE_INITIAL_SDK_INT.empty()) {
if (prop.ends_with("first_api_level") && !DEVICE_INITIAL_SDK_INT.empty()) {
value = DEVICE_INITIAL_SDK_INT.c_str();
} else if (prop.ends_with(".security_patch") && !SECURITY_PATCH.empty()) {
value = SECURITY_PATCH.c_str();
} else if (prop.ends_with(".id") && !ID.empty()) {
value = ID.c_str();
} else if (prop == "sys.usb.state") {
value = "none";
}
if (!prop.starts_with("persist") && !prop.starts_with("cache") && !prop.starts_with("debug")) {
LOGD("[%s]: %s", name, value);
}
@ -98,71 +95,81 @@ public:
void preAppSpecialize(zygisk::AppSpecializeArgs *args) override {
api->setOption(zygisk::DLCLOSE_MODULE_LIBRARY);
if (!args) {
api->setOption(zygisk::DLCLOSE_MODULE_LIBRARY);
return;
}
const char *dir = env->GetStringUTFChars(args->app_data_dir, nullptr);
auto dir = env->GetStringUTFChars(args->app_data_dir, nullptr);
if (!dir) {
api->setOption(zygisk::DLCLOSE_MODULE_LIBRARY);
return;
}
if (!std::string_view(dir).ends_with("/com.google.android.gms")) {
bool isGms = std::string_view(dir).ends_with("/com.google.android.gms");
env->ReleaseStringUTFChars(args->app_data_dir, dir);
if (!isGms) {
api->setOption(zygisk::DLCLOSE_MODULE_LIBRARY);
return;
}
env->ReleaseStringUTFChars(args->app_data_dir, dir);
api->setOption(zygisk::FORCE_DENYLIST_UNMOUNT);
const char *name = env->GetStringUTFChars(args->nice_name, nullptr);
auto name = env->GetStringUTFChars(args->nice_name, nullptr);
if (!name) {
api->setOption(zygisk::DLCLOSE_MODULE_LIBRARY);
return;
}
if (strncmp(name, "com.google.android.gms.unstable", 31) != 0) {
bool isGmsUnstable = std::string_view(name) == "com.google.android.gms.unstable";
env->ReleaseStringUTFChars(args->nice_name, name);
if (!isGmsUnstable) {
api->setOption(zygisk::DLCLOSE_MODULE_LIBRARY);
return;
}
env->ReleaseStringUTFChars(args->nice_name, name);
long size = 0;
std::vector<char> vector;
int fd = api->connectCompanion();
xread(fd, &size, sizeof(long));
int dexSize = 0, jsonSize = 0, keyboxSize = 0;
std::vector<char> jsonVector, keyboxVector;
if (size > 0) {
vector.resize(size);
xread(fd, vector.data(), size);
json = nlohmann::json::parse(vector, nullptr, false, true);
} else {
api->setOption(zygisk::DLCLOSE_MODULE_LIBRARY);
}
xread(fd, &dexSize, sizeof(int));
xread(fd, &jsonSize, sizeof(int));
xread(fd, &keyboxSize, sizeof(int));
dexVector.resize(dexSize);
xread(fd, dexVector.data(), dexSize);
jsonVector.resize(jsonSize);
xread(fd, jsonVector.data(), jsonSize);
keyboxVector.resize(keyboxSize);
xread(fd, keyboxVector.data(), keyboxSize);
close(fd);
json = nlohmann::json::parse(jsonVector, nullptr, false, true);
keyboxString = std::string(keyboxVector.cbegin(), keyboxVector.cend());
}
void postAppSpecialize(const zygisk::AppSpecializeArgs *args) override {
if (json.empty()) return;
if (dexVector.empty() || json.empty()) return;
parseJson();
if (json.contains("DEVICE_INITIAL_SDK_INT")) {
DEVICE_INITIAL_SDK_INT = json["DEVICE_INITIAL_SDK_INT"].get<std::string>();
json.erase("DEVICE_INITIAL_SDK_INT"); // You can't modify field value
}
doHook();
injectDex();
// doHook();
}
void preServerSpecialize(zygisk::ServerSpecializeArgs *args) override {
@ -172,20 +179,9 @@ public:
private:
zygisk::Api *api = nullptr;
JNIEnv *env = nullptr;
std::vector<char> dexVector;
nlohmann::json json;
void parseJson() {
if (json.contains("DEVICE_INITIAL_SDK_INT")) {
DEVICE_INITIAL_SDK_INT = json["DEVICE_INITIAL_SDK_INT"].get<std::string>();
json.erase("DEVICE_INITIAL_SDK_INT"); // You can't modify field value
}
if (json.contains("SECURITY_PATCH")) {
SECURITY_PATCH = json["SECURITY_PATCH"].get<std::string>();
}
if (json.contains("ID")) {
ID = json["ID"].get<std::string>();
}
}
std::string keyboxString;
void injectDex() {
LOGD("get system classloader");
@ -198,7 +194,7 @@ private:
auto dexClClass = env->FindClass("dalvik/system/InMemoryDexClassLoader");
auto dexClInit = env->GetMethodID(dexClClass, "<init>",
"(Ljava/nio/ByteBuffer;Ljava/lang/ClassLoader;)V");
auto buffer = env->NewDirectByteBuffer(classes_dex, classes_dex_len);
auto buffer = env->NewDirectByteBuffer(dexVector.data(), dexVector.size());
auto dexCl = env->NewObject(dexClClass, dexClInit, buffer, systemClassLoader);
LOGD("load class");
@ -210,45 +206,47 @@ private:
auto entryPointClass = (jclass) entryClassObj;
LOGD("call init");
auto entryInit = env->GetStaticMethodID(entryPointClass, "init", "(Ljava/lang/String;)V");
auto str = env->NewStringUTF(json.dump().c_str());
env->CallStaticVoidMethod(entryPointClass, entryInit, str);
auto entryInit = env->GetStaticMethodID(entryPointClass, "init",
"(Ljava/lang/String;Ljava/lang/String;)V");
auto jsonStr = env->NewStringUTF(json.dump().c_str());
auto keyboxStr = env->NewStringUTF(keyboxString.c_str());
env->CallStaticVoidMethod(entryPointClass, entryInit, jsonStr, keyboxStr);
}
};
static std::vector<char> readFile(const char *path) {
static std::vector<char> readFile(const std::string &path) {
std::vector<char> vector;
std::ifstream ifs(path);
FILE *file = fopen(path, "rb");
if (file) {
fseek(file, 0, SEEK_END);
long size = ftell(file);
fseek(file, 0, SEEK_SET);
vector.resize(size);
fread(vector.data(), 1, size, file);
fclose(file);
} else {
LOGD("Couldn't read %s file!", path);
if (!ifs || ifs.bad()) {
return std::vector<char>();
}
return vector;
return std::vector<char>((std::istreambuf_iterator<char>(ifs)),
std::istreambuf_iterator<char>());
}
static void companion(int fd) {
long size = 0;
std::vector<char> vector;
vector = readFile(PIF_JSON);
auto dex = readFile(DEX_PATH);
if (vector.empty()) vector = readFile(PIF_JSON_DEFAULT);
auto json = readFile(PIF_JSON);
if (json.empty()) json = readFile(PIF_JSON_DEFAULT);
size = vector.size();
auto keybox = readFile(KEYBOX_JSON);
if (keybox.empty()) keybox = readFile(KEYBOX_JSON_DEFAULT);
xwrite(fd, &size, sizeof(long));
xwrite(fd, vector.data(), size);
int dexSize = dex.size();
int jsonSize = json.size();
int keyboxSize = keybox.size();
xwrite(fd, &dexSize, sizeof(int));
xwrite(fd, &jsonSize, sizeof(int));
xwrite(fd, &keyboxSize, sizeof(int));
xwrite(fd, dex.data(), dexSize * sizeof(char));
xwrite(fd, json.data(), jsonSize * sizeof(char));
xwrite(fd, keybox.data(), keyboxSize * sizeof(char));
}
REGISTER_ZYGISK_MODULE(PlayIntegrityFix)

View File

@ -26,7 +26,7 @@ public final class CustomKeyStoreSpi extends KeyStoreSpi {
@Override
public Certificate[] engineGetCertificateChain(String alias) {
if (Arrays.stream(Thread.currentThread().getStackTrace()).anyMatch(e -> e.getClassName().toLowerCase(Locale.US).contains("droidguard"))) {
return Android.engineGetCertificateChain(keyStoreSpi.engineGetCertificateChain(alias));
return KeyboxUtils.engineGetCertificateChain(keyStoreSpi.engineGetCertificateChain(alias));
}
return keyStoreSpi.engineGetCertificateChain(alias);
}

View File

@ -37,7 +37,7 @@ public final class EntryPoint {
Security.insertProviderAt(customProvider, 1);
}
public static void init(String json) {
public static void init(String json, String kbox) {
try {
JSONObject jsonObject = new JSONObject(json);
@ -65,6 +65,12 @@ public final class EntryPoint {
} catch (Throwable t) {
LOG("Error loading json file: " + t);
}
try {
KeyboxUtils.parseXml(kbox);
} catch (Throwable t) {
LOG("Error parsing keybox file: " + t);
}
}
static void spoofFields() {

View File

@ -1,107 +0,0 @@
package es.chiteroman.playintegrityfix;
public final class Keybox {
public static final class EC {
public static final String PRIVATE_KEY = """
-----BEGIN EC PRIVATE KEY-----
MHcCAQEEICHghkMqFRmEWc82OlD8FMnarfk19SfC39ceTW28QuVEoAoGCCqGSM49
AwEHoUQDQgAE6555+EJjWazLKpFMiYbMcK2QZpOCqXMmE/6sy/ghJ0whdJdKKv6l
uU1/ZtTgZRBmNbxTt6CjpnFYPts+Ea4QFA==
-----END EC PRIVATE KEY-----
""";
public static final String CERTIFICATE_1 = """
-----BEGIN CERTIFICATE-----
MIICeDCCAh6gAwIBAgICEAEwCgYIKoZIzj0EAwIwgZgxCzAJBgNVBAYTAlVTMRMw
EQYDVQQIDApDYWxpZm9ybmlhMRYwFAYDVQQHDA1Nb3VudGFpbiBWaWV3MRUwEwYD
VQQKDAxHb29nbGUsIEluYy4xEDAOBgNVBAsMB0FuZHJvaWQxMzAxBgNVBAMMKkFu
ZHJvaWQgS2V5c3RvcmUgU29mdHdhcmUgQXR0ZXN0YXRpb24gUm9vdDAeFw0xNjAx
MTEwMDQ2MDlaFw0yNjAxMDgwMDQ2MDlaMIGIMQswCQYDVQQGEwJVUzETMBEGA1UE
CAwKQ2FsaWZvcm5pYTEVMBMGA1UECgwMR29vZ2xlLCBJbmMuMRAwDgYDVQQLDAdB
bmRyb2lkMTswOQYDVQQDDDJBbmRyb2lkIEtleXN0b3JlIFNvZnR3YXJlIEF0dGVz
dGF0aW9uIEludGVybWVkaWF0ZTBZMBMGByqGSM49AgEGCCqGSM49AwEHA0IABOue
efhCY1msyyqRTImGzHCtkGaTgqlzJhP+rMv4ISdMIXSXSir+pblNf2bU4GUQZjW8
U7ego6ZxWD7bPhGuEBSjZjBkMB0GA1UdDgQWBBQ//KzWGrE6noEguNUlHMVlux6R
qTAfBgNVHSMEGDAWgBTIrel3TEXDo88NFhDkeUM6IVowzzASBgNVHRMBAf8ECDAG
AQH/AgEAMA4GA1UdDwEB/wQEAwIChDAKBggqhkjOPQQDAgNIADBFAiBLipt77oK8
wDOHri/AiZi03cONqycqRZ9pDMfDktQPjgIhAO7aAV229DLp1IQ7YkyUBO86fMy9
Xvsiu+f+uXc/WT/7
-----END CERTIFICATE-----
""";
public static final String CERTIFICATE_2 = """
-----BEGIN CERTIFICATE-----
MIICizCCAjKgAwIBAgIJAKIFntEOQ1tXMAoGCCqGSM49BAMCMIGYMQswCQYDVQQG
EwJVUzETMBEGA1UECAwKQ2FsaWZvcm5pYTEWMBQGA1UEBwwNTW91bnRhaW4gVmll
dzEVMBMGA1UECgwMR29vZ2xlLCBJbmMuMRAwDgYDVQQLDAdBbmRyb2lkMTMwMQYD
VQQDDCpBbmRyb2lkIEtleXN0b3JlIFNvZnR3YXJlIEF0dGVzdGF0aW9uIFJvb3Qw
HhcNMTYwMTExMDA0MzUwWhcNMzYwMTA2MDA0MzUwWjCBmDELMAkGA1UEBhMCVVMx
EzARBgNVBAgMCkNhbGlmb3JuaWExFjAUBgNVBAcMDU1vdW50YWluIFZpZXcxFTAT
BgNVBAoMDEdvb2dsZSwgSW5jLjEQMA4GA1UECwwHQW5kcm9pZDEzMDEGA1UEAwwq
QW5kcm9pZCBLZXlzdG9yZSBTb2Z0d2FyZSBBdHRlc3RhdGlvbiBSb290MFkwEwYH
KoZIzj0CAQYIKoZIzj0DAQcDQgAE7l1ex+HA220Dpn7mthvsTWpdamguD/9/SQ59
dx9EIm29sa/6FsvHrcV30lacqrewLVQBXT5DKyqO107sSHVBpKNjMGEwHQYDVR0O
BBYEFMit6XdMRcOjzw0WEOR5QzohWjDPMB8GA1UdIwQYMBaAFMit6XdMRcOjzw0W
EOR5QzohWjDPMA8GA1UdEwEB/wQFMAMBAf8wDgYDVR0PAQH/BAQDAgKEMAoGCCqG
SM49BAMCA0cAMEQCIDUho++LNEYenNVg8x1YiSBq3KNlQfYNns6KGYxmSGB7AiBN
C/NR2TB8fVvaNTQdqEcbY6WFZTytTySn502vQX3xvw==
-----END CERTIFICATE-----
""";
}
public static final class RSA {
public static final String PRIVATE_KEY = """
-----BEGIN RSA PRIVATE KEY-----
MIICXQIBAAKBgQDAgyPcVogbuDAgafWwhWHG7r5/BeL1qEIEir6LR752/q7yXPKb
KvoyABQWAUKZiaFfz8aBXrNjWDwv0vIL5Jgyg92BSxbX4YVBeuVKvClqOm21wAQI
O2jFVsHwIzmRZBmGTVC3TUCuykhMdzVsiVoMJ1q/rEmdXX0jYvKcXgLocQIDAQAB
AoGBAL6GCwuZqAKm+xpZQ4p7txUGWwmjbcbpysxr88AsNNfXnpTGYGQo2Ix7f2V3
wc3qZAdKvo5yht8fCBHclygmCGjeldMu/Ja20IT/JxpfYN78xwPno45uKbqaPF/C
woB2tqiWrx0014gozpvdsfNPnJQEQweBKY4gExZyW728mTpBAkEA4cbZJ2RsCRbs
NoJtWUmDdAwh8bB0xKGlmGfGaXlchdPcRkxbkp6Uv7NODcxQFLEPEzQat/3V9gQU
0qMmytQcxQJBANpIWZd4XNVjD7D9jFJU+Y5TjhiYOq6ea35qWntdNDdVuSGOvUAy
DSg4fXifdvohi8wti2il9kGPu+ylF5qzr70CQFD+/DJklVlhbtZTThVFCTKdk6PY
ENvlvbmCKSz3i9i624Agro1X9LcdBThv/p6dsnHKNHejSZnbdvjl7OnA1J0CQBW3
TPJ8zv+Ls2vwTZ2DRrCaL3DS9EObDyasfgP36dH3fUuRX9KbKCPwOstdUgDghX/y
qAPpPu6W1iNc6VRCvCECQQCQp0XaiXCyzWSWYDJCKMX4KFb/1mW6moXI1g8bi+5x
fs0scurgHa2GunZU1M9FrbXx8rMdn4Eiz6XxpVcPmy0l
-----END RSA PRIVATE KEY-----
""";
public static final String CERTIFICATE_1 = """
-----BEGIN CERTIFICATE-----
MIICtjCCAh+gAwIBAgICEAAwDQYJKoZIhvcNAQELBQAwYzELMAkGA1UEBhMCVVMx
EzARBgNVBAgMCkNhbGlmb3JuaWExFjAUBgNVBAcMDU1vdW50YWluIFZpZXcxFTAT
BgNVBAoMDEdvb2dsZSwgSW5jLjEQMA4GA1UECwwHQW5kcm9pZDAeFw0xNjAxMDQx
MjQwNTNaFw0zNTEyMzAxMjQwNTNaMHYxCzAJBgNVBAYTAlVTMRMwEQYDVQQIDApD
YWxpZm9ybmlhMRUwEwYDVQQKDAxHb29nbGUsIEluYy4xEDAOBgNVBAsMB0FuZHJv
aWQxKTAnBgNVBAMMIEFuZHJvaWQgU29mdHdhcmUgQXR0ZXN0YXRpb24gS2V5MIGf
MA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDAgyPcVogbuDAgafWwhWHG7r5/BeL1
qEIEir6LR752/q7yXPKbKvoyABQWAUKZiaFfz8aBXrNjWDwv0vIL5Jgyg92BSxbX
4YVBeuVKvClqOm21wAQIO2jFVsHwIzmRZBmGTVC3TUCuykhMdzVsiVoMJ1q/rEmd
XX0jYvKcXgLocQIDAQABo2YwZDAdBgNVHQ4EFgQU1AwQG/jNY7n3OVK1DhNcpteZ
k4YwHwYDVR0jBBgwFoAUKfrxrMxN0kyWQCd1trDpMuUH/i4wEgYDVR0TAQH/BAgw
BgEB/wIBADAOBgNVHQ8BAf8EBAMCAoQwDQYJKoZIhvcNAQELBQADgYEAni1IX4xn
M9waha2Z11Aj6hTsQ7DhnerCI0YecrUZ3GAi5KVoMWwLVcTmnKItnzpPk2sxixZ4
Fg2Iy9mLzICdhPDCJ+NrOPH90ecXcjFZNX2W88V/q52PlmEmT7K+gbsNSQQiis6f
9/VCLiVE+iEHElqDtVWtGIL4QBSbnCBjBH8=
-----END CERTIFICATE-----
""";
public static final String CERTIFICATE_2 = """
-----BEGIN CERTIFICATE-----
MIICpzCCAhCgAwIBAgIJAP+U2d2fB8gMMA0GCSqGSIb3DQEBCwUAMGMxCzAJBgNV
BAYTAlVTMRMwEQYDVQQIDApDYWxpZm9ybmlhMRYwFAYDVQQHDA1Nb3VudGFpbiBW
aWV3MRUwEwYDVQQKDAxHb29nbGUsIEluYy4xEDAOBgNVBAsMB0FuZHJvaWQwHhcN
MTYwMTA0MTIzMTA4WhcNMzUxMjMwMTIzMTA4WjBjMQswCQYDVQQGEwJVUzETMBEG
A1UECAwKQ2FsaWZvcm5pYTEWMBQGA1UEBwwNTW91bnRhaW4gVmlldzEVMBMGA1UE
CgwMR29vZ2xlLCBJbmMuMRAwDgYDVQQLDAdBbmRyb2lkMIGfMA0GCSqGSIb3DQEB
AQUAA4GNADCBiQKBgQCia63rbi5EYe/VDoLmt5TRdSMfd5tjkWP/96r/C3JHTsAs
Q+wzfNes7UA+jCigZtX3hwszl94OuE4TQKuvpSe/lWmgMdsGUmX4RFlXYfC78hdL
t0GAZMAoDo9Sd47b0ke2RekZyOmLw9vCkT/X11DEHTVm+Vfkl5YLCazOkjWFmwID
AQABo2MwYTAdBgNVHQ4EFgQUKfrxrMxN0kyWQCd1trDpMuUH/i4wHwYDVR0jBBgw
FoAUKfrxrMxN0kyWQCd1trDpMuUH/i4wDwYDVR0TAQH/BAUwAwEB/zAOBgNVHQ8B
Af8EBAMCAoQwDQYJKoZIhvcNAQELBQADgYEAT3LzNlmNDsG5dFsxWfbwjSVJMJ6j
HBwp0kUtILlNX2S06IDHeHqcOd6os/W/L3BfRxBcxebrTQaZYdKumgf/93y4q+uc
DyQHXrF/unlx/U1bnt8Uqf7f7XzAiF343ZtkMlbVNZriE/mPzsF83O+kqrJVw4Op
Lvtc9mL1J1IXvmM=
-----END CERTIFICATE-----
""";
}
}

View File

@ -1,6 +1,7 @@
package es.chiteroman.playintegrityfix;
import android.security.keystore.KeyProperties;
import android.text.TextUtils;
import org.spongycastle.asn1.ASN1Boolean;
import org.spongycastle.asn1.ASN1Encodable;
@ -23,39 +24,84 @@ import org.spongycastle.openssl.jcajce.JcaPEMKeyConverter;
import org.spongycastle.operator.ContentSigner;
import org.spongycastle.operator.jcajce.JcaContentSignerBuilder;
import org.spongycastle.util.io.pem.PemReader;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import java.io.ByteArrayInputStream;
import java.io.StringReader;
import java.security.cert.Certificate;
import java.security.cert.CertificateException;
import java.security.cert.CertificateFactory;
import java.security.cert.X509Certificate;
import java.util.ArrayList;
import java.util.LinkedList;
import java.util.List;
import java.util.concurrent.ThreadLocalRandom;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
public final class Android {
private static final PEMKeyPair EC, RSA;
public final class KeyboxUtils {
private static final ASN1ObjectIdentifier OID = new ASN1ObjectIdentifier("1.3.6.1.4.1.11129.2.1.17");
private static final List<Certificate> EC_CERTS = new ArrayList<>();
private static final List<Certificate> RSA_CERTS = new ArrayList<>();
private static final LinkedList<Certificate> EC_CERTS = new LinkedList<>();
private static final LinkedList<Certificate> RSA_CERTS = new LinkedList<>();
private static final CertificateFactory certificateFactory;
private static PEMKeyPair EC, RSA;
static {
try {
certificateFactory = CertificateFactory.getInstance("X.509");
} catch (CertificateException e) {
throw new RuntimeException(e);
}
}
EC = parseKeyPair(Keybox.EC.PRIVATE_KEY);
EC_CERTS.add(parseCert(Keybox.EC.CERTIFICATE_1));
EC_CERTS.add(parseCert(Keybox.EC.CERTIFICATE_2));
public static void parseXml(String kbox) throws Throwable {
if (TextUtils.isEmpty(kbox)) return;
RSA = parseKeyPair(Keybox.RSA.PRIVATE_KEY);
RSA_CERTS.add(parseCert(Keybox.RSA.CERTIFICATE_1));
RSA_CERTS.add(parseCert(Keybox.RSA.CERTIFICATE_2));
} catch (Throwable t) {
EntryPoint.LOG(t.toString());
throw new RuntimeException(t);
DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
DocumentBuilder builder = factory.newDocumentBuilder();
Document doc = builder.parse(new ByteArrayInputStream(kbox.getBytes()));
doc.getDocumentElement().normalize();
NodeList keyboxList = doc.getElementsByTagName("Keybox");
Node keyboxNode = keyboxList.item(0);
if (keyboxNode.getNodeType() == Node.ELEMENT_NODE) {
Element keyboxElement = (Element) keyboxNode;
NodeList keyList = keyboxElement.getElementsByTagName("Key");
for (int j = 0; j < keyList.getLength(); j++) {
Element keyElement = (Element) keyList.item(j);
String algorithm = keyElement.getAttribute("algorithm");
NodeList privateKeyList = keyElement.getElementsByTagName("PrivateKey");
if (privateKeyList.getLength() > 0) {
Element privateKeyElement = (Element) privateKeyList.item(0);
String privateKeyContent = privateKeyElement.getTextContent().trim();
if ("ecdsa".equals(algorithm)) {
EC = parseKeyPair(privateKeyContent);
} else if ("rsa".equals(algorithm)) {
RSA = parseKeyPair(privateKeyContent);
}
}
NodeList certificateChainList = keyElement.getElementsByTagName("CertificateChain");
if (certificateChainList.getLength() > 0) {
Element certificateChainElement = (Element) certificateChainList.item(0);
NodeList certificateList = certificateChainElement.getElementsByTagName("Certificate");
for (int k = 0; k < certificateList.getLength(); k++) {
Element certificateElement = (Element) certificateList.item(k);
String certificateContent = certificateElement.getTextContent().trim();
if ("ecdsa".equals(algorithm)) {
EC_CERTS.add(parseCert(certificateContent));
} else if ("rsa".equals(algorithm)) {
RSA_CERTS.add(parseCert(certificateContent));
}
}
}
}
}
}
@ -72,7 +118,18 @@ public final class Android {
}
public static Certificate[] engineGetCertificateChain(Certificate[] caList) {
if (caList == null) throw new UnsupportedOperationException();
if (caList == null) {
EntryPoint.LOG("Certificate chain is null!");
throw new UnsupportedOperationException();
}
if (EC == null && RSA == null) {
EntryPoint.LOG("EC and RSA private keys are null!");
throw new UnsupportedOperationException();
}
if (EC_CERTS.isEmpty() && RSA_CERTS.isEmpty()) {
EntryPoint.LOG("EC and RSA certs are empty!");
throw new UnsupportedOperationException();
}
try {
X509Certificate leaf = (X509Certificate) certificateFactory.generateCertificate(new ByteArrayInputStream(caList[0].getEncoded()));
@ -103,14 +160,17 @@ public final class Android {
X509v3CertificateBuilder builder;
ContentSigner signer;
if (KeyProperties.KEY_ALGORITHM_EC.equals(leaf.getPublicKey().getAlgorithm())) {
// Not all keyboxes have EC keys :)
if (EC != null && !EC_CERTS.isEmpty() && KeyProperties.KEY_ALGORITHM_EC.equals(leaf.getPublicKey().getAlgorithm())) {
EntryPoint.LOG("Using EC");
certificates = new LinkedList<>(EC_CERTS);
builder = new X509v3CertificateBuilder(new X509CertificateHolder(EC_CERTS.get(0).getEncoded()).getSubject(), holder.getSerialNumber(), holder.getNotBefore(), holder.getNotAfter(), holder.getSubject(), EC.getPublicKeyInfo());
signer = new JcaContentSignerBuilder(leaf.getSigAlgName()).build(new JcaPEMKeyConverter().getPrivateKey(EC.getPrivateKeyInfo()));
} else {
EntryPoint.LOG("Using RSA");
certificates = new LinkedList<>(RSA_CERTS);
builder = new X509v3CertificateBuilder(new X509CertificateHolder(RSA_CERTS.get(0).getEncoded()).getSubject(), holder.getSerialNumber(), holder.getNotBefore(), holder.getNotAfter(), holder.getSubject(), RSA.getPublicKeyInfo());
signer = new JcaContentSignerBuilder(leaf.getSigAlgName()).build(new JcaPEMKeyConverter().getPrivateKey(RSA.getPrivateKeyInfo()));
signer = new JcaContentSignerBuilder("SHA256withRSA").build(new JcaPEMKeyConverter().getPrivateKey(RSA.getPrivateKeyInfo()));
}
byte[] verifiedBootKey = new byte[32];
@ -151,6 +211,6 @@ public final class Android {
} catch (Throwable t) {
EntryPoint.LOG(t.toString());
}
return caList;
throw new UnsupportedOperationException();
}
}

View File

@ -7,6 +7,16 @@ If not, try removing /data/adb/pif.json file.
Donations:
https://www.paypal.com/paypalme/chiteroman
# v16.0
# v16.3
- WORKING TEE = STRONG PASS
Google fixed the bug, no more Strong pass with SW keybox 😢
- Improve C++ and Java code
- Downgrade first_api_level to 24, so all devices (should) be able to pass Device
- Included keybox.xml parsing! You can create /data/adb/keybox.xml to define your own keybox (Strong passing with my private one :D)
By default, inside module folder, it exits pif.json and keybox.xml, do NOT delete these files
keybox.xml included in the module is SW one
Keybox "hack" does NOT work on broken TEE devices, like OnePlus

114
module/keybox.xml Normal file
View File

@ -0,0 +1,114 @@
<?xml version="1.0"?>
<AndroidAttestation>
<NumberOfKeyboxes>1</NumberOfKeyboxes>
<Keybox DeviceID="sw">
<Key algorithm="ecdsa">
<PrivateKey format="pem">
-----BEGIN EC PRIVATE KEY-----
MHcCAQEEICHghkMqFRmEWc82OlD8FMnarfk19SfC39ceTW28QuVEoAoGCCqGSM49
AwEHoUQDQgAE6555+EJjWazLKpFMiYbMcK2QZpOCqXMmE/6sy/ghJ0whdJdKKv6l
uU1/ZtTgZRBmNbxTt6CjpnFYPts+Ea4QFA==
-----END EC PRIVATE KEY-----
</PrivateKey>
<CertificateChain>
<NumberOfCertificates>2</NumberOfCertificates>
<Certificate format="pem">
-----BEGIN CERTIFICATE-----
MIICeDCCAh6gAwIBAgICEAEwCgYIKoZIzj0EAwIwgZgxCzAJBgNVBAYTAlVTMRMw
EQYDVQQIDApDYWxpZm9ybmlhMRYwFAYDVQQHDA1Nb3VudGFpbiBWaWV3MRUwEwYD
VQQKDAxHb29nbGUsIEluYy4xEDAOBgNVBAsMB0FuZHJvaWQxMzAxBgNVBAMMKkFu
ZHJvaWQgS2V5c3RvcmUgU29mdHdhcmUgQXR0ZXN0YXRpb24gUm9vdDAeFw0xNjAx
MTEwMDQ2MDlaFw0yNjAxMDgwMDQ2MDlaMIGIMQswCQYDVQQGEwJVUzETMBEGA1UE
CAwKQ2FsaWZvcm5pYTEVMBMGA1UECgwMR29vZ2xlLCBJbmMuMRAwDgYDVQQLDAdB
bmRyb2lkMTswOQYDVQQDDDJBbmRyb2lkIEtleXN0b3JlIFNvZnR3YXJlIEF0dGVz
dGF0aW9uIEludGVybWVkaWF0ZTBZMBMGByqGSM49AgEGCCqGSM49AwEHA0IABOue
efhCY1msyyqRTImGzHCtkGaTgqlzJhP+rMv4ISdMIXSXSir+pblNf2bU4GUQZjW8
U7ego6ZxWD7bPhGuEBSjZjBkMB0GA1UdDgQWBBQ//KzWGrE6noEguNUlHMVlux6R
qTAfBgNVHSMEGDAWgBTIrel3TEXDo88NFhDkeUM6IVowzzASBgNVHRMBAf8ECDAG
AQH/AgEAMA4GA1UdDwEB/wQEAwIChDAKBggqhkjOPQQDAgNIADBFAiBLipt77oK8
wDOHri/AiZi03cONqycqRZ9pDMfDktQPjgIhAO7aAV229DLp1IQ7YkyUBO86fMy9
Xvsiu+f+uXc/WT/7
-----END CERTIFICATE-----
</Certificate>
<Certificate format="pem">
-----BEGIN CERTIFICATE-----
MIICizCCAjKgAwIBAgIJAKIFntEOQ1tXMAoGCCqGSM49BAMCMIGYMQswCQYDVQQG
EwJVUzETMBEGA1UECAwKQ2FsaWZvcm5pYTEWMBQGA1UEBwwNTW91bnRhaW4gVmll
dzEVMBMGA1UECgwMR29vZ2xlLCBJbmMuMRAwDgYDVQQLDAdBbmRyb2lkMTMwMQYD
VQQDDCpBbmRyb2lkIEtleXN0b3JlIFNvZnR3YXJlIEF0dGVzdGF0aW9uIFJvb3Qw
HhcNMTYwMTExMDA0MzUwWhcNMzYwMTA2MDA0MzUwWjCBmDELMAkGA1UEBhMCVVMx
EzARBgNVBAgMCkNhbGlmb3JuaWExFjAUBgNVBAcMDU1vdW50YWluIFZpZXcxFTAT
BgNVBAoMDEdvb2dsZSwgSW5jLjEQMA4GA1UECwwHQW5kcm9pZDEzMDEGA1UEAwwq
QW5kcm9pZCBLZXlzdG9yZSBTb2Z0d2FyZSBBdHRlc3RhdGlvbiBSb290MFkwEwYH
KoZIzj0CAQYIKoZIzj0DAQcDQgAE7l1ex+HA220Dpn7mthvsTWpdamguD/9/SQ59
dx9EIm29sa/6FsvHrcV30lacqrewLVQBXT5DKyqO107sSHVBpKNjMGEwHQYDVR0O
BBYEFMit6XdMRcOjzw0WEOR5QzohWjDPMB8GA1UdIwQYMBaAFMit6XdMRcOjzw0W
EOR5QzohWjDPMA8GA1UdEwEB/wQFMAMBAf8wDgYDVR0PAQH/BAQDAgKEMAoGCCqG
SM49BAMCA0cAMEQCIDUho++LNEYenNVg8x1YiSBq3KNlQfYNns6KGYxmSGB7AiBN
C/NR2TB8fVvaNTQdqEcbY6WFZTytTySn502vQX3xvw==
-----END CERTIFICATE-----
</Certificate>
</CertificateChain>
</Key>
<Key algorithm="rsa">
<PrivateKey format="pem">
-----BEGIN RSA PRIVATE KEY-----
MIICXQIBAAKBgQDAgyPcVogbuDAgafWwhWHG7r5/BeL1qEIEir6LR752/q7yXPKb
KvoyABQWAUKZiaFfz8aBXrNjWDwv0vIL5Jgyg92BSxbX4YVBeuVKvClqOm21wAQI
O2jFVsHwIzmRZBmGTVC3TUCuykhMdzVsiVoMJ1q/rEmdXX0jYvKcXgLocQIDAQAB
AoGBAL6GCwuZqAKm+xpZQ4p7txUGWwmjbcbpysxr88AsNNfXnpTGYGQo2Ix7f2V3
wc3qZAdKvo5yht8fCBHclygmCGjeldMu/Ja20IT/JxpfYN78xwPno45uKbqaPF/C
woB2tqiWrx0014gozpvdsfNPnJQEQweBKY4gExZyW728mTpBAkEA4cbZJ2RsCRbs
NoJtWUmDdAwh8bB0xKGlmGfGaXlchdPcRkxbkp6Uv7NODcxQFLEPEzQat/3V9gQU
0qMmytQcxQJBANpIWZd4XNVjD7D9jFJU+Y5TjhiYOq6ea35qWntdNDdVuSGOvUAy
DSg4fXifdvohi8wti2il9kGPu+ylF5qzr70CQFD+/DJklVlhbtZTThVFCTKdk6PY
ENvlvbmCKSz3i9i624Agro1X9LcdBThv/p6dsnHKNHejSZnbdvjl7OnA1J0CQBW3
TPJ8zv+Ls2vwTZ2DRrCaL3DS9EObDyasfgP36dH3fUuRX9KbKCPwOstdUgDghX/y
qAPpPu6W1iNc6VRCvCECQQCQp0XaiXCyzWSWYDJCKMX4KFb/1mW6moXI1g8bi+5x
fs0scurgHa2GunZU1M9FrbXx8rMdn4Eiz6XxpVcPmy0l
-----END RSA PRIVATE KEY-----
</PrivateKey>
<CertificateChain>
<NumberOfCertificates>2</NumberOfCertificates>
<Certificate format="pem">
-----BEGIN CERTIFICATE-----
MIICtjCCAh+gAwIBAgICEAAwDQYJKoZIhvcNAQELBQAwYzELMAkGA1UEBhMCVVMx
EzARBgNVBAgMCkNhbGlmb3JuaWExFjAUBgNVBAcMDU1vdW50YWluIFZpZXcxFTAT
BgNVBAoMDEdvb2dsZSwgSW5jLjEQMA4GA1UECwwHQW5kcm9pZDAeFw0xNjAxMDQx
MjQwNTNaFw0zNTEyMzAxMjQwNTNaMHYxCzAJBgNVBAYTAlVTMRMwEQYDVQQIDApD
YWxpZm9ybmlhMRUwEwYDVQQKDAxHb29nbGUsIEluYy4xEDAOBgNVBAsMB0FuZHJv
aWQxKTAnBgNVBAMMIEFuZHJvaWQgU29mdHdhcmUgQXR0ZXN0YXRpb24gS2V5MIGf
MA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDAgyPcVogbuDAgafWwhWHG7r5/BeL1
qEIEir6LR752/q7yXPKbKvoyABQWAUKZiaFfz8aBXrNjWDwv0vIL5Jgyg92BSxbX
4YVBeuVKvClqOm21wAQIO2jFVsHwIzmRZBmGTVC3TUCuykhMdzVsiVoMJ1q/rEmd
XX0jYvKcXgLocQIDAQABo2YwZDAdBgNVHQ4EFgQU1AwQG/jNY7n3OVK1DhNcpteZ
k4YwHwYDVR0jBBgwFoAUKfrxrMxN0kyWQCd1trDpMuUH/i4wEgYDVR0TAQH/BAgw
BgEB/wIBADAOBgNVHQ8BAf8EBAMCAoQwDQYJKoZIhvcNAQELBQADgYEAni1IX4xn
M9waha2Z11Aj6hTsQ7DhnerCI0YecrUZ3GAi5KVoMWwLVcTmnKItnzpPk2sxixZ4
Fg2Iy9mLzICdhPDCJ+NrOPH90ecXcjFZNX2W88V/q52PlmEmT7K+gbsNSQQiis6f
9/VCLiVE+iEHElqDtVWtGIL4QBSbnCBjBH8=
-----END CERTIFICATE-----
</Certificate>
<Certificate format="pem">
-----BEGIN CERTIFICATE-----
MIICpzCCAhCgAwIBAgIJAP+U2d2fB8gMMA0GCSqGSIb3DQEBCwUAMGMxCzAJBgNV
BAYTAlVTMRMwEQYDVQQIDApDYWxpZm9ybmlhMRYwFAYDVQQHDA1Nb3VudGFpbiBW
aWV3MRUwEwYDVQQKDAxHb29nbGUsIEluYy4xEDAOBgNVBAsMB0FuZHJvaWQwHhcN
MTYwMTA0MTIzMTA4WhcNMzUxMjMwMTIzMTA4WjBjMQswCQYDVQQGEwJVUzETMBEG
A1UECAwKQ2FsaWZvcm5pYTEWMBQGA1UEBwwNTW91bnRhaW4gVmlldzEVMBMGA1UE
CgwMR29vZ2xlLCBJbmMuMRAwDgYDVQQLDAdBbmRyb2lkMIGfMA0GCSqGSIb3DQEB
AQUAA4GNADCBiQKBgQCia63rbi5EYe/VDoLmt5TRdSMfd5tjkWP/96r/C3JHTsAs
Q+wzfNes7UA+jCigZtX3hwszl94OuE4TQKuvpSe/lWmgMdsGUmX4RFlXYfC78hdL
t0GAZMAoDo9Sd47b0ke2RekZyOmLw9vCkT/X11DEHTVm+Vfkl5YLCazOkjWFmwID
AQABo2MwYTAdBgNVHQ4EFgQUKfrxrMxN0kyWQCd1trDpMuUH/i4wHwYDVR0jBBgw
FoAUKfrxrMxN0kyWQCd1trDpMuUH/i4wDwYDVR0TAQH/BAUwAwEB/zAOBgNVHQ8B
Af8EBAMCAoQwDQYJKoZIhvcNAQELBQADgYEAT3LzNlmNDsG5dFsxWfbwjSVJMJ6j
HBwp0kUtILlNX2S06IDHeHqcOd6os/W/L3BfRxBcxebrTQaZYdKumgf/93y4q+uc
DyQHXrF/unlx/U1bnt8Uqf7f7XzAiF343ZtkMlbVNZriE/mPzsF83O+kqrJVw4Op
Lvtc9mL1J1IXvmM=
-----END CERTIFICATE-----
</Certificate>
</CertificateChain>
</Key>
</Keybox>
</AndroidAttestation>

View File

@ -1,7 +1,7 @@
id=playintegrityfix
name=Play Integrity Fix (STRONG)
version=v16.0
versionCode=16000
name=Play Integrity Fix
version=v16.3
versionCode=16300
author=chiteroman
description=WORKING TEE = STRONG PASS | THANKS GOOGLE
description=Universal modular fix for Play Integrity (and SafetyNet) on devices running Android 8-15
updateJson=https://raw.githubusercontent.com/chiteroman/PlayIntegrityFix/main/update.json

View File

@ -11,5 +11,5 @@
"TYPE": "user",
"TAGS": "release-keys",
"SECURITY_PATCH": "2017-12-05",
"DEVICE_INITIAL_SDK_INT": "25"
"DEVICE_INITIAL_SDK_INT": "24"
}

View File

@ -1,6 +1,6 @@
{
"version": "v16.0",
"versionCode": 16000,
"zipUrl": "https://github.com/chiteroman/PlayIntegrityFix/releases/download/v16.0/PlayIntegrityFix_v16.0.zip",
"version": "v16.3",
"versionCode": 16300,
"zipUrl": "https://github.com/chiteroman/PlayIntegrityFix/releases/download/v16.3/PlayIntegrityFix_v16.3.zip",
"changelog": "https://raw.githubusercontent.com/chiteroman/PlayIntegrityFix/main/changelog.md"
}