التجذير ليس للجميع. إليك كيفية الحصول على أذونات مرتفعة على مستوى Shell في تطبيقك باستخدام مكتبة Shizuku.
هناك الكثير من الأسباب التي قد تجعل الأذونات الممنوحة عادةً لتطبيقك غير كافية. ربما أنت مثلي وتستمتع بإنشاء تطبيقات مخترقة تسيء استخدام Android API. بعض واجهات برمجة التطبيقات التي أستخدمها مقفلة خلف أذونات خاصة. في بعض الأحيان، يمكن فقط لمستخدم Shell (ADB) أو النظام الوصول إليها. بالرغم من ذلك، هناك حل — شيزوكو.
يتيح لك Shizuku الاتصال بواجهات برمجة تطبيقات النظام بشكل مباشر تقريبًا، وبشكل كامل في Java أو Kotlin. سيوضح لك هذا الدليل كيفية تنفيذ Shizuku واستخدامه.
ما هو شيزوكو؟
قبل أن نبدأ باستخدام شيزوكو، قد يكون من المفيد أن نعرف ما هو بالضبط. إذا كنت على دراية ماجيسك، فإن شيزوكو مشابه. ولكن بدلاً من إدارة الوصول إلى الجذر، فإنه يدير الوصول إلى shell.
تدير Shizuku عمليتها الخاصة بأذونات على مستوى الصدفة. تعتمد كيفية تنشيط المستخدم لهذه العملية على جهازه وإصدار Android واختياره. يمكن تنشيط Shizuku من خلال ADB، من خلال ADB اللاسلكي الموجود على الجهاز (على نظام Android 11 والإصدارات الأحدث
)، أو من خلال الوصول إلى الجذر. يمكن للتطبيقات التي تطبق Shizuku أن تطلب إذنًا لاستخدام هذه العملية لإجراء عمليات مرتفعة.مجاني.
4.1.
لماذا شيزوكو؟
في حين أن الوصول إلى النظام على مستوى الصدفة لا يتيح لك القيام بالقدر نفسه جذر، فإنه لا يزال يمنحك وصولاً أكبر من التطبيق العادي. علاوة على ذلك، فإن الطريقة التي يعمل بها Shizuku تمكنك من استخدام واجهات برمجة تطبيقات Android بشكل عادي تقريبًا. لا يتعين عليك الاعتماد على أوامر shell (على الرغم من أنك تستطيع ذلك إذا أردت ذلك).
إذا كان تطبيقك يحتاج إلى أذونات خاصة لا يمكن منحها إلا من خلال ADB (أو باستخدام الجذر)، فإن Shizuku وAndroid 11 يشكلان اقترانًا رائعًا. يمكنك فقط استخدام Shizuku لمنح أذونات خاصة بشكل كامل على الجهاز.
حتى مع الأجهزة التي لا تعمل بنظام Android 11، يمكن أن يكون Shizuku مفيدًا. يوفر التطبيق تعليمات ونصوصًا برمجية للمستخدمين حتى لا تضطر إلى ذلك.
اندماج
إن إضافة Shizuku إلى تطبيقك ليس بالأمر الأسهل، ولكنه ليس بالأمر الصعب أيضًا. لسوء الحظ، ال وثائق المطور ليست كاملة تمامًا، ولكن هذه المقالة قد قمت بتغطيتها. إليك كيفية دمج Shizuku في تطبيقك.
التبعيات
الخطوة الأولى هي إضافة تبعيات شيزوكو. في build.gradle على مستوى الوحدة، أضف ما يلي إلى كتلة التبعيات.
def shizuku_version = '11.0.3'
implementation "dev.rikka.shizuku: api:$shizuku_version"
implementation "dev.rikka.shizuku: provider:$shizuku_version"
تأكد من تحديث الإصدار إذا لزم الأمر. 11.0.3 هو الأحدث في وقت كتابة هذا التقرير.
مزود
لكي يعمل Shizuku، تحتاج إلى إضافة كتلة موفر إلى بيان تطبيقك. افتح AndroidManifest.xml وأضف ما يلي داخل كتلة التطبيق.
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" />
إذن
للحصول على إذن، يستخدم Shizuku إذن وقت التشغيل. سنبدأ في منح هذا الإذن بعد قليل. في الوقت الحالي، قم بإضافته إلى AndroidManifest.xml داخل كتلة البيان.
والآن بعد أن تم إضافة كل ذلك، تم التكامل الأساسي. اسمح لـ Gradle بإجراء مزامنة المشروع، واستمر في الاستخدام.
الاستخدام
التحقق من التوفر
قبل الخوض في كيفية استخدام Shizuku، دعنا نتحدث عن التأكد من أنه متاح للاستخدام فعليًا.
قبل التحقق لمعرفة ما إذا تم منح الإذن، وقبل إجراء استدعاءات واجهة برمجة التطبيقات من خلال Shizuku، يمكنك التأكد من نجاح عمليات التحقق والاستدعاءات هذه بالطريقة التالية:
Shizuku.pingBinder()
إذا تم تثبيت Shizuku وتشغيله، فسيعود هذا حقيقي. وإلا فإنه سيعود كاذبا.
منح الإذن
نظرًا لأن Shizuku يستخدم إذن وقت التشغيل، فيجب منحه لتطبيقك قبل أن تتمكن من القيام بأي شيء باستخدام الوصول إلى Shell. هناك أيضًا إصداران من واجهة برمجة التطبيقات قيد التداول، مع طرق مختلفة لمنحها. سيوضح لك هذا القسم كيفية التعامل مع كليهما.
تدقيق
قبل أن تطلب الإذن، أفضل ما يمكنك فعله هو التحقق مما إذا كان لديك الإذن بالفعل. إذا قمت بذلك، يمكنك الاستمرار في كل ما عليك القيام به. بخلاف ذلك، ستحتاج إلى طلب ذلك قبل المتابعة.
للتحقق مما إذا كان لديك إذن لاستخدام Shizuku، يمكنك استخدام ما يلي. يفترض هذا الرمز أنك تقوم بتشغيله داخل أحد الأنشطة.
كوتلين:
val isGranted = if (Shizuku.isPreV11() || Shizuku.getVersion() < 11) {
checkSelfPermission(ShizukuProvider.PERMISSION) == PackageManager.PERMISSION_GRANTED
} else {
Shizuku.checkSelfPermission() = PackageManager.PERMISSION_GRANTED
}
جافا:
boolean isGranted;
if (Shizuku.isPreV11() || Shizuku.getVersion() < 11) {
isGranted = checkSelfPermission(ShizukuProvider.PERMISSION) == PackageManager.PERMISSION_GRANTED;
} else {
isGranted = Shizuku.checkSelfPermission() == PackageManager.PERMISSION_GRANTED;
}
جاري الطلب
إذا كنت بحاجة إلى طلب إذن لاستخدام Shizuku، فإليك الطريقة.
ال شيزوكو_كود يجب أن يكون المتغير المستخدم أدناه عددًا صحيحًا بقيمة ثابتة (متغير ثابت).
كوتلين:
if (Shizuku.isPreV11() || Shizuku.getVersion() < 11) {
requestPermissions(arrayOf(ShizukuProvider.PERMISSION), SHIZUKU_CODE)
} else {
Shizuku.requestPermission(SHIZUKU_CODE)
}
جافا:
if (Shizuku.isPreV11() || Shizuku.getVersion() < 11) {
requestPermissions(newString[] { ShizukuProvider.PERMISSION }, SHIZUKU_CODE);
} else {
Shizuku.requestPermissions(SHIZUKU_CODE);
}
للاستماع إلى النتيجة، ستحتاج إلى تجاوز النشاط onRequestPermissionsResult() طريقة. ستحتاج أيضًا إلى التنفيذ شيزوكو. OnRequestPermissionResultListener. المثال الذي سأعرضه يفترض أن نشاطك ينفذ هذه الواجهة، ولكن يمكنك تنفيذه في متغير إذا كنت تريد ذلك.
كوتلين:
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.
}
}
جافا:
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.
}
}
باستخدام واجهات برمجة التطبيقات
الآن بعد أن تم إعداد Shizuku ومنح الأذونات، يمكنك البدء فعليًا في الاتصال بواجهات برمجة التطبيقات باستخدام Shizuku. تختلف العملية هنا قليلاً عما قد تكون معتادًا عليه. بدلا من الدعوة getSystemService()
والصب لشيء من هذا القبيل WindowManager
، سيتعين عليك استخدام واجهات برمجة التطبيقات الداخلية لهذه بدلاً من ذلك (على سبيل المثال، IWindowManager
).
يشتمل Shizuku على تجاوز للقائمة السوداء لواجهة برمجة التطبيقات المخفية لنظام Android، لذلك لن تحتاج إلى القلق بشأن ذلك عند استخدامه. إذا كنت مهتمًا بكيفية تجاوز ذلك بنفسك، فراجع البرنامج التعليمي السابق.
فيما يلي مثال سريع (باستخدام الانعكاس) يوضح لك كيف يمكنك الحصول على مثال IPackageManager
واستخدامها لمنح التطبيق إذن التشغيل.
كوتلين:
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)
جافا:
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);
عملية واجهات برمجة التطبيقات الأخرى مشابهة. احصل على المراجع الخاصة بالفئة وفئتها الفرعية Stub. الحصول على المرجع إلى asInterface
طريقة لفئة كعب الروتين. احصل على المراجع للطرق التي تريدها من الفصل نفسه. ثم قم باستدعاء asInterface
مرجع الأسلوب لديك، واستبدال "package"
أعلاه مع أي خدمة تحتاجها. يمكن بعد ذلك تمرير هذا المثيل لاستدعاءات الطريقة.
نصيحة محترف: يمكنك تجنب استخدام الانعكاس تمامًا إذا قمت بتثبيت SDK معدل. تحقق من مستودع GitHub الخاص بـ anggrayudi لحزم SDK المعدلة وتعليمات التثبيت. مع تثبيت هذا، يصبح الكود أعلاه (في Kotlin) أكثر بساطة.
val iPm = IPackageManager.Stub.asInterface(ShizukuBinderWrapper(SystemServiceHelper.getService("package"))))
iPm.grantRuntimePermission("com.zacharee1.systemuituner", android.Manifest.permission.WRITE_SECURE_SETTINGS, 0)
يمكن استخدام أي واجهات برمجة التطبيقات المستندة إلى AIDL بهذه الطريقة من أي مكان في تطبيقك، طالما أن Shizuku قيد التشغيل ويتمتع تطبيقك بالإذن.
خدمة المستخدم
على الرغم من أن طريقة Binder تغطي معظم حالات الاستخدام، فقد تكون هناك أوقات تحتاج فيها إلى واجهة برمجة تطبيقات لا تحتوي على واجهة Binder مباشرة. وفي هذه الحالة، يمكنك استخدام ميزة خدمة المستخدم في Shizuku.
تعمل هذه الطريقة بشكل مشابه للخدمة العادية في Android. يمكنك "بدء تشغيله والتواصل من خلال ربطه بـ ServiceConnection وتشغيل المنطق الخاص بك في فئة الخدمة. الفرق هو أنك لا تستخدم خدمة Android، وأي شيء داخل الخدمة يعمل بأذونات ADB.
الآن هناك بعض القيود. تعمل خدمة المستخدم في عملية ومستخدم منفصلين تمامًا، لذا لا يمكنك التفاعل مع بقية تطبيقك إلا من خلال عمليات الاسترجاعات والمجلدات الخاصة بـ AIDL. نظرًا لأنه لا يعمل أيضًا في عملية تطبيق مناسبة، فقد لا تعمل بعض الأشياء مثل ربط خدمات Android بشكل صحيح.
يتطلب هذا أيضًا إصدار Shizuku 10 أو إصدارًا أحدث. على الرغم من أن معظم مصادر التطبيق لديها الإصدار 11 حاليًا، فلا يزال يتعين عليك تضمين التحقق من الإصدار، والذي سيتم تضمينه في المثال.
تعريف AIDL
للبدء، ستحتاج إلى إنشاء ملف AIDL جديد. يمكنك القيام بذلك في Android Studio عن طريق النقر بزر الماوس الأيمن على أي شيء في عرض ملف Android، والتمرير فوق الخيار "جديد"، واختيار الخيار "AIDL". أدخل اسم الملف (على سبيل المثال، "IUserService") وسيقوم Android Studio بإنشاء ملف قالب لك.
إذا لم تكن على دراية بكيفية عمل AIDLs، فتأكد من الاطلاع عليها وثائق جوجل.
قم بإزالة أساليب القالب من AIDL ثم قم بإضافة ملف destroy()
الطريقة بالمعرف المناسب لـ Shizuku. سيتم استدعاؤه عندما يقوم Shizuku بقتل خدمة المستخدم، ويجب استخدامه لتنظيف أي مراجع أو منطق مستمر لديك.
مثال 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.
}
تنفيذ AIDL
والشيء التالي الذي يجب فعله هو تنفيذ AIDL فعليًا.
جافا:
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);
}
}
كوتلين:
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)
}
}
تفترض الأمثلة المذكورة أعلاه أن لديك واجهة برمجة التطبيقات المخفية لنظام Android تم تثبيت SDK.
إعداد اتصال الخدمة
الآن بعد أن تم تعريف خدمة المستخدم وتنفيذها، فقد حان الوقت لإعدادها للاستخدام. أول شيء يجب عليك فعله هو تحديد ServiceConnection حيث تريد الاتصال به (على سبيل المثال، من النشاط الرئيسي في تطبيقك).
جافا:
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;
}
}
كوتلين:
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;
}
}
ال binder
المتغير هو ما ستستخدمه للتواصل مع خدمة المستخدم من تطبيقك. للتحقق مما إذا كان متاحًا للاستخدام، فقط تأكد من أنه ليس فارغًا pingBinder()
عائدات حقيقي، تمامًا كما في مثال الكود أعلاه.
إنشاء وسائط خدمة المستخدم
قبل أن تتمكن من التحكم في خدمة المستخدم، ستحتاج إلى تحديد بعض الوسائط لكي يستخدمها Shizuku عند بدء الخدمة وإيقافها. يتضمن ذلك أشياء مثل إخبار Shizuku فعليًا باسم فئة الخدمة، وتحديد لاحقة العملية، وما إذا كانت قابلة للتصحيح أم لا، وما هو إصدارها.
جافا:
privatefinal Shizuku.UserServiceArgs serviceArgs = new Shizuku.UserServiceArgs(
newComponentName(BuildConfig.APPLICATION_ID, UserService.class.getName()))
.processNameSuffix("user_service")
.debuggable(BuildConfig.DEBUG)
.version(BuildConfig.VERSION_CODE);
كوتلين:
private val serviceArgs = Shizuku.UserServiceArgs(
ComponentName(BuildConfig.APPLICATION_ID, UserService.class::java.getName()))
.processNameSuffix("user_service")
.debuggable(BuildCOnfig.DEBUG)
.version(BuildConfig.VERSION_CODE)
بدء وإيقاف وربط خدمة المستخدم
تم توحيد إجراءات البداية والربط وإجراءات الإيقاف والفك في شيزوكو. لا توجد طرق بدء وربط منفصلة أو طرق إيقاف وإلغاء ربط.
وإليك كيفية القيام بذلك البدء والربط خدمة المستخدم.
جافا:
if (Shizuku.getVersion >= 10) {
//This only works on Shizuku 10 or later.
Shizuku.bindUserService(serviceArgs, connection);
} else {
//Tell the user to upgrade Shizuku.
}
كوتلين:
if (Shizuku.getVersion() >= 10) {
//This only works on Shizuku 10 or later.
Shizuku.bindUserService(serviceArgs, connection)
} else {
//Tell the user to upgrade Shizuku.
}
وإليك كيفية القيام بذلك توقف وفك خدمة المستخدم.
جافا:
if (Shizuku.getVersion >= 10) {
Shizuku.unbindUserService(serviceArgs, connection, true);
}
كوتلين:
if (Shizuku.getVersion >= 10) {
Shizuku.unbindUserService(serviceArgs, connection, true)
}
استدعاء خدمة المستخدم
بمجرد بدء تشغيل خدمة المستخدم، يمكنك البدء في استخدامها. ما عليك سوى التحقق مما إذا كان binder
المتغير غير فارغ وقابل لاختبار الاتصال، ثم قم بإجراء استدعاء الأسلوب الخاص بك.
جافا:
if (binder != null && binder.pingBinder()) {
binder.grantPermission("com.zacharee1.systemuituner", android.Manifest.permission.WRITE_SECURE_SETTINGS, 0);
}
كوتلين:
if (binder?.pingBinder() == true) {
binder?.grantPermission("com.zacharee1.systemuituner", android.Manifest.permission.WRITE_SECURE_SETTINGS, 0)
}
خاتمة
إذا تابعت كل ذلك، فمن المفترض أن يكون لديك الآن تكامل Shizuku فعال. فقط تذكر أن تطلب من المستخدمين تثبيت Shizuku، والتحقق بشكل صحيح من توفر Shizuku قبل محاولة استخدامه.