Pico Org
主题色
250
1166 字
6 分钟
AndroidBox-01-修改SystemProperty

1. 简介#

Android项目地址

在做Android安全研究时,免不了对系统build.prop属性进行修改。常见修改方法有:

  1. 使用mprop工具
  2. 使用magisk自带的命令resetprop
  3. 安装magiskkernelsu的相关模块
  4. 手动修改framework源码后编译

其中,方法1主要采用的是init进程内存修改bypass,方法2和4的原理都是对framework源码进行修改,方法3根据模块不同有不同的原理。本文主要针对方法1展开分析。

2. 原理#

在看雪大佬文章中,回复楼层出现了mprop其他实现代码

Android源码分析发现,设备对应pixel7android14版本,设置属性函数__system_property_set最终会调用到init进程中的property_servicePropertySet函数,流程如下:

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
  1. 调用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()) {
  1. 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);
  1. 在函数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); }
  1. 在函数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.属性,并进行属性修改通知
  1. 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); }
  1. 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于是:

  1. ro.属性:很简单,只要调用__system_property_set
  2. ro.属性:利用ptraceAttach进程 -> 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.prop

5. 挖坑#

/system/lib64/libbase.so的真正用途需要进一步分析

6. 参考文献#

AndroidBox-01-修改SystemProperty
https://picoorg.github.io/posts/androidbox-01-修改systemproperty/
作者
Pico Org
发布于
2025-01-15
许可协议
CC BY-NC-SA 4.0