基于Android源碼不重啟手機修改Java類Build屬性

改機棵里、改機垮兑、改機...

要重啟...

本套方案是原理是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文件格式,自己作吧油啤。

最后編輯于
?著作權歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末典徘,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子益咬,更是在濱河造成了極大的恐慌逮诲,老刑警劉巖,帶你破解...
    沈念sama閱讀 219,188評論 6 508
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件础废,死亡現(xiàn)場離奇詭異汛骂,居然都是意外死亡,警方通過查閱死者的電腦和手機评腺,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,464評論 3 395
  • 文/潘曉璐 我一進店門帘瞭,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人蒿讥,你說我怎么就攤上這事蝶念。” “怎么了芋绸?”我有些...
    開封第一講書人閱讀 165,562評論 0 356
  • 文/不壞的土叔 我叫張陵媒殉,是天一觀的道長。 經(jīng)常有香客問我摔敛,道長廷蓉,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,893評論 1 295
  • 正文 為了忘掉前任马昙,我火速辦了婚禮桃犬,結果婚禮上,老公的妹妹穿的比我還像新娘行楞。我一直安慰自己攒暇,他們只是感情好,可當我...
    茶點故事閱讀 67,917評論 6 392
  • 文/花漫 我一把揭開白布子房。 她就那樣靜靜地躺著形用,像睡著了一般。 火紅的嫁衣襯著肌膚如雪证杭。 梳的紋絲不亂的頭發(fā)上田度,一...
    開封第一講書人閱讀 51,708評論 1 305
  • 那天,我揣著相機與錄音解愤,去河邊找鬼镇饺。 笑死,一個胖子當著我的面吹牛琢歇,可吹牛的內(nèi)容都是我干的兰怠。 我是一名探鬼主播梦鉴,決...
    沈念sama閱讀 40,430評論 3 420
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼揭保!你這毒婦竟也來了肥橙?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 39,342評論 0 276
  • 序言:老撾萬榮一對情侶失蹤秸侣,失蹤者是張志新(化名)和其女友劉穎存筏,沒想到半個月后,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體味榛,經(jīng)...
    沈念sama閱讀 45,801評論 1 317
  • 正文 獨居荒郊野嶺守林人離奇死亡椭坚,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,976評論 3 337
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了搏色。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片善茎。...
    茶點故事閱讀 40,115評論 1 351
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖频轿,靈堂內(nèi)的尸體忽然破棺而出垂涯,到底是詐尸還是另有隱情,我是刑警寧澤航邢,帶...
    沈念sama閱讀 35,804評論 5 346
  • 正文 年R本政府宣布耕赘,位于F島的核電站,受9級特大地震影響膳殷,放射性物質(zhì)發(fā)生泄漏操骡。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 41,458評論 3 331
  • 文/蒙蒙 一赚窃、第九天 我趴在偏房一處隱蔽的房頂上張望册招。 院中可真熱鬧,春花似錦考榨、人聲如沸跨细。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,008評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至震叙,卻和暖如春掀鹅,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背媒楼。 一陣腳步聲響...
    開封第一講書人閱讀 33,135評論 1 272
  • 我被黑心中介騙來泰國打工乐尊, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人划址。 一個月前我還...
    沈念sama閱讀 48,365評論 3 373
  • 正文 我出身青樓扔嵌,卻偏偏與公主長得像限府,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子痢缎,可洞房花燭夜當晚...
    茶點故事閱讀 45,055評論 2 355

推薦閱讀更多精彩內(nèi)容