This commit is contained in:
chiteroman 2024-06-27 02:07:05 +02:00
parent fdfa3ee793
commit 8850f447a1
15 changed files with 375 additions and 13408 deletions

263
.idea/other.xml Normal file
View File

@ -0,0 +1,263 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="direct_access_persist.xml">
<option name="deviceSelectionList">
<list>
<PersistentDeviceSelectionData>
<option name="api" value="27" />
<option name="brand" value="DOCOMO" />
<option name="codename" value="F01L" />
<option name="id" value="F01L" />
<option name="manufacturer" value="FUJITSU" />
<option name="name" value="F-01L" />
<option name="screenDensity" value="360" />
<option name="screenX" value="720" />
<option name="screenY" value="1280" />
</PersistentDeviceSelectionData>
<PersistentDeviceSelectionData>
<option name="api" value="28" />
<option name="brand" value="DOCOMO" />
<option name="codename" value="SH-01L" />
<option name="id" value="SH-01L" />
<option name="manufacturer" value="SHARP" />
<option name="name" value="AQUOS sense2 SH-01L" />
<option name="screenDensity" value="480" />
<option name="screenX" value="1080" />
<option name="screenY" value="2160" />
</PersistentDeviceSelectionData>
<PersistentDeviceSelectionData>
<option name="api" value="31" />
<option name="brand" value="samsung" />
<option name="codename" value="a51" />
<option name="id" value="a51" />
<option name="manufacturer" value="Samsung" />
<option name="name" value="Galaxy A51" />
<option name="screenDensity" value="420" />
<option name="screenX" value="1080" />
<option name="screenY" value="2400" />
</PersistentDeviceSelectionData>
<PersistentDeviceSelectionData>
<option name="api" value="34" />
<option name="brand" value="google" />
<option name="codename" value="akita" />
<option name="id" value="akita" />
<option name="manufacturer" value="Google" />
<option name="name" value="Pixel 8a" />
<option name="screenDensity" value="420" />
<option name="screenX" value="1080" />
<option name="screenY" value="2400" />
</PersistentDeviceSelectionData>
<PersistentDeviceSelectionData>
<option name="api" value="33" />
<option name="brand" value="samsung" />
<option name="codename" value="b0q" />
<option name="id" value="b0q" />
<option name="manufacturer" value="Samsung" />
<option name="name" value="Galaxy S22 Ultra" />
<option name="screenDensity" value="600" />
<option name="screenX" value="1440" />
<option name="screenY" value="3088" />
</PersistentDeviceSelectionData>
<PersistentDeviceSelectionData>
<option name="api" value="32" />
<option name="brand" value="google" />
<option name="codename" value="bluejay" />
<option name="id" value="bluejay" />
<option name="manufacturer" value="Google" />
<option name="name" value="Pixel 6a" />
<option name="screenDensity" value="420" />
<option name="screenX" value="1080" />
<option name="screenY" value="2400" />
</PersistentDeviceSelectionData>
<PersistentDeviceSelectionData>
<option name="api" value="29" />
<option name="brand" value="samsung" />
<option name="codename" value="crownqlteue" />
<option name="id" value="crownqlteue" />
<option name="manufacturer" value="Samsung" />
<option name="name" value="Galaxy Note9" />
<option name="screenDensity" value="420" />
<option name="screenX" value="2220" />
<option name="screenY" value="1080" />
</PersistentDeviceSelectionData>
<PersistentDeviceSelectionData>
<option name="api" value="34" />
<option name="brand" value="samsung" />
<option name="codename" value="dm3q" />
<option name="id" value="dm3q" />
<option name="manufacturer" value="Samsung" />
<option name="name" value="Galaxy S23 Ultra" />
<option name="screenDensity" value="600" />
<option name="screenX" value="1440" />
<option name="screenY" value="3088" />
</PersistentDeviceSelectionData>
<PersistentDeviceSelectionData>
<option name="api" value="33" />
<option name="brand" value="google" />
<option name="codename" value="felix" />
<option name="id" value="felix" />
<option name="manufacturer" value="Google" />
<option name="name" value="Pixel Fold" />
<option name="screenDensity" value="420" />
<option name="screenX" value="2208" />
<option name="screenY" value="1840" />
</PersistentDeviceSelectionData>
<PersistentDeviceSelectionData>
<option name="api" value="33" />
<option name="brand" value="google" />
<option name="codename" value="felix_camera" />
<option name="id" value="felix_camera" />
<option name="manufacturer" value="Google" />
<option name="name" value="Pixel Fold (Camera-enabled)" />
<option name="screenDensity" value="420" />
<option name="screenX" value="2208" />
<option name="screenY" value="1840" />
</PersistentDeviceSelectionData>
<PersistentDeviceSelectionData>
<option name="api" value="33" />
<option name="brand" value="samsung" />
<option name="codename" value="gts8uwifi" />
<option name="id" value="gts8uwifi" />
<option name="manufacturer" value="Samsung" />
<option name="name" value="Galaxy Tab S8 Ultra" />
<option name="screenDensity" value="320" />
<option name="screenX" value="1848" />
<option name="screenY" value="2960" />
</PersistentDeviceSelectionData>
<PersistentDeviceSelectionData>
<option name="api" value="34" />
<option name="brand" value="google" />
<option name="codename" value="husky" />
<option name="id" value="husky" />
<option name="manufacturer" value="Google" />
<option name="name" value="Pixel 8 Pro" />
<option name="screenDensity" value="390" />
<option name="screenX" value="1008" />
<option name="screenY" value="2244" />
</PersistentDeviceSelectionData>
<PersistentDeviceSelectionData>
<option name="api" value="30" />
<option name="brand" value="motorola" />
<option name="codename" value="java" />
<option name="id" value="java" />
<option name="manufacturer" value="Motorola" />
<option name="name" value="G20" />
<option name="screenDensity" value="280" />
<option name="screenX" value="720" />
<option name="screenY" value="1600" />
</PersistentDeviceSelectionData>
<PersistentDeviceSelectionData>
<option name="api" value="33" />
<option name="brand" value="google" />
<option name="codename" value="lynx" />
<option name="id" value="lynx" />
<option name="manufacturer" value="Google" />
<option name="name" value="Pixel 7a" />
<option name="screenDensity" value="420" />
<option name="screenX" value="1080" />
<option name="screenY" value="2400" />
</PersistentDeviceSelectionData>
<PersistentDeviceSelectionData>
<option name="api" value="31" />
<option name="brand" value="google" />
<option name="codename" value="oriole" />
<option name="id" value="oriole" />
<option name="manufacturer" value="Google" />
<option name="name" value="Pixel 6" />
<option name="screenDensity" value="420" />
<option name="screenX" value="1080" />
<option name="screenY" value="2400" />
</PersistentDeviceSelectionData>
<PersistentDeviceSelectionData>
<option name="api" value="33" />
<option name="brand" value="google" />
<option name="codename" value="panther" />
<option name="id" value="panther" />
<option name="manufacturer" value="Google" />
<option name="name" value="Pixel 7" />
<option name="screenDensity" value="420" />
<option name="screenX" value="1080" />
<option name="screenY" value="2400" />
</PersistentDeviceSelectionData>
<PersistentDeviceSelectionData>
<option name="api" value="31" />
<option name="brand" value="samsung" />
<option name="codename" value="q2q" />
<option name="id" value="q2q" />
<option name="manufacturer" value="Samsung" />
<option name="name" value="Galaxy Z Fold3" />
<option name="screenDensity" value="420" />
<option name="screenX" value="1768" />
<option name="screenY" value="2208" />
</PersistentDeviceSelectionData>
<PersistentDeviceSelectionData>
<option name="api" value="34" />
<option name="brand" value="samsung" />
<option name="codename" value="q5q" />
<option name="id" value="q5q" />
<option name="manufacturer" value="Samsung" />
<option name="name" value="Galaxy Z Fold5" />
<option name="screenDensity" value="420" />
<option name="screenX" value="1812" />
<option name="screenY" value="2176" />
</PersistentDeviceSelectionData>
<PersistentDeviceSelectionData>
<option name="api" value="30" />
<option name="brand" value="google" />
<option name="codename" value="r11" />
<option name="id" value="r11" />
<option name="manufacturer" value="Google" />
<option name="name" value="Pixel Watch" />
<option name="screenDensity" value="320" />
<option name="screenX" value="384" />
<option name="screenY" value="384" />
<option name="type" value="WEAR_OS" />
</PersistentDeviceSelectionData>
<PersistentDeviceSelectionData>
<option name="api" value="30" />
<option name="brand" value="google" />
<option name="codename" value="redfin" />
<option name="id" value="redfin" />
<option name="manufacturer" value="Google" />
<option name="name" value="Pixel 5" />
<option name="screenDensity" value="440" />
<option name="screenX" value="1080" />
<option name="screenY" value="2340" />
</PersistentDeviceSelectionData>
<PersistentDeviceSelectionData>
<option name="api" value="34" />
<option name="brand" value="google" />
<option name="codename" value="shiba" />
<option name="id" value="shiba" />
<option name="manufacturer" value="Google" />
<option name="name" value="Pixel 8" />
<option name="screenDensity" value="420" />
<option name="screenX" value="1080" />
<option name="screenY" value="2400" />
</PersistentDeviceSelectionData>
<PersistentDeviceSelectionData>
<option name="api" value="33" />
<option name="brand" value="google" />
<option name="codename" value="tangorpro" />
<option name="id" value="tangorpro" />
<option name="manufacturer" value="Google" />
<option name="name" value="Pixel Tablet" />
<option name="screenDensity" value="320" />
<option name="screenX" value="1600" />
<option name="screenY" value="2560" />
</PersistentDeviceSelectionData>
<PersistentDeviceSelectionData>
<option name="api" value="29" />
<option name="brand" value="samsung" />
<option name="codename" value="x1q" />
<option name="id" value="x1q" />
<option name="manufacturer" value="Samsung" />
<option name="name" value="Galaxy S20" />
<option name="screenDensity" value="480" />
<option name="screenX" value="1440" />
<option name="screenY" value="3200" />
</PersistentDeviceSelectionData>
</list>
</option>
</component>
</project>

View File

@ -16,11 +16,14 @@ android {
applicationId = "es.chiteroman.playintegrityfix" applicationId = "es.chiteroman.playintegrityfix"
minSdk = 26 minSdk = 26
targetSdk = 34 targetSdk = 34
versionCode = 16300 versionCode = 16400
versionName = "v16.3" versionName = "v16.4"
multiDexEnabled = false multiDexEnabled = false
packaging { packaging {
resources {
excludes += "META-INF/**"
}
jniLibs { jniLibs {
excludes += "**/liblog.so" excludes += "**/liblog.so"
excludes += "**/libdobby.so" excludes += "**/libdobby.so"
@ -33,7 +36,7 @@ android {
arguments += "-DCMAKE_BUILD_TYPE=MinSizeRel" arguments += "-DCMAKE_BUILD_TYPE=MinSizeRel"
arguments += "-DPlugin.Android.BionicLinkerUtil=ON" arguments += "-DPlugin.Android.BionicLinkerUtil=ON"
cppFlags += "-std=c++20" cppFlags += "-std=c++2b"
cppFlags += "-fno-exceptions" cppFlags += "-fno-exceptions"
cppFlags += "-fno-rtti" cppFlags += "-fno-rtti"
cppFlags += "-fvisibility=hidden" cppFlags += "-fvisibility=hidden"
@ -47,7 +50,9 @@ android {
isMinifyEnabled = true isMinifyEnabled = true
isShrinkResources = true isShrinkResources = true
multiDexEnabled = false multiDexEnabled = false
proguardFiles(getDefaultProguardFile("proguard-android-optimize.txt"), "proguard-rules.pro") proguardFiles(
getDefaultProguardFile("proguard-android-optimize.txt"), "proguard-rules.pro"
)
} }
} }
@ -91,8 +96,10 @@ tasks.register("copyFiles") {
doLast { doLast {
val moduleFolder = project.rootDir.resolve("module") val moduleFolder = project.rootDir.resolve("module")
val dexFile = project.layout.buildDirectory.get().asFile.resolve("intermediates/dex/release/minifyReleaseWithR8/classes.dex") val dexFile =
val soDir = project.layout.buildDirectory.get().asFile.resolve("intermediates/stripped_native_libs/release/stripReleaseDebugSymbols/out/lib") 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)

View File

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

File diff suppressed because it is too large Load Diff

View File

@ -7,6 +7,7 @@
#include "zygisk.hpp" #include "zygisk.hpp"
#define LOGD(...) __android_log_print(ANDROID_LOG_DEBUG, "PIF", __VA_ARGS__) #define LOGD(...) __android_log_print(ANDROID_LOG_DEBUG, "PIF", __VA_ARGS__)
#define LOGE(...) __android_log_print(ANDROID_LOG_ERROR, "PIF", __VA_ARGS__)
#define DEX_PATH "/data/adb/modules/playintegrityfix/classes.dex" #define DEX_PATH "/data/adb/modules/playintegrityfix/classes.dex"
@ -14,10 +15,6 @@
#define PIF_JSON_DEFAULT "/data/adb/modules/playintegrityfix/pif.json" #define PIF_JSON_DEFAULT "/data/adb/modules/playintegrityfix/pif.json"
#define KEYBOX_JSON "/data/adb/keybox.xml"
#define KEYBOX_JSON_DEFAULT "/data/adb/modules/playintegrityfix/keybox.xml"
static ssize_t xread(int fd, void *buffer, size_t count) { static ssize_t xread(int fd, void *buffer, size_t count) {
ssize_t total = 0; ssize_t total = 0;
char *buf = (char *) buffer; char *buf = (char *) buffer;
@ -56,7 +53,7 @@ static void modify_callback(void *cookie, const char *name, const char *value, u
std::string_view prop(name); std::string_view prop(name);
if (prop.ends_with("first_api_level") && !DEVICE_INITIAL_SDK_INT.empty()) { if (prop.ends_with("first_api_level")) {
value = DEVICE_INITIAL_SDK_INT.c_str(); value = DEVICE_INITIAL_SDK_INT.c_str();
LOGD("[%s]: %s", name, value); LOGD("[%s]: %s", name, value);
} }
@ -76,6 +73,7 @@ my_system_property_read_callback(const prop_info *pi, T_Callback callback, void
} }
static void doHook() { static void doHook() {
void *handle = DobbySymbolResolver(nullptr, "__system_property_read_callback"); void *handle = DobbySymbolResolver(nullptr, "__system_property_read_callback");
if (handle == nullptr) { if (handle == nullptr) {
LOGD("Couldn't hook __system_property_read_callback"); LOGD("Couldn't hook __system_property_read_callback");
@ -136,38 +134,32 @@ public:
int fd = api->connectCompanion(); int fd = api->connectCompanion();
int dexSize = 0, jsonSize = 0, keyboxSize = 0; int dexSize = 0, jsonSize = 0;
std::vector<char> jsonVector, keyboxVector; std::vector<char> jsonVector;
xread(fd, &dexSize, sizeof(int)); xread(fd, &dexSize, sizeof(int));
xread(fd, &jsonSize, sizeof(int)); xread(fd, &jsonSize, sizeof(int));
xread(fd, &keyboxSize, sizeof(int));
dexVector.resize(dexSize); dexVector.resize(dexSize);
xread(fd, dexVector.data(), dexSize); xread(fd, dexVector.data(), dexSize);
if (jsonSize > 0) {
jsonVector.resize(jsonSize); jsonVector.resize(jsonSize);
xread(fd, jsonVector.data(), jsonSize); xread(fd, jsonVector.data(), jsonSize);
json = nlohmann::json::parse(jsonVector, nullptr, false, true);
keyboxVector.resize(keyboxSize); }
xread(fd, keyboxVector.data(), keyboxSize);
close(fd); close(fd);
json = nlohmann::json::parse(jsonVector, nullptr, false, true); LOGD("Dex file size: %d", dexSize);
LOGD("Json file size: %d", jsonSize);
keyboxString = std::string(keyboxVector.cbegin(), keyboxVector.cend());
} }
void postAppSpecialize(const zygisk::AppSpecializeArgs *args) override { void postAppSpecialize(const zygisk::AppSpecializeArgs *args) override {
if (dexVector.empty() || json.empty()) return; if (dexVector.empty()) return;
if (json.contains("DEVICE_INITIAL_SDK_INT")) { if (needHook()) doHook();
DEVICE_INITIAL_SDK_INT = json["DEVICE_INITIAL_SDK_INT"].get<std::string>(); else api->setOption(zygisk::DLCLOSE_MODULE_LIBRARY);
json.erase("DEVICE_INITIAL_SDK_INT"); // You can't modify field value
}
doHook();
injectDex(); injectDex();
} }
@ -181,7 +173,25 @@ private:
JNIEnv *env = nullptr; JNIEnv *env = nullptr;
std::vector<char> dexVector; std::vector<char> dexVector;
nlohmann::json json; nlohmann::json json;
std::string keyboxString;
bool needHook() {
if (json.contains("DEVICE_INITIAL_SDK_INT")) {
if (json["DEVICE_INITIAL_SDK_INT"].is_string()) {
DEVICE_INITIAL_SDK_INT = json["DEVICE_INITIAL_SDK_INT"].get<std::string>();
} else if (json["DEVICE_INITIAL_SDK_INT"].is_number_integer()) {
DEVICE_INITIAL_SDK_INT = std::to_string(json["DEVICE_INITIAL_SDK_INT"].get<int>());
} else {
LOGE("Couldn't parse DEVICE_INITIAL_SDK_INT from JSON file!");
return false;
}
// Value can't be modified, it's marked as SystemApi field
json.erase("DEVICE_INITIAL_SDK_INT");
return !DEVICE_INITIAL_SDK_INT.empty();
}
return false;
}
void injectDex() { void injectDex() {
LOGD("get system classloader"); LOGD("get system classloader");
@ -194,7 +204,8 @@ private:
auto dexClClass = env->FindClass("dalvik/system/InMemoryDexClassLoader"); auto dexClClass = env->FindClass("dalvik/system/InMemoryDexClassLoader");
auto dexClInit = env->GetMethodID(dexClClass, "<init>", auto dexClInit = env->GetMethodID(dexClClass, "<init>",
"(Ljava/nio/ByteBuffer;Ljava/lang/ClassLoader;)V"); "(Ljava/nio/ByteBuffer;Ljava/lang/ClassLoader;)V");
auto buffer = env->NewDirectByteBuffer(dexVector.data(), dexVector.size()); auto buffer = env->NewDirectByteBuffer(dexVector.data(),
static_cast<jlong>(dexVector.size()));
auto dexCl = env->NewObject(dexClClass, dexClInit, buffer, systemClassLoader); auto dexCl = env->NewObject(dexClClass, dexClInit, buffer, systemClassLoader);
LOGD("load class"); LOGD("load class");
@ -206,11 +217,9 @@ private:
auto entryPointClass = (jclass) entryClassObj; auto entryPointClass = (jclass) entryClassObj;
LOGD("call init"); LOGD("call init");
auto entryInit = env->GetStaticMethodID(entryPointClass, "init", auto entryInit = env->GetStaticMethodID(entryPointClass, "init", "(Ljava/lang/String;)V");
"(Ljava/lang/String;Ljava/lang/String;)V");
auto jsonStr = env->NewStringUTF(json.dump().c_str()); auto jsonStr = env->NewStringUTF(json.dump().c_str());
auto keyboxStr = env->NewStringUTF(keyboxString.c_str()); env->CallStaticVoidMethod(entryPointClass, entryInit, jsonStr);
env->CallStaticVoidMethod(entryPointClass, entryInit, jsonStr, keyboxStr);
} }
}; };
@ -218,12 +227,9 @@ static std::vector<char> readFile(const std::string &path) {
std::ifstream ifs(path); std::ifstream ifs(path);
if (!ifs || ifs.bad()) { if (!ifs || ifs.bad()) return {};
return std::vector<char>();
}
return std::vector<char>((std::istreambuf_iterator<char>(ifs)), return {(std::istreambuf_iterator<char>(ifs)), std::istreambuf_iterator<char>()};
std::istreambuf_iterator<char>());
} }
static void companion(int fd) { static void companion(int fd) {
@ -233,20 +239,17 @@ static void companion(int fd) {
auto json = readFile(PIF_JSON); auto json = readFile(PIF_JSON);
if (json.empty()) json = readFile(PIF_JSON_DEFAULT); if (json.empty()) json = readFile(PIF_JSON_DEFAULT);
auto keybox = readFile(KEYBOX_JSON); int dexSize = static_cast<int>(dex.size());
if (keybox.empty()) keybox = readFile(KEYBOX_JSON_DEFAULT); int jsonSize = static_cast<int>(json.size());
int dexSize = dex.size();
int jsonSize = json.size();
int keyboxSize = keybox.size();
xwrite(fd, &dexSize, sizeof(int)); xwrite(fd, &dexSize, sizeof(int));
xwrite(fd, &jsonSize, sizeof(int)); xwrite(fd, &jsonSize, sizeof(int));
xwrite(fd, &keyboxSize, sizeof(int));
xwrite(fd, dex.data(), dexSize * sizeof(char)); xwrite(fd, dex.data(), dexSize * sizeof(char));
if (jsonSize > 0) {
xwrite(fd, json.data(), jsonSize * sizeof(char)); xwrite(fd, json.data(), jsonSize * sizeof(char));
xwrite(fd, keybox.data(), keyboxSize * sizeof(char)); }
} }
REGISTER_ZYGISK_MODULE(PlayIntegrityFix) REGISTER_ZYGISK_MODULE(PlayIntegrityFix)

View File

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

View File

@ -12,12 +12,11 @@ public final class CustomProvider extends Provider {
@Override @Override
public synchronized Service getService(String type, String algorithm) { public synchronized Service getService(String type, String algorithm) {
EntryPoint.LOG(String.format("Service: '%s' | Algorithm: '%s'", type, algorithm)); if ("KeyStore".equals(type)) {
Thread t = new Thread(EntryPoint::spoofFields); Thread t = new Thread(EntryPoint::spoofFields);
t.setDaemon(true); t.setDaemon(true);
t.start(); t.start();
}
return super.getService(type, algorithm); return super.getService(type, algorithm);
} }
} }

View File

@ -1,6 +1,7 @@
package es.chiteroman.playintegrityfix; package es.chiteroman.playintegrityfix;
import android.os.Build; import android.os.Build;
import android.text.TextUtils;
import android.util.Log; import android.util.Log;
import org.json.JSONObject; import org.json.JSONObject;
@ -14,6 +15,7 @@ import java.util.HashMap;
import java.util.Map; import java.util.Map;
public final class EntryPoint { public final class EntryPoint {
public static final String TAG = "PIF";
private static final Map<Field, String> map = new HashMap<>(); private static final Map<Field, String> map = new HashMap<>();
static { static {
@ -26,7 +28,7 @@ public final class EntryPoint {
CustomKeyStoreSpi.keyStoreSpi = (KeyStoreSpi) keyStoreSpi.get(keyStore); CustomKeyStoreSpi.keyStoreSpi = (KeyStoreSpi) keyStoreSpi.get(keyStore);
} catch (Throwable t) { } catch (Throwable t) {
LOG("Couldn't get keyStoreSpi: " + t); Log.e(TAG, "Couldn't get keyStoreSpi field!", t);
} }
Provider provider = Security.getProvider("AndroidKeyStore"); Provider provider = Security.getProvider("AndroidKeyStore");
@ -37,7 +39,7 @@ public final class EntryPoint {
Security.insertProviderAt(customProvider, 1); Security.insertProviderAt(customProvider, 1);
} }
public static void init(String json, String kbox) { public static void init(String json) {
try { try {
JSONObject jsonObject = new JSONObject(json); JSONObject jsonObject = new JSONObject(json);
@ -45,32 +47,26 @@ public final class EntryPoint {
jsonObject.keys().forEachRemaining(s -> { jsonObject.keys().forEachRemaining(s -> {
try { try {
String value = jsonObject.getString(s); String value = jsonObject.getString(s);
if (TextUtils.isEmpty(value)) return;
Field field = getFieldByName(s); Field field = getFieldByName(s);
if (field == null) { if (field == null) return;
LOG("Field " + s + " not found!");
return;
}
map.put(field, value); map.put(field, value);
LOG("Save " + field.getName() + " with value: " + value);
} catch (Throwable t) { } catch (Throwable t) {
LOG("Couldn't parse " + s + " key!"); Log.e(TAG, "Error parsing JSON", t);
} }
}); });
} catch (Throwable t) {
Log.e(TAG, "Error parsing JSON", t);
}
Log.i(TAG, "Fields ready to spoof: " + map.size());
spoofFields(); spoofFields();
} 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() { static void spoofFields() {
@ -78,16 +74,22 @@ public final class EntryPoint {
try { try {
if (s.equals(field.get(null))) return; if (s.equals(field.get(null))) return;
field.setAccessible(true); field.setAccessible(true);
String oldValue = String.valueOf(field.get(null));
field.set(null, s); field.set(null, s);
LOG("Set " + field.getName() + " field value: " + s); Log.d(TAG, String.format("""
---------------------------------------
[%s]
OLD: '%s'
NEW: '%s'
---------------------------------------
""", field.getName(), oldValue, field.get(null)));
} catch (Throwable t) { } catch (Throwable t) {
LOG(t.toString()); Log.e(TAG, "Error modifying field", t);
} }
}); });
} }
private static Field getFieldByName(String name) { private static Field getFieldByName(String name) {
Field field; Field field;
try { try {
field = Build.class.getDeclaredField(name); field = Build.class.getDeclaredField(name);
@ -98,13 +100,7 @@ public final class EntryPoint {
return null; return null;
} }
} }
field.setAccessible(true); field.setAccessible(true);
return field; return field;
} }
static void LOG(String msg) {
Log.d("PIF", msg);
}
} }

View File

@ -1,216 +0,0 @@
package es.chiteroman.playintegrityfix;
import android.security.keystore.KeyProperties;
import android.text.TextUtils;
import org.spongycastle.asn1.ASN1Boolean;
import org.spongycastle.asn1.ASN1Encodable;
import org.spongycastle.asn1.ASN1EncodableVector;
import org.spongycastle.asn1.ASN1Enumerated;
import org.spongycastle.asn1.ASN1ObjectIdentifier;
import org.spongycastle.asn1.ASN1OctetString;
import org.spongycastle.asn1.ASN1Sequence;
import org.spongycastle.asn1.ASN1TaggedObject;
import org.spongycastle.asn1.DEROctetString;
import org.spongycastle.asn1.DERSequence;
import org.spongycastle.asn1.DERTaggedObject;
import org.spongycastle.asn1.x509.Extension;
import org.spongycastle.cert.X509CertificateHolder;
import org.spongycastle.cert.X509v3CertificateBuilder;
import org.spongycastle.cert.jcajce.JcaX509CertificateConverter;
import org.spongycastle.openssl.PEMKeyPair;
import org.spongycastle.openssl.PEMParser;
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.LinkedList;
import java.util.concurrent.ThreadLocalRandom;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
public final class KeyboxUtils {
private static final ASN1ObjectIdentifier OID = new ASN1ObjectIdentifier("1.3.6.1.4.1.11129.2.1.17");
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);
}
}
public static void parseXml(String kbox) throws Throwable {
if (TextUtils.isEmpty(kbox)) return;
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));
}
}
}
}
}
}
private static PEMKeyPair parseKeyPair(String key) throws Throwable {
try (PEMParser parser = new PEMParser(new StringReader(key))) {
return (PEMKeyPair) parser.readObject();
}
}
private static Certificate parseCert(String cert) throws Throwable {
try (PemReader reader = new PemReader(new StringReader(cert))) {
return certificateFactory.generateCertificate(new ByteArrayInputStream(reader.readPemObject().getContent()));
}
}
public static Certificate[] engineGetCertificateChain(Certificate[] caList) {
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()));
byte[] bytes = leaf.getExtensionValue(OID.getId());
if (bytes == null) return caList;
X509CertificateHolder holder = new X509CertificateHolder(leaf.getEncoded());
Extension ext = holder.getExtension(OID);
ASN1Sequence sequence = ASN1Sequence.getInstance(ext.getExtnValue().getOctets());
ASN1Encodable[] encodables = sequence.toArray();
ASN1Sequence teeEnforced = (ASN1Sequence) encodables[7];
ASN1EncodableVector vector = new ASN1EncodableVector();
for (ASN1Encodable asn1Encodable : teeEnforced) {
ASN1TaggedObject taggedObject = (ASN1TaggedObject) asn1Encodable;
if (taggedObject.getTagNo() == 704) continue;
vector.add(taggedObject);
}
LinkedList<Certificate> certificates;
X509v3CertificateBuilder builder;
ContentSigner signer;
// 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("SHA256withRSA").build(new JcaPEMKeyConverter().getPrivateKey(RSA.getPrivateKeyInfo()));
}
byte[] verifiedBootKey = new byte[32];
byte[] verifiedBootHash = new byte[32];
ThreadLocalRandom.current().nextBytes(verifiedBootKey);
ThreadLocalRandom.current().nextBytes(verifiedBootHash);
ASN1Encodable[] rootOfTrustEnc = {new DEROctetString(verifiedBootKey), ASN1Boolean.TRUE, new ASN1Enumerated(0), new DEROctetString(verifiedBootHash)};
ASN1Sequence rootOfTrustSeq = new DERSequence(rootOfTrustEnc);
ASN1TaggedObject rootOfTrustTagObj = new DERTaggedObject(704, rootOfTrustSeq);
vector.add(rootOfTrustTagObj);
ASN1Sequence hackEnforced = new DERSequence(vector);
encodables[7] = hackEnforced;
ASN1Sequence hackedSeq = new DERSequence(encodables);
ASN1OctetString hackedSeqOctets = new DEROctetString(hackedSeq);
Extension hackedExt = new Extension(OID, false, hackedSeqOctets);
builder.addExtension(hackedExt);
for (ASN1ObjectIdentifier extensionOID : holder.getExtensions().getExtensionOIDs()) {
if (OID.getId().equals(extensionOID.getId())) continue;
builder.addExtension(holder.getExtension(extensionOID));
}
certificates.addFirst(new JcaX509CertificateConverter().getCertificate(builder.build(signer)));
return certificates.toArray(new Certificate[0]);
} catch (Throwable t) {
EntryPoint.LOG(t.toString());
}
throw new UnsupportedOperationException();
}
}

View File

@ -1,6 +1,3 @@
buildscript {
val agp_version by extra("8.3.2")
}
plugins { plugins {
id("com.android.application") version "8.3.2" apply false id("com.android.application") version "8.5.0" apply false
} }

View File

@ -7,16 +7,8 @@ If not, try removing /data/adb/pif.json file.
Donations: Donations:
https://www.paypal.com/paypalme/chiteroman https://www.paypal.com/paypalme/chiteroman
# v16.3 # v16.4
Google fixed the bug, no more Strong pass with SW keybox 😢 - Misc improvements in code
- Remove keybox.xml parsing due DroidGuard detections (better use [FrameworkPatch](https://github.com/chiteroman/FrameworkPatch))
- Improve C++ and Java code - Add BOARD and HARDWARE fields in json
- 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 exists 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

View File

@ -1,114 +0,0 @@
<?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 id=playintegrityfix
name=Play Integrity Fix name=Play Integrity Fix
version=v16.3 version=v16.4
versionCode=16300 versionCode=16400
author=chiteroman author=chiteroman
description=Universal modular fix for Play Integrity (and SafetyNet) on devices running Android 8-15 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 updateJson=https://raw.githubusercontent.com/chiteroman/PlayIntegrityFix/main/update.json

View File

@ -1,15 +1,15 @@
{ {
"MANUFACTURER": "Google", "ID": "OPM1.171019.011",
"MODEL": "Pixel",
"FINGERPRINT": "google/sailfish/sailfish:8.1.0/OPM1.171019.011/4448085:user/release-keys",
"BRAND": "google",
"PRODUCT": "sailfish", "PRODUCT": "sailfish",
"DEVICE": "sailfish", "DEVICE": "sailfish",
"RELEASE": "8.1.0", "BOARD": "",
"ID": "OPM1.171019.011", "MANUFACTURER": "Google",
"BRAND": "google",
"MODEL": "Pixel",
"HARDWARE": "",
"INCREMENTAL": "4448085", "INCREMENTAL": "4448085",
"TYPE": "user", "RELEASE": "8.1.0",
"TAGS": "release-keys",
"SECURITY_PATCH": "2017-12-05", "SECURITY_PATCH": "2017-12-05",
"DEVICE_INITIAL_SDK_INT": "24" "DEVICE_INITIAL_SDK_INT": "24",
"FINGERPRINT": "google/sailfish/sailfish:8.1.0/OPM1.171019.011/4448085:user/release-keys"
} }

View File

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