Frida Android hook native层__system_property_get的最终方案
记录这个问题的起因是,在hook时遇到了修改内容长度,超过原长度时,会出现显示不全的问题。
比如把nexus 5改成nexus 100,只会显示nexus 1。
所以去读了下源码
int __system_property_read(const prop_info *pi, char *name, char *value) { unsigned serial, len; for(;;) { serial = pi->serial; while(SERIAL_DIRTY(serial)) { __futex_wait((volatile void *)&pi->serial, serial, 0); serial = pi->serial; } len = SERIAL_VALUE_LEN(serial); memcpy(value, pi->value, len + 1); if(serial == pi->serial) { if(name != 0) { strcpy(name, pi->name); } return len; } } } int __system_property_get(const char *name, char *value) { const prop_info *pi = __system_property_find(name); if(pi != 0) { return __system_property_read(pi, 0, value); } else { value[0] = 0; return 0; } }
可以看到源码中get调用了read来读value,传进去了find返回的指针,但是name给的是0,所以在read中没法直接判断读的是哪个key,还是要hook get。
而长度的限制来自read内部的len实现,不能通过改参数的办法修改,所以用了个别扭的方法先修改了read返回值的长度,再修改get的value,脚本如下:
Interceptor.attach(Module.findExportByName("libc.so", "__system_property_read"), { onEnter: function (args) { //sLog("__system_property_read onEnter " + Memory.readCString(args[0])); }, onLeave: function (retval) { //sLog(retval) retval.replace(0x5b);//修改read的返回值 } }); var str = "" var args2 = ""; Interceptor.attach(Module.findExportByName("libc.so", "__system_property_get"), { onEnter: function (args) { str = getStr(args[0]); args2 = ptr(args[1]); }, onLeave: function (retval) { if (str) { if (str.indexOf("ro.serialno") != -1) { var before = getStr(args2) putStr(args2, "05b3c6d30a280000") sLog(str + " " + before + " 改成 " + getStr(args2)); } } } });
其中hook read的返回值长度不是乱写的,因为system_properties.h源码中有限制最大长度
#define PROP_NAME_MAX 32 #define PROP_VALUE_MAX 92
hex(92) = '0x5c'
所以我把read的返回值改成了0x5b,减了1,肯定也够用了。
源码全文参考
https://android.googlesource.com/platform/bionic/+/0d787c1fa18c6a1f29ef9840e28a68cf077be1de/libc/bionic/system_properties.c
https://android.googlesource.com/platform/bionic/+/49f0a8f23bba188466c6ee3652858ef4da228c6f/libc/include/sys/system_properties.h