當我查看應用程序的機器代碼時,是否可以從生成的機器代碼中辨別出提示和模式,這些提示和模式可以指示使用哪個編譯器(可能是版本)來生成它?
了解用於生成應用程序的編譯器是否可以幫助我更有效地將工程從生成的對象反向轉換為源代碼,如果確實有幫助,怎麼做?
當我查看應用程序的機器代碼時,是否可以從生成的機器代碼中辨別出提示和模式,這些提示和模式可以指示使用哪個編譯器(可能是版本)來生成它?
了解用於生成應用程序的編譯器是否可以幫助我更有效地將工程從生成的對象反向轉換為源代碼,如果確實有幫助,怎麼做?
這方面有一些學術研究,您想要的關鍵字是“工具鏈來源”。 Nate Rosenblum上有一篇關於這個主題的不錯的論文,距離我閱讀本文已有一段時間了,但是您可以使用許多技術來建立此信息。我認為有些人使用機器學習,而另一些人可以使用一大堆關於編譯器行為的啟發式或公理式。
確定此名稱的用途僅限於IMO。在對抗性情況下,當您試圖獲取有關惡意軟件組或威脅參與者的情報時,它可能會很有用,但請記住,此類信息可能會被混淆或破壞。該信息的一種潛在用途是確定使用某些公司的SDK編譯了一些二進制軟件,其中包括具有該公司獨有的簽名信息的編譯器。建立工具鏈來源可以幫助您確定購買SDK的人違反了許可或合同,例如製造惡意軟件。
行為差異的一個例子是參數編寫。有兩種方法可以將值放置到堆棧上,一種使用'push',另一種使用 mov
,其地址基於 esp
作為目標操作數。因此,一個編譯器可以執行以下操作:
push eaxpush ebx
另一個編譯器可以這樣做:
mov [esp + foo],eaxmov [esp + foo + 4],ebx
他們做到了。通常,至少在一些非常有限的測試/觀察中,MSVC做第一個示例,GCC做第二個示例...
在查看機器代碼時,通常會出現一個“痕跡”,除非產生的二進制代碼是如何清除的。例如,如果您使用的工具是 hexedit 代碼>
,您可以在機器代碼中看到包含構建信息的部分:
很顯然,您可以在其中看到,我使用GCC版本4.6.3。其他編譯器將具有其他類型的簽名 Microsoft的“豐富”簽名。
在Recon上有一個名為“包裝遺傳學:自私密碼”的演講,描述了一種解決方法。他們使用一些統計信息從編譯的程序中提取最常見的代碼序列,並用它來檢測解包的結束,但是該方法可以輕鬆地用於識別特定的編譯器。
請參見幻燈片15: http://blog.zynamics.com/2010/07/16/recon-slides-packer-genetics-the-selfish-code-bochspython/
幻燈片似乎被截斷了,我相信實際演示會獲得更多信息。
了解用於生成應用程序的編譯器是否可以幫助我更有效地將生成的對象反向工程到源代碼,如果確實有幫助,怎麼做?
由於以下原因,我認為了解二手編譯器是非常重要的一步:
我想確定編譯器版本之前應該做的第一件事,除非從字面上講是編譯器版本而不是鏈接器版本,否則請檢查可執行文件的PE標頭的“ MajorLinkerVersion”和“ MinorLinkerVersion”字段,是否為EXE。 ,DLL或SYS。參見下面的列表。
次要版本
0x5 0x0(5.0)Borland C ++ / MS Linker 5.0
0x6 0x0(6.0)Microsoft VIsual Studio 6
0x7 0xA(7.10)Microsoft VIsual Studio 2003
0x8 0x0(8.0)Microsoft VIsual Studio 2005
0x9 0x0(9.0)Microsoft VIsual Studio 2008
0xA 0x0(10.0)Microsoft VIsual Studio 2010
0x2 0x15(2.21)MinGw
0x2 0x19(2.0.0.25)Borland Delphi(鏈接器2.0.0.25)
不幸的是,打包程序和保護程序傾向於覆蓋這些值以編寫自己的值和/或加強猜測原始編譯器的過程。
此外,可執行文件的資源目錄也是搜索特定鏈接器信息的好地方。例如具有名為“ DVCLAL”的資源的RT_RCDATA是Borland C ++或Delphi的標誌,在MSVC生成的可執行文件的情況下,“ RT_MANIFEST”可以告訴我們它鏈接到的運行時DLL的特定版本,因此也可以告訴我們編譯器版本。 / p>
此外,“ TimeDateStamp”字段設置為0x2A425E19的可執行文件是使用Delphi構建的標誌。
現在,如果要從彙編代碼確定編譯器,則該標誌的最新MSVC編譯器版本看到在入口處生成堆棧cookie的函數。
似乎,入口處有一條JMP指令,後跟字符串“ fb:C ++ Hook” Borland C ++的標誌,等等。
了解用於生成應用程序的編譯器是否可以幫助我更有效地將生成的對象反向工程到源代碼,如果確實有幫助,怎麼做?
是的,應該會有所幫助。
甚至更好:
該想法是:
為許多不同的案例(小的小程序)構建測試案例,以展示不同的結構並進行編譯;
查看生成的機器代碼(注意模式)。
其中許多情況可以在編譯器的主要版本( if
和其他控制結構,基本語言函數等)上進行概括。
對於同一程序,可能有一些特定於編譯器的優化會大相徑庭。
(我想知道是否有用於comm的測試用例庫開/有用的案例,以幫助對特定編譯器生成的機器代碼進行逆向工程。)
如果僅談論機器代碼(或彙編代碼),則沒有太多信息。大多數現代編譯器會產生類似的輸出,否則輸出將不足以看到差異。可能表明問題的一件事是編譯器優化,這是我沒有經驗的,應該由其他人來介紹。如果您確實擁有整個ELF文件,並且有可用的符號,則可以根據哪種類型得出結論。庫是鏈接的(例如, libgcc
將是贈品)或編譯器特定功能的名稱。如果ELF包含調試信息,您甚至可能會看到類似“ GCC:(Ubuntu / Linaro 4.6.3-1ubuntu5)4.6.3”的信息。如果您正在使用C ++代碼,則符號名稱修飾可以將其放棄。
但是,正如您問自己的那樣,我很好奇為什麼需要此信息。我不知道知道使它起作用的編譯器將為您帶來多少幫助。我在ARM上進行了更多工作,並且在該平台上知道,編譯器/彙編代碼必須遵守一個應用程序二進制接口。該ABI提供有關如何調用函數,應將哪些寄存器用作什麼的信息,等等。我知道,對於沒有嚴格ABI的平台,操作系統通常會向開發人員提供有關此類主題的信息。無論如何,編譯器都應創建兼容的代碼,因此我不知道用於標識創建代碼的編譯器有什麼用。