Jak nahlédnout do binárních souborů z příkazového řádku Linuxu

Máte tajemný soubor? Příkaz souboru Linux vám rychle řekne, o jaký typ souboru se jedná. Pokud se však jedná o binární soubor, můžete o něm zjistit ještě více. soubor má celou řadu stájových kolegů, kteří vám jej pomohou analyzovat. Ukážeme vám, jak některé z těchto nástrojů používat.

Identifikace typů souborů

Soubory mají obvykle vlastnosti, které softwarovým balíkům umožňují identifikovat, o jaký typ souboru se jedná, a také to, co data v něm představují. Nemělo by smysl pokoušet se otevřít soubor PNG v hudebním přehrávači MP3, takže je užitečné a pragmatické, že soubor s sebou nese nějakou formu ID.

Může to být několik podpisových bajtů na samém začátku souboru. To umožňuje souboru explicitně vyjádřit jeho formát a obsah. Někdy je typ souboru odvozen z charakteristického aspektu vnitřní organizace samotných dat, známého jako architektura souboru.

Některé operační systémy, jako je Windows, se zcela řídí příponou souboru. Můžete to nazvat důvěřivým nebo důvěryhodným, ale systém Windows předpokládá, že jakýkoli soubor s příponou DOCX je ve skutečnosti souborem pro zpracování textu DOCX. Linux takový není, jak brzy uvidíte. Chce to důkaz a podívá se do souboru, aby ho našel.

Zde popsané nástroje již byly nainstalovány v distribucích Manjaro 20, Fedora 21 a Ubuntu 20.04, které jsme použili k průzkumu tohoto článku. Začněme náš průzkum používáním souborový příkaz.

Pomocí souboru Command

V našem aktuálním adresáři máme kolekci různých typů souborů. Jedná se o směs dokumentů, zdrojového kódu, spustitelných souborů a textových souborů.

Příkaz ls nám ukáže, co je v adresáři, a volba -hl (velikosti čitelné pro člověka, dlouhý výpis) nám ukáže velikost každého souboru:

ls -hl

Zkusme soubor na několik z nich a uvidíme, co dostaneme:

file build_instructions.odt
file build_instructions.pdf
file COBOL_Report_Apr60.djvu

Tyto tři formáty souborů jsou správně identifikovány. Pokud je to možné, soubor nám poskytne trochu více informací. Uvádí se, že soubor PDF je v ve formátu verze 1.5.

I když přejmenujeme soubor ODT tak, aby měl příponu s libovolnou hodnotou XYZ, soubor je stále správně identifikován, a to jak v prohlížeči souborů, tak na příkazovém řádku pomocí souboru.

V prohlížeči souborů Files má správnou ikonu. Na příkazovém řádku soubor ignoruje příponu a podívá se do souboru, aby určil jeho typ:

file build_instructions.xyz

Použití souboru na médiích, jako jsou obrazové a hudební soubory, obvykle poskytuje informace týkající se jejich formátu, kódování, rozlišení atd.

file screenshot.png
file screenshot.jpg
file Pachelbel_Canon_In_D.mp3

Zajímavé je, že ani u souborů ve formátu prostého textu soubor neposuzuje soubor podle jeho přípony. Pokud máte například soubor s příponou „.c“, který obsahuje standardní prostý text, ale ne zdrojový kód, soubor jej nezaměňuje za pravý C soubor zdrojového kódu:

file function+headers.h
file makefile
file hello.c

soubor správně identifikuje hlavičkový soubor (.h) jako součást kolekce souborů zdrojového kódu C a ví, že makefile je skript.

  Proč je Poznámkový blok stále skvělý pro psaní poznámek

Použití souboru s binárními soubory

Binární soubory jsou spíše „černou skříňkou“ než jiné. Pomocí příslušného softwarového balíku lze prohlížet obrazové soubory, přehrávat zvukové soubory a otevírat soubory dokumentů. Binární soubory však představují větší výzvu.

Například soubory „hello“ a „wd“ jsou binární spustitelné soubory. Jsou to programy. Soubor s názvem „wd.o“ je objektový soubor. Když je zdrojový kód kompilován kompilátorem, je vytvořen jeden nebo více objektových souborů. Ty obsahují strojový kód, který počítač případně spustí při spuštění hotového programu, spolu s informacemi pro linker. Linker kontroluje každý soubor objektu pro volání funkcí do knihoven. Propojuje je se všemi knihovnami, které program používá. Výsledkem tohoto procesu je spustitelný soubor.

Soubor „watch.exe“ je binární spustitelný soubor, který byl křížově zkompilován pro spuštění v systému Windows:

file wd
file wd.o
file hello
file watch.exe

Vezmeme-li nejprve poslední, soubor nám říká, že soubor „watch.exe“ je spustitelný konzolový program PE32+ pro rodinu procesorů x86 v systému Microsoft Windows. PE je zkratka pro přenosný spustitelný formát, který má 32bitové a 64bitové verze. PE32 je 32bitová verze a PE32+ je 64bitová verze.

Ostatní tři soubory jsou všechny označeny jako Spustitelný a propojitelný formát (ELF) soubory. Toto je standard pro spustitelné soubory a soubory sdílených objektů, jako jsou knihovny. Brzy se podíváme na formát záhlaví ELF.

Co by vás mohlo upoutat je, že dva spustitelné soubory („wd“ a „hello“) jsou identifikovány jako Linux Standard Base (LSB) sdílené objekty a objektový soubor „wd.o“ je identifikován jako LSB přemístitelný. Slovo spustitelný je zřejmé v jeho nepřítomnosti.

Objektové soubory jsou přemístitelné, což znamená, že kód v nich lze načíst do paměti na libovolném místě. Spustitelné soubory jsou uvedeny jako sdílené objekty, protože byly vytvořeny linkerem ze souborů objektů takovým způsobem, že tuto schopnost zdědí.

To umožňuje Randomizace rozložení adresního prostoru (ASMR) k načtení spustitelných souborů do paměti na adresách, které si zvolí. Standardní spustitelné soubory mají ve svých hlavičkách zakódovanou načítací adresu, která určuje, kde se mají načíst do paměti.

ASMR je bezpečnostní technika. Načítání spustitelných souborů do paměti na předvídatelných adresách je činí náchylnými k útoku. Je to proto, že jejich vstupní body a umístění jejich funkcí budou útočníkům vždy známé. Pozice nezávislé spustitelné soubory (PIE) umístěné na náhodné adrese tuto náchylnost překonávají.

  Jak ovládat celý svůj Smarthome prostřednictvím jedné aplikace

Kdybychom sestavit náš program s kompilátorem gcc a poskytnutím volby -no-pie vygenerujeme konvenční spustitelný soubor.

Volba -o (výstupní soubor) nám umožňuje zadat název našeho spustitelného souboru:

gcc -o hello -no-pie hello.c

Použijeme soubor na nový spustitelný soubor a uvidíme, co se změnilo:

file hello

Velikost spustitelného souboru je stejná jako dříve (17 kB):

ls -hl hello

Binární soubor je nyní identifikován jako standardní spustitelný soubor. Děláme to pouze pro demonstrační účely. Pokud kompilujete aplikace tímto způsobem, ztratíte všechny výhody ASMR.

Proč je spustitelný soubor tak velký?

Náš vzorový program hello má 17 KB, takže by se stěží dal nazvat velkým, ale pak je všechno relativní. Zdrojový kód je 120 bajtů:

cat hello.c

Co zvětšuje binární soubor, pokud vše, co dělá, je tisk jednoho řetězce do okna terminálu? Víme, že existuje hlavička ELF, ale ta je u 64bitové binární soustavy dlouhá pouze 64 bajtů. Je jasné, že to musí být něco jiného:

ls -hl hello

Pojďme skenujte binární soubor pomocí strings jako jednoduchý první krok k odhalení toho, co je uvnitř. Dáme to do méně:

strings hello | less

Uvnitř binárního kódu je mnoho řetězců, kromě „Hello, Geek world!“ z našeho zdrojového kódu. Většina z nich jsou štítky pro oblasti v binárním systému a názvy a spojovací informace sdílených objektů. Patří mezi ně knihovny a funkce v rámci těchto knihoven, na kterých závisí binární soubor.

The příkaz ldd ukazuje nám závislosti na sdíleném objektu binárního souboru:

ldd hello

Ve výstupu jsou tři položky a dvě z nich obsahují cestu k adresáři (první nikoli):

linux-vdso.so: Virtuální dynamický sdílený objekt (VDSO) je mechanismus jádra, který umožňuje přístup k sadě rutin v prostoru jádra binárnímu systému v uživatelském prostoru. Tento zabrání režii přepínání kontextu z režimu uživatelského jádra. Sdílené objekty VDSO dodržují formát ELF (Executable and Linkable Format), což umožňuje jejich dynamické propojení s binárním souborem za běhu. VDSO je dynamicky alokováno a využívá ASMR. Schopnost VDSO je poskytována standardem Knihovna GNU C pokud jádro podporuje schéma ASMR.
libc.so.6: The Knihovna GNU C sdílený objekt.
/lib64/ld-linux-x86-64.so.2: Toto je dynamický linker, který chce binární soubor použít. Dynamický linker dotazuje binární soubor, aby zjistil, jaké má závislosti. Spustí tyto sdílené objekty do paměti. Připraví binární soubor ke spuštění a bude schopen najít a přistupovat k závislostem v paměti. Poté spustí program.

Hlavička ELF

Můžeme prozkoumejte a dekódujte hlavičku ELF pomocí obslužného programu readelf a volby -h (záhlaví souboru):

readelf -h hello

Záhlaví je interpretováno za nás.

  Jak hrát Mirror's Edge na Linuxu

První bajt všech binárních souborů ELF je nastaven na hexadecimální hodnotu 0x7F. Další tři bajty jsou nastaveny na 0x45, 0x4C a 0x46. První bajt je příznak, který identifikuje soubor jako binární ELF. Aby to bylo křišťálově jasné, další tři bajty vyhláskují „ELF“. ASCII:

Třída: Označuje, zda je binární soubor 32bitový nebo 64bitový spustitelný soubor (1=32, 2=64).
Data: Označuje endianness při použití. Endian kódování definuje způsob, jakým jsou uložena vícebajtová čísla. V big-endian kódování je číslo uloženo s jeho nejvýznamnějšími bity jako první. V kódování little-endian je číslo uloženo s jeho nejméně významnými bity jako první.
Verze: Verze ELF (aktuálně je to 1).
OS/ABI: Představuje typ binární rozhraní aplikace při použití. Toto definuje rozhraní mezi dvěma binárními moduly, jako je program a sdílená knihovna.
Verze ABI: Verze ABI.
Typ: Typ binárního ELF. Běžné hodnoty jsou ET_REL pro přemístitelný prostředek (jako je soubor objektu), ET_EXEC pro spustitelný soubor zkompilovaný s příznakem -no-pie a ET_DYN pro spustitelný soubor s podporou ASMR.
Stroj: The architektura instrukční sady. Označuje cílovou platformu, pro kterou byl binární soubor vytvořen.
Verze: Vždy nastaveno na 1 pro tuto verzi ELF.
Adresa vstupního bodu: Adresa paměti v binárním systému, na které začíná provádění.

Ostatní položky jsou velikosti a počty oblastí a sekcí v binárním systému, takže lze vypočítat jejich umístění.

Krátký pohled na prvních osm bajtů binárního souboru s hexdumpem zobrazí bajt podpisu a řetězec „ELF“ v prvních čtyřech bytech souboru. Volba -C (kanonická) nám poskytuje ASCII reprezentaci bajtů spolu s jejich hexadecimálními hodnotami a volba -n (číslo) nám umožňuje určit, kolik bajtů chceme vidět:

hexdump -C -n 8 hello

objdump a Granular View

Pokud chcete vidět detaily, můžete použít příkaz objdump s volbou -d (rozložit):

objdump -d hello | less

Tím se rozloží spustitelný strojový kód a zobrazí se v hexadecimálních bajtech vedle ekvivalentu v jazyce symbolických instrukcí. Umístění adresy prvního bye v každém řádku je zobrazeno zcela vlevo.

To je užitečné pouze v případě, že umíte číst jazyk symbolických instrukcí nebo jste zvědaví, co se děje za oponou. Je tu hodně výstupu, takže jsme to zavedli do méně.

Kompilace a propojení

Existuje mnoho způsobů, jak zkompilovat binární soubor. Vývojář se například rozhodne, zda zahrnout informace o ladění. Způsob, jakým je dvojhvězda propojena, také hraje roli v jejím obsahu a velikosti. Pokud binární odkazy sdílejí objekty jako externí závislosti, bude menší než ta, na kterou jsou závislosti staticky propojeny.

Většina vývojářů již zná příkazy, které jsme zde probrali. Pro ostatní však nabízejí několik jednoduchých způsobů, jak se prohrabat a zjistit, co se skrývá uvnitř binární černé skříňky.