1161 字
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.prop
5. 挖坑
/system/lib64/libbase.so
的真正用途需要进一步分析
6. 参考文献
AndroidBox-01-修改SystemProperty
https://picoorg.github.io/posts/androidbox-01-修改systemproperty/