1166 字
6 分钟
AndroidBox-01-修改SystemProperty
1. 简介
在做Android安全研究时,免不了对系统build.prop属性进行修改。常见修改方法有:
- 使用
mprop工具 - 使用
magisk自带的命令resetprop - 安装
magisk或kernelsu的相关模块 - 手动修改
framework源码后编译
其中,方法1主要采用的是init进程内存修改bypass,方法2和4的原理都是对framework源码进行修改,方法3根据模块不同有不同的原理。本文主要针对方法1展开分析。
2. 原理
对Android源码分析发现,设备对应pixel7的android14版本,设置属性函数__system_property_set最终会调用到init进程中的property_service的PropertySet函数,流程如下:
ndk __system_property_set
1 => /system/lib64/libc.so
bionic/libc/bionic/system_property_set.cpp __system_property_set
2 => /system/bin/init
system/core/init/property_service.cpp handle_property_set_fd
3 => /system/bin/init
system/core/init/property_service.cpp HandlePropertySet()
4 => /system/bin/init
system/core/init/property_service.cpp PropertySet
5 => /system/lib64/bootstrap/libc.so
bionic/libc/bionic/system_property_api.cpp __system_property_update
6 => /system/lib64/bootstrap/libc.so
bionic/libc/system_properties/system_properties.cpp SystemProperties::Update- 调用
ndk函数__system_property_set设置属性,会先判断g_propservice_protocol_version值,根据版本调用不同发送方式,后文以kProtocolVersion2为例。
// bionic/libc/bionic/system_property_set.cpp
// https://cs.android.com/android/platform/superproject/main/+/0aee4d97c97a87812f9764920f54b40de6958f1c:bionic/libc/bionic/system_property_set.cpp
__BIONIC_WEAK_FOR_NATIVE_BRIDGE
int __system_property_set(const char* key, const char* value) {
...
if (g_propservice_protocol_version == kProtocolVersion1) { // g_propservice_protocol_version取ro.property_service.version值,大或等于2是kProtocolVersion2,否则是kProtocolVersion1
...
prop_msg msg;
memset(&msg, 0, sizeof msg);
msg.cmd = PROP_MSG_SETPROP;
strlcpy(msg.name, key, sizeof msg.name);
strlcpy(msg.value, value, sizeof msg.value);
return send_prop_msg(&msg);
} else {
...
PropertyServiceConnection connection(key);
...
SocketWriter writer(&connection);
if (!writer.WriteUint32(PROP_MSG_SETPROP2).WriteString(key).WriteString(value).Send()) {- 在
system/core/init/property_service.cpp里函数handle_property_set_fd处理socket数据,进入PROP_MSG_SETPROP2分支,接收数据后调用HandlePropertySet函数
// system/core/init/property_service.cpp
// https://cs.android.com/android/platform/superproject/main/+/0aee4d97c97a87812f9764920f54b40de6958f1c:system/core/init/property_service.cpp
static void handle_property_set_fd(int fd) {
...
int s = accept4(fd, nullptr, nullptr, SOCK_CLOEXEC);
...
if (getsockopt(s, SOL_SOCKET, SO_PEERCRED, &cr, &cr_size) < 0) {
...
SocketConnection socket(s, cr);
...
if (!socket.RecvUint32(&cmd, &timeout_ms)) {
...
switch (cmd) {
...
case PROP_MSG_SETPROP2: {
std::string name;
std::string value;
if (!socket.RecvString(&name, &timeout_ms) ||
!socket.RecvString(&value, &timeout_ms)) {
...
auto result = HandlePropertySet(name, value, source_context, cr, &socket, &error);- 在函数
HandlePropertySet处理权限校验及特殊系统属性,最后进入PropertySet函数
// system/core/init/property_service.cpp
// https://cs.android.com/android/platform/superproject/main/+/0aee4d97c97a87812f9764920f54b40de6958f1c:system/core/init/property_service.cpp
std::optional<uint32_t> HandlePropertySet(const std::string& name, const std::string& value,
const std::string& source_context, const ucred& cr,
SocketConnection* socket, std::string* error) {
if (auto ret = CheckPermissions(name, value, source_context, cr, error); ret != PROP_SUCCESS) {
...
if (StartsWith(name, "ctl.")) {
return {SendControlMessage(name.c_str() + 4, value, cr.pid, socket, error)};
}
... // 特殊处理sys.powerctl,其作用是设备重启,但目前userspace已经弃用了,aosp会记录设备关机的原因
... // 特殊处理selinux.restorecon_recursive,如果来自非init进程,且value不为空,触发异步恢复SELinux,如果来自init进程,会导致执行时间过长
return PropertySet(name, value, socket, error);
}- 在函数
PropertySet处理属性修改,如果prop存在进行__system_property_update修改,否则进行__system_property_add添加
// system/core/init/property_service.cpp
// https://cs.android.com/android/platform/superproject/main/+/0aee4d97c97a87812f9764920f54b40de6958f1c:system/core/init/property_service.cpp
static std::optional<uint32_t> PropertySet(const std::string& name, const std::string& value,
SocketConnection* socket, std::string* error) {
...
prop_info* pi = (prop_info*)__system_property_find(name.c_str());
if (pi != nullptr) {
// ro.* properties are actually "write-once".
if (StartsWith(name, "ro.")) { // ro属性校验
*error = "Read-only property was already set";
return {PROP_ERROR_READ_ONLY_PROPERTY};
}
__system_property_update(pi, value.c_str(), valuelen);
} else {
int rc = __system_property_add(name.c_str(), name.size(), value.c_str(), valuelen);
if (rc < 0) {
*error = "__system_property_add failed";
return {PROP_ERROR_SET_FAILED};
}
}
... // 处理需要持久化的persist.和next_boot.属性,并进行属性修改通知- 在
bionic/libc/bionic/system_property_api.cpp里函数__system_property_update调用SystemProperties::Update
// bionic/libc/bionic/system_property_api.cpp
// https://cs.android.com/android/platform/superproject/main/+/0aee4d97c97a87812f9764920f54b40de6958f1c:bionic/libc/bionic/system_property_api.cpp
static SystemProperties system_properties;
...
__BIONIC_WEAK_FOR_NATIVE_BRIDGE
int __system_property_update(prop_info* pi, const char* value, unsigned int len) {
return system_properties.Update(pi, value, len);
}- 在
bionic/libc/system_properties/system_properties.cpp里函数SystemProperties::Update最终将value写入prop_area
// bionic/libc/system_properties/system_properties.cpp
// https://cs.android.com/android/platform/superproject/main/+/0aee4d97c97a87812f9764920f54b40de6958f1c:bionic/libc/system_properties/system_properties.cpp
int SystemProperties::Update(prop_info* pi, const char* value, unsigned int len) {
...
prop_area* pa = contexts_->GetPropAreaForName(pi->name);
prop_area* override_pa =
have_override ? appcompat_override_contexts_->GetPropAreaForName(pi->name) : nullptr;
...
auto* override_pi = const_cast<prop_info*>(have_override ? override_pa->find(pi->name) : nullptr);
...
memcpy(pa->dirty_backup_area(), pi->value, old_len + 1);
if (have_override) {
memcpy(override_pa->dirty_backup_area(), override_pi->value, old_len + 1);
}3. 实现
一种比较暴力的思路,发现init进程中,只有PropertySet函数会校验ro.属性,并且通过检索发现init modules中只有一个位置包含字符串ro.\x00于是:
- 非
ro.属性:很简单,只要调用__system_property_set; ro.属性:利用ptrace的Attach进程 ->PeekData定位 ->PokeData修改,篡改校验规则后调用__system_property_set,在成功后恢复现场;
4. 其他细节
根据baihong大佬提醒,__system_property_set调用会产生痕迹,修改/system_ext/etc/build.prop属性可能表现与/system/build.prop不一致。
4.1. __system_property_set执行影响
根据提示位置system/libbase/properties.cpp,代码在/system/lib64/libbase.so,发现与「原理」章节流程中第6步不一致,实际未调用。但serial值确实会增加:
// bionic/libc/system_properties/system_properties.cpp
// https://cs.android.com/android/platform/superproject/main/+/0aee4d97c97a87812f9764920f54b40de6958f1c:bionic/libc/system_properties/system_properties.cpp
int SystemProperties::Update(prop_info* pi, const char* value, unsigned int len) {
...
uint32_t serial = atomic_load_explicit(&pi->serial, memory_order_relaxed);
...
serial |= 1;
...
int new_serial = (len << 24) | ((serial + 1) & 0xffffff);
atomic_store_explicit(&pi->serial, new_serial, memory_order_relaxed);
if (have_override) {
atomic_store_explicit(&override_pi->serial, new_serial, memory_order_relaxed);
}
__futex_wake(&pi->serial, INT32_MAX); // Fence by side effect
atomic_store_explicit(serial_pa->serial(),
atomic_load_explicit(serial_pa->serial(), memory_order_relaxed) + 1,
memory_order_release);
if (have_override) {
atomic_store_explicit(override_serial_pa->serial(),
atomic_load_explicit(serial_pa->serial(), memory_order_relaxed) + 1,
memory_order_release);
}
__futex_wake(serial_pa->serial(), INT32_MAX);
return 0;
}4.2. system_ext属性不一致
经验证,通过AndroidBox同样可以修改/system_ext/etc/build.prop中的属性。但是由于其文件属性问题,会导致与初始值不一致:
panther:/ # ls -al /system_ext/etc/build.prop
-rw-r--r-- 1 root root 999 2009-01-01 08:00 /system_ext/etc/build.prop
panther:/ # ls -al /system/build.prop
-rw------- 1 root root 4562 2009-01-01 08:00 /system/build.prop5. 挖坑
/system/lib64/libbase.so的真正用途需要进一步分析
6. 参考文献
AndroidBox-01-修改SystemProperty
https://picoorg.github.io/posts/androidbox-01-修改systemproperty/