軟體工程/工具/反編譯器簡介
反編譯器是一個計算機程式,它執行與編譯器相反的操作。也就是說,它將包含相對較低抽象級別資訊的程式碼檔案(通常設計為計算機可讀而不是人類可讀)轉換為具有較高抽象級別資訊的程式碼檔案(通常設計為人類可讀)。
術語反編譯器通常指將可執行程式(編譯器輸出)轉換為(相對)高階語言的原始碼的程式,該原始碼在編譯後將生成一個與原始可執行程式行為相同的可執行檔案。相比之下,反彙編器將可執行程式轉換為組合語言(彙編器可以將它重新組合成可執行程式)。
反編譯是使用反編譯器的行為,儘管該術語在用作名詞時也可以指反編譯器的輸出。它可以用來恢復丟失的原始碼,並且在某些情況下也對計算機安全、互操作性和錯誤更正很有用。[1] 反編譯的成功取決於被反編譯的程式碼中存在的資訊量以及對其執行的分析的複雜程度。許多虛擬機器(例如 Java 虛擬機器或 .NET Framework 公共語言執行時)使用的位元組碼格式通常包含廣泛的元資料和高階功能,使得反編譯變得相當可行。除錯資料的存在可以使重現原始變數和結構名稱甚至行號成為可能。沒有這種元資料或除錯資料的機器語言更難反編譯。[2]
一些編譯器和編譯後工具生成混淆程式碼(也就是說,它們試圖生成難以反編譯的輸出)。這樣做是為了更難反向工程可執行檔案。
反編譯器可以被認為是由一系列階段組成的,每個階段都貢獻了整體反編譯過程的特定方面。
第一個反編譯階段載入並解析輸入機器程式碼或中間語言程式的二進位制檔案格式。它應該能夠發現關於輸入程式的基本事實,例如體系結構(奔騰、PowerPC 等)和入口點。在許多情況下,它應該能夠找到 C 程式的main函式的等效項,它是使用者編寫程式碼的開始。這排除了執行時初始化程式碼,如果可能,不應該反編譯它。如果可用,符號表和除錯資料也會被載入。前端可能能夠識別使用的庫,即使它們與程式碼連結在一起,這將提供庫介面。如果它能夠確定使用的編譯器或編譯器,它可能會提供有用的資訊來識別程式碼習語。[3]
下一個邏輯階段是將機器程式碼指令反彙編為機器無關的中間表示(IR)。例如,奔騰機器指令
mov eax, [ebx+0x04]
可以被翻譯成 IR
eax := m[ebx+4];
慣用機器程式碼序列是一系列程式碼,其組合語義不能立即從指令的個體語義中顯而易見。作為反彙編階段的一部分,或者作為後期分析的一部分,這些慣用序列需要被翻譯成已知的等效 IR。例如,x86 彙編程式碼
cdq eax ; edx is set to the sign-extension of eax xor eax, edx sub eax, edx
可以被翻譯成
eax := abs(eax);
一些慣用序列是機器無關的;有些只涉及一條指令。例如,xor eax, eax 清除eax暫存器(將其設定為零)。這可以用機器無關的簡化規則來實現,例如a xor a = 0。
通常,最好將慣用序列的檢測儘可能地延遲到後面的階段,這些階段受指令排序的影響較小。例如,編譯器的指令排程階段可能會在慣用序列中插入其他指令,或更改序列中指令的順序。反彙編階段的模式匹配過程可能無法識別已更改的模式。後面的階段將指令表示式組合成更復雜的表示式,並將它們修改成規範(標準化)形式,這使得即使已更改的習語也更有可能在反編譯過程的後期階段匹配更高級別的模式。
識別子程式呼叫、異常處理和 switch 語句的編譯器習語尤其重要。一些語言還廣泛支援字串或長整數。
各種程式分析可以應用於 IR。特別是,表示式傳播將幾個指令的語義組合成更復雜的表示式。例如,
mov eax,[ebx+0x04] add eax,[ebx+0x08] sub [ebx+0x0C],eax
在表示式傳播之後可能會產生以下 IR
m[ebx+12] := m[ebx+12] - (m[ebx+4] + m[ebx+8]);
生成的表示式更像高階語言,並且還消除了機器暫存器eax的使用。後面的分析可能會消除ebx暫存器。
必須使用資料流分析來跟蹤暫存器內容被定義和使用的位置。相同的分析可以應用於用於臨時變數和區域性資料的記憶體位置。然後,可以為每個這樣的連線的定義和使用集形成一個不同的名稱。有可能同一個區域性變數記憶體位置在原始程式的不同部分被用於多個變數。更糟糕的是,資料流分析可能會識別出一條路徑,透過該路徑,值可能在兩個這樣的使用之間流動,即使它在現實中實際上永遠不會發生或重要。這可能會在糟糕的情況下導致需要將記憶體位置定義為型別的並集。反編譯器可以允許使用者顯式地打破這種不自然的依賴關係,這將導致更清晰的程式碼。當然,這意味著一個變數可能在沒有初始化的情況下被使用,因此表明原始程式中存在問題。
一個好的機器碼反編譯器會執行型別分析。在這裡,暫存器或記憶體位置的使用方式會對該位置的可能型別產生約束。例如,一個and指令意味著運算元是一個整數;程式不會對浮點數(特殊庫程式碼除外)或指標執行此類操作。一個add指令會產生三個約束,因為運算元可以是兩個整數,或者一個整數和一個指標(分別具有整數和指標結果;第三個約束來自當型別不同時兩個運算元的排序)。[4]
可以識別各種高階表示式,這些表示式會觸發結構或陣列的識別。然而,由於機器碼甚至一些高階語言(如 C)允許使用強制轉換和指標運算,因此很難區分許多可能性。
上一節中的示例可能導致以下高階程式碼
struct T1 *ebx;
struct T1 {
int v0004;
int v0008;
int v000C;
};
ebx->v000C -= ebx->v0004 + ebx->v0008;
倒數第二階段的反編譯涉及將 IR 結構化為更高級別的結構,例如while迴圈和if/then/else條件語句。例如,機器碼
xor eax, eax l0002: or ebx, ebx jge l0003 add eax,[ebx] mov ebx,[ebx+0x4] jmp l0002 l0003: mov [0x10040000],eax
可以翻譯成
eax = 0;
while (ebx < 0) {
eax += ebx->v0000;
ebx = ebx->v0004;
}
v10040000 = eax;
無結構程式碼比已結構化程式碼更難翻譯成結構化程式碼。解決方案包括複製一些程式碼或添加布爾變數。[5]
最後階段是在反編譯器後端生成高階程式碼。正如編譯器可能有多個後端用於為不同的體系結構生成機器碼一樣,反編譯器也可能有多個後端用於以不同的高階語言生成高階程式碼。
在程式碼生成之前,可能需要允許以互動方式編輯 IR,例如使用某種形式的圖形使用者介面。這將允許使用者輸入註釋以及非通用變數和函式名稱。但是,這些幾乎可以像在反編譯後的編輯中一樣容易地輸入。使用者可能希望更改結構方面,例如將while迴圈轉換為for迴圈。使用簡單的文字編輯器不太容易修改這些內容,儘管原始碼重構工具可以幫助完成此過程。使用者可能需要輸入在型別分析階段未識別的資訊,例如將記憶體表示式修改為陣列或結構表示式。最後,可能需要更正不正確的 IR 或進行更改以使輸出程式碼更易讀。
大多數計算機程式受版權法保護。儘管版權保護的具體範圍因地區而異,但版權法通常賦予作者(程式設計師或僱主)對程式的一系列專有權利。[6] 這些權利包括複製的權利,包括複製到計算機的 RAM 中的副本[需要引用]。由於反編譯過程涉及製作多個此類副本,因此在未經版權持有者授權的情況下,通常禁止進行反編譯。但是,由於反編譯通常是實現軟體互操作性的必要步驟,因此美國和歐洲的版權法在一定程度上允許反編譯。
在美國,版權合理使用抗辯在反編譯案件中已成功使用。例如,在Sega v. Accolade中,法院裁定 Accolade 可以合法地進行反編譯以繞過 Sega 遊戲機使用的軟體鎖定機制。[7]
在歐洲,1991 年軟體指令明確規定了為了實現互操作性而進行反編譯的權利。在軟體保護主義者與學者和獨立軟體開發人員之間進行的激烈辯論的結果中,第 6 條僅在滿足一定條件的情況下才允許反編譯
- 首先,個人或實體必須有權使用要反編譯的程式。
- 其次,反編譯必須是實現與目標程式或其他程式互操作性的必要條件。因此,互操作性資訊不應輕易獲得,例如透過手冊或 API 文件。這是一個重要的限制。反編譯器必須證明必要性。這一重要限制的主要目的是鼓勵開發人員記錄和公開其產品互操作性資訊。[8]
- 第三,反編譯過程必須儘可能地限制在與互操作性相關的目標程式的部分。由於反編譯的目的之一是瞭解程式結構,因此第三個限制可能難以滿足。同樣,證明責任在於反編譯器。
此外,第 6 條規定,透過反編譯獲得的資訊不得用於其他目的,也不得提供給其他人。
總的來說,第 6 條提供的反編譯權將軟體行業中據稱的慣例法典化。很少有歐洲訴訟是從反編譯權中產生的。這可以解釋為以下兩種情況之一:1)反編譯權使用不頻繁,因此反編譯權可能沒有必要,或者 2)反編譯權運作良好,並提供足夠的法律確定性,不會引起法律糾紛。在關於歐洲成員國實施軟體指令的最新報告中,歐盟委員會似乎支援第二種解釋。
- ↑ "Why Decompilation". Program-transformation.org. 2005-04-11. Retrieved 2010-09-15.
- ↑ Miecznikowski, Jerome (2002). "Decompiling Java Bytecode: Problems, Traps and Pitfalls". In R Nigel Horspool (ed.). Compiler Construction: 11th International Conference, proceedings / CC 2002. Springer-Verlag. pp. 111–127. ISBN 3-540-43369-4.
{{cite book}}:|first2=missing|last2=(help); Unknown parameter|loast2=ignored (help) - ↑ Cifuentes, Cristina; Gough, K. John (1995). "二進位制程式的反編譯". 軟體實踐與經驗. 25 (7): 811–829.
{{cite journal}}: 未知引數|month=被忽略 (幫助) - ↑ Mycroft, Alan (1999). "基於型別的反編譯". 在 S. Doaitse Swierstra (編). 程式語言和系統:第八屆歐洲程式語言和系統研討會. Springer-Verlag. pp. 208–223. ISBN 3-540-65699-5.
- ↑ C. Cifuentes. 反編譯技術. 博士論文,昆士蘭科技大學,1994.(可作為 壓縮的 Postscript 第 6 章 獲取)
- ↑ Rowland, Diane (2005). 資訊科技法 (第 3 版). Cavendish. ISBN 1-85941-756-6.
- ↑ "反編譯的合法性". Program-transformation.org. 2004-12-03. 檢索於 2010-09-15.
- ↑ B. Czarnota 和 R.J. Hart,歐洲計算機程式的法律保護:EC 指令指南. 1991,倫敦:Butterworths。