美文网首页
基于Android源码不重启手机修改Java类Build属性

基于Android源码不重启手机修改Java类Build属性

作者: 学海摆渡人 | 来源:发表于2020-04-29 12:29 被阅读0次

改机、改机、改机...

要重启...

本套方案是原理是hook libart.so,当需要更新新属性时,将art虚拟机检测Field属性是否是final标志时,将逻辑改成非final。 当然其次时需要更新build属性时进行修改,当Zygote准备拉起新的子进程时,通过Unix domain socket 方式告知自行编写的native守护程序,就叫它nopd吧。当nopd收到收到消息时,需要根据配置文件的内容进行判断是否需要更改build属性,如果需要则先解析build更新属性文件/data/local/update.prop。如果属性数量大于0的时候,则将更新的属性Field名称和新值发送给zygote,然后zygote 通过jni反射机制修改Java Field内容,完成之后需要通知nopd,让它清理配置文件,如果需要更新,就需将内容写到/data/local/update.prop 即可。重启App就能直接读取到新值了。

需要注意的是,这个只能针对虚拟机,并不能影响properties_service全局的值,如果需要请参考我另一篇文章:动态修改ro属性https://www.jianshu.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;
    }

首先创建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);
                    // 后面追加要更新的内容: 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();
                }
            }

根据标识判断是否是进行zygote修改,重要的zygote_modify这个函数的实现。

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位的还没有做,还不是因为懒!!!!

到这里你应该有很多的问号,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文件格式,自己作吧。

相关文章

网友评论

      本文标题:基于Android源码不重启手机修改Java类Build属性

      本文链接:https://www.haomeiwen.com/subject/jejgwhtx.html