Skip to content

Commit

Permalink
v14.6
Browse files Browse the repository at this point in the history
  • Loading branch information
chiteroman committed Dec 20, 2023
1 parent 4b6accc commit 7ab29dd
Show file tree
Hide file tree
Showing 9 changed files with 199 additions and 72 deletions.
56 changes: 26 additions & 30 deletions app/src/main/cpp/module.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -12,39 +12,35 @@ static std::string SECURITY_PATCH, FIRST_API_LEVEL, VNDK_VERSION, BUILD_ID;

typedef void (*T_Callback)(void *, const char *, const char *, uint32_t);

static std::map<void *, T_Callback> callbacks;
static volatile T_Callback o_callback = nullptr;

static void modify_callback(void *cookie, const char *name, const char *value, uint32_t serial) {

if (cookie == nullptr || name == nullptr || value == nullptr ||
!callbacks.contains(cookie))
return;
if (cookie == nullptr || name == nullptr || value == nullptr || o_callback == nullptr) return;

std::string_view prop(name);

if (prop.ends_with("security_patch")) {
if (!SECURITY_PATCH.empty()) {
value = SECURITY_PATCH.c_str();
}
LOGD("[%s]: %s", name, value);
} else if (prop.ends_with("api_level")) {
if (!FIRST_API_LEVEL.empty()) {
value = FIRST_API_LEVEL.c_str();
}
LOGD("[%s]: %s", name, value);
} else if (prop.ends_with("vndk.version")) {
if (!VNDK_VERSION.empty()) {
value = VNDK_VERSION.c_str();
}
LOGD("[%s]: %s", name, value);
} else if (prop == "ro.build.id") {
if (!BUILD_ID.empty()) {
value = BUILD_ID.c_str();
}
LOGD("[%s]: %s", name, value);
}

return callbacks[cookie](cookie, name, value, serial);
if (!prop.starts_with("cache") && !prop.starts_with("debug")) LOGD("[%s]: %s", name, value);

return o_callback(cookie, name, value, serial);
}

static void (*o_system_property_read_callback)(const prop_info *, T_Callback, void *);
Expand All @@ -54,7 +50,7 @@ my_system_property_read_callback(const prop_info *pi, T_Callback callback, void
if (pi == nullptr || callback == nullptr || cookie == nullptr) {
return o_system_property_read_callback(pi, callback, cookie);
}
callbacks[cookie] = callback;
o_callback = callback;
return o_system_property_read_callback(pi, modify_callback, cookie);
}

Expand All @@ -81,37 +77,41 @@ class PlayIntegrityFix : public zygisk::ModuleBase {

void preAppSpecialize(zygisk::AppSpecializeArgs *args) override {

auto rawProcess = env->GetStringUTFChars(args->nice_name, nullptr);
std::string process(rawProcess);
env->ReleaseStringUTFChars(args->nice_name, rawProcess);
auto name = env->GetStringUTFChars(args->nice_name, nullptr);

if (process.starts_with("com.google.android.gms")) {
if (name && strncmp(name, "com.google.android.gms", 22) == 0) {

api->setOption(zygisk::FORCE_DENYLIST_UNMOUNT);

if (process == "com.google.android.gms.unstable") {
if (strcmp(name, "com.google.android.gms.unstable") == 0) {

int fd = api->connectCompanion();

read(fd, &dexSize, sizeof(dexSize));
read(fd, &jsonSize, sizeof(jsonSize));

auto vectorSize = dexSize + jsonSize;
if (dexSize < 1 || jsonSize < 1) {

if (vectorSize > 0) {
vector.resize(vectorSize);
read(fd, vector.data(), vectorSize);
} else {
LOGD("Couldn't read classes.dex");
LOGD("Couldn't read files in memory!");
api->setOption(zygisk::DLCLOSE_MODULE_LIBRARY);

} else {

auto vectorSize = dexSize + jsonSize;

if (vectorSize > 0) {
vector.resize(vectorSize);
read(fd, vector.data(), vectorSize);
}
}

close(fd);
return;
}
}

api->setOption(zygisk::DLCLOSE_MODULE_LIBRARY);
} else api->setOption(zygisk::DLCLOSE_MODULE_LIBRARY);

} else api->setOption(zygisk::DLCLOSE_MODULE_LIBRARY);

env->ReleaseStringUTFChars(args->nice_name, name);
}

void postAppSpecialize(const zygisk::AppSpecializeArgs *args) override {
Expand Down Expand Up @@ -148,7 +148,6 @@ class PlayIntegrityFix : public zygisk::ModuleBase {
if (json.contains("SECURITY_PATCH")) {
if (json["SECURITY_PATCH"].is_null() || json["SECURITY_PATCH"].empty()) {
LOGD("SECURITY_PATCH is null or empty");
json.erase("SECURITY_PATCH");
} else {
SECURITY_PATCH = json["SECURITY_PATCH"].get<std::string>();
}
Expand Down Expand Up @@ -230,10 +229,7 @@ static void companion(int fd) {

FILE *json = fopen("/data/adb/pif.json", "rb");

if (json == nullptr) {

json = fopen("/data/adb/modules/playintegrityfix/pif.json", "rb");
}
if (json == nullptr) json = fopen("/data/adb/modules/playintegrityfix/pif.json", "rb");

if (json) {
fseek(json, 0, SEEK_END);
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,105 @@
package es.chiteroman.playintegrityfix;

import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.security.Key;
import java.security.KeyStoreException;
import java.security.KeyStoreSpi;
import java.security.NoSuchAlgorithmException;
import java.security.UnrecoverableKeyException;
import java.security.cert.Certificate;
import java.security.cert.CertificateException;
import java.util.Date;
import java.util.Enumeration;

public class CustomKeyStoreSpi extends KeyStoreSpi {
protected static volatile KeyStoreSpi keyStoreSpi;

@Override
public Key engineGetKey(String alias, char[] password) throws NoSuchAlgorithmException, UnrecoverableKeyException {
return keyStoreSpi.engineGetKey(alias, password);
}

@Override
public Certificate[] engineGetCertificateChain(String alias) {

if (EntryPoint.isDroidGuard()) {

EntryPoint.LOG("DroidGuard call certificate chain! Throwing exception.");
throw new UnsupportedOperationException();
}

return keyStoreSpi.engineGetCertificateChain(alias);
}

@Override
public Certificate engineGetCertificate(String alias) {
return keyStoreSpi.engineGetCertificate(alias);
}

@Override
public Date engineGetCreationDate(String alias) {
return keyStoreSpi.engineGetCreationDate(alias);
}

@Override
public void engineSetKeyEntry(String alias, Key key, char[] password, Certificate[] chain) throws KeyStoreException {
keyStoreSpi.engineSetKeyEntry(alias, key, password, chain);
}

@Override
public void engineSetKeyEntry(String alias, byte[] key, Certificate[] chain) throws KeyStoreException {
keyStoreSpi.engineSetKeyEntry(alias, key, chain);
}

@Override
public void engineSetCertificateEntry(String alias, Certificate cert) throws KeyStoreException {
keyStoreSpi.engineSetCertificateEntry(alias, cert);
}

@Override
public void engineDeleteEntry(String alias) throws KeyStoreException {
keyStoreSpi.engineDeleteEntry(alias);
}

@Override
public Enumeration<String> engineAliases() {
return keyStoreSpi.engineAliases();
}

@Override
public boolean engineContainsAlias(String alias) {
return keyStoreSpi.engineContainsAlias(alias);
}

@Override
public int engineSize() {
return keyStoreSpi.engineSize();
}

@Override
public boolean engineIsKeyEntry(String alias) {
return keyStoreSpi.engineIsKeyEntry(alias);
}

@Override
public boolean engineIsCertificateEntry(String alias) {
return keyStoreSpi.engineIsCertificateEntry(alias);
}

@Override
public String engineGetCertificateAlias(Certificate cert) {
return keyStoreSpi.engineGetCertificateAlias(cert);
}

@Override
public void engineStore(OutputStream stream, char[] password) throws CertificateException, IOException, NoSuchAlgorithmException {
keyStoreSpi.engineStore(stream, password);
}

@Override
public void engineLoad(InputStream stream, char[] password) throws CertificateException, IOException, NoSuchAlgorithmException {
keyStoreSpi.engineLoad(stream, password);
}
}
Original file line number Diff line number Diff line change
@@ -1,29 +1,24 @@
package es.chiteroman.playintegrityfix;

import java.security.Provider;
import java.security.ProviderException;
import java.util.Arrays;
import java.util.Locale;

public final class CustomProvider extends Provider {
public class CustomProvider extends Provider {

public CustomProvider(Provider provider) {
protected CustomProvider(Provider provider) {
super(provider.getName(), provider.getVersion(), provider.getInfo());

putAll(provider);

remove("KeyStore.AndroidKeyStore");

put("KeyStore.AndroidKeyStore", CustomKeyStoreSpi.class.getName());
}

@Override
public synchronized Service getService(String type, String algorithm) {
EntryPoint.spoofDevice();
EntryPoint.LOG("[SERVICE] Type: " + type + " | Algorithm: " + algorithm);

if ("KeyPairGenerator".equals(type)) {

if (Arrays.stream(Thread.currentThread().getStackTrace()).anyMatch(e -> e.getClassName().toLowerCase(Locale.US).contains("droidguard"))) {

throw new ProviderException();

}
}
EntryPoint.spoofDevice();

return super.getService(type, algorithm);
}
Expand Down
36 changes: 23 additions & 13 deletions app/src/main/java/es/chiteroman/playintegrityfix/EntryPoint.java
Original file line number Diff line number Diff line change
Expand Up @@ -4,16 +4,18 @@
import android.util.JsonReader;
import android.util.Log;

import org.json.JSONException;

import java.io.StringReader;
import java.lang.reflect.Field;
import java.security.KeyStore;
import java.security.KeyStoreSpi;
import java.security.Provider;
import java.security.Security;
import java.util.Arrays;
import java.util.HashMap;
import java.util.Locale;
import java.util.Map;

public final class EntryPoint {
public class EntryPoint {
private static final Map<String, String> map = new HashMap<>();

public static void init(String data) {
Expand All @@ -22,32 +24,41 @@ public static void init(String data) {
while (reader.hasNext()) {
String key = reader.nextName();
String value = reader.nextString();

if (key == null || key.isEmpty() || key.isBlank() || value == null || value.isEmpty() || value.isBlank())
throw new JSONException("Empty key or value");

map.put(key, value);
}
reader.endObject();
} catch (Exception e) {
LOG("Couldn't parse JSON from Zygisk lib: " + e);
LOG("Remove /data/adb/pif.json");
}

LOG("Map info (keys and values):");
map.forEach((s, s2) -> LOG(String.format("[%s] -> %s", s, s2)));

spoofDevice();
spoofProvider();
}

static void LOG(String msg) {
protected static void LOG(String msg) {
Log.d("PIF/Java", msg);
}

static void spoofDevice() {
protected static void spoofDevice() {
map.forEach(EntryPoint::setFieldValue);
}

protected static boolean isDroidGuard() {
return Arrays.stream(Thread.currentThread().getStackTrace()).anyMatch(e -> e.getClassName().toLowerCase(Locale.ENGLISH).contains("droidguard"));
}

private static void spoofProvider() {
try {
KeyStore keyStore = KeyStore.getInstance("AndroidKeyStore");

Field field = keyStore.getClass().getDeclaredField("keyStoreSpi");

field.setAccessible(true);
CustomKeyStoreSpi.keyStoreSpi = (KeyStoreSpi) field.get(keyStore);

Provider provider = Security.getProvider("AndroidKeyStore");

Provider customProvider = new CustomProvider(provider);
Expand Down Expand Up @@ -79,12 +90,11 @@ private static void setFieldValue(String name, String value) {
String oldValue = null;
try {
oldValue = (String) field.get(null);
if (value.equals(oldValue)) return;
field.set(null, value);
} catch (IllegalAccessException e) {
LOG("Couldn't get or set field: " + e);
}
field.setAccessible(false);
if (value.equals(oldValue)) return;
LOG(String.format("[%s]: %s -> %s", name, oldValue, value));
LOG(String.format("Field '%s' with value '%s' is now set to '%s'", name, oldValue, value));
}
}
4 changes: 2 additions & 2 deletions changelog.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,6 @@ We have a Telegram channel!
If you want to share your knowledge join:
https://t.me/playintegrityfix

# v14.5
# v14.6

Fix Google update
Updated fingerprint
Loading

0 comments on commit 7ab29dd

Please sign in to comment.