Rewrite Xposed hooks

This commit is contained in:
topjohnwu 2019-11-01 07:16:00 -04:00
parent b632715b61
commit 880db8c1e5
11 changed files with 196 additions and 332 deletions

View File

@ -4,6 +4,7 @@
package="com.edison.fingerface">
<uses-permission android:name="android.permission.USE_BIOMETRIC" />
<uses-permission android:name="android.permission.USE_FINGERPRINT" />
<application
android:allowBackup="true"

View File

@ -0,0 +1,25 @@
@file:Suppress("DEPRECATION")
package android.hardware.fingerprint
import android.hardware.biometrics.BiometricPrompt
class MyAuthenticationCallback(private val callback: FingerprintManager.AuthenticationCallback) :
BiometricPrompt.AuthenticationCallback() {
override fun onAuthenticationError(errorCode: Int, errString: CharSequence) {
callback.onAuthenticationError(errorCode, errString)
}
override fun onAuthenticationHelp(helpCode: Int, helpString: CharSequence) {
callback.onAuthenticationHelp(helpCode, helpString)
}
override fun onAuthenticationFailed() {
callback.onAuthenticationFailed()
}
override fun onAuthenticationSucceeded(result: BiometricPrompt.AuthenticationResult) {
callback.onAuthenticationSucceeded(MyAuthenticationResult(result))
}
}

View File

@ -1,18 +0,0 @@
package android.hardware.fingerprint;
public class MyAuthenticationResult extends FingerprintManager.AuthenticationResult {
FingerprintManager.CryptoObject cryptoObject;
public void setCryptoObject(FingerprintManager.CryptoObject cryptoObject) {
this.cryptoObject = cryptoObject;
}
@Override
public FingerprintManager.CryptoObject getCryptoObject() {
if (cryptoObject != null) {
return cryptoObject;
}
return super.getCryptoObject();
}
}

View File

@ -0,0 +1,20 @@
@file:Suppress("DEPRECATION")
package android.hardware.fingerprint
import android.hardware.biometrics.BiometricPrompt
class MyAuthenticationResult(private val result: BiometricPrompt.AuthenticationResult) :
FingerprintManager.AuthenticationResult() {
private fun BiometricPrompt.CryptoObject.toLegacy() = when {
cipher != null -> FingerprintManager.CryptoObject(cipher)
mac != null -> FingerprintManager.CryptoObject(mac)
signature != null -> FingerprintManager.CryptoObject(signature)
else -> null
}
override fun getCryptoObject(): FingerprintManager.CryptoObject? {
return result.cryptoObject?.toLegacy()
}
}

View File

@ -1,175 +0,0 @@
package android.hardware.fingerprint;
import android.content.Context;
import android.content.DialogInterface;
import android.hardware.biometrics.BiometricPrompt;
import android.os.CancellationSignal;
import android.os.Handler;
import android.security.keystore.KeyGenParameterSpec;
import android.security.keystore.KeyPermanentlyInvalidatedException;
import android.security.keystore.KeyProperties;
import android.util.Log;
import javax.crypto.Cipher;
import javax.crypto.KeyGenerator;
import javax.crypto.NoSuchPaddingException;
import javax.crypto.SecretKey;
import java.io.IOException;
import java.security.*;
import java.security.cert.CertificateException;
public class MyFingerprintManager extends FingerprintManager {
private Context mContext;
private KeyGenerator keyGenerator;
private KeyStore keyStore;
private Cipher cipher;
private String KEY_NAME = "MyFingerprintManager";
public void SetContext(Context context) {
mContext = context;
}
@Override
public boolean isHardwareDetected() {
Log.d("XposedHandler", "isHardwareDetected");
return true;
}
@Override
public boolean hasEnrolledFingerprints() {
Log.d("XposedHandler", "hasEnrolledFingerprints");
return true;
}
@Override
public void authenticate(final CryptoObject crypto, CancellationSignal cancel,
int flags, final AuthenticationCallback callback, Handler handler) {
BiometricPrompt biometricPrompt = new BiometricPrompt.Builder(mContext).setTitle("FaceID").setNegativeButton(mContext.getString(android.R.string.cancel), mContext.getMainExecutor(), new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
dialog.dismiss();
}
}).build();
BiometricPrompt.CryptoObject c = null;
if (crypto == null) {
generateKey();
initCipher();
c = new BiometricPrompt.CryptoObject(cipher);
} else if (crypto.getCipher() != null) {
c = new BiometricPrompt.CryptoObject(crypto.getCipher());
} else if (crypto.getMac() != null) {
c = new BiometricPrompt.CryptoObject(crypto.getMac());
} else if (crypto.getSignature() != null) {
c = new BiometricPrompt.CryptoObject(crypto.getSignature());
}
biometricPrompt.authenticate(c, cancel, mContext.getMainExecutor(), new BiometricPrompt.AuthenticationCallback() {
@Override
public void onAuthenticationError(int errorCode, CharSequence errString) {
callback.onAuthenticationError(errorCode, errString);
}
@Override
public void onAuthenticationHelp(int helpCode, CharSequence helpString) {
callback.onAuthenticationHelp(helpCode, helpString);
}
@Override
public void onAuthenticationSucceeded(BiometricPrompt.AuthenticationResult result) {
BiometricPrompt.CryptoObject BioCrypto = result.getCryptoObject();
CryptoObject c = null;
if (BioCrypto.getCipher() != null) {
c = new CryptoObject(BioCrypto.getCipher());
} else if (BioCrypto.getMac() != null) {
c = new CryptoObject(BioCrypto.getMac());
} else if (BioCrypto.getSignature() != null) {
c = new CryptoObject(BioCrypto.getSignature());
}
AuthenticationResult fingerprintResult = new AuthenticationResult();
if (crypto != null) {
fingerprintResult = new MyAuthenticationResult();
((MyAuthenticationResult) fingerprintResult).setCryptoObject(c);
}
callback.onAuthenticationSucceeded(fingerprintResult);
}
@Override
public void onAuthenticationFailed() {
callback.onAuthenticationFailed();
}
});
}
private void generateKey() {
try {
keyStore = KeyStore.getInstance("AndroidKeyStore");
keyStore.load(null);
keyGenerator = KeyGenerator.getInstance(KeyProperties.KEY_ALGORITHM_AES, "AndroidKeyStore");
keyGenerator.init(new
KeyGenParameterSpec.Builder(KEY_NAME, KeyProperties.PURPOSE_ENCRYPT | KeyProperties.PURPOSE_DECRYPT)
.setBlockModes(KeyProperties.BLOCK_MODE_CBC)
.setUserAuthenticationRequired(true)
.setEncryptionPaddings(KeyProperties.ENCRYPTION_PADDING_PKCS7)
.build());
keyGenerator.generateKey();
} catch (KeyStoreException
| NoSuchAlgorithmException
| NoSuchProviderException
| InvalidAlgorithmParameterException
| CertificateException
| IOException exc) {
exc.printStackTrace();
}
}
private boolean initCipher() {
try {
cipher = Cipher.getInstance(
KeyProperties.KEY_ALGORITHM_AES + "/"
+ KeyProperties.BLOCK_MODE_CBC + "/"
+ KeyProperties.ENCRYPTION_PADDING_PKCS7);
} catch (NoSuchAlgorithmException |
NoSuchPaddingException e) {
throw new RuntimeException("Failed to get Cipher", e);
}
try {
keyStore.load(null);
SecretKey key = (SecretKey) keyStore.getKey(KEY_NAME,
null);
cipher.init(Cipher.ENCRYPT_MODE, key);
return true;
} catch (KeyPermanentlyInvalidatedException e) {
return false;
} catch (KeyStoreException | CertificateException
| UnrecoverableKeyException | IOException
| NoSuchAlgorithmException | InvalidKeyException e) {
throw new RuntimeException("Failed to init Cipher", e);
}
}
}

View File

@ -0,0 +1,75 @@
@file:Suppress("DEPRECATION")
package android.hardware.fingerprint
import android.content.Context
import android.content.DialogInterface
import android.hardware.biometrics.BiometricManager
import android.hardware.biometrics.BiometricPrompt
import android.os.CancellationSignal
import android.os.Handler
import android.util.Log
import com.edison.fingerface.HandlerExecutor
class MyFingerprintManager(private val context: Context) : FingerprintManager() {
private val manager = context.getSystemService(BiometricManager::class.java)!!
override fun isHardwareDetected(): Boolean {
Log.d(TAG, "isHardwareDetected")
return when (manager.canAuthenticate()) {
BiometricManager.BIOMETRIC_ERROR_NONE_ENROLLED,
BiometricManager.BIOMETRIC_SUCCESS -> true
BiometricManager.BIOMETRIC_ERROR_HW_UNAVAILABLE,
BiometricManager.BIOMETRIC_ERROR_NO_HARDWARE -> false
else -> false
}
}
override fun hasEnrolledFingerprints(): Boolean {
Log.d(TAG, "hasEnrolledFingerprints")
return when (manager.canAuthenticate()) {
BiometricManager.BIOMETRIC_SUCCESS -> true
BiometricManager.BIOMETRIC_ERROR_NONE_ENROLLED,
BiometricManager.BIOMETRIC_ERROR_HW_UNAVAILABLE,
BiometricManager.BIOMETRIC_ERROR_NO_HARDWARE -> false
else -> false
}
}
private fun CryptoObject.toBio() = when {
cipher != null -> BiometricPrompt.CryptoObject(cipher)
mac != null -> BiometricPrompt.CryptoObject(mac)
signature != null -> BiometricPrompt.CryptoObject(signature)
else -> null
}
override fun authenticate(
crypto: CryptoObject?, cancel: CancellationSignal?,
flags: Int, legacyCallback: AuthenticationCallback, handler: Handler?
) {
Log.d(TAG, "authenticate")
val bioCrypto = crypto?.toBio()
val cancelSignal = cancel ?: CancellationSignal()
val executor = handler?.let { HandlerExecutor(it) } ?: context.mainExecutor
val callback = MyAuthenticationCallback(legacyCallback)
val biometricPrompt = BiometricPrompt.Builder(context).setTitle("Fingerface")
.setNegativeButton(
context.getString(android.R.string.cancel),
executor,
DialogInterface.OnClickListener { dialog, _ -> dialog.dismiss() })
.build()
if (bioCrypto == null) {
biometricPrompt.authenticate(cancelSignal, executor, callback)
} else {
biometricPrompt.authenticate(bioCrypto, cancelSignal, executor, callback)
}
}
companion object {
const val TAG = "Fingerface"
}
}

View File

@ -0,0 +1,13 @@
package com.edison.fingerface
import android.os.Handler
import java.util.concurrent.Executor
import java.util.concurrent.RejectedExecutionException
class HandlerExecutor(private val handler: Handler) : Executor {
override fun execute(command: Runnable) {
if (!handler.post(command)) {
throw RejectedExecutionException("$handler is shutting down");
}
}
}

View File

@ -1,36 +0,0 @@
package com.edison.fingerface
import android.content.Context
import android.content.pm.PackageManager
import android.content.res.XModuleResources
import android.hardware.fingerprint.FingerprintManager
import android.util.Log
import de.robv.android.xposed.XC_MethodHook
import de.robv.android.xposed.XSharedPreferences
import de.robv.android.xposed.XposedBridge
import android.hardware.fingerprint.MyFingerprintManager
class ModFingerface {
companion object {
@JvmStatic
fun initAndroid(prefs: XSharedPreferences, classLoader: ClassLoader, modRes: XModuleResources) {
val getSystemServiceHook = object : XC_MethodHook() {
override fun afterHookedMethod(param: MethodHookParam?) {
if (param!!.args[0].toString() == Context.FINGERPRINT_SERVICE) {
Log.d("XposedHandler", "Get Fingerpring service, call from:${param!!.method.name}, thisObj:${param!!.result}")
val mgr = MyFingerprintManager()
mgr.SetContext(param!!.thisObject as Context)
param!!.result = mgr
}
}
}
XposedBridge.hookAllMethods(
Class.forName("android.app.ContextImpl"),
"getSystemService",
getSystemServiceHook
)
}
}
}

View File

@ -1,46 +0,0 @@
package com.edison.fingerface
import android.content.Context
import android.content.pm.PackageManager
import android.content.res.XModuleResources
import android.hardware.fingerprint.FingerprintManager
import android.util.Log
import de.robv.android.xposed.XC_MethodHook
import de.robv.android.xposed.XSharedPreferences
import de.robv.android.xposed.XposedBridge
import android.hardware.fingerprint.MyFingerprintManager
import de.robv.android.xposed.XposedHelpers
import android.R.attr.classLoader
class ModPackageManagerService {
companion object {
@JvmStatic
fun initAndroid(prefs: XSharedPreferences, classLoader: ClassLoader, modRes: XModuleResources) {
val hasSystemFeatureHook = object : XC_MethodHook() {
override fun afterHookedMethod(param: MethodHookParam?) {
if (param!!.args[0].toString() == PackageManager.FEATURE_FINGERPRINT) {
param!!.result = true
}
}
}
XposedHelpers.findAndHookMethod("android.app.ContextImpl", classLoader,
"getPackageManager", object : XC_MethodHook() {
@Throws(Throwable::class)
override fun afterHookedMethod(param: MethodHookParam?) {
if (param!!.result is PackageManager) {
val clazz = param.result.javaClass
val packageMethod = clazz.getDeclaredMethod("hasSystemFeature", String::class.java)
XposedBridge.hookMethod(packageMethod, hasSystemFeatureHook)
}
}
})
}
}
}

View File

@ -1,57 +0,0 @@
package com.edison.fingerface;
import android.app.AndroidAppHelper;
import android.content.Context;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
import android.content.pm.PermissionInfo;
import android.content.res.XModuleResources;
import android.content.res.XResources;
import android.util.Log;
import android.util.Pair;
import de.robv.android.xposed.*;
import de.robv.android.xposed.callbacks.XC_InitPackageResources;
import de.robv.android.xposed.callbacks.XC_LoadPackage;
import java.util.ArrayList;
import java.util.List;
public class XposedHandler implements IXposedHookZygoteInit, IXposedHookLoadPackage, IXposedHookInitPackageResources {
public static final String PACKAGE_NAME = XposedHandler.class.getPackage().getName();
private static final String TAG = "Fingerface";
private static XSharedPreferences prefs;
public static XModuleResources modRes;
private static String MODULE_PATH = null;
public static List<Pair<XResources, String>> packageResources = new ArrayList<>();
@Override
public void initZygote(StartupParam startupParam) throws Throwable {
MODULE_PATH = startupParam.modulePath;
modRes = XModuleResources.createInstance(MODULE_PATH, null);
prefs = new XSharedPreferences(PACKAGE_NAME);
}
@Override
public void handleInitPackageResources(XC_InitPackageResources.InitPackageResourcesParam resparam) throws Throwable {
if (resparam.packageName.equals("android")) {
return;
}
XModuleResources modRes = XModuleResources.createInstance(MODULE_PATH, resparam.res);
}
@Override
public void handleLoadPackage(XC_LoadPackage.LoadPackageParam lpparam) {
if (!lpparam.processName.equals("android") && !lpparam.processName.equals("com.android.systemui")) {
ModFingerface.initAndroid(prefs, lpparam.classLoader, modRes);
ModPackageManagerService.initAndroid(prefs, lpparam.classLoader, modRes);
}
}
}

View File

@ -0,0 +1,62 @@
package com.edison.fingerface
import android.content.Context
import android.content.pm.PackageManager
import android.content.res.XModuleResources
import android.hardware.fingerprint.MyFingerprintManager
import de.robv.android.xposed.*
import de.robv.android.xposed.callbacks.XC_LoadPackage
class XposedHandler : IXposedHookZygoteInit, IXposedHookLoadPackage {
private lateinit var modulePath: String
private lateinit var modRes: XModuleResources
private lateinit var prefs: XSharedPreferences
override fun initZygote(startupParam: IXposedHookZygoteInit.StartupParam) {
modulePath = startupParam.modulePath
modRes = XModuleResources.createInstance(modulePath, null)
prefs = XSharedPreferences(BuildConfig.APPLICATION_ID)
}
override fun handleLoadPackage(lpparam: XC_LoadPackage.LoadPackageParam) {
if (lpparam.processName != "android" && lpparam.processName != "com.android.systemui") {
hookFingerprintService(lpparam.classLoader)
hookPackageManager(lpparam.classLoader)
}
}
private fun hookFingerprintService(cl: ClassLoader) {
val getSystemServiceHook = object : XC_MethodHook() {
override fun afterHookedMethod(param: MethodHookParam) {
if (param.args[0].toString() == Context.FINGERPRINT_SERVICE) {
param.result = MyFingerprintManager(param.thisObject as Context)
}
}
}
XposedHelpers.findAndHookMethod(
"android.app.ContextImpl", cl,
"getSystemService", String::class.java,
getSystemServiceHook
)
}
private fun hookPackageManager(cl: ClassLoader) {
val hasSystemFeatureHook = object : XC_MethodHook() {
override fun afterHookedMethod(param: MethodHookParam) {
if (param.args[0].toString() == PackageManager.FEATURE_FINGERPRINT) {
param.result = true
}
}
}
XposedHelpers.findAndHookMethod(
"android.app.ApplicationPackageManager", cl,
"hasSystemFeature", String::class.java,
hasSystemFeatureHook
)
}
}