改機棵里、改機垮兑、改機...
要重啟...
本套方案是原理是hook libart.so诉瓦,當需要更新新屬性時川队,將art虛擬機檢測Field屬性是否是final標志時力细,將邏輯改成非final。 當然其次時需要更新build屬性時進行修改固额,當Zygote準備拉起新的子進程時眠蚂,通過Unix domain socket 方式告知自行編寫的native守護程序,就叫它nopd吧斗躏。當nopd收到收到消息時逝慧,需要根據(jù)配置文件的內(nèi)容進行判斷是否需要更改build屬性,如果需要則先解析build更新屬性文件/data/local/update.prop啄糙。如果屬性數(shù)量大于0的時候笛臣,則將更新的屬性Field名稱和新值發(fā)送給zygote,然后zygote 通過jni反射機制修改Java Field內(nèi)容隧饼,完成之后需要通知nopd沈堡,讓它清理配置文件,如果需要更新燕雁,就需將內(nèi)容寫到/data/local/update.prop 即可诞丽。重啟App就能直接讀取到新值了。
需要注意的是拐格,這個只能針對虛擬機僧免,并不能影響properties_service全局的值,如果需要請參考我另一篇文章:動態(tài)修改ro屬性http://www.reibang.com/p/87231b22205d
當搬磚狗語言文字交流不行禁荒,Talk is cheap. Show me the code.
int serv_fd;
int _ret;
serv_fd = socket(AF_UNIX, SOCK_STREAM, 0);
if (serv_fd == 0) {
Nloge("Create socket %s error (%s).", NOP_SOCKET, strerror(errno));
return 1;
}
struct sockaddr_un serv_addr;
serv_addr.sun_family = AF_UNIX;
strcpy(serv_addr.sun_path, NOP_SOCKET);
_ret = ::bind(serv_fd, (struct sockaddr *) &serv_addr, sizeof(serv_addr));
if (_ret < 0) {
Nloge("Bind socket %s error (%s).", NOP_SOCKET, strerror(errno));
return 1;
}
if (chmod(serv_addr.sun_path, S_IRWXU) < 0) {
Nloge("Chmod socket %s error (%s).", NOP_SOCKET, strerror(errno));
return 1;
}
Nlogd("Unix domain %s bound (%d).", NOP_SOCKET, serv_fd);
if (listen(serv_fd, 1) < 0) {
Nloge("listen socket %s error (%s).", NOP_SOCKET, strerror(errno));
return -1;
}
首先創(chuàng)建socket猬膨,等待zygote來連接角撞,當有連接進來的時候...
child_fd = accept(serv_fd, nullptr, nullptr);
if (child_fd < 0) {
Nlogw("Invalid recvfrom sub-connection (%s).", strerror(errno));
continue;
}
Nlogd("Accept client connet for fd %d.\n", child_fd);
memset(buffer, 0, BUFFER_SIZE);
_ret = recv(child_fd, buffer, BUFFER_SIZE, 0);
if (_ret < 0) {
Nlogd("Recv (%s) error for %d.\n", strerror(errno), _ret);
continue;
}
// 0b00000001 0000 0000 0000 0000#
int* buf = (int*)buffer;
int flag = *buf;
if (flag & 1) { //zygote_modifier
// Check is need update.
vector<struct property> propertis;
bool update = updateProperty(&propertis);
if (update) {
int pid = *(buf + 1);
bool restore = (*(buf + 2) == 1);
bool is64bit = (*(buf + 3) == 1);
_ret = zygote_modify(pid, restore, is64bit, &global_offset);
Nlogd("Args(restore %s, is64bit %s, hook_pid %d) - ret %d.", restore ? "true" : "false",
is64bit ? "true" : "false", pid, _ret);
if (!restore) {
memset(buffer, 0, BUFFER_SIZE);
// 后面追加要更新的內(nèi)容: 0|FIELD#VALUE&FIELD#VALUE
*buffer = (char)(_ret + 48); // 0 to '0'
*(buffer + 1) = '|';
int offset = 2;
int count = 0;
int propertis_size = propertis.size();
for (auto &property : propertis) {
memcpy(buffer + offset, property.field, strlen(property.field));
offset += strlen(property.field);
*(buffer + offset) = '#';
offset += 1;
memcpy(buffer + offset, property.value, strlen(property.value));
offset += strlen(property.value);
if (count++ < propertis_size - 1) {
*(buffer + offset) = '&';
offset += 1;
}
}
buffer[offset] = '\0';
send(child_fd, buffer, strlen(buffer), 0);
Nlogd("send reply %d.", _ret);
} else {
Nlogw("restore ret %d.", _ret);
// clear update.prop
clearUpdateProperty();
}
}
根據(jù)標識判斷是否是進行zygote修改呛伴,重要的zygote_modify這個函數(shù)的實現(xiàn)。
int zygote_modify(int zygote_pid, bool restore, bool is64bit, int* global_offset) {
char map[128];
sprintf(map, "/proc/%d/maps", zygote_pid);
int _ret;
FILE* maps_fp = fopen(map, "r");
if (maps_fp == nullptr) {
Nloge("Open %s error: %s.", map, strerror(errno));
return 1;
}
char line[512];
memset(line, 0, sizeof(line));
_uint64 start_maps, end_maps;
char permisson[5];
while (fgets(line, sizeof(line), maps_fp)) {
if (strstr(line, ART_64)) {
_ret = sscanf(line, "%lx-%lx %4s ", &start_maps, &end_maps, permisson);
if (_ret < 3) {
continue;
}
if (permisson[0] == 'r' && permisson[1] == '-' &&
permisson[2] == 'x' && permisson[3] == 'p') {
break;
}
}
}
//Nlogd("art text code %lx-%lx %4s.", start_maps, end_maps, permisson);
fclose(maps_fp);
_uint64 map_len = end_maps - start_maps;
char map_buf[map_len];
_ret = ptrace(PTRACE_ATTACH, zygote_pid, nullptr, nullptr);
if (_ret == -1) {
Nloge("ptrace attach error: %s.", strerror(errno));
return 1;
}
if (is64bit) {
_uint64 mem_data;
_uint64 addr;
//Nlogd("memory search start.");
int offset = -1;
bool isMatch = false;
for (addr = start_maps; addr < end_maps; addr += sizeof(void*)) {
mem_data = ptrace(PTRACE_PEEKTEXT, zygote_pid, (void *)addr, 0);
*((uint32_t *)(map_buf + addr - start_maps)) = mem_data;
if (mem_data == SOUR_CODE_64 && !restore) {
Nlogd("SOUR_CODE_64: 0x%08lX addr: %p.", mem_data, (void *)addr);
offset = (addr - start_maps);
isMatch = true;
} else if (mem_data == HACK_CODE_64 && restore) {
Nlogd("HACK_CODE_64: 0x%08lX addr: %p.", mem_data, (void *)addr);
offset= (addr - start_maps);
isMatch = true;
}
}
Nlogd("memory search finish, offset : %d.", offset);
if (isMatch&& restore) {
_uint64 val = SOUR_CODE_64;
_ret = ptrace(PTRACE_POKETEXT, zygote_pid, (void *)(start_maps + offset), (void *) val);
} else if (isMatch) {
_uint64 val = HACK_CODE_64;
_ret = ptrace(PTRACE_POKETEXT, zygote_pid, (void *)(start_maps + offset), (void *) val);
} else {
_ret = 1;
}
} else if (!is64bit) {
// Supplement this.
}else {
_ret = 1;
}
// if (*global_offset >= 0 && *global_offset <= map_len - sizeof(void*)) {
// if (restore) {
// _uint64 val = SOUR_CODE_64;
// _ret = ptrace(PTRACE_POKETEXT, zygote_pid, (void *)(start_maps + (*global_offset)), (void *) val);
// } else {
// _uint64 val = HACK_CODE_64;
// _ret = ptrace(PTRACE_POKETEXT, zygote_pid, (void *)(start_maps + (*global_offset)), (void *) val);
// }
// } else {
ptrace(PTRACE_DETACH, zygote_pid, nullptr, nullptr);
return _ret;
}
目前只做來64位的修改谒所,32位的還沒有做热康,還不是因為懶!A恿臁=憔!
到這里你應該有很多的問號尖淘,zygote的呢奕锌。。村生。
// test code
static jstring charTojstring(JNIEnv* env, const char* pat) {
jclass strClass = (env)->FindClass("java/lang/String");
jmethodID ctorID = (env)->GetMethodID(strClass, "<init>", "([BLjava/lang/String;)V");
jbyteArray bytes = (env)->NewByteArray(strlen(pat));
(env)->SetByteArrayRegion(bytes, 0, strlen(pat), (jbyte*) pat);
jstring encoding = (env)->NewStringUTF("GB2312");
return (jstring) (env)->NewObject(strClass, ctorID, bytes, encoding);
}
static int connectNopd() {
struct sockaddr_un un;
un.sun_family = AF_UNIX;
strcpy(un.sun_path, "/dev/socket/nopd");
int sock_fd = socket(AF_UNIX, SOCK_STREAM, 0);
int _ret;
if (sock_fd < 0) {
ALOGE("create socket error: %s.", strerror(errno));
return -1;
}
_ret = connect(sock_fd, (struct sockaddr *)&un, sizeof(un));
if (_ret < 0) {
ALOGE("connect socket error: %s.", strerror(errno));
return -1;
}
return sock_fd;
}
static int startHook(char* args) {
int sock_fd = connectNopd();
int _ret;
if (sock_fd != -1) {
int is64Bit = sizeof(&_ret) == 8 ? 1 : 0;
int buffer[4] = {0x1, getpid(), 0x0, is64Bit};
_ret = write(sock_fd, buffer, sizeof(buffer));
if (_ret >= 0) {
char reply[1024];
int _reply_len = read(sock_fd, &reply, sizeof(reply));
_ret = (int) (reply[0] - 48);
ALOGD("_ret: %d.", _ret);
memcpy(args, reply + 2, _reply_len - 2);
close(sock_fd);
return _ret;
}
}
close(sock_fd);
return -1;
}
static int stopHook() {
int sock_fd = connectNopd();
int _ret;
if (sock_fd != -1) {
int is64Bit = sizeof(&_ret) == 8 ? 1 : 0;
int buffer[4] = {0x1, getpid(), 0x1, is64Bit};
_ret = write(sock_fd, buffer, sizeof(buffer));
if (_ret >= 0) {
int reply;
_ret = read(sock_fd, &reply, sizeof(reply));
ALOGD("StopHook zygote ret %d, reply: %d.", _ret, reply);
_ret = reply;
close(sock_fd);
return _ret;
}
}
close(sock_fd);
return -1;
}
static void rebuildProperties(JNIEnv* env) {
char args[1024 - sizeof(int)];
if (startHook(args) == 0) {
jclass jbuildclass = env->FindClass("android/os/Build");
jfieldID jfield;
std::vector<std::string> properties;
std::string str;
char* ch = strtok(args, "&");
while (ch) {
str = ch;
properties.push_back(str);
ch = strtok(NULL, "&");
}
for (auto &str : properties) {
int index = str.find("#");
std::string field = str.substr(0, index);
std::string value = str.substr(index + 1, str.size());
jfield = env->GetStaticFieldID(jbuildclass, field.c_str(), "Ljava/lang/String;");
if (jfield == NULL) {
continue;
}
env->SetStaticObjectField(jbuildclass, jfield, charTojstring(env, value.c_str()));
}
stopHook();
}
}
// Utility routine to fork zygote and specialize the child process.
static pid_t ForkAndSpecializeCommon(JNIEnv* env, uid_t uid, gid_t gid, jintArray javaGids,
jint debug_flags, jobjectArray javaRlimits,
@@ -452,6 +553,11 @@ static pid_t ForkAndSpecializeCommon(JNIEnv* env, uid_t uid, gid_t gid, jintArra
#ifdef ENABLE_SCHED_BOOST
SetForkLoad(true);
#endif
char boot[10];
__system_property_get("sys.boot_completed", boot);
if (strncmp("1", boot, 1) == 0 && sizeof(int *) == 8) {
rebuildProperties(env);
}
......
這時有朋友會問惊暴,為什么不直接把build類屬性的final去掉...沒啥原因,我就是不想來得這么方便趁桃。
好了辽话,完了肄鸽,至于update.prop文件格式,自己作吧油啤。