Xposed framework

Xposed framework is a can not modify the APK under the condition of affecting the program to run (modify the system) framework services, based on it can make many powerful modules, and in the case of functional conflict at the same time.

The basic principle of

Zygote process is the core of Android, all application processes and system service processes are fork out by Zygote process. Xposed Framework into the Android core mechanism, through the transformation of Zygote to achieve some very cool features. The Zygote startup configuration is in the /init.rc script, which starts the process at system startup. The corresponding execution file is /system/bin/app_process, which does the library loading and some initialization function calls.

When the Xposed Framework is installed in the system, will take their own implementation of the App_process to cover the Android native provided file, so that the app_process in the startup process will load XposedBridge. Jar jar package, In this way, the Zygote process and the Dalvik VIRTUAL machine created are hijacked.

For a more detailed framework introduction and plug-in development process, please refer to the official tutorial or some existing Chinese tutorials

The main reason for this article is a message from Sihan in another article of mine:

Xposed alone to send the article. This is a killer framework. It’s a test tool. But many people don’t understand how it works

Since it’s killer stuff, there must be a lot of unique moves and skills. So, over the weekend, I thought about what testers can do based on the framework, and I came up with the following:

  • Penetration testing
  • Test data construction
  • Environmental monitoring
  • Dynamic buried point
  • hotfixes
  • Automatic recording

The following is for the above points, combined with examples to make some simple share (part of the principle and process may not do too detailed explanation, can not understand the message).

1. Penetration test

Take the Android client authentication and authorization module of Testerhome as an example, which uses the authorization protocol of OAuth 2.0, including an important access token access_token. By looking at the source code we can see that there is a setAccess_token method in the TesterUser class

    public void setAccess_token(String access_token) {
        this.access_token = access_token;
    }Copy the code

The input parameter is the access token generated after user authorization, so we can intercept this token in the following way

public void handleLoadPackage(final LoadPackageParam lpparam) throws Throwable { if (! lpparam.packageName.equals("com.testerhome.nativeandroid")) return; XposedBridge.log("Loaded app: " + lpparam.packageName); findAndHookMethod("com.testerhome.nativeandroid.models.TesterUser", lpparam.classLoader,"setAccess_token", String.class,new XC_MethodHook() { @Override protected void beforeHookedMethod(MethodHookParam param) throws Throwable {  // this will be called before the clock was updated by the original method XposedBridge.log("Enter->beforeHookedMethod"); XposedBridge.log("original token: " + (String)param.args[0]); } @Override protected void afterHookedMethod(MethodHookParam param) throws Throwable { // this will be called after the clock was updated by the original method } });Copy the code

The log is as follows:

    12-19 06:19:54.458: I/Xposed(11327): Enter->beforeHookedMethod
    12-19 06:19:54.458: I/Xposed(11327): user token: 0a84d0c29a4b576634baacd5097c39b4e36264f440be5b3affba6b1b5b14603eCopy the code

After obtaining the token, further information about the user can be obtained according to the interaction protocol.

Of course, the attacker can also modify the token value directly.

    param.args[0] = "b6a8d0b02a651a7759051a5c8b1afa02db35636dd4c20c15dcbf050038d7ae2e";Copy the code

In this way, users log in with illegal token values and cannot access legitimate resources.

findAndHookMethod("com.testerhome.nativeandroid.models.TesterUser", lpparam.classLoader,"getAccess_token",new XC_MethodHook() { @Override protected void beforeHookedMethod(MethodHookParam param) throws Throwable { // this will be called before the clock was updated by the original method XposedBridge.log("Enter->beforeHookedMethod:getAccess_token"); } @Override protected void afterHookedMethod(MethodHookParam param) throws Throwable { // this will be called after the clock was updated by the original method XposedBridge.log("Enter->afterHookedMethod:getAccess_token"); XposedBridge.log("hooked token: " + (String)param.getResult()); }});Copy the code

Log printing:

12-19 10:37:43. 590: I/Xposed (15821) : Enter - > beforeHookedMethod: setAccess_token 12-19 10:37:43. 590: I/Xposed (15821) : The original token: 9 b7c274a07e7dbcdb99840b0aa3dfb3d9c200972c4c5706750c2922650af36a6 12-19 10:37:43. 662: I/Xposed (15821) : Enter - > beforeHookedMethod: getAccess_token 12-19 10:37:43. 662: I/Xposed (15821) : Enter - > afterHookedMethod: getAccess_token 12-19 10:37:43. 662: I/Xposed (15821) : hooked token: b6a8d0b02a651a7759051a5c8b1afa02db35636dd4c20c15dcbf050038d7ae2eCopy the code

There are many similar scenarios, mainly through reading code (with source code or decompilation), find key functions and some vulnerabilities in the code, obtain key information or tamper with method input and output parameters, to achieve the purpose of attack and penetration testing.

2. Test data construction

Sometimes special data needs to be constructed during client application testing, such as location, network mode, system version, screen aspect ratio, power, and so on. Some of this data can be constructed manually, but some of it is not. At this point, the Xposed framework can also help you out.

Taking the system time as an example, we write a Demo application that uses the Calendar class to obtain the system time:

    Calendar c = Calendar.getInstance();
    int year = c.get(Calendar.YEAR);
    int month = c.get(Calendar.MONTH);
    int day = c.get(Calendar.DAY_OF_MONTH);
    int hour = c.get(Calendar.HOUR_OF_DAY);
    int minute = c.get(Calendar.MINUTE);
    String time = ""+year+"-"+month+"-"+day+" "+hour+":"+minute;
    timeTV.setText(time);Copy the code

Under normal circumstances, its running result is:



Then, we only need the GET method of the Hook system Calendar class to construct the data we want:

findAndHookMethod("java.util.Calendar", lpparam.classLoader,"get",int.class,new XC_MethodHook() { @Override protected void beforeHookedMethod(MethodHookParam param) throws Throwable { // this will be called before the clock was updated by the original method XposedBridge.log("Enter->beforeHookedMethod:Calendar.get"); } @Override protected void afterHookedMethod(MethodHookParam param) throws Throwable { // this will be called after the clock was updated by the original method XposedBridge.log("Enter->afterHookedMethod:Calendar.get"); param.setResult((int)11); }});Copy the code

Its running results are as follows:

Similarly, other similar data can be constructed by Hook system method, even the Root mobile phone can be disguised as not Root (our company has a mobile phone punch card software, you can use Root spoofing and location forgery at home, of course I have not done so, check me).

3. Environmental monitoring

Because the Xposed framework is loaded when the system is started, so its monitoring ability is much stronger than our own background Service application. As for the objects to be monitored, they can be the system notifications, pop-ups, Toast information, user clicks, power quantity, signal changes and other explicitly perceptible events, as well as internal data such as memory, CPU and IO. Even to the unified exception handling methods (such as Java. Lang. Thread. UncaughtExceptionHandler), the underlying socket interface, page rendering method and so on, mainly to see what you need, rather than what it can do.

The example directly uses the Toast information check introduced in the method of android Toast text check by auxiliary tools in the previous article.

public class XposedHook implements IXposedHookZygoteInit { @Override public void initZygote(StartupParam startupParam) Throws Throwable {/ / hook set goals and methods XposedHelpers findAndHookMethod (Toast. The class, "the show", New XC_MethodHook() {@override protected void beforeHookedMethod(MethodHookParam param) throws Throwable {// Get the Toast object Toast t = (Toast) param.thisObject; Try {// get a unique TextView, i.e. Toast TextView View = t.goetview (); List list = new ArrayList(); if (view instanceof TextView) { list.add((TextView) view); } else if (view instanceof ViewGroup) { finaAllTextView(list, (ViewGroup) view); } if (list.size() ! = 1) { throw new RuntimeException("number of TextViews in toast is not 1"); } TextView text = list.get(0); CharSequence toastMsg = text.gettext (); System.out.println("XposedHookToast:"+toastMsg); } catch (RuntimeException e) { XposedBridge.log(e); }}}); Private void finaAllTextView(List addTo, ViewGroup View) {int count = view.getChildCount(); for (int i = 0; i < count; ++i) { View child = view.getChildAt(i); if (child instanceof TextView) { addTo.add((TextView) child); } else if (child instanceof ViewGroup) { finaAllTextView(addTo, view); }}}}Copy the code

Toast information obtained:

Line 5251: I/ system. out(815): XposedHookToast: Login failed due to incorrect user name or password, password expiration, or account lock Line 5959: I/ system. out(815): XposedHookToast: Failed to connect to serverCopy the code

In this way, you can handle abnormal events that occur during the running of automatic scripts, such as unexpected pop-ups or message bar notifications. It can also be used to shield the Monkey from the possibility of clicking the Exit or logout button at runtime. As long as the target event and its handling are set up in advance, it does a good job of monitoring.

4, dynamic buried point

If the purpose of monitoring is not environmental processing, but information acquisition, then evolution is a burial point. Since the Xposed can directly control a method before and after the call stage, the buried point for it is more like a talent skills, do not have to do more modification and adaptation, can directly in the immobile APP code to achieve easy management, strategy and real-time change to dynamic buried point.

Take the remaining system memory before and after the onCreate method in the MainActivity of the TesterHome client as an example:

findAndHookMethod("com.testerhome.nativeandroid.views.MainActivity", lpparam.classLoader,"onCreate",Bundle.class,new XC_MethodHook() { @Override protected void beforeHookedMethod(MethodHookParam param) throws Throwable { // this will be called before the clock was updated by the original method XposedBridge.log("Enter->beforeHookedMethod:onCreate"); Activity app = (Activity) param.thisObject; long availMem =getAvailMemory(app); XposedBridge.log("availMem before onCreate:"+availMem+"KB"); } @Override protected void afterHookedMethod(MethodHookParam param) throws Throwable { // this will be called after the clock was updated by the original method XposedBridge.log("Enter->afterHookedMethod:onCreate"); Activity app = (Activity) param.thisObject; long availMem =getAvailMemory(app); XposedBridge.log("availMem after onCreate:"+availMem+"KB"); }});Copy the code

Methods for obtaining the remaining memory of the system:

    public long getAvailMemory(Activity app) {
        ActivityManager am = (ActivityManager)app.getSystemService(Context.ACTIVITY_SERVICE);
        ActivityManager.MemoryInfo mi = new ActivityManager.MemoryInfo();
        am.getMemoryInfo(mi);
        return mi.availMem >> 10;
    }Copy the code

After the installation module restarts and runs TesterHome, you can see:

12-19 23:00:48. 442: I/Xposed (4476) : Enter - > beforeHookedMethod: 12-19 23:00:48 onCreate. 442: I/Xposed (4476) : AvailMem before onCreate:1901876KB 12-19 23:00:48.478: I/Xposed(4476): Enter - > afterHookedMethod: onCreate 12-19 23:00:48. 478: I/Xposed (4476) : availMem after onCreate: 1900760 KBCopy the code

You can refer to the client burying point of constant temperature for specific burying point data and process

Burying in this way has several benefits:

  1. No need to move APP source code, low adaptation cost;
  2. Flexible manner, ability to intervene in any process, can collect information and data completely;
  3. Easy to manage, can be added at any time to enable or delete abandoned buried points;
  4. There is no need to participate in the development, the test can achieve the buried point scheme according to the scene;

Of course, there is a big pothole:

  1. Only suitable for internal testing and cannot be distributed to real users for online monitoring.

5. Hot patches

Similar to the dynamic burying principle, since we can test a method by adding a before-and-after process, we can naturally fix the method by adding a before-and-after process, also known as a hot patch, when we find a problem with the method.

At present, the domestic Android more mature hot patch scheme is mainly Dexposed, AndFix, ClassLoader three kinds, the first two are Ali, the third is Tencent. The Dexposed scheme is based on the Xposed framework, but because it only on the application of their own process Hook, so do not need root authority.

About this, more specific information directly look at this article good alibaba-Dexposed framework online hot patch repair use

6. Automatic script recording

This is actually the environment monitoring ability of subdivision, since all events to monitor equipment, so if we targeted the system interactive interface and event monitor, record the interaction between the user and equipment process and information, is it possible to direct the user action in the corresponding automation scripts are generated after come out?

Let’s continue with a small example:

The application under test is still the Demo that obtained the time above. There is only one TextView and Button on the interface. What we need to do is to capture the Button click event and parse the Button information.

In order to ensure universality and consistency, the Hook method must be as low as possible. By looking at the source code and the related mechanism of event click distribution, the performClick method in the Android.view.View class is finally located, which will execute click related operations and event notification.

public boolean performClick() { sendAccessibilityEvent(AccessibilityEvent.TYPE_VIEW_CLICKED); ListenerInfo li = mListenerInfo; if (li ! = null && li.mOnClickListener ! = null) { playSoundEffect(SoundEffectConstants.CLICK); li.mOnClickListener.onClick(this); return true; } return false; }Copy the code

However, its parameters and return values do not contain information about the view, so how to capture the click event and its associated control information? At first I got caught up in the same trap of looking for a method with a view parameter or return value. But then on second thought, this method was in view of the class object instance, looked at the Xposed API, sure enough, there is a direct access to the instance object method. The code is as follows:

findAndHookMethod("android.view.View", lpparam.classLoader,"performClick",new XC_MethodHook() { @Override protected void beforeHookedMethod(MethodHookParam param) throws Throwable { // this will be called before the clock was updated by the original method XposedBridge.log("Enter->beforeHookedMethod:performClick"); } @SuppressLint("NewApi") @Override protected void afterHookedMethod(MethodHookParam param) throws Throwable { // this will be called after the clock was updated by the original method XposedBridge.log("Enter->afterHookedMethod:performClick"); View node = (View)param.thisObject; XposedBridge.log("NodeInfo:"+node.toString()); }});Copy the code

Very neat and logical, and then what’s the result? After installing the module, restarting the device, and starting Demo, click the button to obtain the time, you can see the following logs:

12 to 20 02:30:49. 958: I/Xposed (7346) : Enter - > beforeHookedMethod: 12 to 20 02:30:49 performClick. 958: I/Xposed (7346) : Enter - > afterHookedMethod: 12 ~ 20 02:30:49 performClick. 958: I/Xposed (7346) : NodeInfo: android. Widget. The Button {52910148 VFED.. C. ... PH... 24,76-168148 # 7 f080001 app: id/for the}Copy the code

Very powerful, we see the id of the button: Button1, as set in the application configuration

android:id="@+id/button1" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_alignLeft="@+id/textView1" android:layout_below="@+id/textView1" android:layout_marginTop="15dp" Android :text=" get time "/>Copy the code

Even with the view’s getLocationOnScreen method, we can get the coordinates of the button’s center point:

    12-20 02:57:53.672: I/Xposed(8340): NodeInfo X:24
    12-20 02:57:53.672: I/Xposed(8340): NodeInfo Y:186Copy the code

Similar events can capture and extract information about associated controls in this way, and with this data we can automatically script according to Appium or other framework apis.

The latter

The above are the results of my thinking and experiment in the past two days. Of course, they are not perfect yet. They are just some preliminary ideas. You can also open your imagination, such a powerful tool can do more than that!

The above.

TesterHome first, reprint please declare