前段时间接到一个有趣的需求!我们实现的某某功能的手机对外提供了API!结果有希望只有自己承认的厂商的APK才能使用这些接口!其他的过滤掉!
哈哈!看上去挺变态的!对外公布的API又不让人家用!没办法啊!需求就是爷!一个字做!
回顾android安全机制貌似没有这么一项功能!不过咱能做!谁叫咱能改源码呢!有源码什么不能改了啊!哈哈!。。。。
那么方案调研过程我们省略这里面涉及的知识比较多!直接上方案了!
步骤是先想法知道谁调用了你的接口!然后把调用接口的apk的厂商信息找出来!
同一个厂商的apk的签名应该是一样的!拥有同样的signature以及pubkeyid!所以最终目的是得到signature!
研究查到有如下接口
Java代码
- PackageInfo pkgInfo;
- try {
- pkgInfo = getPackageManager().getPackageInfo("packagename", PackageManager.GET_SIGNATURES);
-
- Signature enterproidSign = pkgInfo.signatures[0];
-
- if (!enterproidSign.toCharsString().equals(key)) {
- // Wrong signature
- return;
- }
- } catch (NameNotFoundException e) {
- // TODO Auto-generated catch block
- e.printStackTrace();
也就是说当务之急就是找到谁调用了这个接口!即是packagename!
本来我们知道想知道谁调用了你只要在参数中加入context的信息就很容易得到apk的唯一标识包名了!
如下面的code
Java代码
- String packageName = this.getApplicationInfo().packageName;
- Log.e("Dean", "getApplicationInfo:"+packageName);
- String dataDir = this.getApplicationInfo().dataDir;
- Log.e("Dean", "getApplicationInfo:dataDir"+dataDir);
- String sourceDir = this.getApplicationInfo().sourceDir;
- Log.e("Dean", "getApplicationInfo:sourceDir"+sourceDir);
- String publicSourceDir = this.getApplicationInfo().publicSourceDir;
- Log.e("Dean", "getApplicationInfo:publicSourceDir"+publicSourceDir);
- int descriptionRes = this.getApplicationInfo().descriptionRes;
- Log.e("Dean", "getApplicationInfo:descriptionRes"+descriptionRes);
偏偏接口中没有context的参数!而且这个方式貌似就有局限性了!
那么我们可以想想在做checkpermission的判断的时候也需要知道是哪个apk吧!可以看看别人是怎么做到的!
看一下源代码:
Java代码
- public int checkCallingOrSelfPermission(String permission) {
- if (permission == null) {
- throw new IllegalArgumentException("permission is null");
- }
- return checkPermission(permission, Binder.getCallingPid(),
- Binder.getCallingUid());
- }
这里可以通过Binder.getCallingPid(),Binder.getCallingUid()的到apk的 pid 和 uid!
那看看源码流程是怎么样的
Java代码
- /**
- * This can be called with or without the global lock held.
- */
- int checkComponentPermission(String permission, int pid, int uid,
- int owningUid, boolean exported) {
- // We might be performing an operation on behalf of an indirect binder
- // invocation, e.g. via {@link #openContentUri}. Check and adjust the
- // client identity accordingly before proceeding.
- Identity tlsIdentity = sCallerIdentity.get();
- if (tlsIdentity != null) {
- Slog.d(TAG, "checkComponentPermission() adjusting {pid,uid} to {"
- + tlsIdentity.pid + "," + tlsIdentity.uid + "}");
- uid = tlsIdentity.uid;
- pid = tlsIdentity.pid;
- }
-
- // Root, system server and our own process get to do everything.
- if (uid == 0 || uid == Process.SYSTEM_UID || pid == MY_PID ||
- !Process.supportsProcesses()) {
- return PackageManager.PERMISSION_GRANTED;
- }
- // If there is a uid that owns whatever is being accessed, it has
- // blanket access to it regardless of the permissions it requires.
- if (owningUid >= 0 && uid == owningUid) {
- return PackageManager.PERMISSION_GRANTED;
- }
- Log.e("Dean", "ActivityManagerService checkComponentPermission uid:" + uid + "pid:" + pid
- + "!Process.supportsProcesses()" + !Process.supportsProcesses() + "owningUid:"
- + owningUid + "exported:" + exported + "permission:" + permission);
-
- // If the target is not exported, then nobody else can get to it.
- if (!exported) {
- Slog.w(TAG, "Permission denied: checkComponentPermission() owningUid=" + owningUid);
- return PackageManager.PERMISSION_DENIED;
- }
- if (permission == null) {
- return PackageManager.PERMISSION_GRANTED;
- }
- try {
- return AppGlobals.getPackageManager()
- .checkUidPermission(permission, uid);
- } catch (RemoteException e) {
- // Should never happen, but if it does... deny!
- Slog.e(TAG, "PackageManager is dead?!?", e);
- }
- return PackageManager.PERMISSION_DENIED;
- }
这样我们发现在对apk拥有的是否是root等权限判断后调用了
Java代码
- AppGlobals.getPackageManager()
- .checkUidPermission(permission, uid);
这样就调回到packagemanage里面来通过uid群找它所拥有的permission,code 如下
Java代码
- public int checkUidPermission(String permName, int uid) {
-
- synchronized (mPackages) {
- Object obj = mSettings.getUserIdLP(uid);
- if (obj != null) {
- GrantedPermissions gp = (GrantedPermissions)obj;
- Log.e("Dean", "PackageManagerService checkUidPermission obj != null");
- if (gp.grantedPermissions.contains(permName)) {
- return PackageManager.PERMISSION_GRANTED;
- }
- } else {
- HashSet<String> perms = mSystemPermissions.get(uid);
- Log.e("Dean", "PackageManagerService checkUidPermission obj == null");
- if (perms != null && perms.contains(permName)) {
- return PackageManager.PERMISSION_GRANTED;
- }
- }
- }
- Log.e("Dean", "PackageManagerService checkUidPermission");
- return PackageManager.PERMISSION_DENIED;
- }
通过这个uid获得GrantedPermissions!这个就是他所拥有的permission列表!
这个方法后面的部分貌似对我们的需求么用!
我们需要得到的是apk的唯一标识包名!
这里有两个方法
一个用uid 获得包名
Java代码
- public String[] getPackagesForUid(int uid)
这里得到的是一个数组
这里之所以会得到一个数组是因为有一个属性android:sharedUserId
多个apk可以共享一个userid!那么这会不会影响我们的结果了!
答案是不会!当然这个答案是做过测试的!如果有android:sharedUserId那么两个apk必须有相同的证书!否则会报INSTALL_FAILED_SHARED_USER_INCOMPATIBLE
错误!也就是说得到的数组的任何一个packagename取到的Signature应该是一样的!
所以此方案可行!
另一个通过pid可以知道当前的进程运行pkglist!
Java代码
- List<RunningAppProcessInfo> getRunningAppProcesses = null;
- try {
- getRunningAppProcesses = ActivityManagerNative.getDefault()
- .getRunningAppProcesses();
- } catch (Exception e) {
- }
-
- for (RunningAppProcessInfo runningappprocessinfo : getRunningAppProcesses) {
- Log.e("Dean", "runningappprocessinfo.pid:"+runningappprocessinfo.pid+"runningappprocessinfo.processName"+runningappprocessinfo.processName);
-
- for(int i = 0;i<runningappprocessinfo.pkgList.length;i++){
- Log.e("Dean","pkgname:"+runningappprocessinfo.pid+":"+runningappprocessinfo.pkgList[i]);
- }
- }
- Log.e("Dean", "...................................................");
- for (RunningAppProcessInfo runningappprocessinfo : getRunningAppProcesses) {
- Log.e("Dean","runningappprocessinfo.pid:"+runningappprocessinfo.pid);
- if (runningappprocessinfo.pid == Binder.getCallingPid()) {
- Log.e("Dean","runningappprocessinfo.pid:"+runningappprocessinfo.pid);
- Log.e("Dean", "runningappprocessinfo" + runningappprocessinfo
- + "runningappprocessinfo.processName:"
- + runningappprocessinfo.processName
- + "runningappprocessinfo.pkgList:"
- + runningappprocessinfo.pkgList);
- ApplicationInfo mAppInfo = null;
- try {
- Log.e("Dean","runningappprocessinfo.pkgList"+runningappprocessinfo.pkgList);
-
- Log.e("Dean","runningappprocessinfo.pkgList"+runningappprocessinfo.pkgList.length);
-
- for(int i = 0;i<runningappprocessinfo.pkgList.length;i++){
- Log.e("Dean","pkgname:"+runningappprocessinfo.pkgList[i]);
- }
-
- mAppInfo = getPackageManager().getApplicationInfo(runningappprocessinfo.processName,
- PackageManager.GET_UNINSTALLED_PACKAGES);
-
- Log.e("Dean", "mAppInfo.publicSourceDir"+mAppInfo.publicSourceDir);
- Log.e("Dean", "mAppInfo.sourceDir"+mAppInfo.sourceDir);
- } catch (Exception e) {
- }
- }
- }