规避安卓4.0调试白名单的方法

Android4.0以上版本增加调试白名单功能,意在提高系统的安全性,但是对我们这些搞自动化测试的人来说,这个东西还是有些麻烦。特别是事先并不知道哪个PC会来调试的情况,就比较难办了。

规避安卓4.0调试白名单的方法_第1张图片

怎么解决呢?看看源代码是如何实现这个功能的吧。

源码位置在android_source/system/core/adb/adb.c,当看到第一行就我就大喜:

property_get("ro.adb.secure",value, "0");
auth_enabled = !strcmp(value,"1");

从上面代码可以看出,只要ro.adb.secure不为1,那么这个功能就关闭了。于是尝试修改这个配置项,它存在于系统根目录default.prop。发现这个文件是只读的,怎么修改也不行,后面进一步了解,他是固化在boot.img中的,要修改的话要修改boot.img中的ramdisk 然后重新刷到机器中,感觉这个方法不是很方便。

下面只有想办法通过其他途径了,那就来看看是adb鉴权的流程吧,如果需要鉴权则:

if (auth_enabled)
adb_auth_init();

其中adb_auth_init()中有一句:

fd = android_get_control_socket("adbd");

上面是一个启动一个PF_UNIX本地socket服务端,这个地方暂时说到这里,后面会再说这个服务的作用。

PC端adb连接手机端adbd成功后会发送一个连接CNXN请求,adbd收到这个请求发现需要鉴权会生成一个随机令牌发送鉴权请求AUTH(ADB_AUTH_TOKEN)给PC客户端。

case A_CNXN:
if (HOST || !auth_enabled) {
            handle_online(t);
            if(!HOST) send_connect(t);
        } else{
            send_auth_request(t);// 请求客户来鉴权ADB_AUTH_TOKEN
       }

客户端收到这个请求后会将本地的私钥通过令牌签名后再次发给服务端(),私钥保存在用户当前目录.android/adbkey中。

case A_AUTH:
        if (p->msg.arg0 == ADB_AUTH_TOKEN){
            t->key = adb_auth_nextkey(t->key);
            if(t->key) {
                // 发送ADB_AUTH_SIGNATURE,将已签名私钥发送adbd
                send_auth_response(p->data,p->msg.data_length, t);

手机端adbd收到后会利用令牌还原私钥,再将该私钥与/data/misc/adb/adb_keys中的已经授杼公钥比对,如果比对成功,那么鉴权就成功了。如果不成功,手机端adbd会继续请求客户端adb进行鉴权,代码:

} elseif (p->msg.arg0 == ADB_AUTH_SIGNATURE) {
            if(adb_auth_verify(t->token, p->data, p->msg.data_length)) {
                adb_auth_verified(t);  // 鉴权成功
                t->failed_auth_attempts = 0;
            } else{
                if(t->failed_auth_attempts++> 10)
                    adb_sleep_ms(1000);
                send_auth_request(t);// 鉴权失败,继续请求客户端来鉴权
            }

此时客户端再来进入ADB_AUTH_TOKEN:

if (p->msg.arg0 == ADB_AUTH_TOKEN) {
            t->key = adb_auth_nextkey(t->key);
            if(t->key) {
                send_auth_response(p->data, p->msg.data_length, t);
            } else{
                /* Nomore private keys to try, send the public key */
            // 当没有私可发,就会把公钥发送手机端请求授权ADB_AUTH_RSAPUBLICKEY
                send_auth_publickey(t);
            }

此时手机端abdb进入:

else if (p->msg.arg0 == ADB_AUTH_RSAPUBLICKEY) {
            adb_auth_confirm_key(p->data, p->msg.data_length, t);// 授权确认
       }

进入adb_auth_confirm_key看看:

void adb_auth_confirm_key(unsigned char *key, size_t len, atransport *t)
{
    char msg[MAX_PAYLOAD];
    int ret;
 
    if (framework_fd < 0) { // 这里的framework_fd就是上层framework与前面的说的本地服务adbd建立的连接
        D("Client not connected\n");
        return;
    }
 
    if (key[len - 1] != '\0') {
        D("Key must be a null-terminated string\n");
        return;
    }
 
    ret = snprintf(msg, sizeof(msg), "PK%s", key);// 简单包装一个消息
    if (ret >= (signed)sizeof(msg)) {
        D("Key too long. ret=%d", ret);
        return;
    }
    D("Sending '%s'\n", msg);
 
    ret = unix_write(framework_fd,msg, ret); // 发给上层去处理
    if (ret < 0) {
        D("Failed to write PK, errno=%d\n", errno);
        return;
   }

再到上层frameworks中找到frameworks/base/services/java/com/android/server/usb/UsbDebuggingManager.java这个类,这个类中会启动一个线程与本地的名为adbd的服务连接,等待发来的RSA鉴权公钥,然后弹出提示框让手机用户确认是否同意。如果同意则会将公钥写入adb_keys,然后返回OK,否则就返回NO。

 

那么如何让系统自动确认而不弹框呢?你可能已经有解决办法了,就是自己写一个socket client端,连接名为adbd的本地socket server。这样adbd就会把RSA公钥发给client,client再把公钥直接写入到/data/misc/adb/adb_keys中,再返回成功的标识OK给adbd,这时adbd就会重新加载adb_keys文件,鉴权就通过了。而且不会有任何提示框。

不过这个client不能在framework上实现,在连接的时候会报权限不足。因为这里面限制只有system权限才可以连接这个名为adbd的PF_UNIX的本地通信服务。所以先要把手机root一下,然后写一个native程序,这样就OK了。

你可能感兴趣的:(Android)