No transaction hook

This commit is contained in:
Nullptr 2023-07-17 14:00:21 +08:00
commit f809e78e6c
No known key found for this signature in database
9 changed files with 148 additions and 91 deletions

View file

@ -24,14 +24,6 @@ android {
buildConfig = true
viewBinding = true
}
applicationVariants.all {
kotlin {
sourceSets.getByName(name) {
kotlin.srcDir("build/generated/ksp/$name/kotlin")
}
}
}
}
kotlin {

View file

@ -43,6 +43,12 @@
</intent-filter>
</activity-alias>
<provider
android:name="icu.nullptr.hidemyapplist.service.ServiceProvider"
android:authorities="com.tsng.hidemyapplist.ServiceProvider"
android:exported="true"
tools:ignore="ExportedContentProvider" />
<meta-data
android:name="com.google.android.gms.ads.APPLICATION_ID"
android:value="ca-app-pub-7935522387526005~2750782344" />

View file

@ -1,5 +1,6 @@
package icu.nullptr.hidemyapplist.service
import android.os.IBinder
import android.os.IBinder.DeathRecipient
import android.os.Parcel
import android.os.RemoteException
@ -13,7 +14,7 @@ import java.lang.reflect.Proxy
object ServiceClient : IHMAService, DeathRecipient {
private const val TAG = "ServiceHelper"
private const val TAG = "ServiceClient"
private class ServiceProxy(private val obj: IHMAService) : InvocationHandler {
override fun invoke(proxy: Any?, method: Method, args: Array<out Any?>?): Any? {
@ -27,14 +28,16 @@ object ServiceClient : IHMAService, DeathRecipient {
@Volatile
private var service: IHMAService? = null
override fun binderDied() {
service = null
Log.e(TAG, "Binder died")
fun linkService(binder: IBinder) {
service = Proxy.newProxyInstance(
javaClass.classLoader,
arrayOf(IHMAService::class.java),
ServiceProxy(IHMAService.Stub.asInterface(binder))
) as IHMAService
binder.linkToDeath(this, 0)
}
override fun asBinder() = service?.asBinder()
private fun getService(): IHMAService? {
private fun getServiceLegacy(): IHMAService? {
if (service != null) return service
val pm = ServiceManager.getService("package")
val data = Parcel.obtain()
@ -65,21 +68,28 @@ object ServiceClient : IHMAService, DeathRecipient {
return service
}
override fun getServiceVersion() = getService()?.serviceVersion ?: 0
override fun binderDied() {
service = null
Log.e(TAG, "Binder died")
}
override fun getFilterCount() = getService()?.filterCount ?: 0
override fun asBinder() = service?.asBinder()
override fun getLogs() = getService()?.logs
override fun getServiceVersion() = getServiceLegacy()?.serviceVersion ?: 0
override fun getFilterCount() = getServiceLegacy()?.filterCount ?: 0
override fun getLogs() = getServiceLegacy()?.logs
override fun clearLogs() {
getService()?.clearLogs()
getServiceLegacy()?.clearLogs()
}
override fun syncConfig(json: String) {
getService()?.syncConfig(json)
getServiceLegacy()?.syncConfig(json)
}
override fun stopService(cleanEnv: Boolean) {
getService()?.stopService(cleanEnv)
getServiceLegacy()?.stopService(cleanEnv)
}
}

View file

@ -0,0 +1,28 @@
package icu.nullptr.hidemyapplist.service
import android.content.ContentProvider
import android.content.ContentValues
import android.net.Uri
import android.os.Bundle
class ServiceProvider : ContentProvider() {
override fun onCreate() = false
override fun query(p0: Uri, p1: Array<out String>?, p2: String?, p3: Array<out String>?, p4: String?) = null
override fun getType(p0: Uri) = null
override fun insert(p0: Uri, p1: ContentValues?) = null
override fun delete(p0: Uri, p1: String?, p2: Array<out String>?) = 0
override fun update(p0: Uri, p1: ContentValues?, p2: String?, p3: Array<out String>?) = 0
override fun call(method: String, arg: String?, extras: Bundle?): Bundle? {
if (callingPackage != "android" || extras == null) return null
val binder = extras.getBinder("binder") ?: return null
ServiceClient.linkService(binder)
return Bundle()
}
}

View file

@ -27,9 +27,9 @@ val minSdkVer by extra(24)
val targetSdkVer by extra(34)
val buildToolsVer by extra("34.0.0")
val appVerName by extra("3.1.1")
val appVerName by extra("3.2")
val configVerCode by extra(90)
val serviceVerCode by extra(95)
val serviceVerCode by extra(96)
val minBackupVerCode by extra(65)
val androidSourceCompatibility = JavaVersion.VERSION_17

View file

@ -5,6 +5,8 @@ import java.util.Set;
public class Constants {
public static final String APP_PACKAGE_NAME = "com.tsng.hidemyapplist";
public static final String PROVIDER_AUTHORITY = APP_PACKAGE_NAME + ".ServiceProvider";
public static final String GMS_PACKAGE_NAME = "com.google.android.gms";
public static final String GSF_PACKAGE_NAME = "com.google.android.gsf";
public static final String UPDATE_URL_BASE = "https://api.nullptr.icu/android/hide-my-applist/static/";

View file

@ -1,63 +0,0 @@
package icu.nullptr.hidemyapplist.xposed
import android.content.pm.IPackageManager
import android.os.Binder
import android.os.Parcel
import com.github.kyuubiran.ezxhelper.utils.findMethod
import com.github.kyuubiran.ezxhelper.utils.hookBefore
import icu.nullptr.hidemyapplist.common.BuildConfig
import icu.nullptr.hidemyapplist.common.Constants
object BridgeService {
private const val TAG = "HMA-Bridge"
private var appUid = 0
fun register(pms: IPackageManager) {
logI(TAG, "Initialize HMAService - Version ${BuildConfig.SERVICE_VERSION}")
val service = HMAService(pms)
appUid = Utils.getPackageUidCompat(service.pms, Constants.APP_PACKAGE_NAME, 0, 0)
val appPackage = Utils.getPackageInfoCompat(service.pms, Constants.APP_PACKAGE_NAME, 0, 0)
if (!Utils.verifyAppSignature(appPackage.applicationInfo.sourceDir)) {
logE(TAG, "Fatal: App signature mismatch")
return
}
logD(TAG, "Client uid: $appUid")
logI(TAG, "Initialize service proxy")
pms.javaClass.findMethod(true) {
name == "onTransact"
}.hookBefore { param ->
val code = param.args[0] as Int
val data = param.args[1] as Parcel
val reply = param.args[2] as Parcel?
if (myTransact(code, data, reply)) param.result = true
}
}
private fun myTransact(code: Int, data: Parcel, reply: Parcel?): Boolean {
if (code == Constants.TRANSACTION) {
if (Binder.getCallingUid() == appUid) {
logD(TAG, "Transaction from client")
runCatching {
data.enforceInterface(Constants.DESCRIPTOR)
when (data.readInt()) {
Constants.ACTION_GET_BINDER -> {
reply?.writeNoException()
reply?.writeStrongBinder(HMAService.instance)
return true
}
else -> logW(TAG, "Unknown action")
}
}.onFailure {
logE(TAG, "Transaction error", it)
}
} else {
logW(TAG, "Someone else trying to get my binder?")
}
data.setDataPosition(0)
reply?.setDataPosition(0)
}
return false
}
}

View file

@ -0,0 +1,78 @@
package icu.nullptr.hidemyapplist.xposed
import android.app.ActivityManagerHidden
import android.content.AttributionSource
import android.content.pm.IPackageManager
import android.os.Build
import android.os.Bundle
import android.os.ServiceManager
import icu.nullptr.hidemyapplist.common.BuildConfig
import icu.nullptr.hidemyapplist.common.Constants
import rikka.hidden.compat.ActivityManagerApis
import rikka.hidden.compat.adapter.UidObserverAdapter
object UserService {
private const val TAG = "HMA-UserService"
private var appUid = 0
private val uidObserver = object : UidObserverAdapter() {
override fun onUidActive(uid: Int) {
if (uid != appUid) return
try {
val provider = ActivityManagerApis.getContentProviderExternal(Constants.PROVIDER_AUTHORITY, 0, null, null)
if (provider == null) {
logE(TAG, "Failed to get provider")
return
}
val extras = Bundle()
extras.putBinder("binder", HMAService.instance)
val reply = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {
val attr = AttributionSource.Builder(1000).setPackageName("android").build()
provider.call(attr, Constants.PROVIDER_AUTHORITY, "", null, extras)
} else if (Build.VERSION.SDK_INT == Build.VERSION_CODES.R) {
provider.call("android", null, Constants.PROVIDER_AUTHORITY, "", null, extras)
} else if (Build.VERSION.SDK_INT == Build.VERSION_CODES.Q) {
provider.call("android", Constants.PROVIDER_AUTHORITY, "", null, extras)
} else {
provider.call("android", "", null, extras)
}
if (reply == null) {
logE(TAG, "Failed to send binder to app")
return
}
logI(TAG, "Send binder to app")
} catch (e: Throwable) {
logE(TAG, "onUidActive", e)
}
}
}
fun register(pms: IPackageManager) {
logI(TAG, "Initialize HMAService - Version ${BuildConfig.SERVICE_VERSION}")
val service = HMAService(pms)
appUid = Utils.getPackageUidCompat(service.pms, Constants.APP_PACKAGE_NAME, 0, 0)
val appPackage = Utils.getPackageInfoCompat(service.pms, Constants.APP_PACKAGE_NAME, 0, 0)
if (!Utils.verifyAppSignature(appPackage.applicationInfo.sourceDir)) {
logE(TAG, "Fatal: App signature mismatch")
return
}
logD(TAG, "Client uid: $appUid")
logI(TAG, "Register observer")
waitSystemService("activity")
ActivityManagerApis.registerUidObserver(
uidObserver,
ActivityManagerHidden.UID_OBSERVER_ACTIVE,
ActivityManagerHidden.PROCESS_STATE_UNKNOWN,
null
)
}
private fun waitSystemService(name: String) {
while (ServiceManager.getService(name) == null) {
Thread.sleep(1000)
}
}
}

View file

@ -8,9 +8,11 @@ import de.robv.android.xposed.IXposedHookZygoteInit
import de.robv.android.xposed.XC_MethodHook
import de.robv.android.xposed.callbacks.XC_LoadPackage
import icu.nullptr.hidemyapplist.common.Constants
import kotlin.concurrent.thread
private const val TAG = "HMA-XposedEntry"
@Suppress("unused")
class XposedEntry : IXposedHookZygoteInit, IXposedHookLoadPackage {
override fun initZygote(startupParam: IXposedHookZygoteInit.StartupParam) {
@ -35,11 +37,13 @@ class XposedEntry : IXposedHookZygoteInit, IXposedHookLoadPackage {
serviceManagerHook?.unhook()
val pms = param.args[1] as IPackageManager
logD(TAG, "Got pms: $pms")
runCatching {
BridgeService.register(pms)
logI(TAG, "Bridge service injected")
}.onFailure {
logE(TAG, "System service crashed", it)
thread {
runCatching {
UserService.register(pms)
logI(TAG, "User service started")
}.onFailure {
logE(TAG, "System service crashed", it)
}
}
}
}