Sådan kigger du ind i binære filer fra Linux-kommandolinjen

Har du en mystisk fil? Linux-filkommandoen vil hurtigt fortælle dig, hvilken type fil det er. Hvis det er en binær fil, kan du dog finde ud af endnu mere om den. filen har en hel række staldkammerater, der vil hjælpe dig med at analysere den. Vi viser dig, hvordan du bruger nogle af disse værktøjer.

Identifikation af filtyper

Filer har normalt karakteristika, der gør det muligt for softwarepakker at identificere, hvilken type fil det er, samt hvad dataene i den repræsenterer. Det ville ikke give mening at forsøge at åbne en PNG-fil i en MP3-musikafspiller, så det er både nyttigt og pragmatisk, at en fil bærer en form for ID med sig.

Dette kan være nogle få signaturbytes helt i begyndelsen af ​​filen. Dette gør det muligt for en fil at være eksplicit om dens format og indhold. Nogle gange udledes filtypen ud fra et karakteristisk aspekt af den interne organisering af selve dataene, kendt som filarkitekturen.

Nogle operativsystemer, f.eks. Windows, styres fuldstændigt af en filtype. Du kan kalde det godtroende eller tillidsfuldt, men Windows antager, at enhver fil med DOCX-udvidelsen virkelig er en DOCX-tekstbehandlingsfil. Linux er ikke sådan, som du snart vil se. Den vil have bevis og kigger inde i filen for at finde den.

Værktøjerne beskrevet her var allerede installeret på Manjaro 20, Fedora 21 og Ubuntu 20.04 distributionerne, vi brugte til at undersøge denne artikel. Lad os starte vores undersøgelse ved at bruge filkommandoen.

Brug af filen Kommando

Vi har en samling af forskellige filtyper i vores nuværende mappe. De er en blanding af dokument-, kildekode-, eksekverbare og tekstfiler.

Kommandoen ls vil vise os, hvad der er i mappen, og -hl (menneskelæselige størrelser, lang liste) vil vise os størrelsen af ​​hver fil:

ls -hl

Lad os prøve at arkivere et par af disse og se, hvad vi får:

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

De tre filformater er korrekt identificeret. Hvor det er muligt, giver filen os lidt mere information. PDF-filen er rapporteret at være i version 1.5 format.

Selvom vi omdøber ODT-filen til at have en udvidelse med den vilkårlige værdi af XYZ, er filen stadig korrekt identificeret, både i Files-filbrowseren og på kommandolinjen ved hjælp af fil.

I Filer-filbrowseren får den det korrekte ikon. På kommandolinjen ignorerer filen udvidelsen og kigger inde i filen for at bestemme dens type:

file build_instructions.xyz

Brug af filer på medier, såsom billed- og musikfiler, giver normalt oplysninger om deres format, kodning, opløsning og så videre:

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

Interessant nok, selv med almindelige tekstfiler, bedømmer filen ikke filen efter dens udvidelse. For eksempel, hvis du har en fil med filtypenavnet “.c”, der indeholder almindelig almindelig tekst, men ikke kildekode, forveksler filen ikke med et ægte C kildekodefil:

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

fil identificerer header-filen (“.h”) korrekt som en del af en C-kildekodesamling af filer, og den ved, at makefilen er et script.

  Sådan installeres Linux på en Chromebook med Crouton

Brug af fil med binære filer

Binære filer er mere en “sort boks” end andre. Billedfiler kan ses, lydfiler kan afspilles, og dokumentfiler kan åbnes med den relevante softwarepakke. Binære filer er dog mere af en udfordring.

For eksempel er filerne “hello” og “wd” binære eksekverbare filer. De er programmer. Filen kaldet “wd.o” er en objektfil. Når kildekoden kompileres af en compiler, oprettes en eller flere objektfiler. Disse indeholder den maskinkode, som computeren til sidst vil udføre, når det færdige program kører, sammen med information om linkeren. Linkeren kontrollerer hver objektfil for funktionskald til biblioteker. Det linker dem til alle biblioteker, programmet bruger. Resultatet af denne proces er en eksekverbar fil.

Filen “watch.exe” er en binær eksekverbar fil, der er blevet krydskompileret til at køre på Windows:

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

Tager man den sidste først, fortæller filen os, at “watch.exe”-filen er et PE32+ eksekverbart konsolprogram til x86-familien af ​​processorer på Microsoft Windows. PE står for bærbart eksekverbart format, som har 32- og 64-bit versioner. PE32 er 32-bit-versionen, og PE32+ er 64-bit-versionen.

De andre tre filer er alle identificeret som Eksekverbart og linkbart format (ELF) filer. Dette er en standard for eksekverbare filer og delte objektfiler, såsom biblioteker. Vi tager et kig på ELF-headerformatet snart.

Hvad der måske fanger dit øje er, at de to eksekverbare filer (“wd” og “hej”) er identificeret som Linux Standard Base (LSB) delte objekter, og objektfilen “wd.o” er identificeret som en LSB, der kan flyttes. Ordet eksekverbar er indlysende i dets fravær.

Objektfiler kan flyttes, hvilket betyder, at koden i dem kan indlæses i hukommelsen hvor som helst. De eksekverbare filer er opført som delte objekter, fordi de er blevet oprettet af linkeren fra objektfilerne på en sådan måde, at de arver denne evne.

Dette giver mulighed for Randomisering af adresserumslayout (ASMR) system til at indlæse de eksekverbare filer i hukommelsen på adresser efter eget valg. Standard eksekverbare filer har en indlæsningsadresse kodet i deres overskrifter, som dikterer, hvor de indlæses i hukommelsen.

ASMR er en sikkerhedsteknik. Indlæsning af eksekverbare filer i hukommelsen på forudsigelige adresser gør dem modtagelige for angreb. Dette skyldes, at deres indgangspunkter og placeringen af ​​deres funktioner altid vil være kendt af angribere. Position uafhængige eksekverbare (PIE) placeret på en tilfældig adresse overvinder denne modtagelighed.

  Sådan laver du et Windows USB-drev på Linux med WoeUSB

Hvis vi kompiler vores program med gcc-kompileren og giver muligheden -no-pie, vil vi generere en konventionel eksekverbar.

Indstillingen -o (outputfil) lader os give et navn til vores eksekverbare:

gcc -o hello -no-pie hello.c

Vi vil bruge filen på den nye eksekverbare og se, hvad der er ændret:

file hello

Størrelsen af ​​den eksekverbare er den samme som før (17 KB):

ls -hl hello

Binæren er nu identificeret som en standard eksekverbar. Vi gør dette kun til demonstrationsformål. Hvis du kompilerer applikationer på denne måde, mister du alle fordelene ved ASMR.

Hvorfor er en eksekverbar så stor?

Vores eksempel på hej-program er 17 KB, så det kan næsten ikke kaldes stort, men så er alting relativt. Kildekoden er 120 bytes:

cat hello.c

Hvad fylder binæren ud, hvis det eneste, den gør, er at udskrive en streng til terminalvinduet? Vi ved, at der er en ELF-header, men den er kun 64-bytes lang for en 64-bit binær. Det må åbenlyst være noget andet:

ls -hl hello

Lad os scan det binære med strenge-kommando som et simpelt første trin til at opdage, hvad der er inde i den. Vi sender det til mindre:

strings hello | less

Der er mange strenge inde i binæren, udover “Hej, nørd-verden!” fra vores kildekode. De fleste af dem er etiketter for regioner inden for det binære, og navnene og linkinformationen for delte objekter. Disse inkluderer bibliotekerne og funktioner inden for disse biblioteker, som binæren afhænger af.

Det ldd kommando viser os de delte objektafhængigheder for en binær:

ldd hello

Der er tre poster i outputtet, og to af dem inkluderer en mappesti (den første gør ikke):

linux-vdso.so: Virtual Dynamic Shared Object (VDSO) er en kernemekanisme, der gør det muligt at få adgang til et sæt kerne-rum-rutiner af en binær bruger-space. Det her undgår overhead af et kontekstskifte fra brugerkernetilstand. Delte VDSO-objekter overholder formatet Executable and Linkable Format (ELF), hvilket gør det muligt at linke dem dynamisk til det binære under kørsel. VDSO’en er dynamisk allokeret og drager fordel af ASMR. VDSO-kapaciteten leveres af standarden GNU C bibliotek hvis kernen understøtter ASMR-skemaet.
libc.so.6: Den GNU C bibliotek fælles objekt.
/lib64/ld-linux-x86-64.so.2: Dette er den dynamiske linker binæren ønsker at bruge. Den dynamiske linker udspørger binæren for at finde ud af, hvilke afhængigheder den har. Det lancerer disse delte objekter i hukommelsen. Det forbereder binæren til at køre og være i stand til at finde og få adgang til afhængighederne i hukommelsen. Derefter starter den programmet.

  Sådan brænder du en ISO-fil til et USB-drev i Linux

ELF-headeren

Vi kan undersøge og afkode ELF-headeren ved at bruge readelf-værktøjet og -h (filoverskrift):

readelf -h hello

Overskriften er fortolket for os.

Den første byte af alle ELF-binære filer er sat til hexadecimal værdi 0x7F. De næste tre bytes er indstillet til 0x45, 0x4C og 0x46. Den første byte er et flag, der identificerer filen som en ELF-binær. For at gøre dette krystalklart, staver de næste tre bytes “ELF” ind ASCII:

Klasse: Angiver om binæren er en 32- eller 64-bit eksekverbar (1=32, 2=64).
Data: Angiver endianitet i brug. Endian-kodning definerer måden, hvorpå multibyte-numre lagres. I big-endian-kodning gemmes et tal med dets vigtigste bit først. I little-endian-kodning gemmes nummeret med dets mindst signifikante bit først.
Version: ELF-versionen (i øjeblikket er den 1).
OS/ABI: Repræsenterer typen af applikations binære grænseflade i brug. Dette definerer grænsefladen mellem to binære moduler, såsom et program og et delt bibliotek.
ABI-version: versionen af ​​ABI.
Type: Typen af ​​ELF binær. De almindelige værdier er ET_REL for en relokaliserbar ressource (såsom en objektfil), ET_EXEC for en eksekverbar kompileret med flaget -no-pie og ET_DYN for en ASMR-bevidst eksekverbar.
Maskine: Den instruktionssæt arkitektur. Dette angiver målplatformen, som binærfilen blev oprettet for.
Version: Altid indstillet til 1, for denne version af ELF.
Entry Point Address: Hukommelsesadressen inden for binæren, hvor udførelsen starter.

De andre poster er størrelser og antal af regioner og sektioner inden for binæren, så deres placeringer kan beregnes.

Et hurtigt kig på de første otte bytes af binæren med hexdump vil vise signaturbyten og “ELF”-strengen i de første fire bytes af filen. Indstillingen -C (kanonisk) giver os ASCII-repræsentationen af ​​bytes sammen med deres hexadecimale værdier, og -n (antal) mulighed lader os specificere, hvor mange bytes vi ønsker at se:

hexdump -C -n 8 hello

objdump og Granular View

Hvis du vil se de små detaljer, kan du bruge objdumpcommand med -d (disassemble) muligheden:

objdump -d hello | less

Dette adskiller den eksekverbare maskinkode og viser den i hexadecimale bytes sammen med den tilsvarende assemblersprog. Adressen for det første farvel i hver linje er vist yderst til venstre.

Dette er kun nyttigt, hvis du kan læse assemblersprog, eller du er nysgerrig efter, hvad der foregår bag gardinet. Der er meget output, så vi har brugt det til mindre.

Kompilere og linke

Der er mange måder at kompilere en binær på. For eksempel vælger udvikleren, om de vil inkludere fejlfindingsoplysninger. Den måde, hvorpå binæren er linket, spiller også en rolle i dens indhold og størrelse. Hvis de binære referencer deler objekter som eksterne afhængigheder, vil den være mindre end én, som afhængighederne statisk linker til.

De fleste udviklere kender allerede de kommandoer, vi har dækket her. For andre tilbyder de dog nogle nemme måder at rode rundt og se, hvad der ligger inde i den binære sorte boks.