Skip to content
On this page

多亲 F22 破解 ADB 安装限制

之前获取 root 之后安装了 InstallerX,虽然可以在手机上任意安装软件了,但是通过 adb 安装软件还是会受到限制,使用 adb install 会报错:Failure

接下来研究一下怎样解除 adb 的软件安装限制。

修改相关配置

在 adb shell 环境下,可以通过 settings 命令修改一些系统配置,比如允许安装非市场应用等。这里找一下有没有想过配置可以解除 adb 安装限制。

分别执行 settings list systemsettings list globalsettings list secure,列出选项。

settings list system

点击查看详情
shell
999D0D40EF0561559C6D36768DF9EAE8=F8754A33CC32A21E1281F993C8B58D14
accelerometer_rotation=0
alarm_alert=content://media/internal/audio/media/27?title=Cesium&canonical=1
alarm_alert_set=1
background_power_saving_enable=1
bd_setting_i=
com.baidu.deviceid=F8754A33CC32A21E1281F993C8B58D14
dim_screen=1
dtmf_tone=1
dtmf_tone_type=0
end_button_behavior=2
haptic_feedback_enabled=1
hearing_aid=0
import_remove_running=false
lockscreen_sounds_enabled=1
mode_ringer_streams_affected=422
mute_streams_affected=111
notification_light_pulse=1
notification_sound=content://media/internal/audio/media/20?title=%E7%8E%BB%E7%92%83&canonical=1
notification_sound_set=1
ota_package_name=1.1.7
pointer_speed=0
radio.data.stall.recovery.action=0
ringtone=content://media/internal/audio/media/60?title=%E9%A6%99%E6%A7%9F&canonical=1
ringtone_set=1
screen_brightness=103
screen_brightness_float=0.39999664
screen_brightness_for_vr=86
screen_brightness_mode=1
screen_off_timeout=600000
sound_effects_enabled=1
status_bar_show_battery_percent=1
tty_mode=0
vibrate_when_ringing=0
voice_wakeup_mode=1
volume_alarm=6
volume_bluetooth_sco=7
volume_music=5
volume_music_speaker=12
volume_music_usb_headset=2
volume_notification=5
volume_ring=5
volume_system=15
volume_voice=6

settings list global

点击查看详情
shell
DUOQIN_IMEI=865028060956344
adb_enabled=1
adb_wifi_enabled=0
add_users_when_locked=0
airplane_mode_on=0
airplane_mode_radios=cell,bluetooth,wifi,nfc,wimax
airplane_mode_toggleable_radios=bluetooth,wifi,nfc
apply_ramping_ringer=0
assisted_gps_enabled=1
audio_safe_volume_state=3
auto_time=1
auto_time_gps=0
auto_time_zone=1
average_time_to_discharge=-1
battery_estimates_last_update_time=1728440075899
bluetooth_disabled_profiles=0
bluetooth_on=0
bluetooth_sanitized_exposure_notification_supported=1
boot_count=9
call_auto_retry=0
car_dock_sound=/product/media/audio/ui/Dock.ogg
car_undock_sound=/product/media/audio/ui/Undock.ogg
cdma_cell_broadcast_sms=1
charging_started_sound=/product/media/audio/ui/ChargingStarted.ogg
data_roaming=0
data_service_enabled=0
database_creation_buildid=RP1A.200720.011
debug_app=null
default_install_location=0
default_restrict_background_data=0
desk_dock_sound=/product/media/audio/ui/Dock.ogg
desk_undock_sound=/product/media/audio/ui/Undock.ogg
development_settings_enabled=1
device_name=F22
device_provisioned=1
dock_audio_media_enabled=1
dock_sounds_enabled=0
dock_sounds_enabled_when_accessbility=0
duoqin.ota.update_running=0
duoqin_guard_unlocked=0
emergency_tone=0
heads_up_notifications_enabled=1
install_non_market_apps=0
isolated_storage_remote=null
lid_behavior=0
location_global_kill_switch=0
lock_sound=/product/media/audio/ui/Lock.ogg
low_battery_sound=/product/media/audio/ui/LowBattery.ogg
low_battery_sound_timeout=0
low_power=0
max_sound_trigger_detection_service_ops_per_day=1000
mobile_data=1
mobile_data_always_on=1
mode_ringer=2
msim_mode_setting=1
multi_sim_data_call=-1
multi_sim_sms=-1
multi_sim_voice_call=-1
netstats_enabled=1
network_recommendations_enabled=0
network_watchlist_last_report_time=1728403200000
notification_bubbles=0
ota_disable_automatic_update=1
power_sounds_enabled=1
preferred_network_mode=9
private_dns_default_mode=off
set_install_location=0
show_first_crash_dialog=1
smart_rat_switch_debug=0
smart_rat_switch_enabled=0
sound_trigger_detection_service_op_timeout=15000
stay_on_while_plugged_in=0
subscription_mode=0
sysui_demo_allowed=null
telephony_misc_feature_config=6
theater_mode_on=0
time_remaining_estimate_based_on_usage=0
time_remaining_estimate_millis=-1
transition_animation_scale=1.0
trusted_sound=/product/media/audio/ui/Trusted.ogg
unlock_sound=/product/media/audio/ui/Unlock.ogg
usb_mass_storage_enabled=1
volume_down_shotcut_keycode=11
volume_up_shotcut_keycode=13
wait_for_debugger=0
webview_fallback_logic_enabled=0
wifi_display_max_resolution=0
wifi_display_on=0
wifi_display_portrait_resolution=0
wifi_display_power_saving_delay=10
wifi_display_power_saving_option=1
wifi_max_dhcp_retry_count=9
wifi_migration_completed=1
wifi_networks_available_notification_on=1
wifi_on=1
wifi_scan_always_enabled=1
wifi_sleep_policy=2
wifi_wakeup_enabled=1
window_animation_scale=1.0
wireless_charging_started_sound=/product/media/audio/ui/WirelessChargingStarted.ogg
zen_duration=null
zen_mode=0
zen_mode_config_etag=1798312944
zen_mode_ringer_level=2

settings list secure

点击查看详情
shell
accessibility_display_inversion_enabled=null
accessibility_display_magnification_enabled=0
accessibility_display_magnification_scale=2.0
accessibility_enabled=0
adaptive_sleep=null
android_id=7cae6200c2822093
assistant=
autofill_service=
aware_enabled=0
aware_lock_enabled=0
backup_enabled=0
backup_transport=com.android.localtransport/.LocalTransport
bluetooth_addr_valid=1
bluetooth_address=10:DF:8B:AC:9F:DE
bluetooth_name=F22
charging_sounds_enabled=1
charging_vibration_enabled=1
clock_seconds=null
default_input_method=com.duoqin.pinyin/.PinyinIME
double_tap_to_wake=1
doze_pulse_on_double_tap=null
doze_tap_gesture=null
enabled_accessibility_services=com.duoqin.remoteservice/com.duoqin.monitor.MonitorService
enabled_input_methods=com.android.inputmethod.latin/.LatinIME:com.duoqin.inputmethod.pinyin/.PinyinIME:com.duoqin.pinyin/.PinyinIME
enabled_notification_assistant=android.ext.services/android.ext.services.notification.Assistant
enabled_notification_listeners=com.android.launcher3/com.android.launcher3.notification.NotificationListener
enabled_notification_policy_access_packages=com.android.camera2
high_priority=null
hush_gesture_used=0
icon_blacklist=null
immersive_mode_confirmations=confirmed
input_methods_subtype_history=com.android.inputmethod.latin/.LatinIME;-921088104:com.duoqin.pinyin/.PinyinIME;-921088104
install_non_market_apps=0
keyguard_slice_uri=null
location_mode=3
location_providers_allowed=gps,network
lock_screen_allow_private_notifications=1
lock_screen_owner_info_enabled=0
lock_screen_show_notifications=1
lock_screen_show_silent_notifications=0
lockscreen.disabled=0
long_press_timeout=400
manual_ringer_toggle_count=0
mock_location=0
mount_play_not_snd=1
mount_ums_autostart=0
mount_ums_notify_enabled=1
mount_ums_prompt=1
multi_press_timeout=300
navigation_mode=0
notification_badging=1
notification_dismiss_rtl=null
notification_history_enabled=null
power_menu_locked_show_content=1
qs_show_brightness=null
screensaver_activate_on_dock=0
screensaver_activate_on_sleep=0
screensaver_components=com.google.android.deskclock/com.android.deskclock.Screensaver
screensaver_default_component=com.google.android.deskclock/com.android.deskclock.Screensaver
screensaver_enabled=1
selected_input_method_subtype=-1
selected_spell_checker=com.android.inputmethod.latin/.spellcheck.AndroidSpellCheckerService
selected_spell_checker_subtype=0
show_ime_with_hard_keyboard=0
show_media_when_bypassing=null
show_zen_settings_suggestion=1
silence_gesture=0
skip_gesture=0
sleep_timeout=-1
snoozed_schedule_condition_provider=
speak_password=1
sync_parent_sounds=0
sysui_do_not_disturb=null
sysui_keyguard_left=null
sysui_keyguard_right=null
sysui_qqs_count=null
sysui_qs_fancy_anim=null
sysui_qs_move_whole_rows=null
sysui_qs_tiles=cell,wifi,airplane,bt,ringermode,dnd,flashlight,screenshot,hotspot,location,battery,screenrecord,cast,night,saver,volume
sysui_tuner_version=4
sysui_volume_down_silent=null
sysui_volume_up_silent=null
touch_exploration_enabled=0
trust_agents_initialized=1
unknown_sources_default_reversed=1
user_setup_complete=1
voice_interaction_service=
voice_recognition_service=
volume_hush_gesture=1
wake_gesture_enabled=1
zen_duration=0
zen_settings_suggestion_viewed=0
zen_settings_updated=1

从中找出一些可能相关的选项进行配置。

shell
# 允许安装非市场应用
settings put global install_non_market_apps 1
settings put secure install_non_market_apps 1
shell
# 疑似多亲守护解锁
settings put global duoqin_guard_unlocked 1
shell
# 未知来源
settings put secure unknown_sources_default_reversed 1

以上设置均未生效,仍然无法通过 adb 安装软件。

测试 pm 命令

查看 adb install 源码:http://aospxref.com/android-11.0.0_r21/xref/system/core/adb/client/adb_install.cpp 可见部分命令是通过 shell 执行 pm 命令来完成的。

另外在 adb shell 环境下通过 pm 命令,确实也可以执行安装等命令,接下来测试一下 pm 命令的限制情况。

执行 adb shell 进入手机 shell,然后执行 pm install base.apk,仍然报错:Failure,执行 pm 查看帮助,可以看到有很多其他命令可以使用:

点击查看详情
shell
Package manager (package) commands:
  help
    Print this help text.

  path [--user USER_ID] PACKAGE
    Print the path to the .apk of the given PACKAGE.

  dump PACKAGE
    Print various system state associated with the given PACKAGE.

  has-feature FEATURE_NAME [version]
    Prints true and returns exit status 0 when system has a FEATURE_NAME,
    otherwise prints false and returns exit status 1

  list features
    Prints all features of the system.

  list instrumentation [-f] [TARGET-PACKAGE]
    Prints all test packages; optionally only those targeting TARGET-PACKAGE
    Options:
      -f: dump the name of the .apk file containing the test package

  list libraries
    Prints all system libraries.

  list packages [-f] [-d] [-e] [-s] [-3] [-i] [-l] [-u] [-U]
      [--show-versioncode] [--apex-only] [--uid UID] [--user USER_ID] [FILTER]
    Prints all packages; optionally only those whose name contains
    the text in FILTER.  Options are:
      -f: see their associated file
      -a: all known packages (but excluding APEXes)
      -d: filter to only show disabled packages
      -e: filter to only show enabled packages
      -s: filter to only show system packages
      -3: filter to only show third party packages
      -i: see the installer for the packages
      -l: ignored (used for compatibility with older releases)
      -U: also show the package UID
      -u: also include uninstalled packages
      --show-versioncode: also show the version code
      --apex-only: only show APEX packages
      --uid UID: filter to only show packages with the given UID
      --user USER_ID: only list packages belonging to the given user

  list permission-groups
    Prints all known permission groups.

  list permissions [-g] [-f] [-d] [-u] [GROUP]
    Prints all known permissions; optionally only those in GROUP.  Options are:
      -g: organize by group
      -f: print all information
      -s: short summary
      -d: only list dangerous permissions
      -u: list only the permissions users will see

  list staged-sessions [--only-ready] [--only-sessionid] [--only-parent]
    Prints all staged sessions.
      --only-ready: show only staged sessions that are ready
      --only-sessionid: show only sessionId of each session
      --only-parent: hide all children sessions

  list users
    Prints all users.

  resolve-activity [--brief] [--components] [--query-flags FLAGS]
       [--user USER_ID] INTENT
    Prints the activity that resolves to the given INTENT.

  query-activities [--brief] [--components] [--query-flags FLAGS]
       [--user USER_ID] INTENT
    Prints all activities that can handle the given INTENT.

  query-services [--brief] [--components] [--query-flags FLAGS]
       [--user USER_ID] INTENT
    Prints all services that can handle the given INTENT.

  query-receivers [--brief] [--components] [--query-flags FLAGS]
       [--user USER_ID] INTENT
    Prints all broadcast receivers that can handle the given INTENT.

  install [-rtfdgw] [-i PACKAGE] [--user USER_ID|all|current]
       [-p INHERIT_PACKAGE] [--install-location 0/1/2]
       [--install-reason 0/1/2/3/4] [--originating-uri URI]
       [--referrer URI] [--abi ABI_NAME] [--force-sdk]
       [--preload] [--instant] [--full] [--dont-kill]
       [--enable-rollback]
       [--force-uuid internal|UUID] [--pkg PACKAGE] [-S BYTES]
       [--apex] [--wait TIMEOUT]
       [PATH [SPLIT...]|-]
    Install an application.  Must provide the apk data to install, either as
    file path(s) or '-' to read from stdin.  Options are:
      -R: disallow replacement of existing application
      -t: allow test packages
      -i: specify package name of installer owning the app
      -f: install application on internal flash
      -d: allow version code downgrade (debuggable packages only)
      -p: partial application install (new split on top of existing pkg)
      -g: grant all runtime permissions
      -S: size in bytes of package, required for stdin
      --user: install under the given user.
      --dont-kill: installing a new feature split, don't kill running app
      --restrict-permissions: don't whitelist restricted permissions at install
      --originating-uri: set URI where app was downloaded from
      --referrer: set URI that instigated the install of the app
      --pkg: specify expected package name of app being installed
      --abi: override the default ABI of the platform
      --instant: cause the app to be installed as an ephemeral install app
      --full: cause the app to be installed as a non-ephemeral full app
      --install-location: force the install location:
          0=auto, 1=internal only, 2=prefer external
      --install-reason: indicates why the app is being installed:
          0=unknown, 1=admin policy, 2=device restore,
          3=device setup, 4=user request
      --force-uuid: force install on to disk volume with given UUID
      --apex: install an .apex file, not an .apk
      --wait: when performing staged install, wait TIMEOUT milliseconds
          for pre-reboot verification to complete. If TIMEOUT is not
          specified it will wait for 60000 milliseconds.

  install-existing [--user USER_ID|all|current]
       [--instant] [--full] [--wait] [--restrict-permissions] PACKAGE
    Installs an existing application for a new user.  Options are:
      --user: install for the given user.
      --instant: install as an instant app
      --full: install as a full app
      --wait: wait until the package is installed
      --restrict-permissions: don't whitelist restricted permissions

  install-create [-lrtsfdg] [-i PACKAGE] [--user USER_ID|all|current]
       [-p INHERIT_PACKAGE] [--install-location 0/1/2]
       [--install-reason 0/1/2/3/4] [--originating-uri URI]
       [--referrer URI] [--abi ABI_NAME] [--force-sdk]
       [--preload] [--instant] [--full] [--dont-kill]
       [--force-uuid internal|UUID] [--pkg PACKAGE] [--apex] [-S BYTES]
       [--multi-package] [--staged]
    Like "install", but starts an install session.  Use "install-write"
    to push data into the session, and "install-commit" to finish.

  install-write [-S BYTES] SESSION_ID SPLIT_NAME [PATH|-]
    Write an apk into the given install session.  If the path is '-', data
    will be read from stdin.  Options are:
      -S: size in bytes of package, required for stdin

  install-remove SESSION_ID SPLIT...
    Mark SPLIT(s) as removed in the given install session.

  install-add-session MULTI_PACKAGE_SESSION_ID CHILD_SESSION_IDs
    Add one or more session IDs to a multi-package session.

  install-commit SESSION_ID
    Commit the given active install session, installing the app.

  install-abandon SESSION_ID
    Delete the given active install session.

  set-install-location LOCATION
    Changes the default install location.  NOTE this is only intended for debugging;
    using this can cause applications to break and other undersireable behavior.
    LOCATION is one of:
    0 [auto]: Let system decide the best location
    1 [internal]: Install on internal device storage
    2 [external]: Install on external media

  get-install-location
    Returns the current install location: 0, 1 or 2 as per set-install-location.

  move-package PACKAGE [internal|UUID]

  move-primary-storage [internal|UUID]

  uninstall [-k] [--user USER_ID] [--versionCode VERSION_CODE]
       PACKAGE [SPLIT...]
    Remove the given package name from the system.  May remove an entire app
    if no SPLIT names specified, otherwise will remove only the splits of the
    given app.  Options are:
      -k: keep the data and cache directories around after package removal.
      --user: remove the app from the given user.
      --versionCode: only uninstall if the app has the given version code.

  clear [--user USER_ID] PACKAGE
    Deletes all data associated with a package.

  enable [--user USER_ID] PACKAGE_OR_COMPONENT
  disable [--user USER_ID] PACKAGE_OR_COMPONENT
  disable-user [--user USER_ID] PACKAGE_OR_COMPONENT
  disable-until-used [--user USER_ID] PACKAGE_OR_COMPONENT
  default-state [--user USER_ID] PACKAGE_OR_COMPONENT
    These commands change the enabled state of a given package or
    component (written as "package/class").

  hide [--user USER_ID] PACKAGE_OR_COMPONENT
  unhide [--user USER_ID] PACKAGE_OR_COMPONENT

  suspend [--user USER_ID] TARGET-PACKAGE
    Suspends the specified package (as user).

  unsuspend [--user USER_ID] TARGET-PACKAGE
    Unsuspends the specified package (as user).

  grant [--user USER_ID] PACKAGE PERMISSION
  revoke [--user USER_ID] PACKAGE PERMISSION
    These commands either grant or revoke permissions to apps.  The permissions
    must be declared as used in the app's manifest, be runtime permissions
    (protection level dangerous), and the app targeting SDK greater than Lollipop MR1.

  reset-permissions
    Revert all runtime permissions to their default state.

  set-permission-enforced PERMISSION [true|false]

  get-privapp-permissions TARGET-PACKAGE
    Prints all privileged permissions for a package.

  get-privapp-deny-permissions TARGET-PACKAGE
    Prints all privileged permissions that are denied for a package.

  get-oem-permissions TARGET-PACKAGE
    Prints all OEM permissions for a package.

  set-app-link [--user USER_ID] PACKAGE {always|ask|never|undefined}
  get-app-link [--user USER_ID] PACKAGE

  trim-caches DESIRED_FREE_SPACE [internal|UUID]
    Trim cache files to reach the given free space.

  list users
    Lists the current users.

  create-user [--profileOf USER_ID] [--managed] [--restricted] [--ephemeral]
      [--guest] [--pre-create-only] [--user-type USER_TYPE] USER_NAME
    Create a new user with the given USER_NAME, printing the new user identifier
    of the user.
    USER_TYPE is the name of a user type, e.g. android.os.usertype.profile.MANAGED.
      If not specified, the default user type is android.os.usertype.full.SECONDARY.
      --managed is shorthand for '--user-type android.os.usertype.profile.MANAGED'.
      --restricted is shorthand for '--user-type android.os.usertype.full.RESTRICTED'.
      --guest is shorthand for '--user-type android.os.usertype.full.GUEST'.

  remove-user USER_ID
    Remove the user with the given USER_IDENTIFIER, deleting all data
    associated with that user

  set-user-restriction [--user USER_ID] RESTRICTION VALUE

  get-max-users

  get-max-running-users

  compile [-m MODE | -r REASON] [-f] [-c] [--split SPLIT_NAME]
          [--reset] [--check-prof (true | false)] (-a | TARGET-PACKAGE)
    Trigger compilation of TARGET-PACKAGE or all packages if "-a".  Options are:
      -a: compile all packages
      -c: clear profile data before compiling
      -f: force compilation even if not needed
      -m: select compilation mode
          MODE is one of the dex2oat compiler filters:
            assume-verified
            extract
            verify
            quicken
            space-profile
            space
            speed-profile
            speed
            everything
      -r: select compilation reason
          REASON is one of:
            first-boot
            boot
            install
            bg-dexopt
            ab-ota
            inactive
            shared
      --reset: restore package to its post-install state
      --check-prof (true | false): look at profiles when doing dexopt?
      --secondary-dex: compile app secondary dex files
      --split SPLIT: compile only the given split name
      --compile-layouts: compile layout resources for faster inflation

  force-dex-opt PACKAGE
    Force immediate execution of dex opt for the given PACKAGE.

  bg-dexopt-job
    Execute the background optimizations immediately.
    Note that the command only runs the background optimizer logic. It may
    overlap with the actual job but the job scheduler will not be able to
    cancel it. It will also run even if the device is not in the idle
    maintenance mode.

  reconcile-secondary-dex-files TARGET-PACKAGE
    Reconciles the package secondary dex files with the generated oat files.

  dump-profiles TARGET-PACKAGE
    Dumps method/class profile files to
    /data/misc/profman/TARGET-PACKAGE.txt

  snapshot-profile TARGET-PACKAGE [--code-path path]
    Take a snapshot of the package profiles to
    /data/misc/profman/TARGET-PACKAGE[-code-path].prof
    If TARGET-PACKAGE=android it will take a snapshot of the boot image

  set-home-activity [--user USER_ID] TARGET-COMPONENT
    Set the default home activity (aka launcher).
    TARGET-COMPONENT can be a package name (com.package.my) or a full
    component (com.package.my/component.name). However, only the package name
    matters: the actual component used will be determined automatically from
    the package.

  set-installer PACKAGE INSTALLER
    Set installer package name

  get-instantapp-resolver
    Return the name of the component that is the current instant app installer.

  set-harmful-app-warning [--user <USER_ID>] <PACKAGE> [<WARNING>]
    Mark the app as harmful with the given warning message.

  get-harmful-app-warning [--user <USER_ID>] <PACKAGE>
    Return the harmful app warning message for the given app, if present

  uninstall-system-updates [<PACKAGE>]
    Removes updates to the given system application and falls back to its
    /system version. Does nothing if the given package is not a system app.
    If no package is specified, removes updates to all system applications.

  get-moduleinfo [--all | --installed] [module-name]
    Displays module info. If module-name is specified only that info is shown
    By default, without any argument only installed modules are shown.
      --all: show all module info
      --installed: show only installed modules

  log-visibility [--enable|--disable] <PACKAGE>
    Turns on debug logging when visibility is blocked for the given package.
      --enable: turn on debug logging (default)
      --disable: turn off debug logging

<INTENT> specifications include these flags and arguments:
    [-a <ACTION>] [-d <DATA_URI>] [-t <MIME_TYPE>] [-i <IDENTIFIER>]
    [-c <CATEGORY> [-c <CATEGORY>] ...]
    [-n <COMPONENT_NAME>]
    [-e|--es <EXTRA_KEY> <EXTRA_STRING_VALUE> ...]
    [--esn <EXTRA_KEY> ...]
    [--ez <EXTRA_KEY> <EXTRA_BOOLEAN_VALUE> ...]
    [--ei <EXTRA_KEY> <EXTRA_INT_VALUE> ...]
    [--el <EXTRA_KEY> <EXTRA_LONG_VALUE> ...]
    [--ef <EXTRA_KEY> <EXTRA_FLOAT_VALUE> ...]
    [--eu <EXTRA_KEY> <EXTRA_URI_VALUE> ...]
    [--ecn <EXTRA_KEY> <EXTRA_COMPONENT_NAME_VALUE>]
    [--eia <EXTRA_KEY> <EXTRA_INT_VALUE>[,<EXTRA_INT_VALUE...]]
        (mutiple extras passed as Integer[])
    [--eial <EXTRA_KEY> <EXTRA_INT_VALUE>[,<EXTRA_INT_VALUE...]]
        (mutiple extras passed as List<Integer>)
    [--ela <EXTRA_KEY> <EXTRA_LONG_VALUE>[,<EXTRA_LONG_VALUE...]]
        (mutiple extras passed as Long[])
    [--elal <EXTRA_KEY> <EXTRA_LONG_VALUE>[,<EXTRA_LONG_VALUE...]]
        (mutiple extras passed as List<Long>)
    [--efa <EXTRA_KEY> <EXTRA_FLOAT_VALUE>[,<EXTRA_FLOAT_VALUE...]]
        (mutiple extras passed as Float[])
    [--efal <EXTRA_KEY> <EXTRA_FLOAT_VALUE>[,<EXTRA_FLOAT_VALUE...]]
        (mutiple extras passed as List<Float>)
    [--esa <EXTRA_KEY> <EXTRA_STRING_VALUE>[,<EXTRA_STRING_VALUE...]]
        (mutiple extras passed as String[]; to embed a comma into a string,
         escape it using "\,")
    [--esal <EXTRA_KEY> <EXTRA_STRING_VALUE>[,<EXTRA_STRING_VALUE...]]
        (mutiple extras passed as List<String>; to embed a comma into a string,
         escape it using "\,")
    [-f <FLAG>]
    [--grant-read-uri-permission] [--grant-write-uri-permission]
    [--grant-persistable-uri-permission] [--grant-prefix-uri-permission]
    [--debug-log-resolution] [--exclude-stopped-packages]
    [--include-stopped-packages]
    [--activity-brought-to-front] [--activity-clear-top]
    [--activity-clear-when-task-reset] [--activity-exclude-from-recents]
    [--activity-launched-from-history] [--activity-multiple-task]
    [--activity-no-animation] [--activity-no-history]
    [--activity-no-user-action] [--activity-previous-is-top]
    [--activity-reorder-to-front] [--activity-reset-task-if-needed]
    [--activity-single-top] [--activity-clear-task]
    [--activity-task-on-home] [--activity-match-external]
    [--receiver-registered-only] [--receiver-replace-pending]
    [--receiver-foreground] [--receiver-no-abort]
    [--receiver-include-background]
    [--selector]
    [<URI> | <PACKAGE> | <COMPONENT>]

输入任意命令,比如 pm list packages等,仍然都会提示同一个错误:Failure。猜测可能是拦截了所有的 pm 命令。

查看了一下 /system/bin 目录下 pm 文件的内容,如下:

shell
#!/system/bin/sh
cmd package "$@"

实际上是将所有参数透传给了 cmd package,执行了一下 cmd package list packages 也是提示同一个错误:Failure

既然 pm 是一个普通 sh 脚本,仅仅是透传参数的话,这里一开始考虑过改写 pm 文件内容,自行将参数传递给三方软件包管理工具,但是由于一下原因放弃了:

  • 此设备安卓版本为 11,已经禁止了 system 分区的写入。想要写入或者伪装写入还挺麻烦。
  • 改写 pm 命令并不能完全绕过 adb 的限制,因为普通的 adb install 命令不是通过 pm 命令执行的。

通过 XPosed 插件 Hook

通过 XPosed 插件的方式,可以 Hook 很多系统的 API,尝试在此修改系统的方法来绕过限制。

安装 LSPosed

在安卓 11 以上目前普遍使用的 XPosed 实现是 LSPosed。

安装 LSPosed 之前需要先获取 root 刷入 Magisk,具体可以参考:《多亲 F22 利用 mtkclient 获取超级用户(root)权限》

  1. 在 Magisk 中启用 Zygisk 模块。
  2. 下载 LSPosed-v1.9.2-7024-zygisk-release.zip,传到手机上。
  3. 在手机上打开 LSPosed,在模块界面点击从本地安装,选择刚刚传入的 zip 文件进行安装,安装完成后根据提示重启手机。
  4. 重启后,通知栏如果没出现 LSPosed Manager 相关的通知,可以在拨号界面输 *#*#5776733#*#* 进入,此时会提示添加桌面快捷方式,确认添加到桌面后即可方便打开 LSPosed Manager。

编写模块

首先查找要 Hook 的位置,通过之前测试 pm 命令可以得知,pm 命令未被完全拦截,仅拦截了带参数的命令,接下来从命令解析方面入手。查看 cmd 源码:cmd.cpp 以及 PackageManagerShellCommand 源码:PackageManagerShellCommand.java,可以看出在 PackageManagerShellCommand 的 onCommand 方法中是对 pm 各种命令的解析,先尝试拦截一下 onCommand 方法。

kotlin
XposedHelpers.findAndHookMethod(
    "com.android.server.pm.PackageManagerShellCommand",
    lpparam.classLoader,
    "onCommand",
    String::class.java,
    object : XC_MethodHook() {
        @Throws(Throwable::class)
        override fun beforeHookedMethod(param: MethodHookParam) {
            val cmd = param.args[0] as String
            XposedBridge.log("InstallHook cmd: $cmd")
        }
    }
)

编译打包,安装到手机上,打开 LSPosed Manager,启用模块,勾选系统框架,重启手机。

执行 adb installadb shell pm list packages 等,可以看到打印出了命令,说明 Hook 成功。

shell
InstallHook cmd: install
InstallHook cmd: list

接下来修改 onCommand 方法,参考 onCommand 源码 可以看到在 onCommand 方法中调用了 runInstallrunList 等方法,尝试在 Hook 的 onCommand 方法中直接调用这些方法。

kotlin
when (cmd) {
    "install" -> {
        val res = XposedHelpers.callMethod(
            param.thisObject,
            "runInstall"
        )
        param.result = res
    }

    "list" -> {
        val res = XposedHelpers.callMethod(
            param.thisObject,
            "runList"
        )
        param.result = res
    }
}

更新模块重启手机,继续测试,adb shell pm list packages 成功了输出了所有的包名,但是 adb install 仍然提示 Failure,说明 runInstall 方法并没有成功执行。

查看 runInstall 方法的源码,发现在 runInstall 方法中调用了 doRunInstall 方法,而 doRunInstall 方法中又调用了 doCreateSession 方法,我们直接将这两个方法都 Hook 一下,看看是否被调用了。

kotlin
val installParamsClass = XposedHelpers.findClass(
    "com.android.server.pm.PackageManagerShellCommand\$InstallParams",
    lpparam.classLoader
)
XposedHelpers.findAndHookMethod(
    "com.android.server.pm.PackageManagerShellCommand",
    lpparam.classLoader,
    "doRunInstall",
    installParamsClass,
    object : XC_MethodHook() {
        @Throws(Throwable::class)
        override fun beforeHookedMethod(param: MethodHookParam) {
            val installParams = param.args[0]
            XposedBridge.log("InstallHook doRunInstall installParams: $installParams")
        }
    }
)
val sessionParamsClass = XposedHelpers.findClass(
    "com.android.server.pm.PackageManagerShellCommand\$SessionParams",
    lpparam.classLoader
)
XposedHelpers.findAndHookMethod(
    "com.android.server.pm.PackageManagerShellCommand",
    lpparam.classLoader,
    "doCreateSession",
    sessionParamsClass,
    String::class.java,
    Int::class.javaPrimitiveType,
    object : XC_MethodHook() {
        @Throws(Throwable::class)
        override fun beforeHookedMethod(param: MethodHookParam) {
            val installParams = param.args[0]
            XposedBridge.log("InstallHook doRunInstall installParams: $installParams")
        }
    }
)

经过测试,doRunInstall 支持到了而 doCreateSession 方法并没有被调用,而且这次输出了新的错误:adb install not allowed。猜测是 doRunInstall 方法中做了限制,比较好的方式就是提取 framework 查看相关实现,有针对性的破解,比如这个文章类似的参考:《逆向 Android Framework,破解第三方 APP 的安装限制 》

但我感觉提取 framework 这个过程比较繁琐,就先到互联网上以相关关键词搜索一下,看看有没有现成的破解方案。找了一圈只找到了一个 XDA 上发布的类似问题:《adb install not allowed》,他也是多亲 F22,可惜是他并未找到解决方案。

接下来抱着试试看的心态,直接将 doRunInstall 的源码从 AOSP 搬过来绕过原来的实现,然后重新打包测试,``adb install` 竟然成功了,看来猜测是正确的。

接下来尝试直接从 Android Studio 中直接运行应用到手机,这次失败了,仍然提示 Failure,看来还是有一些限制没有绕过。根据之前的经验,依次修复调用的命令 pathinstall-createinstall-writeinstall-commit 等,终于 Android Studio 也可以正常运行应用到手机了。

相关资源