在Android系统中,adb shell是一个强大的工具,可以让我们在电脑上通过命令行操作Android设备上的文件和程序。其中,push和pull命令可以实现电脑和手机之间的文件传输,非常方便。但是,有时候(客制化需求或者防止某些内容被盗取)我们可能不希望某些文件被随意传输,比如系统敏感文件或者应用白名单文件。那么,我们该如何实现adb shell push/pull的禁止特定文件功能呢?
android11源码 push/pull的流程
为了解决这个问题,需要先了解android11源码system/core/adb/daemon/file_sync_service.cpp
中push/pull命令的实现流程。我们以adb pull为例,简单介绍一下主要步骤:
- 在电脑端,adb客户端程序接收到用户输入的adb pull命令后,会解析出要传输的源文件路径和目标文件路径,并向adb服务端程序发送一个SYNC服务请求。
- 在Android端,adb服务端程序收到SYNC服务请求后,会启动一个file_sync_service线程来处理文件传输相关的操作。
- file_sync_service线程会从adb客户端程序读取一个SyncRequest结构体,其中包含了请求的ID和路径长度。然后根据路径长度再读取相应的路径字符串。
- file_sync_service线程会根据请求的ID判断是push还是pull操作,并调用相应的函数来处理。对于pull操作,会调用do_recv_v1或do_recv_v2函数(根据协议版本不同而不同, 测试发现是do_recv_v1)。
static bool handle_sync_command(int fd, std::vector<char>& buffer) { D("sync: waiting for request"); SyncRequest request; if (!ReadFdExactly(fd, &request, sizeof(request))) { SendSyncFail(fd, "command read failure"); return false; } size_t path_length = request.path_length; if (path_length > 1024) { SendSyncFail(fd, "path too long11"); return false; } char name[1025]; if (!ReadFdExactly(fd, name, path_length)) { SendSyncFail(fd, "filename read failure"); return false; } name[path_length] = 0; std::string id_name = sync_id_to_name(request.id); D("sync: %s('%s')", id_name.c_str(), name); // 在处理每个命令之前,检查是否禁止了pull或push操作 std::string path(name); switch (request.id) { .............
- do_recv_v1或do_recv_v2函数会根据路径字符串打开手机上对应的文件,并将文件内容按照一定的格式和大小分块发送给adb客户端程序。
- 在电脑端,adb客户端程序收到file_sync_service线程发送的文件内容后,会根据目标文件路径创建或覆盖相应的文件,并将收到的内容写入文件中。
- 当file_sync_service线程发送完所有的文件内容后,会发送一个ID_DONE消息表示传输结束。adb客户端程序收到ID_DONE消息后,会关闭目标文件,并显示传输结果。
isOperationAllowed函数的作用
在android11源码中,有一个新添加的函数isOperationAllowed,它的作用是判断是否允许进行push或pull操作。它的原型如下:
// 通过属性判断是否允许操作 static bool isOperationAllowed(const char* filePath, const char* operationProperty);
static const char* appWhitelist[] = { "system/bin/candump", "system/bin/cansend", // Add more paths here as needed }; // 通过属性判断是否允许操作 static bool isOperationAllowed(const char* filePath, const char* operationProperty) { char operationDisabledValue[PROP_VALUE_MAX]; __system_property_get(operationProperty, operationDisabledValue); LOG(INFO) << operationProperty << "-filePath(" << operationDisabledValue << "," << filePath << ")"; if (strcmp(operationDisabledValue, "1") == 0){ LOG(INFO) << operationProperty << " = true!!!!!"; for (const char* path : appWhitelist) { if (strstr(filePath, path) != nullptr){ return false; } } LOG(INFO) << operationProperty << "22"; } else { LOG(INFO) << operationProperty << "33"; } return true; }
它接受两个参数:filePath是要传输的文件路径,operationProperty是一个系统属性名,表示是否禁止某种操作。例如,persist.adb.push_disabled表示是否禁止push操作,persist.adb.pull_disabled表示是否禁止pull操作。
这个函数的实现逻辑如下:
- 从系统属性中读取operationProperty对应的值,如果值为"1",表示禁止该操作;如果值为其他或者没有设置该属性,则表示允许该操作。
- 如果禁止该操作,则遍历一个预定义的应用白名单数组appWhitelist,检查filePath是否包含数组中的任何一个路径。如果包含,则表示该文件属于白名单中的应用,允许该操作;如果不包含,则表示该文件不属于白名单中的应用,禁止该操作。
- 返回最终的判断结果,true表示允许,false表示禁止。
// 在处理每个命令之前,检查是否禁止了pull或push操作 std::string path(name); switch (request.id) { case ID_LSTAT_V1: if (!do_lstat_v1(fd, name)) return false; break; case ID_LSTAT_V2: case ID_STAT_V2: if (!do_stat_v2(fd, request.id, name)) return false; break; case ID_LIST_V1: if (!do_list_v1(fd, name)) return false; break; case ID_LIST_V2: if (!do_list_v2(fd, name)) return false; break; case ID_SEND_V1: // 检查是否允许push操作 if (!isOperationAllowed(name, "persist.adb.push_disabled")){ LOG(ERROR) << "forbid_push_file"; return false; } if (!do_send_v1(fd, name, buffer)) return false; break; case ID_SEND_V2: // 检查是否允许push操作 if (!isOperationAllowed(name, "persist.adb.push_disabled")){ LOG(ERROR) << "forbid_push_file"; return false; } if (!do_send_v2(fd, name, buffer)) return false; break; case ID_RECV_V1: // 检查是否允许pull操作 if (!isOperationAllowed(name, "persist.adb.pull_disabled")){ LOG(ERROR) << "forbid_pull_file"; return false; } if (!do_recv_v1(fd, name, buffer)) return false; break; case ID_RECV_V2: // 检查是否允许pull操作 if (!isOperationAllowed(name, "persist.adb.pull_disabled")){ LOG(ERROR) << "forbid_pull_file"; return false; } if (!do_recv_v2(fd, name, buffer)) return false; break; case ID_QUIT: return false; default: SendSyncFail(fd, StringPrintf("unknown command %08x", request.id)); return false; } return true; }
这个函数被调用的地方有两个:一个是在handle_send_file函数中,用于判断是否允许push操作;另一个是在do_recv_v1或do_recv_v2函数中,用于判断是否允许pull操作。如果判断结果为false,则会向adb客户端程序发送一个ID_FAIL消息,并终止传输。
总结
通过上面的分析,android11源码中实现了adb shell push/pull禁止特定文件的功能,主要是通过新增一个isOperationAllowed函数来判断是否允许传输某个文件。这个函数的优点是可以通过设置系统属性来动态控制是否禁止push或pull操作,也可以通过修改应用白名单数组来指定哪些应用的文件可以传输。这样可以提高系统的安全性和灵活性。
希望我的博客文章对你有所帮助。如果你有任何问题或建议,请随时与我联系。谢谢!