一、缘由:
使用xposed hook build类下 DEVICE MODEL VERSION MANUFACTURER等静态字段
使用XposedHelpers.setStaticObjectField() hook 失败!
XposedHelpers.setStaticObjectField(android.os.Build.class, "MODEL", null);
1
使用 反射 hook 失败!
Field model = Build.class.getDeclaredField("MODEL");
model.setAccessible(true);
model.set(Build.class, pre.getString("model", null));
1
2
3
二、Build源码分析
打开build源码(/frameworks/base/core/Java/android/os/Build.java)
Build类中主要是一些成员属性
public class Build {
/** Value used for when a build property is unknown. */
public static final String UNKNOWN = "unknown";
/** Either a changelist number, or a label like "M4-rc20". */
public static final String ID = getString("ro.build.id");
/** A build ID string meant for displaying to the user */
public static final String DISPLAY = getString("ro.build.display.id");
/** The name of the overall product. */
public static final String PRODUCT = getString("ro.product.name");
/** The name of the industrial design. */
public static final String DEVICE = getString("ro.product.device");
/** The name of the underlying board, like "goldfish". */
public static final String BOARD = getString("ro.product.board");
/** The name of the instruction set (CPU type + ABI convention) of native code. */
public static final String CPU_ABI = getString("ro.product.cpu.abi");
/** The name of the second instruction set (CPU type + ABI convention) of native code. */
public static final String CPU_ABI2 = getString("ro.product.cpu.abi2");
/** The manufacturer of the product/hardware. */
public static final String MANUFACTURER = getString("ro.product.manufacturer");
/** The brand (e.g., carrier) the software is customized for, if any. */
public static final String BRAND = getString("ro.product.brand");
/** The end-user-visible name for the end product. */
public static final String MODEL = getString("ro.product.model");
/** The system bootloader version number. */
public static final String BOOTLOADER = getString("ro.bootloader");
/**
* The radio firmware version number.
*
* @deprecated The radio firmware version is frequently not
* available when this class is initialized, leading to a blank or
* "unknown" value for this string. Use
* {@link #getRadioVersion} instead.
*/
@Deprecated
public static final String RADIO = getString(TelephonyProperties.PROPERTY_BASEBAND_VERSION);
/** The name of the hardware (from the kernel command line or /proc). */
public static final String HARDWARE = getString("ro.hardware");
/** A hardware serial number, if available. Alphanumeric only, case-insensitive. */
public static final String SERIAL = getString("ro.serialno");
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
三、Build类获取系统信息流程
Build类中除Build. UNKNOWN(这个常量是直接返回”unknown”)之外的每个常量都是通过
private static String getString(String property);
1
这个内部静态方法来获取的。在getString()方法中调用了Systemproperties类的
public static String get(String key);方法来获取这些值。
Systemproperties类是android.os中标记为@hide的一个类,无法直接访问(但可以通过反射方式访问)。在Systemproperties类调用了 private static native String native_get(String key);
这个native方法。此native方法的代码 在Android源码的
“frameworks/base/core/jni/android_os_SystemProperties.cpp”文件中。Systemproperties类代码代码如下。
public class SystemProperties
{
public static final int PROP_NAME_MAX = 31;
public static final int PROP_VALUE_MAX = 91;
private static native String native_get(String key);
private static native String native_get(String key, String def);
private static native int native_get_int(String key, int def);
private static native long native_get_long(String key, long def);
private static native boolean native_get_boolean(String key, boolean def);
private static native void native_set(String key, String def);
public static String get(String key) {
if (key.length() > PROP_NAME_MAX) {
throw new IllegalArgumentException("key.length > " + PROP_NAME_MAX);
}
return native_get(key);
}
public static String get(String key, String def) {
if (key.length() > PROP_NAME_MAX) {
throw new IllegalArgumentException("key.length > " + PROP_NAME_MAX);
}
return native_get(key, def);
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
android_os_SystemProperties.cpp代码如下。
#define LOG_TAG "SysPropJNI"
#include "cutils/properties.h"
#include "utils/misc.h"
#include <utils/Log.h>
#include "jni.h"
#include "android_runtime/AndroidRuntime.h"
#include <nativehelper/JNIHelp.h>
namespace android
{
static jstring SystemProperties_getSS(JNIEnv *env, jobject clazz,
jstring keyJ, jstring defJ)
{
int len;
const char* key;
char buf[PROPERTY_VALUE_MAX];
jstring rvJ = NULL;
if (keyJ == NULL) {
jniThrowNullPointerException(env, "key must not be null.");
goto error;
}
key = env->GetStringUTFChars(keyJ, NULL);
len = property_get(key, buf, "");
if ((len <= 0) && (defJ != NULL)) {
rvJ = defJ;
} else if (len >= 0) {
rvJ = env->NewStringUTF(buf);
} else {
rvJ = env->NewStringUTF("");
}
env->ReleaseStringUTFChars(keyJ, key);
error:
return rvJ;
}
static jstring SystemProperties_getS(JNIEnv *env, jobject clazz,
jstring keyJ)
{
return SystemProperties_getSS(env, clazz, keyJ, NULL);
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
可以看到SystemProperties_getSS中调用了property_get(),property_get()是libcutils提供的一个api,property_get()又调用了libc中的 __system_property_get()函数来访问系统共享内存中的数据。
四、Build类获取到的系统信息来源
在Android系统启动时,“init”守护进程(源代码位于:device/system/init)将启动一个属性服务并分配一段共享内存来存储各项属性。属性服务启动后调用libc中的__system_property_init()函数。__system_property_init()函数将依次读取。/default.prop; /system/build.prop; /system/default.prop; /data/local.prop四个文件,将四个文件中配置的各项属性读入到共享内存中。Build类中的常量大都来源于几个配置文件。/system/build.prop是其中最重要的一个配置文件,大多数Build中的常量都是从这个配置文件中获取到的。
/system/build.prop文件是Android系统在编译时由MakeFile生成,MakeFile中会调用Makefile中调用build/tools/buildinfo.sh脚本,将相关配置写入到build.prop文件中。
<图片引用:http://blog.csdn.net/ccpat/article/details/44776313>
在系统初始化时,Android将分配一个共享内存区来存储的属性。这些是由“init”守护进程完成的,其源代码位于:device/system/init。“init”守护进程将启动一个属性服务。
属性服务在“init”守护进程中运行。每一个客户端想要设置属性时,必须连接属性服务,再向其发送信息。属性服务将会在共享内存区中修改和创建属性。任何客户端想获得属性信息,可以从共享内存直接读取。这提高了读取性能。 客户端应用程序可以调用libcutils中的API函数以GET/SET属性信息。libcutils的源代码位于:device/libs/cutils。API函数是:
int property_get(const char *key, char *value, const char *default_value);
int property_set(const char *key, const char *value);
1
2
而libcutils又调用libc中的 __system_property_xxx 函数获得共享内存中的属性。libc的源代码位于:device/system/bionic。
属性服务调用libc中的__system_property_init函数来初始化属性系统的共享内存。当启动属性服务时,将从以下文件中加载默认属性:
/default.prop
/system/build.prop
/system/default.prop
/data/local.prop
1
2
3
4
属性将会以上述顺序加载。后加载的属性将覆盖原先的值。这些属性加载之后,最后加载的属性会被保持在/data/property中。
特别属性 如果属性名称以“ro.”开头,那么这个属性被视为只读属性。一旦设置,属性值不能改变。
如果属性名称以“persist.”开头,当设置这个属性时,其值也将写入/data/property。
如果属性名称以“net.”开头,当设置这个属性时,“net.change”属性将会自动设置,以加入到最后修改的属性名。(这是很巧妙的。 netresolve模块的使用这个属性来追踪在net.*属性上的任何变化。)
属性“ ctrl.start ”和“ ctrl.stop ”是用来启动和停止服务。
每一项服务必须在/init.rc中定义.系统启动时,与init守护进程将解析init.rc和启动属性服务。一旦收到设置“ ctrl.start ”属性的请求,属性服务将使用该属性值作为服务名找到该服务,启动该服务。这项服务的启动结果将会放入“ init.svc.<服务名>“属性中 。客户端应用程序可以轮询那个属性值,以确定结果
五、属性系统工作原理
Android属性系统由有三个进程,一组属性文件和一块共享内存组成。这块共享内存保存着系统中所有的属性记录,只有Property service能写这块共享内存,并且Property service负责将属性文件中的属性记录加载到共享内存中。
属性读取进程(property consumer)把这块共享内存映射到自己的进程空间,然后直接读取它。属性设置进程(property setter)也加载这块共享到他的进程空间,但是他不能直接写这块共享内存。当他需要增加或者修改属性的时候,通过Unix Socket发生属性给Property service,Property service将代表设置进程写入共享内存和属性文件。
Property service运行于init进程中。init进程首先创建一块共享内存,并把他的句柄fd存放在这块内存中,init进程通过mmap带MAP_SHARE标志的系统调用,把这块内存映射到他的虚拟空间中,最终这块内存所有的更新将会被所有映射这块共享内存的进程看到。共享内存句柄fd和共享内存大小存储在系统环境变量“ANDROID_PROPERTY_WORKSPACE”中,所有的进程包括属性设置进程和属性读取进程都将通过这个系统环境变量获得共享内存的句柄fd和大小,然后把这块内存映射到他们自己的虚拟空间。
然后,init进程将会从以下文件中加载属性:
1: /default.prop
2: /system/build.prop
3: /system/default.prop
4: /data/local.prop
下一步是启动Property service。这步中,将会创建一个Unix Socket服务器,这个Socket有一个闻名的名称“/dev/socket/property_service”。最后init进入死循环,等待socket的连接请求。
在读取进程中,当它初始化libc库的时候,将会获得属性系统共享内存的句柄和大小(bionic/libc/bionic/libc_init_common.c __libc_init_common函数)。并把这块共享内存映射到自己的进程虚拟空间中(bionic/libc/bionic/system_properties.c __system_properties_init函数)。这样读取进程将会向访问普通内存一样访问属性系统的共享内存了。
当前,属性不能被删除。也就是说一旦属性被创建,将不可以被删除,但是它们可以被修改。
六、测试xposed框架 hook
<三、Build类获取系统信息流程> 中得知 在java层调用了
Systemproperties类的public static String get()方法来获取build类中静态字段的值.
hook “android.os.Systemproperties” 类的 get()方法
新建回调函数类 SystemPropertiesHook
public class SystemPropertiesHook extends XC_MethodHook
{
public SystemPropertiesHook()
{
super();
}
protected void beforeHookedMethod(XC_MethodHook.MethodHookParam param) throws Throwable
{
XSharedPreferences pre = new XSharedPreferences(this.getClass().getPackage().getName(), "deviceInfo");
String methodName = param.method.getName();
if (methodName.startsWith("get"))
{
Log.v("getDeviceInfo", "hook systemProperties ------>");
XposedHelpers.setStaticObjectField(android.os.Build.class, "MODEL", pre.getString("model", null));
XposedHelpers.setStaticObjectField(android.os.Build.class, "MANUFACTURER", pre.getString("manufacturer", null));
XposedHelpers.setStaticObjectField(android.os.Build.class, "BRAND", pre.getString("brand", null));
XposedHelpers.setStaticObjectField(android.os.Build.class, "HARDWARE", pre.getString("hardware", null));
XposedHelpers.setStaticObjectField(android.os.Build.class, "RADIO", pre.getString("radio", null));
XposedHelpers.setStaticObjectField(android.os.Build.class, "DEVICE", pre.getString("device", null));
}
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
public class XposedModule implements IXposedHookLoadPackage
{
@Override
public void handleLoadPackage(final XC_LoadPackage.LoadPackageParam lpparam) throws Throwable
{
SystemPropertiesHook systemPropertiesHook = new SystemPropertiesHook();
//systemProperties hook
XposedHelpers.findAndHookMethod("android.os.SystemProperties", lpparam.classLoader, "get", String.class, String.class, systemPropertiesHook);
}
}
1
2
3
4
5
6
7
8
9
10
亲测 hook 成功!
<参考:http://blog.csdn.net/ccpat/article/details/44776313>
<参考:http://www.cnblogs.com/senior-engineer/p/4972848.html>
---------------------
作者:lonelyakin
来源:CSDN
原文:https://blog.csdn.net/jk38687587/article/details/53010760
版权声明:本文为博主原创文章,转载请附上博文链接!