开启adb root
直接看adb源码:
- __android_log_is_debuggable就是判断ro.debuggable属性值,感兴趣可以在 源码下grep下实现看看。
- auth_required :在adb源码下定义的全局变量,默认等于true,。看名字就是是否需要用户授权的flag, 这里不再继续跟代码,只要知道这个值在ro.debuggable=1时,会等于ro.adb.secure这个属性即可。我们不想开启adb时弹出用户授权,因此要将这个属性改为0.
// /packages/modules/adb/daemon/main.cpp
int adbd_main(int server_port) {...#if defined(__ANDROID__)// If we're on userdebug/eng or the device is unlocked, permit no-authentication.bool device_unlocked = "orange" == android::base::GetProperty("ro.boot.verifiedbootstate", "");if (__android_log_is_debuggable() || device_unlocked) {auth_required = android::base::GetBoolProperty("ro.adb.secure", false);}
#endif...
}
当在PC端执行adb root时,android系统会走到下面代码,可以看到如果__android_log_is_debuggable不为true的话,就return了。
函数最后设置了service.adb.root=1,实际测试了下,adb root后再adb shell,
这个属性=1,直接adb shell进去,这个属性=0
// /packages/modules/adb/daemon/restart_service.cppvoid restart_root_service(unique_fd fd) {if (getuid() == 0) {WriteFdExactly(fd.get(), "adbd is already running as root\n");return;}if (!__android_log_is_debuggable()) {WriteFdExactly(fd.get(), "adbd cannot run as root in production builds\n");return;}LOG(INFO) << "adbd restarting as root";android::base::SetProperty("service.adb.root", "1");WriteFdExactly(fd.get(), "restarting adbd as root\n");
}
service.adb.root使用的地方:
PC端执行adb shell后,会走到下面代码,在main函数里,会判断should_drop_privileges,然后通过linux capability机制
给进程降低权限,感兴趣可以搜一下linux capability机制。这里推测就是是否从root降低权限到shell用户。
// packages/modules/adb/daemon/main.cpp
int adbd_main(int server_port) {
...
#if defined(__ANDROID__)drop_privileges(server_port);
#endif
...
}
static void drop_privileges(int server_port) {
...if (should_drop_privileges()) {const bool should_drop_caps = !__android_log_is_debuggable();if (should_drop_caps) {minijail_use_caps(jail.get(), CAP_TO_MASK(CAP_SETUID) | CAP_TO_MASK(CAP_SETGID));}minijail_change_gid(jail.get(), AID_SHELL);minijail_change_uid(jail.get(), AID_SHELL);// minijail_enter() will abort if any priv-dropping step fails.minijail_enter(jail.get());// Whenever ambient capabilities are being used, minijail cannot// simultaneously drop the bounding capability set to just// CAP_SETUID|CAP_SETGID while clearing the inheritable, effective,// and permitted sets. So we need to do that in two steps.using ScopedCaps =std::unique_ptr<std::remove_pointer<cap_t>::type, std::function<void(cap_t)>>;ScopedCaps caps(cap_get_proc(), &cap_free);...
}
关键在should_drop_privileges:
ro.secure在user,userdebug版本=1,也就是说adb shell进来 默认是降权的,非root的。
如果**__android_log_is_debuggable && service.adb.root 为true**,就不会降权为非root. 前面说了PC端输入adb root后, service.adb.root 这个值就会等于1,
综合上面分析:只要把ro.debuggable设置为1,就可以在user版本下实现adb root
static bool should_drop_privileges() {// The properties that affect `adb root` and `adb unroot` are ro.secure and// ro.debuggable. In this context the names don't make the expected behavior// particularly obvious.//// ro.debuggable:// Allowed to become root, but not necessarily the default. Set to 1 on// eng and userdebug builds.//// ro.secure:// Drop privileges by default. Set to 1 on userdebug and user builds.bool ro_secure = android::base::GetBoolProperty("ro.secure", true);bool ro_debuggable = __android_log_is_debuggable();// Drop privileges if ro.secure is set...bool drop = ro_secure;// ... except "adb root" lets you keep privileges in a debuggable build.std::string prop = android::base::GetProperty("service.adb.root", "");bool adb_root = (prop == "1");bool adb_unroot = (prop == "0");if (ro_debuggable && adb_root) {drop = false;}// ... and "adb unroot" lets you explicitly drop privileges.if (adb_unroot) {drop = true;}
具体的修改策略:
在编译版本时,定义一个全局变量,可以把它定义在编译脚本中,
export USE_ROOT_ADB =1
USE_ROOT_ADB 代表在user版本中启用adb root
# /build/make/core/main.mk
## user/userdebug ##
# +++ 这里加一个编译过程中对USE_ROOT_ADB的值的打印。
$(warning USE_ROOT_ADB $(USE_ROOT_ADB))
# user_variant:不是user,就是userdebug
user_variant := $(filter user userdebug,$(TARGET_BUILD_VARIANT))
enable_target_debugging := true
tags_to_install :=
ifneq (,$(user_variant))# Target is secure in user builds.ADDITIONAL_SYSTEM_PROPERTIES += ro.secure=1ADDITIONAL_SYSTEM_PROPERTIES += security.perf_harden=1ifeq ($(user_variant),user)# +++ 如果是user版本,并且USE_ROOT_ADB=1,将ro.adb.secure设置为0ifeq ($(USE_ROOT_ADB),true)ADDITIONAL_SYSTEM_PROPERTIES += ro.adb.secure=0elseADDITIONAL_SYSTEM_PROPERTIES += ro.adb.secure=1endifendififeq ($(user_variant),userdebug)# Pick up some extra useful toolstags_to_install += debugelse# +++ 如果是user版本,并且USE_ROOT_ADB=1,将tags_to_install 追加debug字段,# +++ 还有要注意,源码未修改前,如果是user版本,enable_target_debugging会被置为空# +++ 这里改为:如果是user版本,enable_target_debugging依然是true.# @@@ 后面继续分析tags_to_install 和 enable_target_debuggingifeq ($(USE_ROOT_ADB),true)# Pick up some extra useful toolstags_to_install += debugelse# Disable debugging in plain user builds.enable_target_debugging :=endifendif # Disallow mock locations by default for user buildsADDITIONAL_SYSTEM_PROPERTIES += ro.allow.mock.location=0else # !user_variant# Turn on checkjni for non-user builds.ADDITIONAL_SYSTEM_PROPERTIES += ro.kernel.android.checkjni=1# Set device insecure for non-user builds.ADDITIONAL_SYSTEM_PROPERTIES += ro.secure=0# Allow mock locations by default for non user buildsADDITIONAL_SYSTEM_PROPERTIES += ro.allow.mock.location=1
endif # !user_variant
# @@@ 如果enable_target_debugging为true,ro.debuggable=1
# @@@ 达到了我们想要的效果
ifeq (true,$(strip $(enable_target_debugging)))# Target is more debuggable and adbd is on by defaultADDITIONAL_SYSTEM_PROPERTIES += ro.debuggable=1# Enable Dalvik lock contention logging.ADDITIONAL_SYSTEM_PROPERTIES += dalvik.vm.lockprof.threshold=500
else # !enable_target_debugging# Target is less debuggable and adbd is off by defaultADDITIONAL_SYSTEM_PROPERTIES += ro.debuggable=0
endif # !enable_target_debugging
adb remount 和 su
这两个放到一起说,上一节说到将tags_to_install 追加debug字段。
还是在/build/make/core/main.mk中,tags_to_install=true,编译时会带上PRODUCT_PACKAGES_DEBUG
# $(1): product makefile
define product-installed-files$(eval _pif_modules := \$(call get-product-var,$(1),PRODUCT_PACKAGES) \$(if $(filter eng,$(tags_to_install)),$(call get-product-var,$(1),PRODUCT_PACKAGES_ENG)) \# @@@ 当tags_to_install有debug字段,编译时会带上PRODUCT_PACKAGES_DEBUG$(if $(filter debug,$(tags_to_install)),$(call get-product-var,$(1),PRODUCT_PACKAGES_DEBUG)) \$(if $(filter tests,$(tags_to_install)),$(call get-product-var,$(1),PRODUCT_PACKAGES_TESTS)) \$(if $(filter asan,$(tags_to_install)),$(call get-product-var,$(1),PRODUCT_PACKAGES_DEBUG_ASAN)) \$(if $(filter java_coverage,$(tags_to_install)),$(call get-product-var,$(1),PRODUCT_PACKAGES_DEBUG_JAVA_COVERAGE)) \$(call auto-included-modules) \) \
PRODUCT_PACKAGES_DEBUG定义在/build/make/target/product/base_system.mk, 当然也可以在自己的mk中对PRODUCT_PACKAGES_DEBUG进行追加。su 就在PRODUCT_PACKAGES_DEBUG中,如果再加上remount, 就实现了user版本下使用su和adb remount
383 # Packages included only for eng or userdebug builds, previously debug tagged
384 PRODUCT_PACKAGES_DEBUG := \
385 adb_keys \
386 arping \
387 dmuserd \
388 idlcli \
389 init-debug.rc \
390 iotop \
391 iperf3 \
392 iw \
393 layertracegenerator \
394 libclang_rt.ubsan_standalone \
395 logpersist.start \
396 logtagd.rc \
397 procrank \
398 profcollectd \
399 profcollectctl \
400 record_binder \
401 servicedispatcher \
402 showmap \
403 sqlite3 \
404 ss \
405 start_with_lockagent \
406 strace \# @@@ 已经有su
407 su \
408 sanitizer-status \
409 tracepath \
410 tracepath6 \
411 traceroute6 \
412 unwind_info \
413 unwind_reg_info \
414 unwind_symbols \# +++ 加上remountremount \
另外这里还涉及到selinux,
在system/sepolicy/private下,可以看到su.te中对su的定义:
permissive su;
su被定义为permissive domain,这是什么?
参考redhat上的定义:
Permissive Domains
When SELinux is running in permissive mode, SELinux does not deny access, but denials are logged for actions that would have been denied if running in enforcing mode. Previously, it was not possible to make a single domain permissive (remember: processes run in domains). In certain situations, this led to making the whole system permissive to troubleshoot issues.
Permissive domains allow an administrator to configure a single process (domain) to run permissive, rather than making the whole system permissive. SELinux checks are still performed for permissive domains; however, the kernel allows access and reports an AVC denial for situations where SELinux would have denied access.
Permissive domains have the following uses:
They can be used for making a single process (domain) run permissive to troubleshoot an issue without putting the entire system at risk by making it permissive.
They allow an administrator to create policies for new applications. Previously, it was recommended that a minimal policy be created, and then the entire machine put into permissive mode, so that the application could run, but SELinux denials still logged. audit2allow could then be used to help write the policy. This put the whole system at risk. With permissive domains, only the domain in the new policy can be marked permissive, without putting the whole system at risk.
理解就是可以在强制模式(enforcing mode)下,定义一个宽容模式的domain,跑在Permissive domain下的进程,只会打印avc log,而不会限制它的行为。su这么牛逼的进程,当然是Permissive domain。
但是在user版本下,是不允许定义Permissive domain的。 为此需要修改如下代码:
# /system/sepolicy/Android.mk$(LOCAL_BUILT_MODULE): $(sepolicy.recovery.conf) $(HOST_OUT_EXECUTABLES)/checkpolicy \$(HOST_OUT_EXECUTABLES)/sepolicy-analyze@mkdir -p $(dir $@)$(hide) $(CHECKPOLICY_ASAN_OPTIONS) $(HOST_OUT_EXECUTABLES)/checkpolicy -M -c \$(POLICYVERS) -o $@.tmp $<$(hide) $(HOST_OUT_EXECUTABLES)/sepolicy-analyze $@.tmp permissive > $@.permissivedomains# +++ 下面的if表示如果是user版本,并且permissivedomains存在的话,就会输出如下错误,并退出。因此需要用USE_ROOT_ADB宏来排除这部分处理。注意:该文件下有2处检查permissivedomains 的地方,需要用同样的方式注释掉。
ifndef USE_ROOT_ADB$(hide) if [ "$(TARGET_BUILD_VARIANT)" = "user" -a -s $@.permissivedomains ]; then \echo "==========" 1>&2; \echo "ERROR: permissive domains not allowed in user builds" 1>&2; \echo "List of invalid domains:" 1>&2; \cat $@.permissivedomains 1>&2; \exit 1; \fi
endif$(hide) mv $@.tmp $@
关闭selinux
临时关闭selinux,只要设置setenforce 0即可,但重启后就会恢复为强制模式。
这里讨论永久关闭。
如果不想深究,直接修改IsEnforcing(),让它永久返回false, 这样走到security_setenforce,永久关闭selinux。
// /system/core/init/selinux.cppvoid SelinuxSetEnforcement() {bool kernel_enforcing = (security_getenforce() == 1);bool is_enforcing = IsEnforcing();if (kernel_enforcing != is_enforcing) {if (security_setenforce(is_enforcing)) {PLOG(FATAL) << "security_setenforce(" << (is_enforcing ? "true" : "false")<< ") failed";}}if (auto result = WriteFile("/sys/fs/selinux/checkreqprot", "0"); !result.ok()) {LOG(FATAL) << "Unable to write to /sys/fs/selinux/checkreqprot: " << result.error();}
}
bool IsEnforcing() {// MODIFY START 永久返回falsereturn false;// MODIFY ENDif (ALLOW_PERMISSIVE_SELINUX) {return StatusFromProperty() == SELINUX_ENFORCING;}return true;
}
如果想继续看下是怎么实现的,我们继续分析,先看security_getenforce函数,这里不贴代码实现了,源码在external/selinux下,主要就是读取/sys/fs/selinux/enforce 的值。可以是1 or 0
/sys/fs/selinux/enforce中的值,又是根据内核的cmdline中的androidboot.selinux来的。可以在自己的mk中修改这个值:
BOARD_KERNEL_CMDLINE += androidboot.selinux=enforcing or permissive
再分析IsEnforcing函数,如果ALLOW_PERMISSIVE_SELINUX为false时,直接返回true。ALLOW_PERMISSIVE_SELINUX在mk中可以看到,如果是user版本,该值就是false, 这里可以看出,如果编译user版本,一定会开启selinux,即使上面配置内核启动参数cmdline中的androidboot.selinux=permissive,也无济于事。
#/system/core/init/Android.mk# userdebug,eng版本走if,user走else
ifneq (,$(filter userdebug eng,$(TARGET_BUILD_VARIANT)))init_options += \-DALLOW_FIRST_STAGE_CONSOLE=1 \-DALLOW_LOCAL_PROP_OVERRIDE=1 \-DALLOW_PERMISSIVE_SELINUX=1 \-DREBOOT_BOOTLOADER_ON_PANIC=1 \-DWORLD_WRITABLE_KMSG=1 \-DDUMP_ON_UMOUNT_FAILURE=1elseinit_options += \-DALLOW_FIRST_STAGE_CONSOLE=0 \-DALLOW_LOCAL_PROP_OVERRIDE=0 \-DALLOW_PERMISSIVE_SELINUX=0 \-DREBOOT_BOOTLOADER_ON_PANIC=0 \-DWORLD_WRITABLE_KMSG=0 \-DDUMP_ON_UMOUNT_FAILURE=0endif
试想,如果我们修改上面的mk,让ALLOW_PERMISSIVE_SELINUX在user版本下也等于1,会不会也可以实现关闭selinux。继续分析:
如果ALLOW_PERMISSIVE_SELINUX=1,IsEnforcing函数返回值就依赖
StatusFromProperty() == SELINUX_ENFORCING;
在android12之前,StatusFromProperty函数只读取cmdline的androidboot.selinux,
那么在android12之前,可以通过修改:
ALLOW_PERMISSIVE_SELINUX=1 并且 BOARD_KERNEL_CMDLINE += androidboot.selinux=permissive
实现user版本永久关闭selinux。
从android12开始,StatusFromProperty函数还会判断Bootconfig的androidboot.selinux值。内核cmdline和Bootconfig的androidboot.selinux值都等于enforcing时,StatusFromProperty会返回enforcing。
那么从android12开始,可以通过修改:
ALLOW_PERMISSIVE_SELINUX=1 并且 ( BOARD_KERNEL_CMDLINE += androidboot.selinux=permissive 或者 BOARD_BOOTCONFIG += androidboot..selinux=permissive)
实现user版本永久关闭selinux。
EnforcingStatus StatusFromProperty() {EnforcingStatus status = SELINUX_ENFORCING;ImportKernelCmdline([&](const std::string& key, const std::string& value) {if (key == "androidboot.selinux" && value == "permissive") {status = SELINUX_PERMISSIVE;}});if (status == SELINUX_ENFORCING) {ImportBootconfig([&](const std::string& key, const std::string& value) {if (key == "androidboot.selinux" && value == "permissive") {status = SELINUX_PERMISSIVE;}});}return status;}