Android Media Server Agent Solution

Android Media Server Agent Solution

[NT_PROXY]

background

In white list mode, IQI app can not play video, but can display video pictures, video advertisements and so on.

If you switch to full proxy or non-proxy pure public network environment, there is no problem.

Analysis

problem analysis

1. Edgecraft Video Playing Depends on android Measerver

2. Measerver is an elf process initiated directly by init.rc, which is not managed by Package Manager of framework layer, and its UID is ROOT_UID(0)

3. The object added to the proxy app whitelist is package name, which is finally applied to the underlying uidrange, where the mapping is done through Android native Package Manager.

Conclusion:

Unable to add mediaserver to proxy via framework's java interface

In-depth analysis of Android proxy process

Android allocates a unique UID for each apk, as follows:

<package name="com.github.appproxy" codePath="/system/priv-app/appproxy" nativeLibraryPath="/system/priv-app/appproxy/lib" primaryCpuAbi="armeabi-v7a" flags="1145683527" ft="163f34bc438" it="163ee430640" ut="163f34bc438" version="4050700" userId="10016">

Source tracking:

if (mConfig.allowedApplications != null) {
    // Add ranges covering all UIDs for allowedApplications.
    int start = -1, stop = -1;
    for (int uid : getAppsUids(mConfig.allowedApplications, userHandle)) {
        if (start == -1) {
            start = uid;
        } else if (uid != stop + 1) {
            mVpnUsers.add(new UidRange(start, stop));
            start = uid;
        }
        stop = uid;
    }
    if (start != -1) mVpnUsers.add(new UidRange(start, stop));
} else if (mConfig.disallowedApplications != null) {
    // Add all ranges for user skipping UIDs for disallowedApplications.
    final UidRange userRange = UidRange.createForUser(userHandle);
    int start = userRange.start;
    for (int uid : getAppsUids(mConfig.disallowedApplications, userHandle)) {
        if (uid == start) {
            start++;
        } else {
            mVpnUsers.add(new UidRange(start, uid - 1));
            start = uid + 1;
        }
    }
    if (start <= userRange.stop) mVpnUsers.add(new UidRange(start, userRange.stop));
} else {
    // Add all UIDs for the user.
    mVpnUsers.add(UidRange.createForUser(userHandle));
}

private void onUserAdded(int userHandle) {
    // If the user is restricted tie them to the owner's VPN
    synchronized(Vpn.this) {
        UserManager mgr = UserManager.get(mContext);
        UserInfo user = mgr.getUserInfo(userHandle);
        if (user.isRestricted()) {
            try {
                addVpnUserLocked(userHandle);
                if (mNetworkAgent != null) {
                    final List<UidRange> ranges = uidRangesForUser(userHandle);
                    mNetworkAgent.addUidRanges(ranges.toArray(new UidRange[ranges.size()]));
                }
            } catch (Exception e) {
                Log.wtf(TAG, "Failed to add restricted user to owner", e);
            }
        }
    }
}

public void addUidRanges(UidRange[] ranges) {
    queueOrSendMessage(EVENT_UID_RANGES_ADDED, ranges);
}

case NetworkAgent.EVENT_UID_RANGES_ADDED: {
    NetworkAgentInfo nai = mNetworkAgentInfos.get(msg.replyTo);
    if (nai == null) {
        loge("EVENT_UID_RANGES_ADDED from unknown NetworkAgent");
        break;
    }
    try {
        mNetd.addVpnUidRanges(nai.network.netId, (UidRange[])msg.obj);
    } catch (Exception e) {
        // Never crash!
        loge("Exception in addVpnUidRanges: " + e);
    }
    break;
}

public void addVpnUidRanges(int netId, UidRange[] ranges) {
    mContext.enforceCallingOrSelfPermission(CONNECTIVITY_INTERNAL, TAG);
    Object[] argv = new Object[3 + MAX_UID_RANGES_PER_COMMAND];
    argv[0] = "users";
    argv[1] = "add";
    argv[2] = netId;
    int argc = 3;
    // Avoid overly long commands by limiting number of UID ranges per command.
    for (int i = 0; i < ranges.length; i++) {
        argv[argc++] = ranges[i].toString();
        if (i == (ranges.length - 1) || argc == argv.length) {
            try {
                mConnector.execute("network", Arrays.copyOf(argv, argc));
            } catch (NativeDaemonConnectorException e) {
                throw e.rethrowAsParcelableException();
            }
            argc = 3;
        }
    }
}
if (!strcmp(argv[1], "users")) {
    if (argc < 4) {
        return syntaxError(client, "Missing argument");
    }
    unsigned netId = stringToNetId(argv[3]);
    UidRanges uidRanges;
    if (!uidRanges.parseFrom(argc - 4, argv + 4)) {
        return syntaxError(client, "Invalid UIDs");
    }
    if (!strcmp(argv[2], "add")) {
        if (int ret = sNetCtrl->addUsersToNetwork(netId, uidRanges)) {
            return operationError(client, "addUsersToNetwork() failed", ret);
        }
    } else if (!strcmp(argv[2], "remove")) {
        if (int ret = sNetCtrl->removeUsersFromNetwork(netId, uidRanges)) {
            return operationError(client, "removeUsersFromNetwork() failed", ret);
        }
    } else {
        return syntaxError(client, "Unknown argument");
    }
    return success(client);
}

/*Supporting only virtual networks*/
int NetworkController::addUsersToNetwork(unsigned netId, const UidRanges& uidRanges) {
    android::RWLock::AutoWLock lock(mRWLock);
    Network* network = getNetworkLocked(netId);
    if (!network) {
        ALOGE("no such netId %u", netId);
        return -ENONET;
    }
    if (network->getType() != Network::VIRTUAL) {
        ALOGE("cannot add users to non-virtual network with netId %u", netId);
        return -EINVAL;
    }
    if (int ret = static_cast<VirtualNetwork*>(network)->addUsers(uidRanges)) {
        return ret;
    }
    return 0;
}

So far, the add action is just an addition to storage.

Logic for adding routing policy tables:
RouteController.cpp

WARN_UNUSED_RESULT int modifyVirtualNetwork(unsigned netId, const char* interface,
                                            const UidRanges& uidRanges, bool secure, bool add,
                                            bool modifyNonUidBasedRules) {
    uint32_t table = getRouteTableForInterface(interface);
    if (table == RT_TABLE_UNSPEC) {
        return -ESRCH;
    }

    for (const UidRanges::Range& range : uidRanges.getRanges()) {
        if (int ret = modifyVpnUidRangeRule(table, range.first, range.second, secure, add)) {
            return ret;
        }
        if (int ret = modifyExplicitNetworkRule(netId, table, PERMISSION_NONE, range.first,
                                                range.second, add)) {
            return ret;
        }
        if (int ret = modifyOutputInterfaceRule(interface, table, PERMISSION_NONE, range.first,
                                                range.second, add)) {
            return ret;
        }
    }

    if (modifyNonUidBasedRules) {
        if (int ret = modifyIncomingPacketMark(netId, interface, PERMISSION_NONE, add)) {
            return ret;
        }
        if (int ret = modifyVpnOutputToLocalRule(interface, add)) {
            return ret;
        }
        if (int ret = modifyVpnSystemPermissionRule(netId, table, secure, add)) {
            return ret;
        }
        return modifyExplicitNetworkRule(netId, table, PERMISSION_NONE, UID_ROOT, UID_ROOT, add);
    }

    return 0;
}

// Adds or removes a routing rule for IPv4 and IPv6.
//
// + If |table| is non-zero, the rule points at the specified routing table. Otherwise, the rule
//   returns ENETUNREACH.
// + If |mask| is non-zero, the rule matches the specified fwmark and mask. Otherwise, |fwmark| is
//   ignored.
// + If |iif| is non-NULL, the rule matches the specified incoming interface.
// + If |oif| is non-NULL, the rule matches the specified outgoing interface.
// + If |uidStart| and |uidEnd| are not INVALID_UID, the rule matches packets from UIDs in that
//   range (inclusive). Otherwise, the rule matches packets from all UIDs.
//
// Returns 0 on success or negative errno on failure.
WARN_UNUSED_RESULT int modifyIpRule(uint16_t action, uint32_t priority, uint32_t table,
                                    uint32_t fwmark, uint32_t mask, const char* iif,
                                    const char* oif, uid_t uidStart, uid_t uidEnd) {

}

programme

Through source code analysis, whitelist is actually aimed at UID, the root user when mediaserver, so our goal is to add root to VPN List, just need the following routing strategy:

from all uidrange 0-0 lookup tun0

Interestingly, native VPN implementations deal with root users separately, with the following rules:

13000:  from all fwmark 0x10072/0x1ffff uidrange 0-0 lookup tun0 

With the rule we need, we just hit a mark, because the mark of mediaserver is not 0x10072/0x1ffff, so it can not match this rule, so it can not be proxyed.

The source code corresponding to the above rules is as follows:

WARN_UNUSED_RESULT int modifyVirtualNetwork(unsigned netId, const char* interface,
                                            const UidRanges& uidRanges, bool secure, bool add,
                                            bool modifyNonUidBasedRules) {
    if (modifyNonUidBasedRules) {
        return modifyExplicitNetworkRule(netId, table, PERMISSION_NONE, UID_ROOT, UID_ROOT, add);
    }
    return 0;
}

WARN_UNUSED_RESULT int modifyExplicitNetworkRule(unsigned netId, uint32_t table,
                                                 Permission permission, uid_t uidStart,
                                                 uid_t uidEnd, bool add) {
    Fwmark fwmark;
    Fwmark mask;
    uint32_t priority;

    fwmark.netId = netId;
    mask.netId = FWMARK_NET_ID_MASK;

    fwmark.explicitlySelected = true;
    mask.explicitlySelected = true;

    fwmark.permission = permission;
    mask.permission = permission;

    priority = RULE_PRIORITY_EXPLICIT_NETWORK;

    return modifyIpRule(add ? RTM_NEWRULE : RTM_DELRULE, priority, table,
                        fwmark.intValue, mask.intValue, IIF_NONE, OIF_NONE, uidStart, uidEnd);
}

Remove mark logic from this source code

Keywords: network Android VPN Java

Added by kingconnections on Sat, 18 May 2019 02:47:13 +0300