No transaction hook
This commit is contained in:
parent
721dcb8dbc
commit
f809e78e6c
9 changed files with 148 additions and 91 deletions
|
|
@ -24,14 +24,6 @@ android {
|
|||
buildConfig = true
|
||||
viewBinding = true
|
||||
}
|
||||
|
||||
applicationVariants.all {
|
||||
kotlin {
|
||||
sourceSets.getByName(name) {
|
||||
kotlin.srcDir("build/generated/ksp/$name/kotlin")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
kotlin {
|
||||
|
|
|
|||
|
|
@ -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" />
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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()
|
||||
}
|
||||
}
|
||||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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/";
|
||||
|
|
|
|||
|
|
@ -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
|
||||
}
|
||||
}
|
||||
|
|
@ -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)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in a new issue