Skip to content
This repository has been archived by the owner on Jul 19, 2024. It is now read-only.

Commit

Permalink
Generate certificate chain by injecting custom provider
Browse files Browse the repository at this point in the history
This commit is based on PlayIntegrityForkKsBypass by hex3l. Basic
idea is to inject a custom KeystoreSpi, overriding generateKeyPair
and engineGetCertificateChain method with a software generated
keypair.

Improvements worth mentioning:
1. Refresh BC provider before building key pair to avoid using old
   BC shipped with Android.
2. Keep BC class in proguard or R8 will remove ECDSA support
   in release build.

Co-authored-by: Wang Han <[email protected]>
  • Loading branch information
hex3l and aviraxp committed Jun 16, 2024
1 parent 0f41882 commit 116ffa5
Show file tree
Hide file tree
Showing 16 changed files with 659 additions and 22 deletions.
8 changes: 2 additions & 6 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,12 +1,7 @@
*.iml
.gradle
/local.properties
/.idea/caches
/.idea/libraries
/.idea/modules.xml
/.idea/workspace.xml
/.idea/navEditor.xml
/.idea/assetWizardSettings.xml
/.idea/*
.DS_Store
/build
/captures
Expand All @@ -16,3 +11,4 @@ local.properties
/module/zygisk/*
*.dex
*.zip
/app/release/*
2 changes: 1 addition & 1 deletion .idea/compiler.xml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion .idea/gradle.xml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion .idea/misc.xml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

5 changes: 5 additions & 0 deletions app/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ android {
excludes += "**/liblog.so"
excludes += "**/libdobby.so"
}
resources.excludes.add("META-INF/versions/9/OSGI-INF/MANIFEST.MF")
}

externalNativeBuild {
Expand All @@ -43,6 +44,9 @@ android {
}

buildTypes {
debug {
multiDexEnabled = false
}
release {
isMinifyEnabled = true
isShrinkResources = true
Expand All @@ -66,6 +70,7 @@ android {

dependencies {
implementation("dev.rikka.ndk.thirdparty:cxx:1.2.0")
implementation("org.bouncycastle:bcpkix-jdk18on:1.78.1")
}

tasks.register("updateModuleProp") {
Expand Down
9 changes: 8 additions & 1 deletion app/proguard-rules.pro
Original file line number Diff line number Diff line change
@@ -1,3 +1,10 @@
-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.CustomKeyStoreSpi
-keep class es.chiteroman.playintegrityfix.CustomKeyStoreKeyPairGeneratorSpi$EC
-keep class es.chiteroman.playintegrityfix.CustomKeyStoreKeyPairGeneratorSpi$RSA

-keep class org.bouncycastle.jcajce.provider.** { *; }
-keep class org.bouncycastle.jce.provider.** { *; }

-dontwarn javax.naming.**
29 changes: 25 additions & 4 deletions app/src/main/cpp/main.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,8 @@

#define PIF_JSON_DEFAULT "/data/adb/modules/playintegrityfix/pif.json"

#define KEYBOX_FILE_PATH "/data/adb/keybox.xml"

static std::string DEVICE_INITIAL_SDK_INT, SECURITY_PATCH, ID;

typedef void (*T_Callback)(void *, const char *, const char *, uint32_t);
Expand Down Expand Up @@ -137,17 +139,19 @@ class PlayIntegrityFix : public zygisk::ModuleBase {
return;
}

long dexSize = 0, jsonSize = 0;
long dexSize = 0, jsonSize = 0, xmlSize = 0;

int fd = api->connectCompanion();

xread(fd, &dexSize, sizeof(long));
xread(fd, &jsonSize, sizeof(long));
xread(fd, &xmlSize, sizeof(long));

LOGD("Dex file size: %ld", dexSize);
LOGD("Json file size: %ld", jsonSize);
LOGD("Xml file size: %ld", xmlSize);

if (dexSize < 1 || jsonSize < 1) {
if (dexSize < 1 || jsonSize < 1 || xmlSize < 1) {
close(fd);
api->setOption(zygisk::DLCLOSE_MODULE_LIBRARY);
return;
Expand All @@ -157,17 +161,23 @@ class PlayIntegrityFix : public zygisk::ModuleBase {
xread(fd, dexVector.data(), dexSize);

std::vector<uint8_t> jsonVector;
std::vector<uint8_t> xmlVector;

jsonVector.resize(jsonSize);
xread(fd, jsonVector.data(), jsonSize);

xmlVector.resize(xmlSize);
xread(fd, xmlVector.data(), xmlSize);

close(fd);

json = nlohmann::json::parse(jsonVector, nullptr, false, true);
std::string xmlString(xmlVector.begin(), xmlVector.end());
xml = xmlString;
}

void postAppSpecialize(const zygisk::AppSpecializeArgs *args) override {
if (dexVector.empty() || json.empty()) return;
if (dexVector.empty() || json.empty() || xml.empty()) return;

parseJson();

Expand All @@ -185,6 +195,7 @@ class PlayIntegrityFix : public zygisk::ModuleBase {
JNIEnv *env = nullptr;
std::vector<uint8_t> dexVector;
nlohmann::json json;
std::string xml;

void parseJson() {
if (json.contains("DEVICE_INITIAL_SDK_INT")) {
Expand Down Expand Up @@ -221,6 +232,11 @@ class PlayIntegrityFix : public zygisk::ModuleBase {

auto entryPointClass = (jclass) entryClassObj;

LOGD("receive xml");
auto receiveXml = env->GetStaticMethodID(entryPointClass, "receiveXml", "(Ljava/lang/String;)V");
auto xmlString = env->NewStringUTF(xml.c_str());
env->CallStaticVoidMethod(entryPointClass, receiveXml, xmlString);

LOGD("call init");
auto entryInit = env->GetStaticMethodID(entryPointClass, "init", "(Ljava/lang/String;)V");
auto str = env->NewStringUTF(json.dump().c_str());
Expand Down Expand Up @@ -251,22 +267,27 @@ static std::vector<uint8_t> readFile(const char *path) {

static void companion(int fd) {

std::vector<uint8_t> dexVector, jsonVector;
std::vector<uint8_t> dexVector, jsonVector, xmlVector;

dexVector = readFile(CLASSES_DEX);

jsonVector = readFile(PIF_JSON);

if (jsonVector.empty()) jsonVector = readFile(PIF_JSON_DEFAULT);

xmlVector = readFile(KEYBOX_FILE_PATH);

long dexSize = dexVector.size();
long jsonSize = jsonVector.size();
long xmlSize = xmlVector.size();

xwrite(fd, &dexSize, sizeof(long));
xwrite(fd, &jsonSize, sizeof(long));
xwrite(fd, &xmlSize, sizeof(long));

xwrite(fd, dexVector.data(), dexSize);
xwrite(fd, jsonVector.data(), jsonSize);
xwrite(fd, xmlVector.data(), xmlSize);
}

REGISTER_ZYGISK_MODULE(PlayIntegrityFix)
Expand Down
61 changes: 61 additions & 0 deletions app/src/main/java/es/chiteroman/playintegrityfix/CertUtils.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
package es.chiteroman.playintegrityfix;

import org.bouncycastle.asn1.x500.X500Name;
import org.bouncycastle.cert.X509CertificateHolder;
import org.bouncycastle.cert.jcajce.JcaX509CertificateConverter;
import org.bouncycastle.openssl.PEMKeyPair;
import org.bouncycastle.openssl.PEMParser;
import org.bouncycastle.openssl.jcajce.JcaPEMKeyConverter;
import org.bouncycastle.util.io.pem.PemObject;
import org.bouncycastle.util.io.pem.PemReader;

import java.io.IOException;
import java.io.StringReader;
import java.security.KeyPair;
import java.security.PrivateKey;
import java.security.cert.Certificate;

public class CertUtils {

public static Certificate parseCert(String cert) throws Throwable {
PemObject pemObject;
try (PemReader reader = new PemReader(new StringReader(cert))) {
pemObject = reader.readPemObject();
}

X509CertificateHolder holder = new X509CertificateHolder(pemObject.getContent());

return (new JcaX509CertificateConverter().getCertificate(holder));
}

public static X500Name parseCertSubject(String cert) throws Throwable {
PemObject pemObject;
try (PemReader reader = new PemReader(new StringReader(cert))) {
pemObject = reader.readPemObject();
}

X509CertificateHolder holder = new X509CertificateHolder(pemObject.getContent());

return holder.getSubject();
}

public static KeyPair parseKeyPair(String key) throws Throwable {
Object object;
try (PEMParser parser = new PEMParser(new StringReader(key))) {
object = parser.readObject();
}

PEMKeyPair pemKeyPair = (PEMKeyPair) object;

return new JcaPEMKeyConverter().getKeyPair(pemKeyPair);
}

public static PrivateKey parsePrivateKey(String keyPair) throws RuntimeException {
try (PEMParser parser = new PEMParser(new StringReader(keyPair))) {
PEMKeyPair pemKeyPair = (PEMKeyPair) parser.readObject();
return new JcaPEMKeyConverter().getPrivateKey(pemKeyPair.getPrivateKeyInfo());
} catch (IOException e) {
throw new RuntimeException(e);
}
}
}
Loading

0 comments on commit 116ffa5

Please sign in to comment.