One, foreword

There are many business modules that provide Deeplink services. Deeplink is simply an entry point for external applications.

Apps may choose to provide inconsistent services for different types of jumps, and in this case, it is necessary to distinguish between external jumps. In general, we use reflection to call the mReferrer field in AcElasticity to get the package name from which to jump.

The specific code is as follows;

/** * Get referrer * by reflection@return* /
private String reflectGetReferrer(a) {
    try {
        Field referrerField =
        Activity.class.getDeclaredField("mReferrer");
        referrerField.setAccessible(true);
        return (String) referrerField.get(this);
    } catch (NoSuchFieldException e) {
        e.printStackTrace();
    } catch (IllegalAccessException e) {
        e.printStackTrace();
    }
    return "";
}
Copy the code

But could the Referrer have been forged?

Once the mReferrer is forged, business logic will be wrong at least, and economic losses will be caused at worst. In this case, is there any way to find a safer source acquisition method?

This requires an analysis of the source of the mReferrer. Let’s do a mReferrer source alternative source analysis. The reason is unusual, because this time will use a lot of debugging means to reverse source analysis.

Second, where does the mReferrer come from

2.1 Search mReferrer and source backtracking

Use the search function to search the mReferrer in the Activity class. Use the Find Lead function to Find the mReferrer field.

The mReferrer is assigned in the Attach method of the Activity.

2.2 Trace the call stack using breakpoint debugging

We add a breakpoint to the Attach method to track the Attach call;

The red box is the Attach call path, which is executed in the main thread. The Attach can be seen from the call stack is ActivityThread performLaunchActivity calls.

PerformLaunchActivity calls Attach with the referrer parameter of r, which is an ActivityClientRecord object.

We further find the ActivityClientRecord constructor where the referrer is assigned.

Add a breakpoint in the constructor to view the call stack;

Find that the ActivityClientRecord is instantiated in the Execute of the LaunchActivityItem and the mReferrer of the LaunchActivityItem is passed in.

The mReferrer of the LaunchActivityItem is assigned in the setValues method, and we need to debug to see who called setValues. This is what we see when we look at the caller of setValues using a regular mode breakpoint.

LaunchActivityItem is a serialized and deserialized object in a local process.

In activities, serialized object transfers are usually done with binders, whose servers are in the System process. Deserialization is implemented here, so there must be a process of serialization in the remote binder services. We can debug this breakpoint in the System process, which should be serialization.

2.3 Breakpoint Debugging

The way to debug the System process is also relatively simple;

  • Step1: download and install Android X86 emulator (note that Google API version must be installed, play version does not support system process debugging).

  • Step2: select the System process during debugging.

Through debugging, we found the assignment stack (note that the stack is already a Binder process).

We follow the instructions of the stack step by step. It is important to note that when we look at the debug stack, we only need to focus on the class name and method name, not the line number in the stack, because the line number may not be accurate. If the differences are too great during debugging, you can try switching to an emulator version.

This is followed by ActivityStackSupervisor’s RealStart Activityacked method.

In ActivityStackSupervisor, we can see that this parameter is generated from R.launchedFrompackage. The r is ActivityRecord. Look for the LaunchedFromPackage assignment. Finally find the initialization method for ActivityRecord.

2.4 Object instantiation process

Add breakpoints to initialization methods for stack debugging;

Follow the stack step by step to ActivityStarter’s execute method, where you can see that the source of the package is mRequest.callingPackage.

By searching the Vaule write of the Request’s callingPackage object pair, mRequest.callingPackage comes from ActivityStarter’s setCallingPackage method, The setCallingPackage method must be called to inject the contents of the callingPackage.

After another step in the stack, call the method ActivityTaskManagerService startActivity method; StartActivity passes in the package at build time using setCallingPackage. It’s consistent with what we thought.

This analysis is close to the truth.

2.5 Analysis of Remote service Binder invocation

We all know ActivityTaskManagerService is a remote service, from its working process can be seen, is a process of binder. Because ActivityTaskManagerService extends IActivityTaskManager Stub, then we will go to find IActivityTaskManager. The Stub is a remote call.

To find the place where he remote calls, we have to find first IActivityTaskManager. How is a Stub of the caller.

Global search IActivityTaskManager. Stub or search IActivityTaskManager. Stub. AsInterface, here for the convenience of the Android source code using the online search platform.

We found the following code in ActivityTaskManager;

@TestApi
@SystemService(Context.ACTIVITY_TASK_SERVICE)
public class ActivityTaskManager {
 
    ActivityTaskManager(Context context, Handler handler) {
    }
 
    / * *@hide* /
    public static IActivityTaskManager getService(a) {
        return IActivityTaskManagerSingleton.get();
    }
 
    @UnsupportedAppUsage(trackingBug = 129726065)
    private static final Singleton<IActivityTaskManager> IActivityTaskManagerSingleton =
            new Singleton<IActivityTaskManager>() {
                @Override
                protected IActivityTaskManager create(a) {
                    final IBinder b = ServiceManager.getService(Context.ACTIVITY_TASK_SERVICE);
                    // The remote call object is generated here
                    returnIActivityTaskManager.Stub.asInterface(b); }}; }Copy the code

I.e. ActivityTaskManager. GetService () method can get IActivityTaskManager. The handle to the remote invocation stubs.

So ActivityTaskManagerService startActivity method call writing should be ActivityTaskManager. GetService () startActivity, the next plan is to find the method call.

2.6 Universal search is not universal

In accordance with the normal way of thinking, we will again use the search function on the online source website search ActivityTaskManager. GetService () startActivity.

Can’t search? Here it is important to note that because startActivity methods there are many parameters, is likely to code is a newline, once a newline, search ActivityTaskManager. GetService (). You can’t search to startActivity.

Search is not a panacea, so let’s consider adding breakpoints.

So where do I put the breakpoint? Whether we could add a breakpoint on ActivityTaskManagerService startActivity?

The answer is no, if you try to add breakpoints to a binder process call (remote service call) method. Then you just get the following call stack.

It is obvious that the call stack points directly to the Binder remote, which is not the call stack we want. We know that call startActivity source must be ActivityTaskManager. GetService () startActivity.

This line of code must be called in the App process by binder’s client, so let’s try adding a breakpoint on getService(). Note that the breakpoint has been added because startActivity should be called by the attacker, i.e. by the application that Deeplink is calling.

So. We need to debug Deeplink’s initiator. We can write a Demo for debugging.

Click on the button to launch Deeplink and then perform a breakpoint, at which point you will find the following stack.

Click next breakpoint (Step Over) just ActivityTaskManager. GetService () method call startActivity.

So we get the following call stack;

ContextImpl.startActivty() Instrumentation.execStartActivity() ActivityTaskManager.getService() .startActivity(whoThread, who.getBasePackageName(), intent, intent.resolveTypeIfNeeded(who.getContentResolver()), token, target ! =null ? target.mEmbeddedID : null,
                     requestCode, 0.null, options);
Copy the code

You can see that the callingPackage is implemented using getBasePackageName. Who is the context, which is our Activity.

This confirms that the mReferrer is actually implemented using the context’s getBasePackageName().

Iii. How to avoid package name being forged

3.1 Paying Attention to PID and Uid

How can I prevent PackageName from being forged?

When we debug ActivityRecord, we find that the properties of ActivityRecord also have PID and Uid;

As long as we get the Uid, we can call the packageManager method according to the Uid to get the registration of the corresponding Uid.

3.2 Investigate whether the Uid is possible to be forged

The next step is to verify that the Uid can be forged. Debug to find the source of the Uid and view the source of the callingUid at the initialization method breakpoint of ActivityRecord.

We found that the Uid was actually obtained using binder.getCallinguID in ActivityStarter. The Binder process is not an application-level intervention. You can use the Uid without worrying about being forged. All that is left is how to get the PackageName using the Uid.

3.3 Replacing the PackageName with a Uid

We found ActivityTaskManagerService retrieval code, just provide a method to get the Uid.

So we need to get ActivityTaskManagerService references, search IActivityTaskManager Stub.

ActivityTaskManager is not referenced in the app layer (it is a hidden class, but there are ways to do this, so you can explore it yourself).

Let’s keep looking;

Eventually found ActivityManager provides a method to obtain ActivityTaskManagerService so, but, unfortunately, getTaskService is a blacklist method, are forbidden to call.

Finally we found ActivityTaskManagerService getLaunchedFromUid method is been ActivityManageService packaging.

public class ActivityManagerService extends IActivityManager.Stub
        implements Watchdog.Monitor.BatteryStatsImpl.BatteryCallback {
 
 
    @VisibleForTesting
    public ActivityTaskManagerService mActivityTaskManager;
 
 
    @Override
    public boolean updateConfiguration(Configuration values) {
        return mActivityTaskManager.updateConfiguration(values);
    }
 
    @Override
    public int getLaunchedFromUid(IBinder activityToken) {
        return mActivityTaskManager.getLaunchedFromUid(activityToken);
    }
 
    public String getLaunchedFromPackage(IBinder activityToken) {
        returnmActivityTaskManager.getLaunchedFromPackage(activityToken); }}Copy the code

So you can use ActivityManageService to call it, as shown below (note that the code may vary from system to system version).

private String reRealPackage(a) {
    try {
        Method getServiceMethod = ActivityManager.class.getMethod("getService");
        Object sIActivityManager = getServiceMethod.invoke(null);
        Method sGetLaunchedFromUidMethod = sIActivityManager.getClass().getMethod("getLaunchedFromUid", IBinder.class);
        Method sGetActivityTokenMethod = Activity.class.getMethod("getActivityToken");
        IBinder binder = (IBinder) sGetActivityTokenMethod.invoke(this);
        int uid = (int) sGetLaunchedFromUidMethod.invoke(sIActivityManager, binder);
        return getPackageManager().getPackagesForUid(uid)[0];
    } catch (Exception e) {
        e.printStackTrace();
    }
    return "null";
}
Copy the code

Is it safe to replace the PackageName with a Uid? Is there more to this? Keep it in suspense, and you can discuss it in the comments section.

Four,

The mReferrer can easily be forged by overwriting the context’s getBasePackageName(), so be careful when using it. The Uid obtained from ActivityManageService cannot be forged, so consider using the Uid to convert PackageName.

Author: Chen Long, Vivo Internet Client Team