Chrome的小胡瓜(Courgette)


在Chrome中有一個很有意思的工具courgette,翻譯成中文是小胡瓜的意思。我很難把這個單詞和這個小工具聯繫在一起,也許作者比較偏愛這個蔬菜吧! 

背景

我們用C++編寫程序時,經常會出現修改了一行代碼,重新編譯一下之後,再去對比一下新的二進制文件,就可以發現千差萬別了。往往出現我們要發佈一個修改了一行代碼的補丁,需要替換整個DLL或者EXE。這對於Chrome中類似於Chrome.dll(10M左右)文件來說,升級一次的代價還是挺高的。本人在去年下半年負責的一個升級服務器項目中,就曾動過念頭,是否有一種方法可以尋找出兩個二進制文件的差異,將這個差異打成一個補丁,用戶下載後,可以通過這個補丁,可以自動生成新的DLL。可惜功力不夠,最後放棄了。最近讀Chrome的代碼時,發現Chrome已經實現了該功能。這個就是Courgette的功勞了。 

Courgette做什麼的?

Courgette主要用於Chrome的升級過程,他的主要作用是,針對兩個版本不同的二進制文件(Binary File),尋找其中區別,生成補丁文件;另外就是根據這個補丁文件加上舊版本的二進制文件生成新版本的二進制文件的還原過程了。類似於加解密流程。 

Courgette的實現原理?

Courgette構建在一個開源代碼bsdiff和bspatch 基礎上的。並在bsdiff的基礎上做了一些優化。 

本人是半路出家的人,對編譯原理和彙編瞭解不深,按照我對bsdiff的算法 理解,一個二進制文件裏面,包含了代碼部分(函數,數據),指向這些函數的指針列表(編譯鏈接產生,包含了如何定位函數等信息),由於這些地址是內部的相對地址,即使更改了一小行代碼,重新編譯後,函數的地址將發生變化了,指向這些函數的指針值也全部變化了。因此,即使更改了一個小小的變量,也會導致很多部分的修改。bsdiff的原理就是對二進制文件進行反彙編,將上面所說的兩部分進行分別處理,對於代碼部分,其實就和普通的文本文件類似了,改變不會太大,這部分體積基本上佔去了整個二進制文件的80%左右。然後對動態指針部分進行一些更新處理,就基本上達到了打補丁的目標了。 
    server:
        diff = bsdiff(original, update)
        transmit diff

    client:
        receive diff
        update = bspatch(original, diff) 
大致流程如上,製作補丁時調用bsdiff函數,根據兩個不同版本的二進制文件,生成補丁文件。客戶端下載補丁後,調用bspatch函數,根據舊版本二進制文件和補丁生成新的二進制文件。

Chrome在bsdiff的基礎上做了一些優化,主要體現在動態指針列表部分,chrome對代碼部分的每一個模塊地址分配了一個標籤(Label),這些標籤都是整數,並把這些標籤保存到一個數組中,然後指針列表中映射的地址改爲指向數組的索引,由於一些函數地址被調用多次,通過這種方法確實可以減少一些體積。做了一些優化後,在bsdiff的基礎上又減少了30%左右吧(這是google的說法,我沒驗證過)。
 
    server:
        asm_old = disassemble(original)
        asm_new = disassemble(update)
        asm_new_adjusted = adjust(asm_new, asm_old)
        asm_diff = bsdiff(asm_old, asm_new_adjusted)
        transmit asm_diff

    client:
        receive asm_diff
        asm_old = disassemble(original)
        asm_new_adjusted = bspatch(asm_old, asm_diff)
        update = assemble(asm_new_adjusted)
Chrome的大致處理流程如上,和bsdiff流程類似,多了 asm_new_adjusted = adjust(asm_new, asm_old)這一個步驟,這個步驟主要就是上面說的對地址標籤化的過程。 
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章