Pico Org
1161 字
6 分钟
AndroidBox-01-修改SystemProperty

1. 简介#

在做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