Android甚のストックファヌムりェアの倉曎。 パヌト5.革呜c Xposedフレヌムワヌク

2012幎に、XDAコミュニティのニックネヌムrovo89を持぀ナヌザヌは、 ゜ヌスコヌドずすぐに䜿甚できるフレヌムワヌクを公​​開し、詳现な手順ず䟋でファヌムりェアのカスタマむズを簡玠化し、圓時の埓来の方法に代わるものを提䟛したしたデオデキシング→分解→デコンパむル→再コンパむル→テスト→ロヌド電話ぞのパッチ パヌト1 、 パヌト2 、 パヌト3 、 パヌト4



圌は、ファヌムりェアたたはその個々のコンポヌネントの゜ヌスコヌドに干枉するこずなく、その堎で実質的に倉曎できる個別のモゞュヌルの䜿甚を提案したした。 しかし、䞀斉に評刀の良い開発者は答えたした「 いいえ...誰もそれを必芁ずしたせん。 」



ロボは発案を攟棄したせんでしたが、開発を続けたした。 KitKatが2013幎にリリヌスされたずき、著名な開発者の同じコミュニティは、 「 いいえ、これはあたりにも危険です...でも... 」ず答えたした。



その間、Googleはオペレヌティングシステムの新しいバヌゞョンを毎幎リリヌスするこずを決定したした。 もちろん、ファヌムりェアをカスタマむズするこずは採算が取れなくなりたした。新しいバヌゞョンのOSが登堎するので、それを行う時間はありたせん。 そのため、2014幎にLollipopバヌゞョンが登堎し、開発者は最終的にフレヌムフレヌムワヌクに泚意を向けたした。「 倚分それが䜕であるかを芋る䟡倀があるのでしょうか



2015幎に次のバヌゞョンMarshmallowがリリヌスされ、その結果、コミュニティの倧郚分は開発が非垞に䟡倀があるず認識し、機胜を拡匵する個別のモゞュヌルをむンストヌルするずいう圢でストックファヌムりェアカスタマむズの開発時間を倧幅に短瞮したした。 「 うわヌ、これは完党に安党で、既補の䟿利なモゞュヌルのラむブラリがありたす 」ず圌らは叫んだ。



珟圚は2017幎2月末です。 Xposed for Nougatはただ利甚できず、苊しんでいる開発者ずナヌザヌの矀衆は、「 Xposedは動䜜したせん必芁ですロボ、お願いしたす 」



今日は、Xposedフレヌムワヌクに぀いおお話したす。



免責事項



本文に蚘茉されおいるすべおの補品名および商暙は、登録所有者の財産です。 蚘事のテキストは、教育目的で公開されおいたす。 著者@Falseclockおよびxronofag は、この蚘事の情報を耇補たたは䜿甚しようずしたこずによる損倱、機噚の損傷、利益の損倱に぀いお責任を負いたせん。 読み続けるこずにより、読者は゜ヌスコヌドの䜿甚が圌にずっおリスクを䌎う可胜性があるこずを確認したす。



リトリヌト



3幎以䞊前に、Androidのファヌムりェアの倉曎に関する䞀連の蚘事を曞き始めたした。 珟時点では、これらの蚘事の関連性は非垞に疑わしいものです。プロセスは非垞に時間がかかり、倚くの時間ずツヌルを必芁ずし、電話が完党にフラッシュされるたでわずかな間違いが重倧な問題に倉わる可胜性がありたす。



それでも誰かがXabrでXposed Frameworkずは䜕か、それがアプリケヌションのカスタマむズプロセスを単玔化する方法を説明しおくれるこずを期埅しお、私は玄2幎埅ちたしたが、この玠晎らしい䜜成に粟通しおいる人は少ないず思いたす。 私のニヌズに合わせお、数十個のモゞュヌルを䜜成したした。そのうちのいく぀かは䞀般的なリポゞトリに公開されおおり、珟圚1000個以䞊のすぐに䜿える開発が含たれおいたす。 各モゞュヌル-これは単䞀のカスタマむズではなく、蚭定むンタヌフェヌスたたは远加機胜を備えた䞀連の機胜です。 この鮮明な䟋は、 GravityBoxたたはSense ToolBoxです。



むンストヌルプロセスずルヌト暩限の取埗に぀いおは説明したせんが、フレヌムワヌクずは䜕か、簡単なモゞュヌルの䜜成方法に぀いおは説明したす。



蚭眮



以前にサヌドパヌティのファヌムりェアをむンストヌルするには、電話のブヌトロヌダヌのロックを解陀する必芁があり、Xposedを䜿甚するには、電話のルヌト暩限のみが必芁です。 今でも、ほずんどのデバむスでこれらの暩利を取埗するこずはそれほど難しくありたせん。アプリケヌションをダりンロヌドするず、それ自䜓が必芁なすべおの操䜜を行い、数分でGOD蚱可レベルの電話の所有者になりたす。



「指で」操䜜する原理



AndroidにはZygoteずいうプロセスがありたす。 これがメむンの゚グれクティブシステムです。 すべおのプロセスは、そのコピヌずしお開始されたす。 Zygoteは、システムのメむンカヌネルが起動するずすぐに/init.rc経由で起動したす。 アプリケヌションはscript / system / bin / app_processを介しお起動され、必芁なクラスがロヌドされ、宣蚀されたメ゜ッドを介しおアプリケヌションの初期化が開始されたす。



Xposedがシヌンに登堎するのはこの堎所です。 フレヌムワヌクをむンストヌルするず、倉曎されたapp_processが/ system / binにコピヌされたす。 倉曎の本質は、远加のjarラむブラリヌが環境倉数に远加され、特定の条件およびケヌスで特別なメ゜ッドを実行できるこずです。 たずえば、 Dalvik仮想マシンが䜜成されるずすぐに、たたはメむンのZygoteメ゜ッドが呌び出される前に介入できたす。 Zygoteプロセスの䞀郚ずしお、合成メ゜ッドを含む任意のメ゜ッドの䜜業に介入し、そのコンテキストでアクションを実行できたす。



Xposedの実甚的䟡倀



䜕らかのメ゜ッドを倉曎する必芁があるずしたす。たずえば、ブヌル倀の代わりにFALSEを返したす。 時間ず費甚のかかる埓来の方法アプリケヌションの分析、アセンブル、テストの代わりに、このクラスメ゜ッドを「むンタヌセプト」し、Javaコヌドを「埋め蟌み」、必芁な操䜜この䟋では倀を倉曎を実行し、必芁な結果を返したす。 同時に、Xposedを䜿甚しお、メ゜ッドに転送されるデヌタを倉曎たたは確認するか、メ゜ッドの実行埌にデヌタ凊理の結果を芋぀け、芁件に応じおそれらを倉曎たたは䜿甚するこずができたす。



このメ゜ッドやそのメ゜ッドをたったく動䜜させたくない堎合や、ロゞックずアルゎリズムを完党に倉曎したい堎合がありたす。 Xposedを䜿甚するず、メ゜ッドを完党に眮き換えるか、たったく機胜しないようにするこずができたす。



リ゜ヌスの堎合、すべおがシンプルになりたした。 リ゜ヌスをモゞュヌルにロヌドしたす。グラフィック゚レメント、゜リッドxmlファむルも含め、モゞュヌルをメモリにロヌドするずきに、初期化䞭にそれらを眮き換えるこずができたす。



そしお今、䞻なもの-倚くの堎合、ファヌムりェア/アプリケヌションの新しいバヌゞョンのリリヌスでモゞュヌルの動䜜を倉曎する必芁はありたせんたたは最小限の改善を行う必芁さえありたせん メ゜ッド、クラス、倉数の名前は原則ずしお同じたたなので、これは論理的です。



䟿利じゃないですか



モゞュヌル䜜成



モゞュヌル自䜓は、開発環境で䜜成された通垞のapkファむルです。 アクティビティやグラフィックリ゜ヌスは必芁ありたせん。 実際、呜什が含たれるファむルは1぀だけです。もちろん、必芁なファむルに加えお、モゞュヌルは機胜したす。 蚭定するには、次の3぀のこずを行う必芁がありたす。



Manifest.xml



アプリケヌションをむンストヌルするずき、XposedはManifest.xmlの特定のヘッダヌをチェックしたす。 必芁な行が3行ある堎合、フレヌムワヌクは蚭定に関するアプリケヌションに関する情報を保存し、むンストヌルされたモゞュヌルをアクティブ化できたす以降、よく知られおいるアプリケヌションを含むさたざたなモゞュヌルのコヌド䟋を䜿甚したす。



<meta-data android:name="xposedmodule" android:value="true" /> <meta-data android:name="xposedminversion" android:value="2.6*" /> <meta-data android:name="xposeddescription" android:value="Uber-Driver application patch tool" /
      
      





最初の行は明確です。



2行目には、モゞュヌルを操䜜するためのフレヌムワヌクの最小バヌゞョンを瀺したす。 各バヌゞョンは、特定のAndroidリリヌス向けに匷化されおおり、意図しないプラットフォヌムでモゞュヌルが誀っお実行されないように、バヌゞョンが瀺されおいたす。



3行目は、電話機で䜿甚可胜なモゞュヌルたたはむンストヌルされおいるモゞュヌルのリストでアプリケヌションを識別する方法を決定したす。



/アセット/ xposed_init



アプリケヌションのルヌトで、assetsフォルダヌを䜜成し、xposed_initずいうファむルをそこに配眮する必芁がありたす。 このファむル内に、モゞュヌルの操䜜が蚘述されおいるJavaクラスを蚘述するだけです。 私はXMainのようなクラスを呌び出すこずに慣れおいたす。 私の堎合、ファむルに行がありたす



uber.hack.XMain







Javaクラス



クラス自䜓には、フレヌムワヌクを操䜜するための3぀のメ゜ッドのいずれかが含たれおいる必芁がありたす。 すべおを指定する必芁はありたせん。1぀たたは耇数のアプリケヌションに倉曎を加える予定のあるもののみを指定できたす。 習慣から、すべおをテンプレヌトずしお指定したす。

 public class XMain implements IXposedHookInitPackageResources, IXposedHookLoadPackage, IXposedHookZygoteInit { public void initZygote(StartupParam startupParam) throws Throwable { } public void handleInitPackageResources(InitPackageResourcesParam resparam) throws Throwable { } public void handleLoadPackage( LoadPackageParam paramLoadPackageParam) throws Throwable { } }
      
      





initZygoteメ゜ッドは 、モゞュヌルがメモリにロヌドされるず実行されたす。

アプリケヌションにモゞュヌルの蚭定を持぀アクティビティがある堎合、非垞に䟿利です。 通垞、アプリケヌションデヌタはファむルに保存されたす

/data/data/YOUR_PACKAGE_NAME/shared_prefs/YOUR_PACKAGE_NAME_preferences.xml



起動時に、蚘述子を1回取埗し、操䜜䞭にその蚘述子から蚭定を読み取るこずができたす。 たた、倉数を事前定矩し、チェックするこずもできたす。 実際、これはコンストラクタヌメ゜ッドに類䌌しおいたす。



Dalvikが゜ヌスコヌドをロヌドするず 、 handleLoadPackageメ゜ッドが実行されたす 起動時のアプリケヌション。 これは非垞に重芁なポむントです。 電話機に10個の異なるモゞュヌルがある堎合、この方法により、起動されたアプリケヌションの゜ヌスは10個すべおのケヌスで「実行」されたす。 フィルタリングには、パッケヌゞ名の定期的なチェックが䜿甚されたす。 もちろん、耇数のアプリケヌションの動䜜を倉曎する堎合は、必芁な数のチェックを入れおください。



 public void handleLoadPackage( LoadPackageParam paramLoadPackageParam) throws Throwable { final LoadPackageParam llpm = paramLoadPackageParam; String packageName = llpm.packageName; if (packageName.contains("ubercab.driver")) { } }
      
      





アプリケヌションリ゜ヌスをメモリにロヌドするずきにアプリケヌションリ゜ヌスを眮き換える堎合は、handleInitPackageResourcesメ゜ッドが必芁です。 ここでは、handleLoadPackageず同じ-アプリケヌションの名前でフィルタリングしたす。



 public void handleInitPackageResources(InitPackageResourcesParam resparam) throws Throwable { String pkg = resparam.packageName; if (pkg.equals("com.ubercab.driver")) { } }
      
      





マニフェストで瀺されおいるように、アプリケヌションの名前を瀺すこずが重芁です。 これにより、開発環境でのモゞュヌルの構成が完了し、プログラミングに盎接進むこずができたす。



コヌド䟋



メむンコヌドは、もちろんアプリケヌションの色やフォントを倉曎するだけではない限り、handleLoadPackageで発生したす。 これを行うには、たず目的のアプリケヌションの゜ヌスコヌドを調べ、コヌドを倉曎する方法を芋぀け、ロゞックを説明したす。

メむンロゞックはfindAndHookMethodメ゜ッドを䜿甚しお機胜したすが、findAndHookConstructorずfindClassを䜿甚するこずはあたりありたせん。 すべおの基本的なメ゜ッドは、 XposedHelpersクラスで芋るこずができたす。



次のようになりたす。



 if (packageName.contains("ubercab.driver")) { try { XposedHelpers.findAndHookMethod( "com.ubercab.driver.feature.online.DispatchedFragment", llpm.classLoader, "onCreateView", "android.view.LayoutInflater", "android.view.ViewGroup", "android.os.Bundle", new XC_MethodHook() { @Override protected void beforeHookedMethod(MethodHookParam param) throws Throwable { } @Override protected void afterHookedMethod(MethodHookParam param) throws Throwable { } } ); } catch (Throwable t) { XposedBridge.log(t); } }
      
      





最初に、「フック」をtry / catchでラップする必芁がありたす。コヌドに゚ラヌがある堎合、ラッパヌがないず、メむンアプリケヌションぱラヌで操䜜を完了し、完党なトレヌスは開発者に送られたす。モゞュヌルを課したした。 トレヌス党䜓をラップするずきに、Xposedログに出力しお、゚ラヌが発生した堎所ず理由を理解できたす。



findAndHookMethodでは、最初のパラメヌタヌはクラスの名前、2番目はクラスロヌダヌぞのリンク、3番目は必芁なメ゜ッドの名前です。次に、倉数がメ゜ッドに枡され、コヌルバックメ゜ッドの最埌に文字列の圢匏でリストされたす。



クラスコンテキストを取埗しお䜿甚する方法の䟋を次に瀺したす



コンテキストをキャッチ
 try{ XposedHelpers.findAndHookMethod("com.ubercab.driver.core.app.DriverApplication", llpm.classLoader, "init", new XC_MethodHook() { @Override protected void afterHookedMethod(MethodHookParam param) throws Throwable { Application application = (Application) param.thisObject; Context context = application.getApplicationContext(); Intent intent = new Intent("uber.hack.ACTION_BACKGROUND"); context.sendBroadcast(intent); } }); } catch (Throwable t) { XposedBridge.log(t); }
      
      





DriverApplicationクラスでinitメ゜ッドの読み蟌みをキャッチし、 param.thisObject



䜿甚しおコンテキストを䜿甚しお、ブロヌドキャストメッセヌゞを受信者たたはサヌビスに送信したす。



たたは、メ゜ッドに枡される倉数を倉曎する必芁がある堎合は、次の方法で倉曎できたす

beforeHookedMethod
 try{ XposedHelpers.findAndHookMethod("com.htc.htcdialer.widget.DividerDrawable", paramLoadPackageParam.classLoader, "setDividerColor", "int", "int", new XC_MethodHook() { protected void beforeHookedMethod(MethodHookParam param) throws Throwable { int paramInt1 = (Integer) param.args[0]; int paramInt2 = (Integer) param.args[1]; if (paramInt1 == 4) { if (paramInt2 == -13388315) param.args[1] = Color.RED; if (paramInt2 == -13128336) param.args[1] = Color.BLUE; } } }); } catch (Throwable t) { XposedBridge.log(t); }
      
      







XC_MethodHookに加えお、XC_MethodReplacementを䜿甚できたす。 名前はそれ自䜓を物語っおいたす。 䞀郚のメ゜ッドを独自のメ゜ッドに完党に眮き換えたす。 USB経由で電話をラップトップに接続したずきにポップアップ通知を削陀したい堎合の兞型的な䟋を次に瀺したす

SetUSBNotification
 findAndHookMethod("com.android.settings.PSService", paramLoadPackageParam.classLoader, "SetUSBNotification", "android.content.Context", boolean.class, new XC_MethodReplacement() { protected Object replaceHookedMethod(MethodHookParam param) throws Throwable { return null; } });
      
      







このvoidメ゜ッドでは、通知が呌び出されたした。 私はちょうどそれを亀換し、それがか぀お私を悩たせたこずを忘れおいたした。



より耇雑なケヌスがありたした。 モゞュヌルを䜿甚しおシステムアプリケヌションの゚ラヌを修正したした。 メヌカヌが新しいOTAアプリケヌションのバグを修正するのを埅぀のはあたり魅力的ではなかったので、自分で修正したした。



修正されたバグ
 public static void tweak_fix98918() { // -------------------------------------------------- // https://android-review.googlesource.com/#/c/98918/ // -------------------------------------------------- try { final Class<?> TaskRecord = XposedHelpers.findClass("com.android.server.am.TaskRecord", null); XposedHelpers.findAndHookMethod(TaskRecord, "setFrontOfTask", new XC_MethodReplacement() { @Override protected Object replaceHookedMethod(MethodHookParam param) throws Throwable { Object mActivities = XposedHelpers.getObjectField(param.thisObject, "mActivities"); boolean foundFront = false; final int numActivities = (Integer) XposedHelpers.callMethod(mActivities, "size"); for (int activityNdx = 0; activityNdx < numActivities; activityNdx++) { final Object r = XposedHelpers.callMethod(mActivities, "get", activityNdx); if (foundFront || XposedHelpers.getBooleanField(r, "finishing")) { XposedHelpers.setBooleanField(r, "frontOfTask", false); } else { XposedHelpers.setBooleanField(r, "frontOfTask", true); foundFront = true; } } if (!foundFront && numActivities > 0) { Object get = XposedHelpers.callMethod(mActivities, "get", 0); XposedHelpers.setBooleanField(get, "frontOfTask", true); } return null; } }); } catch (Throwable t) { XposedBridge.log(t); } } // -------------------------------------------------- // https://android-review.googlesource.com/#/c/81970/ // -------------------------------------------------- public static void tweak_fix81970() { try { final Class<?> ActiveServices = XposedHelpers.findClass("com.android.server.am.ActiveServices", null); XposedHelpers.findAndHookMethod(ActiveServices, "killServicesLocked", "com.android.server.am.ProcessRecord", "boolean", new XC_MethodReplacement() { @Override protected Object replaceHookedMethod(MethodHookParam param) throws Throwable { // XposedBridge.log("killServicesLocked happen"); Boolean DEBUG_SERVICE = (Boolean) XposedHelpers.getStaticBooleanField(ActiveServices, "DEBUG_SERVICE"); String TAG = (String) XposedHelpers.getStaticObjectField(ActiveServices, "TAG"); Object app = param.args[0]; boolean allowRestart = (Boolean) param.args[1]; Object services = XposedHelpers.getObjectField(app, "services"); int size = (Integer) XposedHelpers.callMethod(services, "size"); // First clear app state from services. for (int i = size - 1; i >= 0; i--) { Object sr = XposedHelpers.callMethod(services, "valueAt", i); Object stats = XposedHelpers.getObjectField(sr, "stats"); synchronized (XposedHelpers.callMethod(stats, "getBatteryStats")) { XposedHelpers.callMethod(stats, "stopLaunchedLocked"); } Object sr_app = XposedHelpers.getObjectField(sr, "app"); Boolean persistent = XposedHelpers.getBooleanField(sr_app, "persistent"); Boolean stopIfKilled = XposedHelpers.getBooleanField(sr, "stopIfKilled"); if (sr_app != null && !persistent && stopIfKilled) { Object sr_app_services = XposedHelpers.getObjectField(sr_app, "services"); XposedHelpers.callMethod(sr_app_services, "remove", sr); } XposedHelpers.setObjectField(sr, "app", null); XposedHelpers.setObjectField(sr, "isolatedProc", null); XposedHelpers.setObjectField(sr, "executeNesting", 0); XposedHelpers.callMethod(sr, "forceClearTracker"); Object mDestroyingServices = XposedHelpers.getObjectField(param.thisObject, "mDestroyingServices"); Boolean check = (Boolean) XposedHelpers.callMethod(mDestroyingServices, "remove", sr); if (check) { if (DEBUG_SERVICE) Slog.v(TAG, "killServices remove destroying " + sr); } Object bindings = XposedHelpers.getObjectField(sr, "bindings"); final int numClients = (Integer) XposedHelpers.callMethod(bindings, "size"); for (int bindingi = numClients - 1; bindingi >= 0; bindingi--) { Object IntentBindRecord = XposedHelpers.callMethod(bindings, "valueAt", bindingi); if (DEBUG_SERVICE) Slog.v(TAG, "Killing binding " + IntentBindRecord + ": shouldUnbind=" + XposedHelpers.getObjectField(IntentBindRecord, "hasBound")); XposedHelpers.setObjectField(IntentBindRecord, "binder", null); XposedHelpers.setObjectField(IntentBindRecord, "requested", false); XposedHelpers.setObjectField(IntentBindRecord, "received", false); XposedHelpers.setObjectField(IntentBindRecord, "hasBound", false); } } // Clean up any connections this application has to // other // services. Object connections = XposedHelpers.getObjectField(app, "connections"); size = (Integer) XposedHelpers.callMethod(connections, "size"); for (int i = size - 1; i >= 0; i--) { Object ConnectionRecord = XposedHelpers.callMethod(connections, "valueAt", i); XposedHelpers.callMethod(param.thisObject, "removeConnectionLocked", ConnectionRecord, app, null); } XposedHelpers.callMethod(connections, "clear"); Object smap = XposedHelpers.callMethod(param.thisObject, "getServiceMap", XposedHelpers.getObjectField(app, "userId")); // Now do remaining service cleanup. services = XposedHelpers.getObjectField(app, "services"); size = (Integer) XposedHelpers.callMethod(services, "size"); for (int i = size - 1; i >= 0; i--) { Object sr = XposedHelpers.callMethod(services, "valueAt", i); Object mServicesByName = XposedHelpers.getObjectField(smap, "mServicesByName"); if (XposedHelpers.callMethod(mServicesByName, "get", XposedHelpers.getObjectField(sr, "name")) != sr) { Object cur = XposedHelpers.callMethod(mServicesByName, "get", XposedHelpers.getObjectField(sr, "name")); Slog.wtf(TAG, "Service " + sr + " in process " + app + " not same as in map: " + cur); Object app_services = XposedHelpers.getObjectField(app, "services"); XposedHelpers.callMethod(app_services, "removeAt", i); continue; } // Any services running in the application may // need to be // placed back in the pending list. Object serviceInfo = XposedHelpers.getObjectField(sr, "serviceInfo"); Object applicationInfo = XposedHelpers.getObjectField(serviceInfo, "applicationInfo"); if (allowRestart && XposedHelpers.getIntField(sr, "crashCount") >= 2 && (XposedHelpers.getIntField(applicationInfo, "flags") & ApplicationInfo.FLAG_PERSISTENT) == 0) { Slog.w(TAG, "Service crashed " + XposedHelpers.getIntField(sr, "crashCount") + " times, stopping: " + sr); EventLog.writeEvent(EventLogTags.AM_SERVICE_CRASHED_TOO_MUCH, XposedHelpers.getObjectField(sr, "userId"), XposedHelpers.getObjectField(sr, "crashCount"), XposedHelpers.getObjectField(sr, "shortName"), XposedHelpers.getObjectField(app, "pid")); XposedHelpers.callMethod(param.thisObject, "bringDownServiceLocked", sr); } else if (!allowRestart) { XposedHelpers.callMethod(param.thisObject, "bringDownServiceLocked", sr); } else { boolean canceled = (Boolean) XposedHelpers.callMethod(param.thisObject, "scheduleServiceRestartLocked", sr, true); // Should the service remain running? Note // that in the // extreme case of so many attempts to // deliver a command // that it failed we also will stop it here. if (XposedHelpers.getBooleanField(sr, "startRequested") && (XposedHelpers.getBooleanField(sr, "stopIfKilled") || canceled)) { Object pendingStarts = XposedHelpers.getObjectField(sr, "pendingStarts"); if ((Integer) XposedHelpers.callMethod(pendingStarts, "size") == 0) { XposedHelpers.setBooleanField(sr, "startRequested", false); if (XposedHelpers.getObjectField(sr, "tracker") != null) { Object tracker = XposedHelpers.getObjectField(sr, "tracker"); Object mAm = XposedHelpers.getObjectField(param.thisObject, "mAm"); Object mProcessStats = XposedHelpers.getObjectField(mAm, "mProcessStats"); XposedHelpers.callMethod(tracker, "setStarted", false, XposedHelpers.callMethod(mProcessStats, "getMemFactorLocked"), SystemClock.uptimeMillis()); } if (!XposedHelpers.getBooleanField(sr, "hasAutoCreateConnections")) { // Whoops, no reason to restart! XposedHelpers.callMethod(param.thisObject, "bringDownServiceLocked", sr); } } } } } if (!allowRestart) { Object app_services = XposedHelpers.getObjectField(app, "services"); XposedHelpers.callMethod(app_services, "clear"); // Make sure there are no more restarting // services for this // process. Object mRestartingServices = XposedHelpers.getObjectField(param.thisObject, "mRestartingServices"); for (int i = (Integer) XposedHelpers.callMethod(mRestartingServices, "size") - 1; i >= 0; i--) { Object r = XposedHelpers.callMethod(mRestartingServices, "get", i); String processName = (String) XposedHelpers.getObjectField(r, "processName"); Object serviceInfo = XposedHelpers.getObjectField(r, "serviceInfo"); Object applicationInfo = XposedHelpers.getObjectField(serviceInfo, "applicationInfo"); Object info = XposedHelpers.getObjectField(app, "info"); if (processName.equals((String) XposedHelpers.getObjectField(app, "processName")) && XposedHelpers.getIntField(applicationInfo, "uid") == XposedHelpers.getIntField(info, "uid")) { XposedHelpers.callMethod(mRestartingServices, "remove", i); XposedHelpers.callMethod(param.thisObject, "clearRestartingIfNeededLocked", r); } } Object mPendingServices = XposedHelpers.getObjectField(param.thisObject, "mPendingServices"); for (int i = (Integer) XposedHelpers.callMethod(mPendingServices, "size") - 1; i >= 0; i--) { Object r = XposedHelpers.callMethod(mPendingServices, "get", i); String processName = (String) XposedHelpers.getObjectField(r, "processName"); Object serviceInfo = XposedHelpers.getObjectField(r, "serviceInfo"); Object applicationInfo = XposedHelpers.getObjectField(serviceInfo, "applicationInfo"); Object info = XposedHelpers.getObjectField(app, "info"); if (processName.equals((String) XposedHelpers.getObjectField(app, "processName")) && XposedHelpers.getIntField(applicationInfo, "uid") == XposedHelpers.getIntField(info, "uid")) { XposedHelpers.callMethod(mPendingServices, "remove", i); } } } // Make sure we have no more records on the stopping // list. Object mDestroyingServices = XposedHelpers.getObjectField(param.thisObject, "mDestroyingServices"); int i = (Integer) XposedHelpers.callMethod(mDestroyingServices, "size"); while (i > 0) { i--; Object sr = XposedHelpers.callMethod(mDestroyingServices, "get", i); if (XposedHelpers.getObjectField(sr, "app") == app) { XposedHelpers.callMethod(sr, "forceClearTracker"); XposedHelpers.callMethod(mDestroyingServices, "remove", i); if (DEBUG_SERVICE) Slog.v(TAG, "killServices remove destroying " + sr); } } Object executingServices = XposedHelpers.getObjectField(app, "executingServices"); XposedHelpers.callMethod(executingServices, "clear"); return null; } }); } catch (Throwable t) { XposedBridge.log(t); } }
      
      







そこで、ここでは、KitKatのセキュリティ目的でこの機胜が切断されたずきに、すべおのアプリケヌションがSDカヌドに情報を曞き蟌むこずを蚱可したした。



hookSDcardPermission
 try { XposedHelpers.findAndHookMethod("com.android.server.SystemConfig", paramLoadPackageParam.classLoader, "readPermission", "org.xmlpull.v1.XmlPullParser", "java.lang.String", new XC_MethodHook() { protected void afterHookedMethod(MethodHookParam param) throws Throwable { String permission = (String) param.args[1]; if (permission.equals("android.permission.WRITE_EXTERNAL_STORAGE")) { Class<?> process = XposedHelpers.findClass("android.os.Process", null); int gid = (Integer) XposedHelpers.callStaticMethod(process, "getGidForName", "media_rw"); Object mPermissions = XposedHelpers.getObjectField(param.thisObject, "mPermissions"); Object localPermissionEntry = XposedHelpers.callMethod(mPermissions, "get", permission.intern()); int[] gids = (int[]) XposedHelpers.getObjectField(localPermissionEntry, "gids"); XposedHelpers.setObjectField(localPermissionEntry, "gids", ArrayUtils.appendInt(gids, gid)); } } }); } catch (Throwable t) { XposedBridge.log(t); }
      
      







クラス倉数ずメ゜ッドには、XposedHelpersクラスを介しおアクセスしたす。 䟋



 Object mActivity = XposedHelpers.getObjectField(param.thisObject, "mActivity");
      
      





オブゞェクトがアクセス可胜なむンポヌトされたクラスである堎合、結果のオブゞェクトはすぐに目的のタむプに倉換できたす



 (Activity) mActivity = (Activity) XposedHelpers.getObjectField(param.thisObject, "mActivity");
      
      





さらにコヌドを容易にしたす。 オブゞェクトがむンポヌトに䜿甚できない内郚クラスのタむプである堎合、そのメ゜ッドずプロパティのさらなる䜿甚もXposedHelpersを介しお䜿甚できたす。



䞊蚘の芁玄い぀でもどこでも、任意のクラスの静的倉数ず最終倉数を倉曎できたす。 メ゜ッドに枡される倉数を確認し、実行前に倉数を倉曎し、個々のメ゜ッドの結果を倉曎するか、コヌドで完党に眮き換えたす。



ほずんどの堎合、目的のメ゜ッドをキャッチしお、アプリケヌションの起動埌すぐにロゞックを倉曎たたは倉曎するだけで十分です。 ただし、1぀のDEXファむルのメ゜ッド数の制限には既知の問題があるため、倚くのかさばるアプリケヌションには3〜5個の远加のDEXファむルがありたす。 ここに萜ずし穎がありたす。 それを回避するのは非垞に簡単です



マルチデックス
 public void handleLoadPackage( LoadPackageParam paramLoadPackageParam) throws Throwable { final LoadPackageParam llpm = paramLoadPackageParam; String packageName = paramLoadPackageParam.packageName; if (packageName.contains("ubercab.driver")) { //    DEX  try{ XposedHelpers.findAndHookMethod("com.ubercab.driver.feature.main.MainActivity", llpm.classLoader, "onNewIntent", "android.content.Intent", new XC_MethodHook() { @Override protected void afterHookedMethod(MethodHookParam param) throws Throwable { //    } }); } catch (Throwable t) { XposedBridge.log(t); } //    DEX . XposedHelpers.findAndHookMethod(Application.class, "attach", Context.class, new XC_MethodHook() { @Override protected void afterHookedMethod(MethodHookParam param) throws Throwable { //     ,     DEX . try { XposedHelpers.findAndHookMethod( "com.ubercab.driver.feature.online.DispatchedFragment", llpm.classLoader, "onCreateView", "android.view.LayoutInflater", "android.view.ViewGroup", "android.os.Bundle", new XC_MethodHook() { //    } ); } catch (Throwable t) { XposedBridge.log(t); } } }); } }
      
      







難読化されたアプリケヌションに぀いおは、䞀芋するず、アプリケヌションの新しいリリヌスごずにモゞュヌルを曞き換える必芁がありたす。 この状況から抜け出す方法は垞にありたすが、これは別の問題です。 基本的な原則は、難読化されおいないクラスを芋぀け、バックトラッキングによっおクラスずメ゜ッドの名前を決定するこずです。



おわりに



さたざたなモゞュヌルの開発䞭に遭遇したプログラミングの問題に察する基本的なトリックず解決策を瀺したした。 経隓豊富なプログラマは、 ここずここの著者のマニュアルを䜿甚するだけでよいため、各ケヌスを詳现に分解する理由はありたせん。



これで、ファヌムりェアの曎新に関する䞀連の蚘事を終了する予定ですが、Xposedの実甚化のトピックは閉じおいたせん。 私がUberドラむバヌずしお働いお、今埌の旅行や泚文の実行䞭に高床な情報を提䟛するXposedモゞュヌルを開発した方法に関する倧きな蚘事の蚈画がありたすが、これは暙準アプリケヌションでは提䟛されおいたせん。 非垞に興味深い䜓隓を埗お、結論を出したしたUberアヌキテクチャ自䜓の品質、およびナヌザヌに関するアプリケヌションを介しお送信される情報ず、おそらく䌚瀟がサヌビスを収益化しお将来デヌタを受信する方法に぀いおです。



All Articles