Heeft u een mysteriebestand? Het Linux file
commando zal u snel vertellen welk type bestand het is. Als het echter een binair bestand is, kunt u er nog meer over te weten komen. file
heeft een hele reeks stalgenoten die u zullen helpen het te analyseren. We laten u zien hoe u enkele van deze tools kunt gebruiken.
Identificatie van bestandstypen
Bestanden hebben meestal kenmerken waarmee softwarepakketten kunnen identificeren welk type bestand het is, en wat de gegevens erin vertegenwoordigen. Het zou niet logisch zijn om te proberen een PNG-bestand te openen in een mp3-muziekspeler, dus het is zowel handig als pragmatisch dat een bestand een of andere vorm van ID met zich meedraagt.
Dit kunnen enkele handtekeningbytes zijn aan het begin van het bestand. Hierdoor kan een bestand expliciet zijn over zijn formaat en inhoud. Soms wordt het bestandstype afgeleid uit een onderscheidend aspect van de interne organisatie van de gegevens zelf, bekend als de bestandsarchitectuur.
Sommige besturingssystemen, zoals Windows, worden volledig gestuurd door de bestandsextensie. Je kunt het goedgelovig of vertrouwend noemen, maar Windows gaat ervan uit dat elk bestand met de DOCX-extensie echt een DOCX-tekstverwerkingsbestand is. Linux is niet zo, zoals je snel zult zien. Het wil bewijs en kijkt in het bestand om het te vinden.
De tools die hier worden beschreven, waren al geïnstalleerd op de Manjaro 20-, Fedora 21- en Ubuntu 20.04-distributies die we hebben gebruikt om dit artikel te onderzoeken. Laten we ons onderzoek beginnen door de file
opdracht.
Gebruik het bestand Command
We hebben een verzameling verschillende bestandstypen in onze huidige directory. Ze zijn een combinatie van document-, broncode-, uitvoerbare bestanden en tekstbestanden.
De ls
commando laat ons zien wat er in de directory staat, en het -hl
(door mensen leesbare formaten, lange lijst) optie toont ons de grootte van elk bestand:
ls -hl
Laten we proberen file
op een paar hiervan en kijk wat we krijgen:
file build_instructions.odt
file build_instructions.pdf
file COBOL_Report_Apr60.djvu
De drie bestandsindelingen zijn correct geïdentificeerd. Waar mogelijk, file
geeft ons wat meer informatie. Het PDF-bestand zou in de indeling van versie 1.5 zijn.
Zelfs als we het ODT-bestand hernoemen zodat het een extensie heeft met de willekeurige waarde van XYZ, wordt het bestand nog steeds correct geïdentificeerd, beide binnen de Files
bestandsbrowser en op de opdrachtregel met file
.
Binnen de Files
bestandsbrowser, krijgt deze het juiste pictogram. Op de opdrachtregel, file
negeert de extensie en kijkt in het bestand om het type te bepalen:
file build_instructions.xyz
Gebruik makend van file
op media, zoals beeld- en muziekbestanden, levert meestal informatie op over hun formaat, codering, resolutie, enzovoort:
file screenshot.png
file screenshot.jpg
file Pachelbel_Canon_In_D.mp3
Interessant is dat zelfs met platte tekstbestanden, file
beoordeelt het bestand niet op basis van de extensie. Als u bijvoorbeeld een bestand heeft met de extensie “.c”, dat standaard platte tekst bevat maar geen broncode, file
verwart het niet met een echt C-broncodebestand:
file function+headers.h
file makefile
file hello.c
file
identificeert het header-bestand (“.h”) correct als onderdeel van een C-broncodeverzameling van bestanden, en het weet dat het makefile een script is.
Gebruik een bestand met binaire bestanden
Binaire bestanden zijn meer een “zwarte doos” dan andere. Afbeeldingsbestanden kunnen worden bekeken, geluidsbestanden kunnen worden afgespeeld en documentbestanden kunnen worden geopend met het juiste softwarepakket. Binaire bestanden zijn echter een grotere uitdaging.
De bestanden “hallo” en “wd” zijn bijvoorbeeld binaire uitvoerbare bestanden. Het zijn programma’s. Het bestand met de naam “wd.o” is een objectbestand. Wanneer de broncode wordt gecompileerd door een compiler, worden een of meer objectbestanden gemaakt. Deze bevatten de machinecode die de computer uiteindelijk zal uitvoeren wanneer het voltooide programma wordt uitgevoerd, samen met informatie voor de linker. De linker controleert elk objectbestand op functieaanroepen naar bibliotheken. Het koppelt ze aan alle bibliotheken die het programma gebruikt. Het resultaat van dit proces is een uitvoerbaar bestand.
Het bestand “watch.exe” is een binair uitvoerbaar bestand dat is gecompileerd om op Windows te draaien:
file wd
file wd.o
file hello
file watch.exe
Eerst de laatste nemen, file
vertelt ons dat het “watch.exe” -bestand een PE32 + uitvoerbaar consoleprogramma is voor de x86-processorfamilie op Microsoft Windows. PE staat voor portable executable format, dat 32- en 64-bit versies heeft. De PE32 is de 32-bits versie en de PE32 + is de 64-bits versie.
De andere drie bestanden worden allemaal geïdentificeerd als Executable and Linkable Format (ELF) -bestanden. Dit is een standaard voor uitvoerbare bestanden en gedeelde objectbestanden, zoals bibliotheken. We zullen binnenkort het ELF-headerformaat bekijken.
Wat misschien opvalt, is dat de twee uitvoerbare bestanden (“wd” en “hallo”) worden geïdentificeerd als gedeelde Linux Standard Base (LSB) -objecten, en het objectbestand “wd.o” wordt geïdentificeerd als een LSB-verplaatsbaar. Het woord uitvoerbaar is duidelijk in zijn afwezigheid.
Objectbestanden zijn verplaatsbaar, wat betekent dat de code erin op elke locatie in het geheugen kan worden geladen. De uitvoerbare bestanden worden weergegeven als gedeelde objecten omdat ze zo door de linker van de objectbestanden zijn gemaakt dat ze deze mogelijkheid erven.
Hierdoor kan het Address Space Layout Randomization (ASMR) -systeem de uitvoerbare bestanden in het geheugen laden op adressen naar keuze. Standaard uitvoerbare bestanden hebben een laadadres gecodeerd in hun headers, die dicteren waar ze in het geheugen worden geladen.
ASMR is een beveiligingstechniek. Door uitvoerbare bestanden op voorspelbare adressen in het geheugen te laden, worden ze vatbaarder voor aanvallen. Dit komt doordat hun toegangspunten en de locaties van hun functies altijd bekend zijn bij aanvallers. Positie Independent Executables (PIE) die op een willekeurig adres zijn gepositioneerd, overwinnen deze gevoeligheid.
Als we ons programma compileren met de gcc
compiler en geef het -no-pie
optie, zullen we een conventioneel uitvoerbaar bestand genereren.
De -o
(uitvoerbestand) optie laat ons een naam opgeven voor ons uitvoerbare bestand:
gcc -o hello -no-pie hello.c
We zullen gebruiken file
op het nieuwe uitvoerbare bestand en kijk wat er is veranderd:
file hello
De grootte van het uitvoerbare bestand is hetzelfde als voorheen (17 KB):
ls -hl hello
Het binaire bestand wordt nu geïdentificeerd als een standaard uitvoerbaar bestand. We doen dit alleen voor demonstratiedoeleinden. Als je op deze manier applicaties compileert, verlies je alle voordelen van de ASMR.
Waarom is een uitvoerbaar bestand zo groot?
Ons voorbeeld hello
programma is 17 KB, dus het kan nauwelijks groot worden genoemd, maar alles is relatief. De broncode is 120 bytes:
cat hello.c
Wat bulkt het binaire bestand als het alleen maar één string naar het terminalvenster afdrukt? We weten dat er een ELF-header is, maar dat is slechts 64 bytes lang voor een 64-bits binair bestand. Het moet duidelijk iets anders zijn:
ls -hl hello
Laten we het binaire bestand scannen met de strings
commando als een eenvoudige eerste stap om te ontdekken wat erin zit. We spuiten het erin less
:
strings hello | less
Er zijn veel strings in het binaire bestand, naast de “Hallo, Geek-wereld!” uit onze broncode. De meeste zijn labels voor regio’s binnen het binaire bestand en de namen en koppelingsinformatie van gedeelde objecten. Deze omvatten de bibliotheken en functies binnen die bibliotheken, waarvan het binaire bestand afhankelijk is.
De ldd
commando toont ons de gedeelde objectafhankelijkheden van een binair bestand:
ldd hello
Er zijn drie items in de uitvoer en twee ervan bevatten een directorypad (de eerste niet):
- linux-vdso.so: Virtual Dynamic Shared Object (VDSO) is een kernelmechanisme waarmee een set kernelruimte-routines toegankelijk is voor een binaire gebruikersruimte. Dit vermijdt de overhead van een contextwisseling vanuit de gebruikerskernelmodus. VDSO-gedeelde objecten voldoen aan het Executable and Linkable Format (ELF) -formaat, waardoor ze tijdens runtime dynamisch kunnen worden gekoppeld aan het binaire bestand. De VDSO wordt dynamisch toegewezen en maakt gebruik van ASMR. De VDSO-mogelijkheid wordt geleverd door de standaard GNU C-bibliotheek als de kernel het ASMR-schema ondersteunt.
- libc.so.6: Het gedeelde object van de GNU C-bibliotheek.
- /lib64/ld-linux-x86-64.so.2: Dit is de dynamische linker die het binaire bestand wil gebruiken. De dynamische linker ondervraagt het binaire bestand om te ontdekken welke afhankelijkheden het heeft. Het lanceert die gedeelde objecten in het geheugen. Het bereidt het binaire bestand voor om te worden uitgevoerd en om de afhankelijkheden in het geheugen te kunnen vinden en openen. Vervolgens wordt het programma gestart.
De ELF-koptekst
We kunnen de ELF-header onderzoeken en decoderen met behulp van de readelf
hulpprogramma en het -h
(file header) optie:
readelf -h hello
De koptekst wordt voor ons geïnterpreteerd.
De eerste byte van alle ELF-binaries is ingesteld op hexadecimale waarde 0x7F. De volgende drie bytes zijn ingesteld op 0x45, 0x4C en 0x46. De eerste byte is een vlag die het bestand identificeert als een ELF-binair bestand. Om dit glashelder te maken, spellen de volgende drie bytes “ELF” in ASCII:
- Klasse: Geeft aan of het binaire bestand een 32- of 64-bits uitvoerbaar bestand is (1 = 32, 2 = 64).
- Gegevens: Geeft de endianness in gebruik aan. Endian-codering definieert de manier waarop multibyte-nummers worden opgeslagen. Bij big-endian-codering wordt een nummer opgeslagen met de meest significante bits eerst. Bij little-endian-codering wordt het nummer opgeslagen met de minst significante bits eerst.
- Versie: De versie van ELF (momenteel is het 1).
- OS / ABI: Geeft het type gebruikte binaire interface van de toepassing weer. Dit definieert de interface tussen twee binaire modules, zoals een programma en een gedeelde bibliotheek.
- ABI-versie: De versie van de ABI.
-
Type: Het type ELF-binair bestand. De gemeenschappelijke waarden zijn
ET_REL
voor een verplaatsbare bron (zoals een objectbestand),ET_EXEC
voor een uitvoerbaar bestand dat is gecompileerd met de-no-pie
vlag, enET_DYN
voor een ASMR-bewust uitvoerbaar bestand. - Machine: De instructieset-architectuur. Dit geeft het doelplatform aan waarvoor het binaire bestand is gemaakt.
- Versie: Altijd op 1 zetten, voor deze versie van ELF.
- Toegangspunt Adres: Het geheugenadres binnen het binaire bestand waarop de uitvoering begint.
De andere items zijn maten en aantallen regio’s en secties binnen het binaire bestand, zodat hun locaties kunnen worden berekend.
Een snelle blik op de eerste acht bytes van het binaire bestand met hexdump
zal de handtekeningbyte en “ELF” -reeks in de eerste vier bytes van het bestand tonen. De -C
(canonieke) optie geeft ons de ASCII-weergave van de bytes naast hun hexadecimale waarden, en de -n
(nummer) optie laat ons specificeren hoeveel bytes we willen zien:
hexdump -C -n 8 hello
objdump en de Granular View
Als je de precieze details wilt zien, kun je de objdump
commando met de -d
(demonteer) optie:
objdump -d hello | less
Hiermee wordt de uitvoerbare machinecode gedemonteerd en weergegeven in hexadecimale bytes naast het equivalent van de assembleertaal. De adreslocatie van de eerste bye in elke regel wordt uiterst links weergegeven.
Dit is alleen handig als je assembleertaal kunt lezen, of als je nieuwsgierig bent naar wat er achter het gordijn gebeurt. Er is veel output, dus we hebben het doorgesluisd less
.
Compileren en koppelen
Er zijn veel manieren om een binair bestand te compileren. De ontwikkelaar kiest bijvoorbeeld of hij foutopsporingsinformatie wil opnemen. De manier waarop het binaire bestand is gekoppeld, speelt ook een rol bij de inhoud en grootte. Als de binaire verwijzingen objecten delen als externe afhankelijkheden, zal deze kleiner zijn dan een waaraan de afhankelijkheden statisch zijn gekoppeld.
De meeste ontwikkelaars kennen de opdrachten die we hier hebben behandeld al. Voor anderen bieden ze echter enkele eenvoudige manieren om rond te snuffelen en te zien wat er in de binaire zwarte doos zit.