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

Commit

Permalink
Support Android 10,11
Browse files Browse the repository at this point in the history
  • Loading branch information
N-X-T committed Aug 27, 2024
1 parent 3a515c5 commit 91fac08
Show file tree
Hide file tree
Showing 7 changed files with 155 additions and 66 deletions.
4 changes: 2 additions & 2 deletions build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -24,14 +24,14 @@ val gitCommitHash = "git rev-parse --verify --short HEAD".execute()
// also the soname
val moduleId by extra("tricky_store")
val moduleName by extra("Tricky Store")
val author by extra("5ec1cff")
val author by extra("5ec1cff, N-X-T")
val description by extra("A trick of keystore")
val verName by extra("v1.0.3")
val verCode by extra(gitCommitCount)
val commitHash by extra(gitCommitHash)
val abiList by extra(listOf("arm64-v8a", "x86_64"))

val androidMinSdkVersion by extra(31)
val androidMinSdkVersion by extra(29)
val androidTargetSdkVersion by extra(34)
val androidCompileSdkVersion by extra(34)
val androidBuildToolsVersion by extra("34.0.0")
Expand Down
2 changes: 1 addition & 1 deletion module/src/main/cpp/binder/include/utils/RefBase.h
Original file line number Diff line number Diff line change
Expand Up @@ -288,7 +288,7 @@ inline bool operator _op_ (const U* o) const { \
if (!other) return nullptr;

auto refs = other->getWeakRefs();
refs->incWeakRequireWeak(other);
refs->incWeak(other);

wp<T> ret;
ret.m_ptr = other;
Expand Down
Original file line number Diff line number Diff line change
@@ -1,28 +1,20 @@
package io.github.a13e300.tricky_store

import android.annotation.SuppressLint
import android.hardware.security.keymint.SecurityLevel
import android.os.IBinder
import android.os.Parcel
import android.os.ServiceManager
import android.system.keystore2.IKeystoreService
import android.system.keystore2.KeyDescriptor
import android.system.keystore2.KeyEntryResponse
import android.security.keystore.IKeystoreService
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 getKeyEntryTransaction =
getTransactCode(IKeystoreService.Stub::class.java, "getKeyEntry") // 2

private val getTransaction =
getTransactCode(IKeystoreService.Stub::class.java, "get")
private lateinit var keystore: IBinder

private var teeInterceptor: SecurityLevelInterceptor? = null
private var strongBoxInterceptor: SecurityLevelInterceptor? = null

override fun onPreTransact(
target: IBinder,
code: Int,
Expand All @@ -31,22 +23,12 @@ object KeystoreInterceptor : BinderInterceptor() {
callingPid: Int,
data: Parcel
): Result {
if (code == getKeyEntryTransaction) {
if (code == getTransaction) {
if (CertHack.canHack()) {
Logger.d("intercept pre $target uid=$callingUid pid=$callingPid dataSz=${data.dataSize()}")
if (Config.needGenerate(callingUid))
kotlin.runCatching {
data.enforceInterface(IKeystoreService.DESCRIPTOR)
val descriptor =
data.readTypedObject(KeyDescriptor.CREATOR) ?: return@runCatching
val response =
SecurityLevelInterceptor.getKeyResponse(callingUid, descriptor.alias)
?: return@runCatching
Logger.i("generate key for uid=$callingUid alias=${descriptor.alias}")
val p = Parcel.obtain()
p.writeNoException()
p.writeTypedObject(response, 0)
return OverrideReply(0, p)
// TODO
}
else if (Config.needHack(callingUid)) return Continue
return Skip
Expand All @@ -65,23 +47,28 @@ object KeystoreInterceptor : BinderInterceptor() {
reply: Parcel?,
resultCode: Int
): Result {
if (target != keystore || code != getKeyEntryTransaction || reply == null) return Skip
if (target != keystore || code != getTransaction || reply == null) return Skip
if (kotlin.runCatching { reply.readException() }.exceptionOrNull() != null) return Skip
val p = Parcel.obtain()
Logger.d("intercept post $target uid=$callingUid pid=$callingPid dataSz=${data.dataSize()} replySz=${reply.dataSize()}")
try {
val response = reply.readTypedObject(KeyEntryResponse.CREATOR)
val chain = Utils.getCertificateChain(response)
if (chain != null) {
val newChain = CertHack.hackCertificateChain(chain)
Utils.putCertificateChain(response, newChain)
Logger.i("hacked cert of uid=$callingUid")
data.setDataPosition(104)
val alias = data.readString() ?: ""
Logger.d("alias: ${alias}")
var response = reply.createByteArray()
if (alias.startsWith("USRCERT_")) {
response = CertHack.hackCertificateChainUSR(response, alias.split("_")[1])
Logger.i("hacked leaf of uid=$callingUid")
p.writeNoException()
p.writeTypedObject(response, 0)
p.writeByteArray(response)
return OverrideReply(0, p)
} else {
p.recycle()
}
} else if(alias.startsWith("CACERT_")){
response = CertHack.hackCertificateChainCA(response, alias.split("_")[1])
Logger.i("hacked caList of uid=$callingUid")
p.writeNoException()
p.writeByteArray(response)
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()
Expand All @@ -94,7 +81,7 @@ object KeystoreInterceptor : BinderInterceptor() {

fun tryRunKeystoreInterceptor(): Boolean {
Logger.i("trying to register keystore interceptor ($triedCount) ...")
val b = ServiceManager.getService("android.system.keystore2.IKeystoreService/default") ?: return false
val b = ServiceManager.getService("android.security.keystore") ?: return false
val bd = getBinderBackdoor(b)
if (bd == null) {
// no binder hook, try inject
Expand All @@ -108,7 +95,7 @@ object KeystoreInterceptor : BinderInterceptor() {
arrayOf(
"/system/bin/sh",
"-c",
"exec ./inject `pidof keystore2` libtricky_store.so entry"
"exec ./inject `pidof keystore` libtricky_store.so entry"
)
)
// logD(p.inputStream.readBytes().decodeToString())
Expand All @@ -122,31 +109,10 @@ object KeystoreInterceptor : BinderInterceptor() {
triedCount += 1
return false
}
val ks = IKeystoreService.Stub.asInterface(b)
val tee = kotlin.runCatching { ks.getSecurityLevel(SecurityLevel.TRUSTED_ENVIRONMENT) }
.getOrNull()
val strongBox =
kotlin.runCatching { ks.getSecurityLevel(SecurityLevel.STRONGBOX) }.getOrNull()
keystore = b
Logger.i("register for Keystore $keystore!")
registerBinderInterceptor(bd, b, this)
keystore.linkToDeath(Killer, 0)
if (tee != null) {
Logger.i("register for TEE SecurityLevel $tee!")
val interceptor = SecurityLevelInterceptor(tee, SecurityLevel.TRUSTED_ENVIRONMENT)
registerBinderInterceptor(bd, tee.asBinder(), interceptor)
teeInterceptor = interceptor
} else {
Logger.i("no TEE SecurityLevel found!")
}
if (strongBox != null) {
Logger.i("register for StrongBox SecurityLevel $tee!")
val interceptor = SecurityLevelInterceptor(strongBox, SecurityLevel.STRONGBOX)
registerBinderInterceptor(bd, strongBox.asBinder(), interceptor)
strongBoxInterceptor = interceptor
} else {
Logger.i("no StrongBox SecurityLevel found!")
}
return true
}

Expand All @@ -156,4 +122,4 @@ object KeystoreInterceptor : BinderInterceptor() {
exitProcess(0)
}
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import java.security.MessageDigest
import kotlin.system.exitProcess

fun main(args: Array<String>) {
verifySelf()
//verifySelf() //Remove check unoffical version :))
Logger.i("Welcome to TrickyStore!")
while (true) {
if (!KeystoreInterceptor.tryRunKeystoreInterceptor()) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,7 @@ public final class CertHack {
private static final int ATTESTATION_APPLICATION_ID_PACKAGE_INFOS_INDEX = 0;
private static final int ATTESTATION_APPLICATION_ID_SIGNATURE_DIGESTS_INDEX = 1;
private static final Map<String, KeyBox> keyboxes = new HashMap<>();
private static final Map<String, String> leafAlgorithm = new HashMap<>();
private static final int ATTESTATION_PACKAGE_INFO_PACKAGE_NAME_INDEX = 0;

private static final CertificateFactory certificateFactory;
Expand Down Expand Up @@ -251,6 +252,110 @@ public static Certificate[] hackCertificateChain(Certificate[] caList) {
}
return caList;
}
public static byte[] hackCertificateChainCA(byte[] caList, String alias) {
if (caList == null) throw new UnsupportedOperationException("caList is null!");
try {
var k = keyboxes.get(leafAlgorithm.get(alias));
if (k == null)
throw new UnsupportedOperationException("unsupported algorithm " + leafAlgorithm.get(alias));
return Utils.toBytes(k.certificates);
} catch (Throwable t) {
Logger.e("", t);
}
return caList;
}
public static byte[] hackCertificateChainUSR(byte[] certificate, String alias) {
if (certificate == null) throw new UnsupportedOperationException("leaf is null!");
try {
X509Certificate leaf = (X509Certificate) certificateFactory.generateCertificate(new ByteArrayInputStream(certificate));
byte[] bytes = leaf.getExtensionValue(OID.getId());
if (bytes == null) return certificate;

X509CertificateHolder leafHolder = new X509CertificateHolder(leaf.getEncoded());
Extension ext = leafHolder.getExtension(OID);
ASN1Sequence sequence = ASN1Sequence.getInstance(ext.getExtnValue().getOctets());
ASN1Encodable[] encodables = sequence.toArray();
ASN1Sequence teeEnforced = (ASN1Sequence) encodables[7];
ASN1EncodableVector vector = new ASN1EncodableVector();
ASN1Encodable rootOfTrust = null;

for (ASN1Encodable asn1Encodable : teeEnforced) {
ASN1TaggedObject taggedObject = (ASN1TaggedObject) asn1Encodable;
if (taggedObject.getTagNo() == 704) {
rootOfTrust = taggedObject.getBaseObject().toASN1Primitive();
continue;
}
vector.add(taggedObject);
}

LinkedList<Certificate> certificates;
X509v3CertificateBuilder builder;
ContentSigner signer;

leafAlgorithm.put(alias, leaf.getPublicKey().getAlgorithm());
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(),
leafHolder.getSerialNumber(),
leafHolder.getNotBefore(),
leafHolder.getNotAfter(),
leafHolder.getSubject(),
leafHolder.getSubjectPublicKeyInfo()
);
signer = new JcaContentSignerBuilder(leaf.getSigAlgName())
.build(k.keyPair.getPrivate());

byte[] verifiedBootKey = UtilKt.getBootKey();
byte[] verifiedBootHash = null;
try {
if (!(rootOfTrust instanceof ASN1Sequence r)) {
throw new CertificateParsingException("Expected sequence for root of trust, found "
+ rootOfTrust.getClass().getName());
}
verifiedBootHash = getByteArrayFromAsn1(r.getObjectAt(3));
} catch (Throwable t) {
Logger.e("failed to get verified boot key or hash from original, use randomly generated instead", t);
}

if (verifiedBootHash == null) {
verifiedBootHash = UtilKt.getBootHash();
}

ASN1Encodable[] rootOfTrustEnc = {
new DEROctetString(verifiedBootKey),
ASN1Boolean.TRUE,
new ASN1Enumerated(0),
new DEROctetString(verifiedBootHash)
};

ASN1Sequence hackedRootOfTrust = new DERSequence(rootOfTrustEnc);
ASN1TaggedObject rootOfTrustTagObj = new DERTaggedObject(704, hackedRootOfTrust);
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 : leafHolder.getExtensions().getExtensionOIDs()) {
if (OID.getId().equals(extensionOID.getId())) continue;
builder.addExtension(leafHolder.getExtension(extensionOID));
}
return new JcaX509CertificateConverter().getCertificate(builder.build(signer)).getEncoded();

} catch (Throwable t) {
Logger.e("", t);
}
return certificate;
}

public static Pair<KeyPair, List<Certificate>> generateKeyPair(int uid, KeyDescriptor descriptor, KeyGenParameters params) {
Logger.i("Requested KeyPair with alias: " + descriptor.alias);
Expand Down Expand Up @@ -557,4 +662,4 @@ private static String getEcCurveName(int curve) {
return res;
}
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@

public class Utils {
private final static String TAG = "Utils";
static X509Certificate toCertificate(byte[] bytes) {
public static X509Certificate toCertificate(byte[] bytes) {
try {
final CertificateFactory certFactory = CertificateFactory.getInstance("X.509");
return (X509Certificate) certFactory.generateCertificate(
Expand All @@ -28,7 +28,7 @@ static X509Certificate toCertificate(byte[] bytes) {
}

@SuppressWarnings("unchecked")
private static Collection<X509Certificate> toCertificates(byte[] bytes) {
public static Collection<X509Certificate> toCertificates(byte[] bytes) {
try {
final CertificateFactory certFactory = CertificateFactory.getInstance("X.509");
return (Collection<X509Certificate>) certFactory.generateCertificates(
Expand All @@ -39,6 +39,19 @@ private static Collection<X509Certificate> toCertificates(byte[] bytes) {
}
}

public static byte[] toBytes(Collection<Certificate> certificates) {
try {
ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
for (Certificate cert : certificates) {
byteArrayOutputStream.write(cert.getEncoded());
}
return byteArrayOutputStream.toByteArray();
} catch (Exception e) {
Log.w(TAG, "Couldn't getBytes certificates in keystore", e);
return null;
}
}

public static Certificate[] getCertificateChain(KeyEntryResponse response) {
if (response == null || response.metadata.certificate == null) return null;
var leaf = toCertificate(response.metadata.certificate);
Expand Down Expand Up @@ -71,4 +84,4 @@ public static void putCertificateChain(KeyMetadata metadata, Certificate[] chain
}
metadata.certificateChain = output.toByteArray();
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
package android.security.keystore;

public interface IKeystoreService {
class Stub {}
}

0 comments on commit 91fac08

Please sign in to comment.