From 2db359090e249def23c111ef18e2a218c79db6eb Mon Sep 17 00:00:00 2001 From: 5ec1cff Date: Fri, 12 Jul 2024 06:41:41 +0800 Subject: [PATCH] Refactor --- .../io/github/a13e300/tricky_store/Config.kt | 68 +++++ .../tricky_store/KeystoreInterceptor.kt | 108 +++++++ .../io/github/a13e300/tricky_store/Main.kt | 265 +----------------- .../tricky_store/binder/BinderInterceptor.kt | 108 +++++++ .../Android.java => keystore/CertHack.java} | 19 +- .../{fwpatch => keystore}/Utils.java | 2 +- .../{fwpatch => keystore}/XMLParser.java | 2 +- 7 files changed, 303 insertions(+), 269 deletions(-) create mode 100644 service/src/main/java/io/github/a13e300/tricky_store/Config.kt create mode 100644 service/src/main/java/io/github/a13e300/tricky_store/KeystoreInterceptor.kt create mode 100644 service/src/main/java/io/github/a13e300/tricky_store/binder/BinderInterceptor.kt rename service/src/main/java/io/github/a13e300/tricky_store/{fwpatch/Android.java => keystore/CertHack.java} (95%) rename service/src/main/java/io/github/a13e300/tricky_store/{fwpatch => keystore}/Utils.java (98%) rename service/src/main/java/io/github/a13e300/tricky_store/{fwpatch => keystore}/XMLParser.java (98%) diff --git a/service/src/main/java/io/github/a13e300/tricky_store/Config.kt b/service/src/main/java/io/github/a13e300/tricky_store/Config.kt new file mode 100644 index 0000000..5f57ebc --- /dev/null +++ b/service/src/main/java/io/github/a13e300/tricky_store/Config.kt @@ -0,0 +1,68 @@ +package io.github.a13e300.tricky_store + +import android.os.FileObserver +import io.github.a13e300.tricky_store.keystore.CertHack +import java.io.File + +object Config { + val targetPackages = mutableSetOf() + private val DEFAULT_TARGET_PACKAGES = listOf( + "com.google.android.gms", + "icu.nullptr.nativetest", + "io.github.vvb2060.mahoshojo", + "io.github.vvb2060.keyattestation" + ) + + private fun updateTargetPackages(f: File?) = runCatching { + targetPackages.clear() + f?.readLines()?.mapNotNullTo(targetPackages) { + if (it.isNotBlank() && !it.startsWith("#")) it else null + } + Logger.i("update target packages: $targetPackages") + }.onFailure { + Logger.e("failed to update target files", it) + } + + private fun updateKeyBox(f: File?) = runCatching { + CertHack.readFromXml(f?.readText()) + }.onFailure { + Logger.e("failed to update keybox", it) + } + + private const val CONFIG_PATH = "/data/adb/tricky_store" + private const val TARGET_FILE = "target.txt" + private const val KEYBOX_FILE = "keybox.xml" + private val root = File(CONFIG_PATH) + + object ConfigObserver : FileObserver(root, CLOSE_WRITE or DELETE) { + override fun onEvent(event: Int, path: String?) { + path ?: return + val f = when (event) { + CLOSE_WRITE -> File(root, path) + DELETE -> null + else -> return + } + when (path) { + TARGET_FILE -> updateTargetPackages(f) + KEYBOX_FILE -> updateKeyBox(f) + } + } + } + + fun initialize() { + root.mkdirs() + val target = File(root, TARGET_FILE) + if (!target.exists()) { + target.createNewFile() + target.writeText(DEFAULT_TARGET_PACKAGES.joinToString("\n")) + } + updateTargetPackages(target) + val keybox = File(root, KEYBOX_FILE) + if (!keybox.exists()) { + Logger.e("keybox file not found, please put it to $keybox !") + } else { + updateKeyBox(keybox) + } + ConfigObserver.startWatching() + } +} diff --git a/service/src/main/java/io/github/a13e300/tricky_store/KeystoreInterceptor.kt b/service/src/main/java/io/github/a13e300/tricky_store/KeystoreInterceptor.kt new file mode 100644 index 0000000..db82e47 --- /dev/null +++ b/service/src/main/java/io/github/a13e300/tricky_store/KeystoreInterceptor.kt @@ -0,0 +1,108 @@ +package io.github.a13e300.tricky_store + +import android.annotation.SuppressLint +import android.content.pm.IPackageManager +import android.os.IBinder +import android.os.Parcel +import android.os.ServiceManager +import android.system.keystore2.IKeystoreService +import android.system.keystore2.KeyEntryResponse +import io.github.a13e300.tricky_store.binder.BinderInterceptor +import io.github.a13e300.tricky_store.keystore.CertHack +import io.github.a13e300.tricky_store.keystore.Utils +import kotlin.system.exitProcess + +@SuppressLint("BlockedPrivateApi") +object KeystoreInterceptor : BinderInterceptor() { + private val targetTransaction = IKeystoreService.Stub::class.java.getDeclaredField("TRANSACTION_getKeyEntry").apply { isAccessible = true }.getInt(null) // 2 + + private var iPm: IPackageManager? = null + + private fun getPm(): IPackageManager? { + if (iPm == null) { + iPm = IPackageManager.Stub.asInterface(ServiceManager.getService("package")) + } + return iPm + } + + override fun onPreTransact( + target: IBinder, + code: Int, + flags: Int, + callingUid: Int, + callingPid: Int, + data: Parcel + ): Result { + if (code == targetTransaction) { + Logger.d("intercept pre $target uid=$callingUid pid=$callingPid dataSz=${data.dataSize()}") + kotlin.runCatching { + val ps = getPm()?.getPackagesForUid(callingUid) + if (ps?.any { it in Config.targetPackages } == true) return Continue + }.onFailure { Logger.e("failed to get packages", it) } + } + return Skip + } + + override fun onPostTransact( + target: IBinder, + code: Int, + flags: Int, + callingUid: Int, + callingPid: Int, + data: Parcel, + reply: Parcel?, + resultCode: Int + ): Result { + if (code != targetTransaction || reply == null) return Skip + val p = Parcel.obtain() + Logger.d("intercept post $target uid=$callingUid pid=$callingPid dataSz=${data.dataSize()} replySz=${reply.dataSize()}") + try { + reply.readException() + val response = reply.readTypedObject(KeyEntryResponse.CREATOR) + val chain = Utils.getCertificateChain(response) + if (chain != null) { + val newChain = CertHack.engineGetCertificateChain(chain) + Utils.putCertificateChain(response, newChain) + p.writeNoException() + p.writeTypedObject(response, 0) + return OverrideReply(0, p) + } else { + p.recycle() + } + } catch (t: Throwable) { + Logger.e("failed to hack certificate chain of uid=$callingUid pid=$callingPid!", t) + p.recycle() + } + return Skip + } + + fun tryRunKeystoreInterceptor(): Boolean { + Logger.i("trying to register keystore interceptor ...") + val b = ServiceManager.getService("android.system.keystore2.IKeystoreService/default") ?: return false + val bd = getBinderBackdoor(b) + if (bd == null) { + // no binder hook, try inject + Logger.i("trying to inject keystore ...") + val p = Runtime.getRuntime().exec( + arrayOf( + "/system/bin/sh", + "-c", + "exec ./inject `pidof keystore2` libtricky_store.so entry" + ) + ) + // logD(p.inputStream.readBytes().decodeToString()) + // logD(p.errorStream.readBytes().decodeToString()) + if (p.waitFor() != 0) { + Logger.e("failed to inject! daemon exit") + exitProcess(1) + } + return false + } + registerBinderInterceptor(bd, b, this) + b.linkToDeath({ + Logger.d("keystore exit, daemon restart") + exitProcess(0) + }, 0) + return true + } +} diff --git a/service/src/main/java/io/github/a13e300/tricky_store/Main.kt b/service/src/main/java/io/github/a13e300/tricky_store/Main.kt index 1e19458..15033d3 100644 --- a/service/src/main/java/io/github/a13e300/tricky_store/Main.kt +++ b/service/src/main/java/io/github/a13e300/tricky_store/Main.kt @@ -1,264 +1,15 @@ 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 -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() { - sealed class Result - data object Skip : Result() - data object Continue : Result() - data class OverrideData(val data: Parcel) : Result() - data class OverrideReply(val code: Int = 0, val reply: Parcel) : Result() - - open fun onPreTransact(target: IBinder, code: Int, flags: Int, callingUid: Int, callingPid: Int, data: Parcel): Result = Skip - open fun onPostTransact(target: IBinder, code: Int, flags: Int, callingUid: Int, callingPid: Int, data: Parcel, reply: Parcel?, resultCode: Int): Result = Skip - - override fun onTransact(code: Int, data: Parcel, reply: Parcel?, flags: Int): Boolean { - val result = when (code) { - 1 -> { // PRE_TRANSACT - val target = data.readStrongBinder() - val theCode = data.readInt() - val theFlags = data.readInt() - val callingUid = data.readInt() - val callingPid = data.readInt() - val sz = data.readLong() - val theData = Parcel.obtain() - try { - theData.appendFrom(data, data.dataPosition(), sz.toInt()) - theData.setDataPosition(0) - onPreTransact(target, theCode, theFlags, callingUid, callingPid, theData) - } finally { - theData.recycle() - } - } - 2 -> { // POST_TRANSACT - val target = data.readStrongBinder() - val theCode = data.readInt() - val theFlags = data.readInt() - val callingUid = data.readInt() - val callingPid = data.readInt() - val resultCode = data.readInt() - val theData = Parcel.obtain() - val theReply = Parcel.obtain() - try { - val sz = data.readLong().toInt() - theData.appendFrom(data, data.dataPosition(), sz) - theData.setDataPosition(0) - data.setDataPosition(data.dataPosition() + sz) - val sz2 = data.readLong().toInt() - if (sz2 != 0) { - theReply.appendFrom(data, data.dataPosition(), sz2) - theReply.setDataPosition(0) - } - onPostTransact(target, theCode, theFlags, callingUid, callingPid, theData, if (sz2 == 0) null else theReply, resultCode) - } finally { - theData.recycle() - theReply.recycle() - } - } - else -> return super.onTransact(code, data, reply, flags) - } - when (result) { - Skip -> reply!!.writeInt(1) - Continue -> reply!!.writeInt(2) - is OverrideReply -> { - reply!!.writeInt(3) - reply.writeInt(result.code) - reply.writeLong(result.reply.dataSize().toLong()) - println("override reply code=${result.code} size=${result.reply.dataSize()}") - reply.appendFrom(result.reply, 0, result.reply.dataSize()) - result.reply.recycle() - } - is OverrideData -> { - reply!!.writeInt(4) - reply.writeLong(result.data.dataSize().toLong()) - reply.appendFrom(result.data, 0, result.data.dataSize()) - result.data.recycle() - } - else -> {} - } - return true - } -} - -fun getBinderBackdoor(b: IBinder): IBinder? { - val data = Parcel.obtain() - val reply = Parcel.obtain() - try { - b.transact(0xdeadbeef.toInt(), data, reply, 0) - return reply.readStrongBinder() - } catch (ignored: Throwable) { - return null - } finally { - data.recycle() - reply.recycle() - } -} - -fun registerBinderInterceptor(backdoor: IBinder, target: IBinder, interceptor: BinderInterceptor) { - val data = Parcel.obtain() - val reply = Parcel.obtain() - data.writeStrongBinder(target) - data.writeStrongBinder(interceptor) - backdoor.transact(1, data, reply, 0) -} - -val targetPackages = mutableSetOf() -val DEFAULT_TARGET_PACKAGES = listOf("com.google.android.gms", "icu.nullptr.nativetest", "io.github.vvb2060.mahoshojo", "io.github.vvb2060.keyattestation") - -var iPm: IPackageManager? = null - -fun getPm(): IPackageManager? { - if (iPm == null) { - iPm = IPackageManager.Stub.asInterface(ServiceManager.getService("package")) - } - 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) { - private 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 - b.linkToDeath({ - Logger.d("keystore exit, daemon exit") - exitProcess(0) - }, 0) - val bd = getBinderBackdoor(b) ?: return true - val targetTransaction = IKeystoreService.Stub::class.java.getDeclaredField("TRANSACTION_getKeyEntry").apply { isAccessible = true }.getInt(null) // 2 - val interceptor = object : BinderInterceptor() { - override fun onPreTransact( - target: IBinder, - code: Int, - flags: Int, - callingUid: Int, - callingPid: Int, - data: Parcel - ): Result { - if (code == targetTransaction) { - Logger.d("intercept pre $target uid=$callingUid pid=$callingPid dataSz=${data.dataSize()}") - kotlin.runCatching { - val ps = getPm()?.getPackagesForUid(callingUid) - if (ps?.any { it in targetPackages } == true) return Continue - }.onFailure { Logger.e("failed to get packages", it) } - } - return Skip - } - - override fun onPostTransact( - target: IBinder, - code: Int, - flags: Int, - callingUid: Int, - callingPid: Int, - data: Parcel, - reply: Parcel?, - resultCode: Int - ): Result { - if (code != targetTransaction || reply == null) return Skip - val p = Parcel.obtain() - Logger.d("intercept post $target uid=$callingUid pid=$callingPid dataSz=${data.dataSize()} replySz=${reply.dataSize()}") - try { - reply.readException() - val response = reply.readTypedObject(KeyEntryResponse.CREATOR) - val chain = Utils.getCertificateChain(response) - val newChain = Android.engineGetCertificateChain(chain) - Utils.putCertificateChain(response, newChain) - p.writeNoException() - p.writeTypedObject(response, 0) - return OverrideReply(0, p) - } catch (t: Throwable) { - Logger.e("failed to hack certificate chain!", t) - p.recycle() - } - 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) - } -} - fun main(args: Array) { + Logger.i("Welcome to TrickyStore!") while (true) { - Thread.sleep(1000) - // true -> can inject, false -> service not found, loop -> running - if (!tryRunKeystoreInterceptor()) continue - // no binder hook, try inject - val p = Runtime.getRuntime().exec( - arrayOf( - "/system/bin/sh", - "-c", - "exec ./inject `pidof keystore2` libtricky_store.so entry" - ) - ) - // logD(p.inputStream.readBytes().decodeToString()) - // logD(p.errorStream.readBytes().decodeToString()) - if (p.waitFor() != 0) { - Logger.e("failed to inject! daemon exit") - exitProcess(1) + if (!KeystoreInterceptor.tryRunKeystoreInterceptor()) { + Thread.sleep(1000) + continue + } + Config.initialize() + while (true) { + Thread.sleep(1000000) } } } diff --git a/service/src/main/java/io/github/a13e300/tricky_store/binder/BinderInterceptor.kt b/service/src/main/java/io/github/a13e300/tricky_store/binder/BinderInterceptor.kt new file mode 100644 index 0000000..f1eeb96 --- /dev/null +++ b/service/src/main/java/io/github/a13e300/tricky_store/binder/BinderInterceptor.kt @@ -0,0 +1,108 @@ +package io.github.a13e300.tricky_store.binder + +import android.os.Binder +import android.os.IBinder +import android.os.Parcel + + +open class BinderInterceptor : Binder() { + sealed class Result + data object Skip : Result() + data object Continue : Result() + data class OverrideData(val data: Parcel) : Result() + data class OverrideReply(val code: Int = 0, val reply: Parcel) : Result() + + companion object { + fun getBinderBackdoor(b: IBinder): IBinder? { + val data = Parcel.obtain() + val reply = Parcel.obtain() + try { + b.transact(0xdeadbeef.toInt(), data, reply, 0) + return reply.readStrongBinder() + } catch (ignored: Throwable) { + return null + } finally { + data.recycle() + reply.recycle() + } + } + + fun registerBinderInterceptor(backdoor: IBinder, target: IBinder, interceptor: BinderInterceptor) { + val data = Parcel.obtain() + val reply = Parcel.obtain() + data.writeStrongBinder(target) + data.writeStrongBinder(interceptor) + backdoor.transact(1, data, reply, 0) + } + } + + open fun onPreTransact(target: IBinder, code: Int, flags: Int, callingUid: Int, callingPid: Int, data: Parcel): Result = Skip + open fun onPostTransact(target: IBinder, code: Int, flags: Int, callingUid: Int, callingPid: Int, data: Parcel, reply: Parcel?, resultCode: Int): Result = Skip + + override fun onTransact(code: Int, data: Parcel, reply: Parcel?, flags: Int): Boolean { + val result = when (code) { + 1 -> { // PRE_TRANSACT + val target = data.readStrongBinder() + val theCode = data.readInt() + val theFlags = data.readInt() + val callingUid = data.readInt() + val callingPid = data.readInt() + val sz = data.readLong() + val theData = Parcel.obtain() + try { + theData.appendFrom(data, data.dataPosition(), sz.toInt()) + theData.setDataPosition(0) + onPreTransact(target, theCode, theFlags, callingUid, callingPid, theData) + } finally { + theData.recycle() + } + } + 2 -> { // POST_TRANSACT + val target = data.readStrongBinder() + val theCode = data.readInt() + val theFlags = data.readInt() + val callingUid = data.readInt() + val callingPid = data.readInt() + val resultCode = data.readInt() + val theData = Parcel.obtain() + val theReply = Parcel.obtain() + try { + val sz = data.readLong().toInt() + theData.appendFrom(data, data.dataPosition(), sz) + theData.setDataPosition(0) + data.setDataPosition(data.dataPosition() + sz) + val sz2 = data.readLong().toInt() + if (sz2 != 0) { + theReply.appendFrom(data, data.dataPosition(), sz2) + theReply.setDataPosition(0) + } + onPostTransact(target, theCode, theFlags, callingUid, callingPid, theData, if (sz2 == 0) null else theReply, resultCode) + } finally { + theData.recycle() + theReply.recycle() + } + } + else -> return super.onTransact(code, data, reply, flags) + } + when (result) { + Skip -> reply!!.writeInt(1) + Continue -> reply!!.writeInt(2) + is OverrideReply -> { + reply!!.writeInt(3) + reply.writeInt(result.code) + reply.writeLong(result.reply.dataSize().toLong()) + println("override reply code=${result.code} size=${result.reply.dataSize()}") + reply.appendFrom(result.reply, 0, result.reply.dataSize()) + result.reply.recycle() + } + is OverrideData -> { + reply!!.writeInt(4) + reply.writeLong(result.data.dataSize().toLong()) + reply.appendFrom(result.data, 0, result.data.dataSize()) + result.data.recycle() + } + else -> {} + } + return true + } +} \ No newline at end of file diff --git a/service/src/main/java/io/github/a13e300/tricky_store/fwpatch/Android.java b/service/src/main/java/io/github/a13e300/tricky_store/keystore/CertHack.java similarity index 95% rename from service/src/main/java/io/github/a13e300/tricky_store/fwpatch/Android.java rename to service/src/main/java/io/github/a13e300/tricky_store/keystore/CertHack.java index 77e05dd..b395b40 100644 --- a/service/src/main/java/io/github/a13e300/tricky_store/fwpatch/Android.java +++ b/service/src/main/java/io/github/a13e300/tricky_store/keystore/CertHack.java @@ -1,8 +1,6 @@ -package io.github.a13e300.tricky_store.fwpatch; +package io.github.a13e300.tricky_store.keystore; -import android.os.Build; import android.security.keystore.KeyProperties; -import android.util.Log; import org.bouncycastle.asn1.ASN1Boolean; import org.bouncycastle.asn1.ASN1Encodable; @@ -28,11 +26,9 @@ import org.bouncycastle.util.io.pem.PemReader; import java.io.ByteArrayInputStream; import java.io.StringReader; -import java.lang.reflect.Field; import java.security.cert.Certificate; import java.security.cert.CertificateFactory; import java.security.cert.X509Certificate; -import java.util.ArrayList; import java.util.HashMap; import java.util.LinkedList; import java.util.List; @@ -42,8 +38,7 @@ import java.util.concurrent.ThreadLocalRandom; import io.github.a13e300.tricky_store.Logger; -public final class Android { - private static final String TAG = "chiteroman"; +public final class CertHack { private static final ASN1ObjectIdentifier OID = new ASN1ObjectIdentifier("1.3.6.1.4.1.11129.2.1.17"); record KeyBox(PEMKeyPair privateKey, List certificates) {} @@ -55,13 +50,17 @@ public final class Android { try { certificateFactory = CertificateFactory.getInstance("X.509"); } catch (Throwable t) { - Log.e(TAG, t.toString()); + Logger.e("", t); throw new RuntimeException(t); } } public static void readFromXml(String data) { keyboxes.clear(); + if (data == null) { + Logger.i("clear all keyboxes"); + return; + } XMLParser xmlParser = new XMLParser(data); try { @@ -90,7 +89,7 @@ public final class Android { } keyboxes.put(algo, new KeyBox(parseKeyPair(privateKey), certificateChain)); } - Logger.d("update " + numberOfKeyboxes + " keyboxes"); + Logger.i("update " + numberOfKeyboxes + " keyboxes"); } catch (Throwable t) { Logger.e("Error loading xml file: " + t); } @@ -183,7 +182,7 @@ public final class Android { return certificates.toArray(new Certificate[0]); } catch (Throwable t) { - Log.e(TAG, t.toString()); + Logger.e("", t); } return caList; } diff --git a/service/src/main/java/io/github/a13e300/tricky_store/fwpatch/Utils.java b/service/src/main/java/io/github/a13e300/tricky_store/keystore/Utils.java similarity index 98% rename from service/src/main/java/io/github/a13e300/tricky_store/fwpatch/Utils.java rename to service/src/main/java/io/github/a13e300/tricky_store/keystore/Utils.java index 47b761e..a2821f1 100644 --- a/service/src/main/java/io/github/a13e300/tricky_store/fwpatch/Utils.java +++ b/service/src/main/java/io/github/a13e300/tricky_store/keystore/Utils.java @@ -1,4 +1,4 @@ -package io.github.a13e300.tricky_store.fwpatch; +package io.github.a13e300.tricky_store.keystore; import android.system.keystore2.KeyEntryResponse; import android.util.Log; diff --git a/service/src/main/java/io/github/a13e300/tricky_store/fwpatch/XMLParser.java b/service/src/main/java/io/github/a13e300/tricky_store/keystore/XMLParser.java similarity index 98% rename from service/src/main/java/io/github/a13e300/tricky_store/fwpatch/XMLParser.java rename to service/src/main/java/io/github/a13e300/tricky_store/keystore/XMLParser.java index 4c16ee0..6bcedb4 100644 --- a/service/src/main/java/io/github/a13e300/tricky_store/fwpatch/XMLParser.java +++ b/service/src/main/java/io/github/a13e300/tricky_store/keystore/XMLParser.java @@ -1,4 +1,4 @@ -package io.github.a13e300.tricky_store.fwpatch; +package io.github.a13e300.tricky_store.keystore; import org.xmlpull.v1.XmlPullParser; import org.xmlpull.v1.XmlPullParserException;