App-machtigingen verhogen met de Shizuku-bibliotheek

click fraud protection

Rooten is niet voor iedereen weggelegd. Hier ziet u hoe u verhoogde machtigingen op shell-niveau in uw app kunt krijgen met behulp van de Shizuku-bibliotheek.

Er zijn veel redenen waarom de machtigingen die normaal gesproken aan uw app worden verleend, mogelijk niet voldoende zijn. Misschien ben je net als ik en vind je het leuk om hacky-apps te maken die misbruik maken van de Android API. Sommige van de API's die ik gebruik zijn vergrendeld achter speciale machtigingen. Soms heeft alleen de shell-gebruiker (ADB) of het systeem er toegang toe. Er is echter een oplossing: Shizuku.

Met Shizuku kun je systeem-API's vrijwel rechtstreeks en volledig in Java of Kotlin aanroepen. Deze handleiding laat u zien hoe u Shizuku implementeert en gebruikt.

Wat is Shizuku?

Voordat we Shizuku gaan gebruiken, kan het handig zijn om te weten wat het precies is. Als je bekend bent met Magisk, dan is Shizuku vergelijkbaar. Maar in plaats van root-toegang te beheren, beheert het shell-toegang.

Shizuku voert zijn eigen proces uit met machtigingen op shell-niveau. Hoe de gebruiker dat proces activeert, hangt af van zijn apparaat, Android-versie en keuze. Shizuku kan worden geactiveerd via ADB, via draadloze ADB op het apparaat (

op Android 11 en hoger), of via root-toegang. Apps die Shizuku implementeren, kunnen vervolgens toestemming vragen om dat proces te gebruiken om verhoogde bewerkingen uit te voeren.

ShizukuOntwikkelaar: Xingchen & Rikka

Prijs: gratis.

4.1.

Downloaden

Waarom Shizuku?

Hoewel je met toegang tot het systeem op shell-niveau niet zoveel kunt doen wortel, het geeft je nog steeds meer toegang dan een normale app. Bovendien kun je dankzij de manier waarop Shizuku werkt de Android API's bijna zoals normaal gebruiken. U hoeft niet op shell-opdrachten te vertrouwen (hoewel u dat wel kunt als u dat wilt).

Als je app speciale machtigingen nodig heeft die alleen via ADB (of met root) kunnen worden verleend, vormen Shizuku en Android 11 een geweldige combinatie. Je kunt Shizuku gewoon gebruiken om speciale machtigingen volledig op het apparaat te verlenen.

Zelfs met apparaten die niet op Android 11 draaien, kan Shizuku handig zijn. De app biedt instructies en scripts voor gebruikers, zodat u dat niet hoeft te doen.

Integratie

Shizuku aan je app toevoegen is niet de eenvoudigste, maar ook niet moeilijk. Helaas, de documentatie voor ontwikkelaars is niet helemaal compleet, maar dit artikel heeft u gedekt. Hier leest u hoe u Shizuku in uw app kunt integreren.

Afhankelijkheden

De eerste stap is het toevoegen van de Shizuku-afhankelijkheden. Voeg in uw build.gradle op moduleniveau het volgende toe aan het afhankelijkhedenblok.

def shizuku_version = '11.0.3'

implementation "dev.rikka.shizuku: api:$shizuku_version"
implementation "dev.rikka.shizuku: provider:$shizuku_version"

Zorg ervoor dat u de versie indien nodig bijwerkt. 11.0.3 is de meest recente op het moment van schrijven.

Aanbieder

Om Shizuku te laten werken, moet je een providerblok toevoegen aan het manifest van je app. Open AndroidManifest.xml en voeg het volgende toe in het applicatieblok.

 android: name="rikka.shizuku.ShizukuProvider"
android: authorities="${applicationId}.shizuku"
android: multiprocess="false"
android: enabled="true"
android: exported="true"
android: permission="android.permission.INTERACT_ACROSS_USERS_FULL" />

Toestemming

Voor autorisatie gebruikt Shizuku een runtime-toestemming. We zullen zometeen op het verlenen van die toestemming ingaan. Voeg het voorlopig toe aan AndroidManifest.xml in het manifestblok.

Nu dat allemaal is toegevoegd, is de basisintegratie voltooid. Laat Gradle een projectsynchronisatie uitvoeren en ga verder met Gebruik.


Gebruik

Beschikbaarheid controleren

Voordat we ingaan op het gebruik van Shizuku, willen we eerst eerst nagaan of het daadwerkelijk beschikbaar is voor gebruik.

Voordat u controleert of de toestemming is verleend en voordat u API-aanroepen via Shizuku doet, kunt u er met de volgende methode voor zorgen dat deze controles en aanroepen slagen:

Shizuku.pingBinder()

Als Shizuku is geïnstalleerd en actief is, zal dit terugkeren WAAR. Anders wordt false geretourneerd.

Toestemming verlenen

Omdat Shizuku runtime-toestemming gebruikt, moet deze aan uw app worden verleend voordat u iets met shell-toegang kunt doen. Er zijn ook twee API-versies in omloop, met verschillende manieren om deze toe te kennen. In dit gedeelte wordt uitgelegd hoe u met beide kunt omgaan.

Controleren

Voordat u de toestemming aanvraagt, kunt u het beste controleren of u deze al heeft. Als u dat doet, kunt u doorgaan met alles wat u moet doen. Anders moet u dit aanvragen voordat u verdergaat.

Om te controleren of je toestemming hebt om Shizuku te gebruiken, kun je het volgende gebruiken. Deze code gaat ervan uit dat u deze binnen een activiteit uitvoert.

Kotlin:

val isGranted = if (Shizuku.isPreV11() || Shizuku.getVersion() < 11) {
checkSelfPermission(ShizukuProvider.PERMISSION) == PackageManager.PERMISSION_GRANTED
} else {
Shizuku.checkSelfPermission() = PackageManager.PERMISSION_GRANTED
}

Java:

boolean isGranted;
if (Shizuku.isPreV11() || Shizuku.getVersion() < 11) {
isGranted = checkSelfPermission(ShizukuProvider.PERMISSION) == PackageManager.PERMISSION_GRANTED;
} else {
isGranted = Shizuku.checkSelfPermission() == PackageManager.PERMISSION_GRANTED;
}

Verzoeken

Als je toestemming moet vragen om Shizuku te gebruiken, kun je dit als volgt doen.

De SHIZUKU_CODE De hieronder gebruikte variabele moet een geheel getal zijn met een vaste waarde (statische variabele).

Kotlin:

if (Shizuku.isPreV11() || Shizuku.getVersion() < 11) {
requestPermissions(arrayOf(ShizukuProvider.PERMISSION), SHIZUKU_CODE)
} else {
Shizuku.requestPermission(SHIZUKU_CODE)
}

Java:

if (Shizuku.isPreV11() || Shizuku.getVersion() < 11) {
requestPermissions(newString[] { ShizukuProvider.PERMISSION }, SHIZUKU_CODE);
} else {
Shizuku.requestPermissions(SHIZUKU_CODE);
}

Als u naar het resultaat wilt luisteren, moet u de activiteiten van Activiteit overschrijven onRequestPermissionsResult() methode. Je zult ook moeten implementeren Shizuku. OnRequestPermissionResultListener. In het voorbeeld dat ik ga laten zien, wordt ervan uitgegaan dat uw activiteit die interface implementeert, maar u kunt deze desgewenst in een variabele implementeren.

Kotlin:

classExampleActivity : AppCompatActivity, Shizuku.OnRequestPermissionResultListener {
...

override void onRequestPermissionsResult(requestCode: Int, permissions: Array<String>, grantResults: IntArray) {
permissions.forEachIndexed { index, permission ->
if (permission == ShizukuProvider.PERMISSION) {
onRequestPermissionResult(requestCode, grantResults[index])
}
}
}

override voidonRequestPermissionResult(requestCode: Int, grantResult: Int){
val isGranted = grantResult == PackageManager.PERMISSION_GRANTED
//Dostuff based on the result.

}
}

Java:

publicclassExampleActivityextendsAppCompatActivityimplementsShizuku.OnRequestPermissionResultListener{
...

@Override
publicvoidonRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults){
for (int i = 0; i < permissions.length; i++) {
String permission = permissions[i];
int result = grantResults[i];

if (permission.equals(ShizukuProvider.PERMISSION) {
onRequestPermissionResult(requestCode, result);
}
}
}

@Override
publicvoidonRequestPermissionResult(int requestCode, int grantResult){
boolean isGranted = grantResult == PackageManager.PERMISSION_GRANTED;
//Dostuff based on the result.
}
}

API's gebruiken

Nu Shizuku is ingesteld en machtigingen zijn verleend, kunt u daadwerkelijk API's gaan aanroepen met Shizuku. Het proces hier is een beetje anders dan u misschien gewend bent. In plaats van te bellen getSystemService() en casten naar zoiets als WindowManager, moet u hiervoor de interne API's gebruiken (bijv. IWindowManager).

Shizuku bevat een bypass voor de verborgen API-blacklist van Android, zodat u zich daar geen zorgen over hoeft te maken als u deze gebruikt. Als je echter nieuwsgierig bent hoe je dat zelf kunt omzeilen, bekijk dan mijn vorige tutorial.

Hier is een snel voorbeeld (met behulp van reflectie) dat laat zien hoe u een exemplaar kunt krijgen van IPackageManager en gebruik het om een ​​app runtime-toestemming te verlenen.

Kotlin:

val iPmClass = Class.forName("android.content.pm.IPackageManager")
val iPmStub = Class.forName("android.content.pm.IPackageManager\$Stub")
val asInterfaceMethod = iPmStub.getMethod("asInterface", IBinder::class.java)
val grantRuntimePermissionMethod = iPmClass.getMethod("grantRuntimePermission", String:: class.java /* package name */, String:: class.java /* permission name */, Int:: class.java /* user ID */)

val iPmInstance = asInterfaceMethod.invoke(null, ShizukuBinderWrapper(SystemServiceHelper.getSystemService("package")))

grantRuntimePermissionMethod.invoke(iPmInstance, "com.zacharee1.systemuituner", android.Manifest.permission.WRITE_SECURE_SETTINGS, 0)

Java:

Class< ?>> iPmClass = Class.forName("android.content.pm.IPackageManager");
Class< ?>> iPmStub = Class.forName("android.content.pm.IPackageManager$Stub");
Method asInterfaceMethod = iPmStub.getMethod("asInterface", IBinder.class);
Method grantRuntimePermissionMethod = iPmClass.getMethod("grantRuntimePermission", String.class, String.class, int.class);

val iPmInstance = asInterfaceMethod.invoke(null, new ShizukuBinderWrapper(SystemServiceHelper.getSystemService("package")));

grantRuntimePermissionMethod.invoke(iPmInstance, "com.zacharee1.systemuituner", android.Manifest.permission.WRITE_SECURE_SETTINGS, 0);

Het proces voor andere API's is vergelijkbaar. Haal de verwijzingen naar de klasse en de bijbehorende Stub-subklasse op. Haal de verwijzing naar de asInterface methode voor de Stub-klasse. Haal de verwijzingen naar de gewenste methoden op uit de klasse zelf. Roep dan de asInterface methodereferentie die u hebt, vervangen "package" hierboven met welke service u ook nodig heeft. Die instantie kan vervolgens worden doorgegeven voor methodeaanroepen.

Pro-tip: u kunt het gebruik van reflectie helemaal vermijden als u een aangepaste SDK installeert. Bekijk de GitHub-repository van anggrayudi voor de gewijzigde SDK's en installatie-instructies. Als dit is geïnstalleerd, wordt de bovenstaande code (in Kotlin) een stuk eenvoudiger.

val iPm = IPackageManager.Stub.asInterface(ShizukuBinderWrapper(SystemServiceHelper.getService("package"))))
iPm.grantRuntimePermission("com.zacharee1.systemuituner", android.Manifest.permission.WRITE_SECURE_SETTINGS, 0)

Alle op AIDL gebaseerde API's kunnen met deze methode overal in uw app worden gebruikt, zolang Shizuku actief is en uw app toestemming heeft.

Gebruikersservice

Hoewel de Binder-methode de meeste gebruiksscenario's dekt, kunnen er momenten zijn waarop u een API nodig heeft die geen directe Binder-interface heeft. In dat geval kun je gebruik maken van de Gebruikersservice in Shizuku.

Deze methode werkt op dezelfde manier als een normale service in Android. U "start" het, communiceert door eraan te binden met een ServiceConnection en voert uw logica uit in de serviceklasse. Het verschil is dat u de Android-service niet gebruikt en dat alles binnen de service wordt uitgevoerd met ADB-machtigingen.

Nu zijn er enkele beperkingen. De gebruikersservice wordt uitgevoerd in een volledig gescheiden proces en gebruiker, zodat u alleen via uw eigen AIDL-callbacks en binders met de rest van uw app kunt communiceren. Omdat het ook niet in een correct app-proces wordt uitgevoerd, werken sommige zaken, zoals het binden van Android-services, mogelijk niet correct.

Hiervoor is ook Shizuku versie 10 of hoger vereist. Hoewel de meeste bronnen voor de app momenteel versie 11 hebben, moet u toch een versiecontrole opnemen, die in het voorbeeld zal worden opgenomen.

Een AIDL definiëren

Om aan de slag te gaan, moet u een nieuw AIDL-bestand maken. U kunt dit in Android Studio doen door met de rechtermuisknop op iets in de Android-bestandsweergave te klikken, de muisaanwijzer op de optie "Nieuw" te plaatsen en de optie "AIDL" te kiezen. Voer de naam van het bestand in (bijvoorbeeld 'IUserService') en Android Studio maakt een sjabloonbestand voor u.

Als u niet bekend bent met hoe AIDL's werken, kijk dan eens naar dit artikel Documentatie van Google.

Verwijder de sjabloonmethoden uit de AIDL en voeg vervolgens a toe destroy() methode met de juiste ID voor Shizuku. Dit wordt aangeroepen wanneer Shizuku de gebruikersservice beëindigt en moet worden gebruikt om eventuele verwijzingen of doorlopende logica op te schonen.

Voorbeeld AIDL:

packagetk.zwander.shizukudemo;

interfaceIUserService{
voiddestroy()= 16777114;

void grantPermission(String packageName, String permission, int userId) = 1; //You can specify your own method IDs in the AIDL. Check out the documentation for more details on this.
}

Implementatie van de AIDL

Het volgende wat we moeten doen is de AIDL daadwerkelijk implementeren.

Java:

publicclassUserServiceextendsIUserService.Stub{
//Make sure to include an empty constructor.
publicUserService(){
}

@Override
publicvoiddestroy(){
//Shizuku wants the service to be killed. Clean up and then exit.
System.exit(0);
}

@Override
publicvoidgrantPermission(String packageName, String permission, int userId){
//No need to use ShizukuBinderWrapper.
IPackageManager.Stub.asInterface(SystemServiceHelper.getService("package")).grantRuntimePermission(packageName, permission, userId);
}
}

Kotlin:

classUserService : IUserService.Stub {
//Include an empty constructor.
constructor() {
}

override fun destroy(){
//Shizuku wants the service to be killed. Clean up and exit.
System.exit(0)
}

override fun grantPermission(packageName: String, permission: String, userId: Int) {
//No need for ShizukuBinderWrapper.
IPackageManager.Stub.asInterface(SystemServiceHelper.getService("package")).grantRuntimePermission(packageName, permission, userId)
}
}

Bij de bovenstaande voorbeelden wordt ervan uitgegaan dat u de Verborgen Android-API SDK geïnstalleerd.

De serviceverbinding instellen

Nu de Gebruikersservice is gedefinieerd en geïmplementeerd, is het tijd om deze in te stellen voor gebruik. Het eerste dat u moet doen, is een ServiceConnection definiëren waarmee u ermee wilt communiceren (bijvoorbeeld vanuit de hoofdactiviteit in uw app).

Java:

private IUserService binder = null;

privatefinal ServiceConnection connection = new ServiceConnection() {
@Override
publicvoidonServiceConnected(ComponentName componentName, IBinder binder){
if (binder != null && binder.pingBinder()) {
binder = IUserService.Stub.asInterface(binder);
}
}

@Override
publicvoidonServiceDisconnected(ComponentName componentName){
binder = null;
}
}

Kotlin:

privatevar binder: IUserService? = null

private val connection = object: ServiceConnection {
override fun onServiceConnected(componentName: ComponentName, binder: IBinder?){
if (binder != null && binder.pingBinder()) {
binder = IUserService.Stub.asInterface(binder)
}
}

override fun onServiceDisconnected(componentName: ComponentName){
binder = null;
}
}

De binder variabele is wat u gaat gebruiken om vanuit uw app met de gebruikersservice te communiceren. Om te controleren of het beschikbaar is voor gebruik, controleert u gewoon of het niet nul is en zo pingBinder() geeft terug WAAR, net als in het codevoorbeeld hierboven.

De argumenten voor de gebruikersservice maken

Voordat u de Gebruikersservice kunt beheren, moet u enkele argumenten definiëren die Shizuku moet gebruiken bij het starten en stoppen ervan. Deze omvatten zaken als het feitelijk aan Shizuku vertellen van de klassenaam van de service, het specificeren van het procesachtervoegsel, of er wel of niet fouten kunnen worden opgespoord en welke versie het is.

Java:

privatefinal Shizuku.UserServiceArgs serviceArgs = new Shizuku.UserServiceArgs(
newComponentName(BuildConfig.APPLICATION_ID, UserService.class.getName()))
.processNameSuffix("user_service")
.debuggable(BuildConfig.DEBUG)
.version(BuildConfig.VERSION_CODE);

Kotlin:

private val serviceArgs = Shizuku.UserServiceArgs(
ComponentName(BuildConfig.APPLICATION_ID, UserService.class::java.getName()))
.processNameSuffix("user_service")
.debuggable(BuildCOnfig.DEBUG)
.version(BuildConfig.VERSION_CODE)

De gebruikersservice starten, stoppen en binden

De start- en bindacties en de stop- en unbind-acties zijn verenigd in Shizuku. Er zijn geen afzonderlijke start- en bindmethoden of stop- en unbind-methoden.

Hier leest u hoe u dat kunt doen starten en binden de Gebruikersservice.

Java:

if (Shizuku.getVersion >= 10) {
//This only works on Shizuku 10 or later.
Shizuku.bindUserService(serviceArgs, connection);
} else {
//Tell the user to upgrade Shizuku.
}

Kotlin:

if (Shizuku.getVersion() >= 10) {
//This only works on Shizuku 10 or later.
Shizuku.bindUserService(serviceArgs, connection)
} else {
//Tell the user to upgrade Shizuku.
}

Hier leest u hoe u dat kunt doen stoppen en losmaken de Gebruikersservice.

Java:

if (Shizuku.getVersion >= 10) {
Shizuku.unbindUserService(serviceArgs, connection, true);
}

Kotlin:

if (Shizuku.getVersion >= 10) {
Shizuku.unbindUserService(serviceArgs, connection, true)
}

Een beroep doen op de Gebruikersservice

Zodra de Gebruikersservice is gestart, kunt u deze gaan gebruiken. Controleer eenvoudig of de binder variabele niet-null en pingbaar is, en voer vervolgens uw methodeaanroep uit.

Java:

if (binder != null && binder.pingBinder()) {
binder.grantPermission("com.zacharee1.systemuituner", android.Manifest.permission.WRITE_SECURE_SETTINGS, 0);
}

Kotlin:

if (binder?.pingBinder() == true) {
binder?.grantPermission("com.zacharee1.systemuituner", android.Manifest.permission.WRITE_SECURE_SETTINGS, 0)
}

Conclusie

Als je dit allemaal hebt gevolgd, zou je nu een werkende Shizuku-integratie moeten hebben. Vergeet niet om uw gebruikers te vertellen dat ze Shizuku moeten installeren en goed te controleren of Shizuku beschikbaar is voordat u het probeert te gebruiken.