Forum: Compiler & IDEs Finde den Code zum Kompilat


von Lukas S. (lukker)


Lesenswert?

Ich habe hier den Inhalt des Flahsspeichers aus einen STM32 und ein 
Git-Repository mit mehreren Versionsständen des Codes. Ich soll nun 
herausfinden, welcher Versionsstand genau den vorhandenen Flashinhalt 
ergeben hat.
Beim Kompilieren erhalte ich eine Datei.elf, das Speicherabbild trägt 
die Erweiterung.bin. Es ist exakt zwei Megabyte groß, genau so groß wie 
der Flash des STM32. Ein direkter Vergleich mit der Datei.elf ist gar 
nicht nötig, da sie nur wenige Kilobyte groß und damit sicher 
verschieden ist.

Ich habe mit dem objcopy.exe aus einem Unterverzeichnis des STM32CubeIDE 
aus der Datei.elf eine Datei.bin gemacht um sie mit dem Flashinhalt 
vergleichen zu können, sie ist aber nicht zwei Megabyte groß.

Gibt es eine Möglichkeit, die kompilierte .elf so mit dem .bin zu 
vergleichen, dass Equivalenz festgestellt werden kann (oder .hex oder 
wasauchimmer mit den beiden vorhandenen Dateien möglich ist)?

von Nemopuk (nemopuk)


Lesenswert?

Vermutlich wurde das bin File nicht erzeugt, sonder aus gelesen. Es ist 
größer, als das Programm. Ein direkter 1:1 Vergleich wird nicht gehen.

Ich würde versuchen, nur die ersten n Bytes zu vergleichen, entsprechend 
der kleineren bin Datei.

von Rüdiger B. (rbruns)


Lesenswert?

Im bin sollte doch eine Versionsnummer zu finden sein, wird doch als 
Info im Programm angezigt oder ?

von G. K. (zumsel)


Lesenswert?

Lukas S. schrieb:

> Gibt es eine Möglichkeit, die kompilierte .elf so mit dem .bin zu
> vergleichen, dass Equivalenz festgestellt werden kann (oder .hex oder
> wasauchimmer mit den beiden vorhandenen Dateien möglich ist)?

Möglicherweise wird ein einfacher Vergleich nicht reichen:

https://www.google.com/search?q=reproducible+builds

von Oliver S. (oliverso)


Lesenswert?

Du brauchst die toolchain-Version, die für die Erzeugung des 
Flashinhalts benutzt wurde, dazu die verwendeten Optionen. Das kann 
kompliziert werden.

Wie schon gesagt wurde, wenns da irgendwelche Strings gibt, die die 
Version identifizieren, kanst du mit einem hex-Editor danach suchen.

Oliver

von Harald K. (kirnbichler)


Lesenswert?

Rüdiger B. schrieb:
> Im bin sollte doch eine Versionsnummer zu finden sein

Das setzt voraus, daß derjenige, der die Compilate erzeugte, über soviel 
Selbstdisziplin verfügte, diese für jeden Durchlauf hochzusetzen.

Ich habe mir angewöhnt, im lokalen Git auch die erzeugen Binaries als 
Assets zu speichern. Die nehmen zwar mehr Platz weg als der reine 
Sourcecode, sind aber an anderer Stelle ungemein praktisch.

von G. K. (zumsel)


Lesenswert?

Harald K. schrieb:

> Das setzt voraus, daß derjenige, der die Compilate erzeugte, über soviel
> Selbstdisziplin verfügte, diese für jeden Durchlauf hochzusetzen.

Das kann man automatisieren.

von Alexander (alecxs)


Lesenswert?

Man könnte es durch Ghidra jagen und anschließend durch eine KI.

Lukas S. schrieb:
> Ich habe hier den Inhalt des Flahsspeichers aus einen STM32 und ein
> Git-Repository mit mehreren Versionsständen des Codes.

Wieviele Kandidaten kommen in Frage?

Lukas S. schrieb:
> Ich soll nun herausfinden, welcher Versionsstand genau den vorhandenen
> Flashinhalt ergeben hat.

Wie wichtig ist das, wirst Du extra dafür bezahlt, hast Du ein paar 
Wochen Zeit?

von Harald K. (kirnbichler)


Lesenswert?

G. K. schrieb:
> Das kann man automatisieren.

Auch das erfordert die nötige Disziplin, es zu tun. Wenn man eh' so 
arbeitet, ist das kein Problem, aber eben nicht jeder arbeitet so.

Wieviele Projekte mir schon über den Weg gelaufen sind, in denen die 
Versionskennung einfach ein #define mit einem String waren, in dem dann 
auch irgendein Datum stand ...

Strukturiert arbeiten geht anders.

von Lukas S. (lukker)


Lesenswert?

Rüdiger B. schrieb:
> Im bin sollte doch eine Versionsnummer zu finden sein, wird doch als
> Info im Programm angezigt oder ?

Leider nein. Das Gerät bietet keine Möglichkeit an, eine Firmwareversion 
auszulesen. Kein Display und auch auf keinem Datenbus.

von O. D. (odbs)


Lesenswert?

Dann wird es schwierig bzw. langwierig.

Die genaue Konfiguration der Toolchain ist meistens im Makefile 
abgebildet, aber wie schon erwähnt können unterschiedliche Versionen des 
Compilers unterschiedlichen Output erzeugen. Auch eine andere Version 
einer verwendeten C-Library erzeugt ggf. anderen Output, auch wenn dein 
eigener Quelltext identisch compiliert wird. Vielleicht kannst du hier 
anhand des Zeitraums des Builds und der verwendeten Umgebung raten, 
welche Versionen wahrscheinlich zum Einsatz kamen.

In der Luftfahrt haben wir bei Software zwingend einen "Software 
Configuration Index" (SCI), wo neben dem Quellcode und 
Konfigurationsparametern der Software selbst auch alle relevanten 
Details zur Build-Umgebung und deren Konfiguration mit abgelegt werden 
müssen. Ein Test ist, dass jemand nur mit den Daten des SCI einen binär 
identischen Output erzeugen kann.

Wir compilieren zur Sicherheit immer in einer virtuellen Maschine und 
archivieren die zusammen mit dem Quellcode und dem SCI. So kann auch ein 
älterer Code einen Bugfix bekommen, ohne große Wahrscheinlichkeit, neue 
Fehler einzuführen. Getestet wird natürlich trotzdem.

von Rbx (rcx)


Lesenswert?

Lukas S. schrieb:
> Gibt es eine Möglichkeit, die kompilierte .elf so mit dem .bin zu
> vergleichen, dass Equivalenz festgestellt werden kann (oder .hex oder
> wasauchimmer mit den beiden vorhandenen Dateien möglich ist)?

Das ginge nur, wenn schon ein paar Varianten als Referenzen zum 
Vergleichen da wären.
Im Hexeditor kann man so einiges machen, z.B. auch den überflüssigen 
Teil der Hochsprache wegknipsen.
Mit einem Emu könntest du einfach mehrere Versionen erstellen, und beide 
Gruppen nach z.B. nach Größe sortieren und schauen, was das hergibt.

Oliver S. schrieb:
> Das kann
> kompliziert werden.

Das vermute ich auch, man könnte schauen, ob Programme wie Peid (als 
Plugin) oder elfdiff, objcopy, Rust usw. noch was bringen. Je mehr man 
sucht und Hilfen nutzt, desto höher die Wahrscheinlichkeit, brauchbare 
Ansätze zu finden.

Lukas S. schrieb:
> Ich habe mit dem objcopy.exe aus einem Unterverzeichnis des STM32CubeIDE
> aus der Datei.elf eine Datei.bin gemacht um sie mit dem Flashinhalt
> vergleichen zu können
Um objcopy sinnvoll zu nutzen, braucht es wohl noch eine gewisse 
Einarbeitung?

von Martin H. (mahi)


Lesenswert?

Bevor ich mir da große Gedanken über die Theorie machen würde, würde ich 
einfach mal die generierte bin und das Speicherabbild in Beyond Compare 
reinwerfen und schauen was rauskommt. Das geht schneller als hier einen 
Post zu verfassen.
Wenn's nix bringt, hat man zwei Minuten "verloren", kann aber immernoch 
in der Theorie rumstochern...

von Bruno V. (bruno_v)


Lesenswert?

Das kommt in Klitschen und auch in renommierten Firmen immer wieder vor. 
Die ganz große Inventur ist wie folgt:

1) Hinweise im Bin
Nimm das 2MB bin und untersuche es auf Zeichenpattern, die wie ein
 * Datum (also sowas wie "2023" oder oder "11.23", manchmal auch mit je 
einem '.' dazwischen)
 * Pfad ("c:\...")
 * Version (egal ob Compiler oder Programmversion)
 * Build-Nummer
aussehen.

2) Sammeln: Sammel alle .elf Files, die in Frage kommen können und schau 
Dir Gemeinsamkeiten/Unterschiede an (Größe, Datum, ...)

3) Speicherabbild: Lade das heute aktuelle Elf-File in den Prozessor. 
Stelle sicher, dass es richtig ist, die HW läuft. Dann lese den 
Prozessor zurück und vergleiche elf/bin. So gewinnst Du eine Idee, wo 
genau das elf im Prozessor-Abbild liegt

4) Toolchain: Bild den aktuellen Code und schaue, ob das aktuelle 
elf-File rausfällt. Falls nein, schaue wo die Unterschiede liegen. Falls 
ja, hast Du vielleicht die "richtige" Toolchain mit den richtigen 
Einstellungen.

5) Puzzelarbeit: Ohne jeden Hinweis bleibt Dir nur, die bisherigen 
Git-Stände auszuchecken, zu kompilieren, in den Prozessor einzubrennen 
und den Prozessor zurückzulesen. Mit ein paar Erfahrungen aus 1-4 kannst 
Du Dir hoffentlich einige Schritte sparen.

von Oliver S. (oliverso)


Lesenswert?

Rbx schrieb:
> Das vermute ich auch, man könnte schauen, ob Programme wie Peid (als
> Plugin) oder elfdiff, objcopy, Rust usw. noch was bringen.

Na ja, man sollte ja zumindest die Basics beherrschen, und zumindest 
einen guten Hexeditor und ebenso guten Diassembler haben und auch 
benutzen können.

Ansonsten wirds völlig unmöglich.

Wie gesagt, ich würde als erstes die Quellen diffen, und schauen, wo da 
überhaupt die Unterschiede zwischen den Versionen sind. Gibts da 
eindeutige Strings, wirds einfach. Wenn nicht, muß man halt nach anderen 
Unterschieden suchen, die sich in einem disassemblierten Kompilat 
identifizieren lassen.

Aber selbst wenn man die Version dann identifiziert hat, kommt beim 
Kompilieren vermutlich nicht 1:1 das selben Kompilat raus. Wenn das das 
Ziel ist, muss man halt noch mit toolchains und Optionen spielen.

Oliver

: Bearbeitet durch User
von Rbx (rcx)


Lesenswert?

Oliver S. schrieb:
> Wenn nicht, muß man halt nach anderen
> Unterschieden suchen, die sich in einem disassemblierten Kompilat
> identifizieren lassen.

Ich hatte ganz vergessen, hinzuschreiben, dass man auch Tests oder 
Profiler einsetzen kann um Unterschiede zu finden, sofern solche 
Möglichkeiten verfügbar sind.

von Malte _. (malte) Benutzerseite


Lesenswert?

G. K. schrieb:
> Harald K. schrieb:
>
>> Das setzt voraus, daß derjenige, der die Compilate erzeugte, über soviel
>> Selbstdisziplin verfügte, diese für jeden Durchlauf hochzusetzen.
>
> Das kann man automatisieren.

Ironischer weise macht genau dass eine einfache Auffindbarkeit durch 
Vergleichen unmöglich, da damit die einfache Reproduzierbarkeit nicht 
gegeben ist. Noch schlimmer wird es nur wenn die _ DATE __ oder _ TIME 
__ Makros verwendet wurden.

Also manuell hochgezählte Versionsinfos (in der Versionsverwaltung 
eingecheckt) in das Kompilat einbinden (dann ist es reproduzierbar).

Und/oder den Kompilat Durchlauf und den GIT Hash als extra Informationen 
hinten ans Binary anhängen. Dann lässt sich wenigstens mit wenig 
Aufwand ein individuelles Vergleichstool schreiben. Und das Compilieren 
wenn es zu einem Kunden geht nur einem Buildserver erledigen lassen, 
dann landen nicht versehentlich lokale Änderungen im Kompilat.

Dem TO viel Erfolg. Seine Aufgabe reicht irgendwo von leicht bis 
unmöglich.

von Harald K. (kirnbichler)


Lesenswert?

Malte _. schrieb:
> Also manuell hochgezählte Versionsinfos (in der Versionsverwaltung
> eingecheckt) in das Kompilat einbinden (dann ist es reproduzierbar).

Wenn in der Versionsverwaltung zehn Commits mit gleicher 
Versionsinformation sind ...

von Malte _. (malte) Benutzerseite


Lesenswert?

Harald K. schrieb:
> Wenn in der Versionsverwaltung zehn Commits mit gleicher
> Versionsinformation sind ...

Vermutlich könnte man auch mit einem Hook bei jedem Commit ein Inkrement 
mit einchecken, dann wäre das auch gegeben.
Ansonsten, 10 Versionen ließen sich auch durchprobieren, besser als 
völlig im Trüben zu stochern.
Reproduzierbarkeit ist das Wichtigste. Dazu gehört auch die Bibliotheken 
und den Compiler zu archivieren.
Notfalls sucht dann der Buildserver ein Wochenende lang automatisiert 
durch neu kompilieren und Vergleichen das richtige Kompilat.

von Bruno V. (bruno_v)


Lesenswert?

Harald K. schrieb:
> Wenn in der Versionsverwaltung zehn Commits mit gleicher
> Versionsinformation sind ...

Bei SVN wäre es üblich (weil einfach), die Revision ins Compilat 
einzubauen (Version oder Text) und gleichzeitig ein Zeichen, ob es 
lokale Modifikationen oder Mixed-Range gibt. Z.B. V12_B4711M für lokale 
Modifikationen und V12_B4711 ohne.

von JoHu (nuos)


Lesenswert?

Den ausgelesenen EEPROM Dump gegen eine lokal erzeugte Datei  nur simple 
1:1 binär zu vergleichen schlägt idR immer Fehl, da der Programmcode idR 
nur ab einer bestimmten Adresse im Dump anfängt und oft hinter dem 
Programmcode ein Speicherblock kommt in dem das Programm persitent zu 
speichernde Werte ablegt wie die Konfiguration oder irgendwelche Zähler 
wenn das nötig ist   (z.B. Betriebsstunden, Lifecyle, Abnutzungszähler 
für Werkzeuge/ Betriebsmittel, Fehlerzähler).

Ich hatte schonmal an meinen Flashversuchen gezweifelt, da beim 
verifizieren /runterladen des EEPROMS immer  ein anderer HASH rauskam 
als beim Hochladen. Bis ich im Hex/Binärvergleich per Hand gesehen habe, 
dass nach dem Flashen sofort das Program beim Aufstart seine 
Konfig/Zähler initialisiert hatte und daher nur ein Speicherblock 
irgendwo hinter dem Programm abweichungen zum geflashten File hatte....

Bitte melde dich an um einen Beitrag zu schreiben. Anmeldung ist kostenlos und dauert nur eine Minute.
Bestehender Account
Schon ein Account bei Google/GoogleMail? Keine Anmeldung erforderlich!
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.