如何從 Linux 命令行查看二進製文件

已發表: 2022-01-29
一個程式化的 Linux 終端,在筆記本電腦上帶有綠色文本行。
fatmawati achmad zaenuri/Shutterstock

有神秘檔案嗎? Linux file命令會很快告訴你它是什麼類型的文件。 但是,如果它是一個二進製文件,您可以找到更多關於它的信息。 file有一大堆穩定的伙伴,可以幫助你分析它。 我們將向您展示如何使用其中一些工具。

識別文件類型

文件通常具有允許軟件包識別它是哪種類型的文件以及其中的數據代表什麼的特徵。 嘗試在 MP3 音樂播放器中打開 PNG 文件是沒有意義的,因此文件帶有某種形式的 ID 既有用又實用。

這可能是文件開頭的幾個簽名字節。 這允許文件明確其格式和內容。 有時,文件類型是從數據本身的內部組織的一個獨特方面推斷出來的,稱為文件架構。

某些操作系統(如 Windows)完全由文件擴展名引導。 您可以稱其為輕信或信任,但 Windows 假定任何具有 DOCX 擴展名的文件確實是 DOCX 文字處理文件。 Linux 不是那樣的,你很快就會看到。 它需要證據並在文件中查找以找到它。

此處描述的工具已經安裝在我們用於研究本文的 Manjaro 20、Fedora 21 和 Ubuntu 20.04 發行版上。 讓我們使用file命令開始我們的調查。

使用文件命令

我們在當前目錄中有一組不同的文件類型。 它們是文檔、源代碼、可執行文件和文本文件的混合體。

廣告

ls命令將向我們顯示目錄中的內容,而-hl (人類可讀大小,長列表)選項將向我們顯示每個文件的大小:

 ls -hl 

讓我們嘗試其中一些file ,看看我們得到了什麼:

 文件 build_instructions.odt
 文件 build_instructions.pdf
 文件 COBOL_Report_Apr60.djvu 

正確識別了這三種文件格式。 在可能的情況下, file為我們提供了更多信息。 該 PDF 文件被報告為 1.5 版格式。

即使我們將 ODT 文件重命名為具有任意值 XYZ 的擴展名,該文件仍然可以正確識別,無論是在Files文件瀏覽器中還是在使用file的命令行上。

在文件文件瀏覽器中正確識別 OpenDocument 文件,即使其擴展名是 XYZ。

Files文件瀏覽器中,它被賦予了正確的圖標。 在命令行上, file忽略擴展名並在文件內部查看以確定其類型:

 文件 build_instructions.xyz 

廣告

在媒體上使用file ,例如圖像和音樂文件,通常會產生有關其格式、編碼、分辨率等的信息:

 文件截圖.png
 文件截圖.jpg
 文件 Pachelbel_Canon_In_D.mp3 

有趣的是,即使是純文本文件, file也不會通過擴展名來判斷文件。 例如,如果您有一個擴展名為“.c”的文件,其中包含標準純文本但不包含源代碼,則file不會將其誤認為是真正的 C 源代碼文件:

 文件函數+headers.h
 文件生成文件
文件你好.c 

file正確地將頭文件(“.h”)標識為 C 源代碼文件集合的一部分,並且它知道 makefile 是一個腳本。

將文件與二進製文件一起使用

二進製文件比其他文件更像是一個“黑匣子”。 可以查看圖像文件,可以播放聲音文件,可以通過相應的軟件包打開文檔文件。 但是,二進製文件更具挑戰性。

例如,文件“hello”和“wd”是二進制可執行文件。 它們是程序。 名為“wd.o”的文件是一個目標文件。 當編譯器編譯源代碼時,會創建一個或多個目標文件。 這些包含計算機最終將在完成的程序運行時執行的機器代碼,以及鏈接器的信息。 鏈接器檢查每個目標文件是否有對庫的函數調用。 它將它們鏈接到程序使用的任何庫。 這個過程的結果是一個可執行文件。

文件“watch.exe”是一個二進制可執行文件,已被交叉編譯以在 Windows 上運行:

 文件 wd
 文件 wd.o
 文件你好
文件 watch.exe 

廣告

先看最後一個, file告訴我們“watch.exe”文件是一個 PE32+ 可執行的控制台程序,用於 Microsoft Windows 上的 x86 系列處理器。 PE 代表可移植的可執行格式,它有 32 位和 64 位版本。 PE32 是 32 位版本,PE32+ 是 64 位版本。

其他三個文件都被標識為可執行和可鏈接格式 (ELF) 文件。 這是可執行文件和共享對象文件(例如庫)的標準。 我們很快就會看一下 ELF 標頭格式。

可能引起您注意的是這兩個可執行文件(“wd”和“hello”)被標識為 Linux 標準基礎 (LSB) 共享對象,而對象文件“wd.o”被標識為 LSB 可重定位文件。 可執行這個詞在沒有的情況下是顯而易見的。

目標文件是可重定位的,這意味著其中的代碼可以在任何位置加載到內存中。 可執行文件被列為共享對象,因為它們是由鏈接器從對象文件以繼承此功能的方式創建的。

這允許地址空間佈局隨機化 (ASMR) 系統將可執行文件加載到其選擇的地址處的內存中。 標準可執行文件在其標頭中編碼了一個加載地址,該地址指示它們被加載到內存中的位置。

ASMR 是一種安全技術。 將可執行文件加載到可預測地址的內存中會使它們容易受到攻擊。 這是因為攻擊者始終知道它們的入口點及其功能的位置。 位於隨機地址的位置無關可執行文件 (PIE) 克服了這種敏感性。

廣告

如果我們使用gcc編譯器編譯我們的程序並提供-no-pie選項,我們將生成一個常規的可執行文件。

-o (輸出文件)選項讓我們為我們的可執行文件提供一個名稱:

 gcc -o hello -no-pie hello.c

我們將在新的可執行文件上使用file並查看發生了什麼變化:

 文件你好

可執行文件的大小與之前相同(17 KB):

 ls -hl 你好

該二進製文件現在被標識為標準可執行文件。 我們這樣做只是為了演示目的。 如果您以這種方式編譯應用程序,您將失去 ASMR 的所有優勢。

為什麼可執行文件這麼大?

我們的示例hello程序為 17 KB,因此很難稱其為大,但是,一切都是相對的。 源代碼為 120 字節:

 貓你好.c
廣告

如果它所做的只是將一個字符串打印到終端窗口,那麼會增加二進製文件的大小? 我們知道有一個 ELF 標頭,但對於 64 位二進製文件來說,它只有 64 字節長。 顯然,它必須是別的東西:

 ls -hl 你好

讓我們使用strings命令掃描二進製文件作為簡單的第一步,以發現其中的內容。 我們將它輸入到less中:

 字符串你好 | 較少的

除了“Hello, Geek world!”之外,二進製文件中還有很多字符串。 從我們的源代碼。 其中大部分是二進製文件中區域的標籤,以及共享對象的名稱和鏈接信息。 這些包括庫和這些庫中的函數,二進製文件依賴於這些庫。

ldd命令向我們展示了二進製文件的共享對象依賴關係:

 你好

輸出中有三個條目,其中兩個包含目錄路徑(第一個沒有):

  • linux-vdso.so:虛擬動態共享對象(VDSO)是一種內核機制,允許用戶空間二進製文件訪問一組內核空間例程。 這避免了從用戶內核模式進行上下文切換的開銷。 VDSO 共享對象遵循可執行和可鏈接格式 (ELF) 格式,允許它們在運行時動態鏈接到二進製文件。 VDSO 是動態分配的,並利用了 ASMR。 如果內核支持 ASMR 方案,則 VDSO 功能由標準 GNU C 庫提供。
  • libc.so.6: GNU C 庫共享對象。
  • /lib64/ld-linux-x86-64.so.2:這是二進製文件要使用的動態鏈接器。 動態鏈接器詢問二進製文件以發現它具有哪些依賴項。 它將這些共享對象啟動到內存中。 它準備二進製文件以運行並能夠在內存中查找和訪問依賴項。 然後,它啟動程序。

ELF 標頭

我們可以使用readelf實用程序和-h (文件頭)選項檢查和解碼 ELF 頭:

 readelf -h 你好

標題是為我們解釋的。

廣告

所有 ELF 二進製文件的第一個字節設置為十六進制值 0x7F。 接下來的三個字節設置為 0x45、0x4C 和 0x46。 第一個字節是一個標誌,將文件標識為 ELF 二進製文件。 為了清楚起見,接下來的三個字節用 ASCII 拼出“ELF”:

  • 類:指示二進製文件是 32 位還是 64 位可執行文件(1=32、2=64)。
  • 數據:表示使用的字節序。 Endian 編碼定義了存儲多字節數字的方式。 在大端編碼中,數字首先存儲其最高有效位。 在 little-endian 編碼中,數字首先存儲其最低有效位。
  • 版本: ELF 的版本(目前為 1)。
  • OS/ABI:表示正在使用的應用程序二進制接口的類型。 這定義了兩個二進制模塊之間的接口,例如程序和共享庫。
  • ABI 版本:ABI的版本。
  • 類型: ELF 二進製文件的類型。 常見的值是ET_REL表示可重定位資源(例如目標文件), ET_EXEC表示使用-no-pie標誌編譯的可執行文件, ET_DYN表示支持 ASMR 的可執行文件。
  • 機器:指令集架構。 這表示為其創建二進製文件的目標平台。
  • 版本:對於此版本的 ELF,始終設置為 1。
  • 入口點地址:開始執行的二進製文件中的內存地址。

其他條目是二進製文件中區域和部分的大小和數量,因此可以計算它們的位置。

使用hexdump快速查看二進製文件的前八個字節將在文件的前四個字節中顯示簽名字節和“ELF”字符串。 -C (規範)選項為我們提供了字節的 ASCII 表示及其十六進制值,而-n (數字)選項讓我們指定要查看的字節數:

 hexdump -C -n 8 你好

objdump 和粒度視圖

如果您想查看詳細信息,可以使用帶有-d (反彙編)選項的objdump命令:

 objdump -d 你好 | 較少的

這會反彙編可執行的機器代碼,並以十六進製字節顯示它,並與等效的彙編語言一起顯示。 每行中第一個 bye 的地址位置顯示在最左側。

這僅在您可以閱讀彙編語言或者您對幕後發生的事情感到好奇時才有用。 有很多輸出,所以我們將其輸入到less中。

編譯和鏈接

有很多方法可以編譯二進製文件。 例如,開發者選擇是否包含調試信息。 二進製文件的鏈接方式也影響其內容和大小。 如果二進制引用共享對像作為外部依賴項,它將小於依賴項靜態鏈接的對象。

廣告

大多數開發人員已經知道我們在這裡介紹的命令。 不過,對於其他人來說,它們提供了一些簡單的方法來翻找並查看二進制黑匣子中的內容。