Android Native 之 adbd进程分析

目录

1、adbd守护进程

2、adbd权限降级

3、adbd命令解析

[1)adb shell](#1)adb shell)

[2)adb root](#2)adb root)

[3)adb reboot](#3)adb reboot)

4、案例

[1)案例之实现不需要执行adb root命令自动具有root权限](#1)案例之实现不需要执行adb root命令自动具有root权限)

[2)案例之实现不需要RSA认证直接能够使用adb shell命令](#2)案例之实现不需要RSA认证直接能够使用adb shell命令)


adb源码的架构,其实在system/packages/modules/adb/中有注释,如下两个方向:

  • PC主机与adb设备的拓扑图

根据下面一段文字的介绍,android设备的adb主要靠守护进程adbd来实现,具体代码实现是在Daemon里面。

1、adbd守护进程

adbd作为android设备的守护进程,android.bp文件定义如下:

回到adbd主函数中调用adbd_main函数里面,源码如下:

cpp 复制代码
//aosp/packages/modules/adb/daemon/main.cpp
int adbd_main(int server_port) {
    //...省略...
    //重点1:auth_required为true表示进行RSA校验,其默认值根据ro.adb.secure
#if defined(__ANDROID__)
    bool device_unlocked = android::base::GetProperty("ro.boot.verifiedbootstate", "") == "orange";
    if (device_unlocked || __android_log_is_debuggable()) {
#if defined(__ANDROID_RECOVERY__)
        auth_required = false;  // Bypass authorization when the device transitions to
#else
        // If we're on userdebug/eng or the device is unlocked, permit no-authentication.
        auth_required = android::base::GetBoolProperty("ro.adb.secure", false);
#endif
    }
#endif
    //重点2:是否进行权限降级
#if defined(__ANDROID__)
    drop_privileges(server_port);
#endif
#if defined(__ANDROID__)
    // A thread gets spawned as a side-effect of initializing the watchdog, so it needs to happen after we drop privileges.
    watchdog::Initialize();
#endif
    //重点3:证书权限相关校验,如果auth_required为false其adb/daemon/auth.cpp直接返回不需要校验
    // adbd_auth_init will spawn a thread, so we need to defer it until after selinux transitions.
    adbd_auth_init();
    bool is_usb = false;
#if defined(__ANDROID__)
    if (access(USB_FFS_ADB_EP0, F_OK) == 0) {
        // Listen on USB.
        usb_init();
        is_usb = true;
    }
#endif
    // If one of these properties is set, also listen on that port.
    // If one of the properties isn't set and we couldn't listen on usb, listen on the default port.
    std::vector<std::string> addrs;
    std::string prop_addr = android::base::GetProperty("service.adb.listen_addrs", "");
    if (prop_addr.empty()) {
        //...省略...
    } else {
        addrs = android::base::Split(prop_addr, ",");
        setup_adb(addrs);
    }
    //重点3:关键日志,adbd启动,如果没有这行日志,说明adbd根本无法使用
    LOG(INFO) << "adbd started";
    D("adbd_main(): pre init_jdwp()");
    init_jdwp();
    D("adbd_main(): post init_jdwp()");
    D("Event loop starting");
    //重点4:进入fd事件轮询,即监听pc端发送过来的数据,最终传递在daemon_service_to_fd函数中进行解析
    fdevent_loop();
    return 0;
}

总结如上adbd守护进程主函数大致流程如下:

  • 设置auth_required状态,为true表示进行RSA校验,其默认值根据ro.adb.secure 。重点,我们可以定制ro.adb.secure属性的值来实现不需要设备授权直接使用adb命令
  • 通过drop_privileges方法来实现权限降级
  • 通过setup_adb设置地址(串口号?),这里除了判断是usb链接还是wifi链接之外,还处理多台usb设备之间的关系
  • 进入fdevent_loop事件轮询,监听PC adb端发送过来的命令

2、adbd权限降级

前面已经介绍了drop_privileges来实现权限降级,那么什么是权限降级呢?这涉及到linux dac机制,在android_filesystem_config.h 文件中定义了大量的权限等级:

AID_XXX定义了android系统中基本能分解的所有用户组和权限(0-100000)。其中AID_ROOT等于0被作为最高用户组。咨询AI这其实就是Linux DAC的实现机制。

回到drop_privileges函数如下内容:

cpp 复制代码
static void drop_privileges(int server_port) {
    ScopedMinijail jail(minijail_new());
    // Add extra groups:
    // AID_ADB to access the USB driver
    // AID_LOG to read system logs (adb logcat)
    // AID_INPUT to diagnose input issues (getevent)
    // AID_INET to diagnose network issues (ping)
    // AID_NET_BT and AID_NET_BT_ADMIN to diagnose bluetooth (hcidump)
    // AID_SDCARD_R to allow reading from the SD card
    // AID_SDCARD_RW to allow writing to the SD card
    // AID_NET_BW_STATS to read out qtaguid statistics
    // AID_READPROC for reading /proc entries across UID boundaries
    // AID_UHID for using 'hid' command to read/write to /dev/uhid
    // AID_EXT_DATA_RW for writing to /sdcard/Android/data (devices without sdcardfs)
    // AID_EXT_OBB_RW for writing to /sdcard/Android/obb (devices without sdcardfs)
    // AID_READTRACEFS for reading tracefs entries
    gid_t groups[] = {AID_ADB,          AID_LOG,          AID_INPUT,    AID_INET,
                      AID_NET_BT,       AID_NET_BT_ADMIN, AID_SDCARD_R, AID_SDCARD_RW,
                      AID_NET_BW_STATS, AID_READPROC,     AID_UHID,     AID_EXT_DATA_RW,
                      AID_EXT_OBB_RW,   AID_READTRACEFS};
    minijail_set_supplementary_gids(jail.get(), arraysize(groups), groups);
    // Don't listen on a port (default 5037) if running in secure mode.
    // Don't run as root if running in secure mode.
    if (should_drop_privileges()) {
        //...省略...
        //核心代码:通过minijail_change_gid切换用户组ID为AID_SHELL,属于Linux DAC降低权限
        minijail_change_gid(jail.get(), AID_SHELL);
        minijail_change_uid(jail.get(), AID_SHELL);
        //核心代码:通过minijail_enter执行最终的沙箱隔离操作,属于Linux DAC机制
        // minijail_enter() will abort if any priv-dropping step fails.
        minijail_enter(jail.get());
        //...省略...
        D("Local port disabled");
    } else {
        //核心代码:通过minijail_enter执行最终的沙箱隔离操作,默认是ROOT?
        // minijail_enter() will abort if any priv-dropping step fails.
        minijail_enter(jail.get());
        //防错处理,如果变量root_seclabel设置的selinux安全上下文不是0,即不是root用户,强制属性service.adb.root的在值为0
        if (root_seclabel != nullptr) {
            if (selinux_android_setcon(root_seclabel) < 0) {
                // If we failed to become root, don't try again to avoid a
                // restart loop.
                android::base::SetProperty("service.adb.root", "0");
                LOG(FATAL) << "Could not set SELinux context";
            }
        }
    }
}

如上代码总结如下:

  • 通过should_drop_privileges来判断是否需要进行权限降级,返回true进行权限降级
  • 进行权限降级:切换用户组为AID_SHELL,来达到降级的目的
  • 不进行权限降级:没有地方去切换用户组等级,默认为root组?

接下来继续分析一下should_drop_privileges是如何来判断需要进行权限降级?

  • 所以最后的核心:最后通过下一小节可以了解到执行adb root之后就会设置属性service.adb.root为1;执行adb unroot之后就会设置属性service.adb.root为0,即adb root之后,adb终端重启并进入最高用户权限等级。

3、adbd命令解析

PC主机发送过来的adb xxx命令,最后由守护进程adb/daemon/services通过套接字或者fd的方式来接收adb相关命令,如下代码逻辑:

如上代码,解析几条重要的adb xxx命令之后,通过create_service_thread的方式去启动线程来执行对应的命令。

1)adb shell

daemon_service_to_fd方法解析为adb shell,直接调用ShellService来进行命令参数拼接

这里比较核心的方法StartSubprocess,在adb/daemon守护进程里面基本上通过它来创建子进程,然后用shell作为执行源。

2)adb root

我们执行adb root的时候,会执行如下函数,如果不是debug版本直接返回,如果是debug版本设置service.adb.root属性值为1.

3)adb reboot

adb reboot命令同上,但是这里区分一下,并没有使用终端shell方式,第二个参数为null,cmd直接定义为/system/bin/reboot,即后续直接创建子进程,以reboot文件作为执行源。

4、案例

1)案例之实现不需要执行adb root命令自动具有root权限

根据adbd权限降级和adb root命令执行可以了解如下逻辑:

  • 执行adb root之后:service.adb.root 为1,在drop_privileges中使用了root用户组
  • 执行adb unroot之后:service.adb.root 为0,在drop_privileges中使用了shell用户组

因此如果我们想绕过adb root命令,直接带有root用户组权限,那么可以强制函数should_drop_privileges返回false。如下案例:

2)案例之实现不需要RSA认证直接能够使用adb shell命令

安卓设备通过usb链接电脑之后,我们在开发者模式菜单开启usb调试之后,通常还无法直接通过adb shell链接设备。会弹出如下对话框进行授权

如果我们想跳过这个对话框的显示,那么可以根据adbd守护进程中讲解的,强制属性ro.adb.secure的值为false即可,在debug版本或者解锁版本中,此值如果没有设置默认当成false。

相关推荐
宾有为38 分钟前
【Android】如何抓取 Android 设备的 UDP/TCP 数据包?
android·tcp/ip·udp·wireshark·抓包·tcp抓包·udp抓包
_一条咸鱼_1 小时前
Android Runtime JNI环境构建与注册过程原理(15)
android·面试·android jetpack
花花鱼1 小时前
Canvas: trying to draw too large(256032000bytes) bitmap.
android
移动开发者1号1 小时前
Fragment懒加载优化方案总结
android·kotlin
夕泠爱吃糖2 小时前
MySQL事务
android·数据库·mysql
移动开发者1号2 小时前
Android Activity启动模式面试题
android·kotlin
robch2 小时前
mysql prepare statement
android·数据库·mysql
alexhilton2 小时前
Jetpack Compose 中ViewModel的最佳实践
android·kotlin·android jetpack
工业互联网专业2 小时前
基于Android的跳蚤市场_springboot+vue
android·vue.js·spring boot·毕业设计·源码·课程设计·跳蚤市场
Chuck_Chan2 小时前
Launcher3体系化之路
android·app