You've already forked TrickyStore
mirror of
https://github.com/5ec1cff/TrickyStore.git
synced 2025-09-06 06:37:07 +00:00
support configuration
This commit is contained in:
@@ -0,0 +1,19 @@
|
||||
package io.github.a13e300.tricky_store;
|
||||
|
||||
import android.util.Log;
|
||||
|
||||
public class Logger {
|
||||
private static final String TAG = "TrickyStore";
|
||||
public static void d(String msg) {
|
||||
Log.d(TAG, msg);
|
||||
}
|
||||
|
||||
public static void e(String msg) {
|
||||
Log.e(TAG, msg);
|
||||
}
|
||||
|
||||
public static void e(String msg, Throwable t) {
|
||||
Log.e(TAG, msg, t);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -3,7 +3,9 @@ package io.github.a13e300.tricky_store
|
||||
import android.annotation.SuppressLint
|
||||
import android.content.pm.IPackageManager
|
||||
import android.os.Binder
|
||||
import android.os.FileObserver
|
||||
import android.os.IBinder
|
||||
import android.os.Looper
|
||||
import android.os.Parcel
|
||||
import android.os.ServiceManager
|
||||
import android.system.keystore2.IKeystoreService
|
||||
@@ -11,6 +13,7 @@ import android.system.keystore2.KeyEntryResponse
|
||||
import android.util.Log
|
||||
import io.github.a13e300.tricky_store.fwpatch.Android
|
||||
import io.github.a13e300.tricky_store.fwpatch.Utils
|
||||
import java.io.File
|
||||
import kotlin.system.exitProcess
|
||||
|
||||
open class BinderInterceptor : Binder() {
|
||||
@@ -113,7 +116,8 @@ fun registerBinderInterceptor(backdoor: IBinder, target: IBinder, interceptor: B
|
||||
backdoor.transact(1, data, reply, 0)
|
||||
}
|
||||
|
||||
val targetPackages = arrayOf("com.google.android.gms", "icu.nullptr.nativetest", "io.github.vvb2060.mahoshojo", "io.github.vvb2060.keyattestation")
|
||||
val targetPackages = mutableSetOf<String>()
|
||||
val DEFAULT_TARGET_PACKAGES = listOf("com.google.android.gms", "icu.nullptr.nativetest", "io.github.vvb2060.mahoshojo", "io.github.vvb2060.keyattestation")
|
||||
|
||||
const val TAG = "TrickyStore"
|
||||
|
||||
@@ -138,6 +142,40 @@ fun getPm(): IPackageManager? {
|
||||
return iPm
|
||||
}
|
||||
|
||||
fun updateTargetPackages(f: File) = runCatching {
|
||||
targetPackages.clear()
|
||||
f.readLines().mapNotNullTo(targetPackages) {
|
||||
if (it.isNotBlank() && !it.startsWith("#")) it else null
|
||||
}
|
||||
Logger.d("update target packages: $targetPackages")
|
||||
}.onFailure {
|
||||
Logger.e("failed to update target files", it)
|
||||
}
|
||||
|
||||
fun updateKeyBox(f: File) = runCatching {
|
||||
Android.readFromXml(f.readText())
|
||||
}.onFailure {
|
||||
Logger.e("failed to update keybox", it)
|
||||
}
|
||||
|
||||
const val CONFIG_PATH = "/data/adb/tricky_store"
|
||||
const val TARGET_FILE = "target.txt"
|
||||
const val KEYBOX_FILE = "keybox.xml"
|
||||
|
||||
class ConfigObserver : FileObserver(CONFIG_PATH, CLOSE_WRITE) {
|
||||
val root = File(CONFIG_PATH)
|
||||
override fun onEvent(event: Int, path: String?) {
|
||||
path ?: return
|
||||
if (event == CLOSE_WRITE) {
|
||||
val f = File(root, path)
|
||||
when (f.name) {
|
||||
TARGET_FILE -> updateTargetPackages(f)
|
||||
KEYBOX_FILE -> updateKeyBox(f)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@SuppressLint("BlockedPrivateApi")
|
||||
fun tryRunKeystoreInterceptor(): Boolean {
|
||||
val b = ServiceManager.getService("android.system.keystore2.IKeystoreService/default") ?: return false
|
||||
@@ -195,6 +233,22 @@ fun tryRunKeystoreInterceptor(): Boolean {
|
||||
return Skip
|
||||
}
|
||||
}
|
||||
val path = File(CONFIG_PATH)
|
||||
path.mkdirs()
|
||||
val target = File(path, TARGET_FILE)
|
||||
if (!target.exists()) {
|
||||
target.createNewFile()
|
||||
target.writeText(DEFAULT_TARGET_PACKAGES.joinToString("\n"))
|
||||
}
|
||||
updateTargetPackages(target)
|
||||
val keybox = File(path, KEYBOX_FILE)
|
||||
if (!keybox.exists()) {
|
||||
Logger.e("keybox file not found, please put it to $keybox !")
|
||||
} else {
|
||||
updateKeyBox(keybox)
|
||||
}
|
||||
val monitor = ConfigObserver()
|
||||
monitor.startWatching()
|
||||
registerBinderInterceptor(bd, b, interceptor)
|
||||
while (true) {
|
||||
Thread.sleep(1000000)
|
||||
|
||||
@@ -37,46 +37,65 @@ import java.util.HashMap;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
import java.util.concurrent.ThreadLocalRandom;
|
||||
|
||||
import io.github.a13e300.tricky_store.Logger;
|
||||
|
||||
public final class Android {
|
||||
private static final String TAG = "chiteroman";
|
||||
private static final PEMKeyPair EC, RSA;
|
||||
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 Map<String, String> map = new HashMap<>();
|
||||
|
||||
record KeyBox(PEMKeyPair privateKey, List<Certificate> certificates) {}
|
||||
private static final Map<String, KeyBox> keyboxes = new HashMap<>();
|
||||
|
||||
private static final CertificateFactory certificateFactory;
|
||||
|
||||
static {
|
||||
map.put("MANUFACTURER", "Google");
|
||||
map.put("MODEL", "Pixel");
|
||||
map.put("FINGERPRINT", "google/sailfish/sailfish:8.1.0/OPM1.171019.011/4448085:user/release-keys");
|
||||
map.put("BRAND", "google");
|
||||
map.put("PRODUCT", "sailfish");
|
||||
map.put("DEVICE", "sailfish");
|
||||
map.put("RELEASE", "8.1.0");
|
||||
map.put("ID", "OPM1.171019.011");
|
||||
map.put("INCREMENTAL", "4448085");
|
||||
map.put("SECURITY_PATCH", "2017-12-05");
|
||||
map.put("TYPE", "user");
|
||||
map.put("TAGS", "release-keys");
|
||||
try {
|
||||
certificateFactory = CertificateFactory.getInstance("X.509");
|
||||
|
||||
EC = parseKeyPair(Keybox.EC.PRIVATE_KEY);
|
||||
EC_CERTS.add(parseCert(Keybox.EC.CERTIFICATE_1));
|
||||
EC_CERTS.add(parseCert(Keybox.EC.CERTIFICATE_2));
|
||||
|
||||
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) {
|
||||
Log.e(TAG, t.toString());
|
||||
throw new RuntimeException(t);
|
||||
}
|
||||
}
|
||||
|
||||
public static void readFromXml(String data) {
|
||||
keyboxes.clear();
|
||||
XMLParser xmlParser = new XMLParser(data);
|
||||
|
||||
try {
|
||||
int numberOfKeyboxes = Integer.parseInt(Objects.requireNonNull(xmlParser.obtainPath(
|
||||
"AndroidAttestation.NumberOfKeyboxes").get("text")));
|
||||
for (int i = 0; i < numberOfKeyboxes; i++) {
|
||||
String keyboxAlgorithm = xmlParser.obtainPath(
|
||||
"AndroidAttestation.Keybox.Key[" + i + "]").get("algorithm");
|
||||
String privateKey = xmlParser.obtainPath(
|
||||
"AndroidAttestation.Keybox.Key[" + i + "].PrivateKey").get("text");
|
||||
int numberOfCertificates = Integer.parseInt(Objects.requireNonNull(xmlParser.obtainPath(
|
||||
"AndroidAttestation.Keybox.Key[" + i + "].CertificateChain.NumberOfCertificates").get("text")));
|
||||
|
||||
LinkedList<Certificate> certificateChain = new LinkedList<>();
|
||||
|
||||
for (int j = 0; j < numberOfCertificates; j++) {
|
||||
Map<String,String> certData= xmlParser.obtainPath(
|
||||
"AndroidAttestation.Keybox.Key[" + i + "].CertificateChain.Certificate[" + j + "]");
|
||||
certificateChain.add(parseCert(certData.get("text")));
|
||||
}
|
||||
String algo;
|
||||
if (keyboxAlgorithm.toLowerCase().equals("ecdsa")) {
|
||||
algo = KeyProperties.KEY_ALGORITHM_EC;
|
||||
} else {
|
||||
algo = KeyProperties.KEY_ALGORITHM_RSA;
|
||||
}
|
||||
keyboxes.put(algo, new KeyBox(parseKeyPair(privateKey), certificateChain));
|
||||
}
|
||||
Logger.d("update " + numberOfKeyboxes + " keyboxes");
|
||||
} catch (Throwable t) {
|
||||
Logger.e("Error loading xml file: " + t);
|
||||
}
|
||||
}
|
||||
|
||||
private static PEMKeyPair parseKeyPair(String key) throws Throwable {
|
||||
try (PEMParser parser = new PEMParser(new StringReader(key))) {
|
||||
return (PEMKeyPair) parser.readObject();
|
||||
@@ -104,7 +123,7 @@ public final class Android {
|
||||
}
|
||||
|
||||
public static Certificate[] engineGetCertificateChain(Certificate[] caList) {
|
||||
if (caList == null) throw new UnsupportedOperationException();
|
||||
if (caList == null) throw new UnsupportedOperationException("caList is null!");
|
||||
try {
|
||||
X509Certificate leaf = (X509Certificate) certificateFactory.generateCertificate(new ByteArrayInputStream(caList[0].getEncoded()));
|
||||
|
||||
@@ -135,15 +154,12 @@ public final class Android {
|
||||
X509v3CertificateBuilder builder;
|
||||
ContentSigner signer;
|
||||
|
||||
if (KeyProperties.KEY_ALGORITHM_EC.equals(leaf.getPublicKey().getAlgorithm())) {
|
||||
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 {
|
||||
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()));
|
||||
}
|
||||
var k = keyboxes.get(leaf.getPublicKey().getAlgorithm());
|
||||
if (k == null) throw new UnsupportedOperationException("unsupported algorithm " + leaf.getPublicKey().getAlgorithm());
|
||||
certificates = new LinkedList<>(k.certificates);
|
||||
builder = new X509v3CertificateBuilder(new X509CertificateHolder(certificates.get(0).getEncoded()).getSubject(), holder.getSerialNumber(), holder.getNotBefore(), holder.getNotAfter(), holder.getSubject(), k.privateKey.getPublicKeyInfo());
|
||||
signer = new JcaContentSignerBuilder(leaf.getSigAlgName()).build(new JcaPEMKeyConverter().getPrivateKey(k.privateKey.getPrivateKeyInfo()));
|
||||
|
||||
|
||||
byte[] verifiedBootKey = new byte[32];
|
||||
byte[] verifiedBootHash = new byte[32];
|
||||
|
||||
@@ -1,107 +0,0 @@
|
||||
package io.github.a13e300.tricky_store.fwpatch;
|
||||
|
||||
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-----
|
||||
""";
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,96 @@
|
||||
package io.github.a13e300.tricky_store.fwpatch;
|
||||
|
||||
import org.xmlpull.v1.XmlPullParser;
|
||||
import org.xmlpull.v1.XmlPullParserException;
|
||||
import org.xmlpull.v1.XmlPullParserFactory;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.StringReader;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
public class XMLParser {
|
||||
|
||||
private final String xml;
|
||||
|
||||
public XMLParser(String xml) {
|
||||
this.xml = xml;
|
||||
}
|
||||
|
||||
public Map<String, String> obtainPath(String path) throws Exception {
|
||||
XmlPullParserFactory xmlFactoryObject = XmlPullParserFactory.newInstance();
|
||||
XmlPullParser parser = xmlFactoryObject.newPullParser();
|
||||
parser.setFeature(XmlPullParser.FEATURE_PROCESS_NAMESPACES, false);
|
||||
parser.setInput(new StringReader(xml));
|
||||
|
||||
String[] tags = path.split("\\.");
|
||||
|
||||
return readData(parser, tags, 0, new HashMap<>());
|
||||
}
|
||||
|
||||
private Map<String, String> readData(XmlPullParser parser, String[] tags, int index,
|
||||
Map<String, Integer> tagCounts) throws IOException, XmlPullParserException {
|
||||
while (parser.next() != XmlPullParser.END_DOCUMENT) {
|
||||
if (parser.getEventType() != XmlPullParser.START_TAG) {
|
||||
continue;
|
||||
}
|
||||
|
||||
String name = parser.getName();
|
||||
|
||||
if (name.equals(tags[index].split("\\[")[0])) {
|
||||
|
||||
String[] tagParts = tags[index].split("\\[");
|
||||
if (tagParts.length > 1) {
|
||||
if (tagCounts.getOrDefault(name, 0) < Integer.parseInt(tagParts[1].replace("]", ""))) {
|
||||
tagCounts.put(name, tagCounts.getOrDefault(name, 0) + 1);
|
||||
return readData(parser, tags, index, tagCounts);
|
||||
} else {
|
||||
if (index == tags.length - 1) {
|
||||
return readAttributes(parser);
|
||||
} else {
|
||||
return readData(parser, tags, index + 1, tagCounts);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if (index == tags.length - 1) {
|
||||
return readAttributes(parser);
|
||||
} else {
|
||||
return readData(parser, tags, index + 1, tagCounts);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
skip(parser);
|
||||
}
|
||||
}
|
||||
|
||||
throw new XmlPullParserException("Path not found");
|
||||
}
|
||||
|
||||
private Map<String, String> readAttributes(XmlPullParser parser) throws IOException, XmlPullParserException {
|
||||
Map<String, String> attributes = new HashMap<>();
|
||||
for (int i = 0; i < parser.getAttributeCount(); i++) {
|
||||
attributes.put(parser.getAttributeName(i), parser.getAttributeValue(i));
|
||||
}
|
||||
if (parser.next() == XmlPullParser.TEXT) {
|
||||
attributes.put("text", parser.getText());
|
||||
}
|
||||
return attributes;
|
||||
}
|
||||
|
||||
private void skip(XmlPullParser parser) throws XmlPullParserException, IOException {
|
||||
if (parser.getEventType() != XmlPullParser.START_TAG) {
|
||||
throw new IllegalStateException();
|
||||
}
|
||||
int depth = 1;
|
||||
while (depth != 0) {
|
||||
switch (parser.next()) {
|
||||
case XmlPullParser.END_TAG:
|
||||
depth--;
|
||||
break;
|
||||
case XmlPullParser.START_TAG:
|
||||
depth++;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user