星光/宝骏/缤果/长安 车机CarPlay盒子权限破解原理普及----码农版本

从 app 申请 usbdevice 权限说起,通用 app 申请 usbdevice 权限代码如下

一般通过监听 ACTION_USB_DEVICE_ATTACHED usb 设备插入广播获取 UsbDevice 设备,或者通过 UsbManager 枚举出我们感兴趣的 UsbDevice 设备

然后调用 requestPermission(UsbDevice, PendingIntent) 申请权限,这时候系统一般会弹对话框询问是否允许访问usb设备,用户点确定或取消都将

收到自定义的回调广播 ACTION_USB_PERMISSION

 private static final String ACTION_USB_PERMISSION = "com.android.example.USB_PERMISSION";
	
 public void getPermission() {
        HashMap<String, UsbDevice> deviceList = usbManager.getDeviceList();
        Iterator<UsbDevice> deviceIterator = deviceList.values().iterator();
        PendingIntent mPermissionIntent = PendingIntent.getBroadcast(AdbActivity.this, 0, new Intent(ACTION_USB_PERMISSION), 0);
        IntentFilter filter = new IntentFilter(ACTION_USB_PERMISSION);
        registerReceiver(mUsbReceiver, filter);

        while(deviceIterator.hasNext()){
            UsbDevice device = deviceIterator.next();
            Log.d("AdbActivity", "getPermission: "+device.getDeviceName());
            if (device.getDeviceName().contains("/dev/bus/usb/001")) {
                if (usbManager.hasPermission(device)){
                    Toast.makeText(AdbActivity.this,"已授权", Toast.LENGTH_SHORT).show();
                    usbManager.openDevice(device);
                }else {
                    usbManager.requestPermission(device, mPermissionIntent);
                }
            }
        }
    }
	
	private BroadcastReceiver mUsbReceiver = new BroadcastReceiver() {
        public void onReceive(Context context, Intent intent) {
            String action = intent.getAction();
            if (ACTION_USB_PERMISSION.equals(action)){
                synchronized (this) {
                    UsbDevice device = (UsbDevice)intent.getParcelableExtra(UsbManager.EXTRA_DEVICE);
                    if (intent.getBooleanExtra(UsbManager.EXTRA_PERMISSION_GRANTED, false)) {
                        if(device != null){
                            usbManager.openDevice(device);
                            hintText.setText("openDevice");
                        }
                    }else {
                        Toast.makeText(context,"permission denied for device ", Toast.LENGTH_SHORT).show();
                        Log.d("AdbActivity", "permission denied for device " + device);
                    }
                }
            }
        }
    };
	

接下来分析下系统弹权限框已经授权流程

UsbManager 其实调用 UsbService 中 requestDevicePermission()

UsbService 调用 UsbUserSettingsManager 中 requestPermission()

frameworks\base\services\usb\java\com\android\server\usb\UsbService.java

    @Override
    public void requestDevicePermission(UsbDevice device, String packageName, PendingIntent pi) {
        final int uid = Binder.getCallingUid();
        final int userId = UserHandle.getUserId(uid);

        final long token = Binder.clearCallingIdentity();
        try {
            getSettingsForUser(userId).requestPermission(device, packageName, pi, uid);
        } finally {
            Binder.restoreCallingIdentity(token);
        }
    }
	
	private UsbUserSettingsManager getSettingsForUser(@UserIdInt int userIdInt) {
        return mSettingsManager.getSettingsForUser(userIdInt);
    }

frameworks\base\services\usb\java\com\android\server\usb\UsbSettingsManager.java

	@NonNull UsbUserSettingsManager getSettingsForUser(@UserIdInt int userId) {
    synchronized (mSettingsByUser) {
            UsbUserSettingsManager settings = mSettingsByUser.get(userId);
            if (settings == null) {
                settings = new UsbUserSettingsManager(mContext, UserHandle.of(userId),
                        new UsbPermissionManager(mContext, UserHandle.of(userId)));
                mSettingsByUser.put(userId, settings);
            }
            return settings;
        }
    }

先判断 UsbDevice 是否已经授权,若已授权,直接将 PendingIntent 扔回去

若无授权,则准备走弹出申请权限框 requestPermissionDialog()

其实最终调用 UsbPermissionManager 中 requestPermissionDialog()

frameworks\base\services\usb\java\com\android\server\usb\UsbUserSettingsManager.java

	public void requestPermission(UsbDevice device, String packageName, PendingIntent pi, int uid) {
        Intent intent = new Intent();

        // respond immediately if permission has already been granted
        if (hasPermission(device, packageName, uid)) {
            intent.putExtra(UsbManager.EXTRA_DEVICE, device);
            intent.putExtra(UsbManager.EXTRA_PERMISSION_GRANTED, true);
            try {
                pi.send(mUserContext, 0, intent);
            } catch (PendingIntent.CanceledException e) {
                if (DEBUG) Slog.d(TAG, "requestPermission PendingIntent was cancelled");
            }
            return;
        }
        if (isCameraDevicePresent(device)) {
            if (!isCameraPermissionGranted(packageName, uid)) {
                intent.putExtra(UsbManager.EXTRA_DEVICE, device);
                intent.putExtra(UsbManager.EXTRA_PERMISSION_GRANTED, false);
                try {
                    pi.send(mUserContext, 0, intent);
                } catch (PendingIntent.CanceledException e) {
                    if (DEBUG) Slog.d(TAG, "requestPermission PendingIntent was cancelled");
                }
                return;
            }
        }

        requestPermissionDialog(device, null, canBeDefault(device, packageName), packageName, pi,
                uid);
    }
	
	
	private void requestPermissionDialog(@Nullable UsbDevice device,
                                         @Nullable UsbAccessory accessory,
                                         boolean canBeDefault,
                                         String packageName,
                                         PendingIntent pi,
                                         int uid) {
        // compare uid with packageName to foil apps pretending to be someone else
        try {
            ApplicationInfo aInfo = mPackageManager.getApplicationInfo(packageName, 0);
            if (aInfo.uid != uid) {
                throw new IllegalArgumentException("package " + packageName +
                        " does not match caller's uid " + uid);
            }
        } catch (PackageManager.NameNotFoundException e) {
            throw new IllegalArgumentException("package " + packageName + " not found");
        }

        mUsbPermissionManager.requestPermissionDialog(device,
                accessory, canBeDefault, packageName, uid, mUserContext, pi);
    }

可以看到直接指定了包名和类名,最终是拉起 SystemUI 中 UsbPermissionActivity

frameworks\base\services\usb\java\com\android\server\usb\UsbPermissionManager.java

 /**
     * Creates UI dialog to request permission for the given package to access the device
     * or accessory.
     *
     * @param device The USB device attached
     * @param accessory The USB accessory attached
     * @param canBeDefault Whether the calling pacakge can set as default handler
     * of the USB device or accessory
     * @param packageName The package name of the calling package
     * @param uid The uid of the calling package
     * @param userContext The context to start the UI dialog
     * @param pi PendingIntent for returning result
     */
    void requestPermissionDialog(@Nullable UsbDevice device,
                                 @Nullable UsbAccessory accessory,
                                 boolean canBeDefault,
                                 @NonNull String packageName,
                                 int uid,
                                 @NonNull Context userContext,
                                 @NonNull PendingIntent pi) {
        long identity = Binder.clearCallingIdentity();
        Intent intent = new Intent();
        if (device != null) {
            intent.putExtra(UsbManager.EXTRA_DEVICE, device);
        } else {
            intent.putExtra(UsbManager.EXTRA_ACCESSORY, accessory);
        }
        intent.putExtra(Intent.EXTRA_INTENT, pi);
        intent.putExtra(Intent.EXTRA_UID, uid);
        intent.putExtra(UsbManager.EXTRA_CAN_BE_DEFAULT, canBeDefault);
        intent.putExtra(UsbManager.EXTRA_PACKAGE, packageName);
        intent.setClassName("com.android.systemui",
                "com.android.systemui.usb.UsbPermissionActivity");
        intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);

        try {
            userContext.startActivityAsUser(intent, mUser);
        } catch (ActivityNotFoundException e) {
            Slog.e(LOG_TAG, "unable to start UsbPermissionActivity");
        } finally {
            Binder.restoreCallingIdentity(identity);
        }
    }

UsbPermissionActivity 就一个普通 AlertActivity,通常很多系统开发人员做默认授权时都会来改这里,

将 mPermissionGranted=true,直接 finish() 这样就能达到默认授权且不弹框的目的,因为最终授权是在ondestory()中

核心方法就下面两个,一个是仅授权一次,另一个是总是授权(前提是apk没被卸载,apk卸载后授权也会情况)

service.grantDevicePermission(mDevice, mUid);

service.setDevicePackage(mDevice, mPackageName, userId);

饶了半天又得回到 UsbService 中了

frameworks\base\packages\SystemUI\src\com\android\systemui\usb\UsbPermissionActivity.java

   <activity android:name=".usb.UsbPermissionActivity"
            android:exported="true"
            android:permission="android.permission.MANAGE_USB"
            android:theme="@style/Theme.SystemUI.Dialog.Alert"
            android:finishOnCloseSystemDialogs="true"
            android:excludeFromRecents="true">
        </activity>
		
	 @Override
    public void onDestroy() {
        IBinder b = ServiceManager.getService(USB_SERVICE);
        IUsbManager service = IUsbManager.Stub.asInterface(b);

        // send response via pending intent
        Intent intent = new Intent();
        try {
            if (mDevice != null) {
                intent.putExtra(UsbManager.EXTRA_DEVICE, mDevice);
                if (mPermissionGranted) {
                    service.grantDevicePermission(mDevice, mUid);
                    if (mAlwaysUse != null && mAlwaysUse.isChecked()) {
                        final int userId = UserHandle.getUserId(mUid);
                        service.setDevicePackage(mDevice, mPackageName, userId);
                    }
                }
            }
            if (mAccessory != null) {
                intent.putExtra(UsbManager.EXTRA_ACCESSORY, mAccessory);
                if (mPermissionGranted) {
                    service.grantAccessoryPermission(mAccessory, mUid);
                    if (mAlwaysUse != null && mAlwaysUse.isChecked()) {
                        final int userId = UserHandle.getUserId(mUid);
                        service.setAccessoryPackage(mAccessory, mPackageName, userId);
                    }
                }
            }
            intent.putExtra(UsbManager.EXTRA_PERMISSION_GRANTED, mPermissionGranted);
            mPendingIntent.send(this, 0, intent);
        } catch (PendingIntent.CanceledException e) {
            Log.w(TAG, "PendingIntent was cancelled");
        } catch (RemoteException e) {
            Log.e(TAG, "IUsbService connection failed", e);
        }

        if (mDisconnectedReceiver != null) {
            unregisterReceiver(mDisconnectedReceiver);
        }
        super.onDestroy();
    }	
		

frameworks\base\services\usb\java\com\android\server\usb\UsbService.java
frameworks\base\services\usb\java\com\android\server\usb\UsbUserSettingsManager.java
frameworks\base\services\usb\java\com\android\server\usb\UsbPermissionManager.java

可以看到仅授权一次其实最终就是将 device 和 uid 保存到 mDevicePermissionMap 中

看注释可知这只是一个临时的权限机制

/** Temporary mapping USB device name to list of UIDs with permissions for the device*/
private final HashMap<String, SparseBooleanArray> mDevicePermissionMap = new HashMap<>();

	
    @Override
    public void grantDevicePermission(UsbDevice device, int uid) {
        mContext.enforceCallingOrSelfPermission(android.Manifest.permission.MANAGE_USB, null);
        final int userId = UserHandle.getUserId(uid);

        final long token = Binder.clearCallingIdentity();
        try {
            getSettingsForUser(userId).grantDevicePermission(device, uid);
        } finally {
            Binder.restoreCallingIdentity(token);
        }
    }
	
	public void grantDevicePermission(UsbDevice device, int uid) {
        mUsbPermissionManager.grantDevicePermission(device, uid);
    }
	
	void grantDevicePermission(@NonNull UsbDevice device, int uid) {
        synchronized (mLock) {
            String deviceName = device.getDeviceName();
            SparseBooleanArray uidList = mDevicePermissionMap.get(deviceName);
            if (uidList == null) {
                uidList = new SparseBooleanArray(1);
                mDevicePermissionMap.put(deviceName, uidList);
            }
            uidList.put(uid, true);
        }
    }
	
	boolean hasPermission(@NonNull UsbDevice device, int uid) {
        synchronized (mLock) {
            if (uid == Process.SYSTEM_UID || mDisablePermissionDialogs) {
                return true;
            }
            SparseBooleanArray uidList = mDevicePermissionMap.get(device.getDeviceName());
            if (uidList == null) {
                return false;
            }
            return uidList.get(uid);
        }
    }

再看看下持久保存授权是怎么做到的,调用 UsbProfileGroupSettingsManager 中 setDevicePackage()

packageName 为空,将 device 信息从 mDevicePreferenceMap 中移除,changed 改变重新修改 mSettingsFile 内容

mSettingsFile 实际路径为 data/system/users/0/usb_device_manager.xml

因为安卓支持多用户登录,默认用户id为0,这就是参数中为什么有 userId 原因,用于区分不同用户的操作数据

packageName 不为空,将 device 信息添加到 mDevicePreferenceMap 中,changed 改变写入数据到 mSettingsFile

frameworks\base\services\usb\java\com\android\server\usb\UsbService.java
frameworks\base\services\usb\java\com\android\server\usb\UsbProfileGroupSettingsManager.java

	@Override
    public void setDevicePackage(UsbDevice device, String packageName, int userId) {
        device = Preconditions.checkNotNull(device);

        mContext.enforceCallingOrSelfPermission(android.Manifest.permission.MANAGE_USB, null);

        UserHandle user = UserHandle.of(userId);
        final long token = Binder.clearCallingIdentity();
        try {
            mSettingsManager.getSettingsForProfileGroup(user).setDevicePackage(device, packageName,
                    user);
        } finally {
            Binder.restoreCallingIdentity(token);
        }
    }

	void setDevicePackage(@NonNull UsbDevice device, @Nullable String packageName,
            @NonNull UserHandle user) {
        DeviceFilter filter = new DeviceFilter(device);
        boolean changed;
        synchronized (mLock) {
            if (packageName == null) {
                changed = (mDevicePreferenceMap.remove(filter) != null);
            } else {
                UserPackage userPackage = new UserPackage(packageName, user);

                changed = !userPackage.equals(mDevicePreferenceMap.get(filter));
                if (changed) {
                    mDevicePreferenceMap.put(filter, userPackage);
                }
            }
            if (changed) {
                scheduleWriteSettingsLocked();
            }
        }
    }
	
	mSettingsFile = new AtomicFile(new File(
                Environment.getUserSystemDirectory(user.getIdentifier()),
                "usb_device_manager.xml"), "usb-state");
	
	private void scheduleWriteSettingsLocked() {
        if (mIsWriteSettingsScheduled) {
            return;
        } else {
            mIsWriteSettingsScheduled = true;
        }

        AsyncTask.execute(() -> {
            synchronized (mLock) {
                FileOutputStream fos = null;
                try {
                    fos = mSettingsFile.startWrite();

                    FastXmlSerializer serializer = new FastXmlSerializer();
                    serializer.setOutput(fos, StandardCharsets.UTF_8.name());
                    serializer.startDocument(null, true);
                    serializer.setFeature("http://xmlpull.org/v1/doc/features.html#indent-output",
                                    true);
                    serializer.startTag(null, "settings");

                    for (DeviceFilter filter : mDevicePreferenceMap.keySet()) {
                        serializer.startTag(null, "preference");
                        serializer.attribute(null, "package",
                                mDevicePreferenceMap.get(filter).packageName);
                        serializer.attribute(null, "user",
                                String.valueOf(getSerial(mDevicePreferenceMap.get(filter).user)));
                        filter.write(serializer);
                        serializer.endTag(null, "preference");
                    }

                    for (AccessoryFilter filter : mAccessoryPreferenceMap.keySet()) {
                        serializer.startTag(null, "preference");
                        serializer.attribute(null, "package",
                                mAccessoryPreferenceMap.get(filter).packageName);
                        serializer.attribute(null, "user", String.valueOf(
                                        getSerial(mAccessoryPreferenceMap.get(filter).user)));
                        filter.write(serializer);
                        serializer.endTag(null, "preference");
                    }

                    serializer.endTag(null, "settings");
                    serializer.endDocument();

                    mSettingsFile.finishWrite(fos);
                } catch (IOException e) {
                    Slog.e(TAG, "Failed to write settings", e);
                    if (fos != null) {
                        mSettingsFile.failWrite(fos);
                    }
                }

                mIsWriteSettingsScheduled = false;
            }
        });
    }
	

最终 DeviceFilter write() 将 usbdevice 关键信息写入 xml 中

frameworks/base/core/java/android/hardware/usb/DeviceFilter.java

public void write(XmlSerializer serializer) throws IOException {
        serializer.startTag(null, "usb-device");
        if (mVendorId != -1) {
            serializer.attribute(null, "vendor-id", Integer.toString(mVendorId));
        }
        if (mProductId != -1) {
            serializer.attribute(null, "product-id", Integer.toString(mProductId));
        }
        if (mClass != -1) {
            serializer.attribute(null, "class", Integer.toString(mClass));
        }
        if (mSubclass != -1) {
            serializer.attribute(null, "subclass", Integer.toString(mSubclass));
        }
        if (mProtocol != -1) {
            serializer.attribute(null, "protocol", Integer.toString(mProtocol));
        }
        if (mManufacturerName != null) {
            serializer.attribute(null, "manufacturer-name", mManufacturerName);
        }
        if (mProductName != null) {
            serializer.attribute(null, "product-name", mProductName);
        }
        if (mSerialNumber != null) {
            serializer.attribute(null, "serial-number", mSerialNumber);
        }
        serializer.endTag(null, "usb-device");
    }

一个完整的 usb_device_manager.xml 信息内容如下

<?xml version='1.0' encoding='utf-8' standalone='yes' ?>
<settings>
    <preference package="com.difengze.carlink" user="0">
        <usb-device vendor-id="6353" product-id="15616" class="0" subclass="0" protocol="0" manufacturer-name="Allwinner Technology Inc." product-name="Tina Accessory" serial-number="20080411" />
    </preference>
</settings>	

流程搞清楚了,针对一些定制太离谱的系统我们可以直接构造出 usb_device_manager.xml 数据,推送到 data/system/users/0/ 路径下,重启设备一次也能达到破解权限目的

相关推荐

最近更新

  1. docker php8.1+nginx base 镜像 dockerfile 配置

    2024-03-28 17:52:01       94 阅读
  2. Could not load dynamic library ‘cudart64_100.dll‘

    2024-03-28 17:52:01       100 阅读
  3. 在Django里面运行非项目文件

    2024-03-28 17:52:01       82 阅读
  4. Python语言-面向对象

    2024-03-28 17:52:01       91 阅读

热门阅读

  1. 利用python脚本,根据词条爬取百度图片(爬虫)

    2024-03-28 17:52:01       39 阅读
  2. linux内核网络“每日读书”

    2024-03-28 17:52:01       43 阅读
  3. 【OpenGL实践06】如何读入模型文件obj数据

    2024-03-28 17:52:01       31 阅读
  4. OpenAI奥特曼豪赌1.42亿破解长生不老

    2024-03-28 17:52:01       42 阅读
  5. lua 判断字符串是否包含指定字符

    2024-03-28 17:52:01       38 阅读
  6. 网络安全进入AI赋能时代

    2024-03-28 17:52:01       38 阅读
  7. MySQL高级部分学习笔记总结

    2024-03-28 17:52:01       33 阅读