Hinweis perev. : Der Autor dieses Materials ist Architekt bei Barclays und der britische Open Source-Enthusiast Ian Miell. Ziel ist es, ein praktisches Docker-Image (mit einer "schlafenden" Binärdatei) zu erstellen, das nicht heruntergeladen werden muss, sondern einfach per Copy & Paste kopiert werden muss. Durch Versuch, Irrtum und Experimentieren mit dem Assembler-Code erreicht er das Ziel, indem er ein Bild mit einer Größe von weniger als einem Kilobyte vorbereitet.
Hier ist es (in base64 codiert)H4sICIa2A1sCA2IA7Vrrbts2FFYL7M9+7QUGGNyfDYhtkuJFFLAhWZOhBYJmaLMOWBAEFC+xVlkyJLpYEBjdY+0l+k6jfGvqtkEWp2qD8TMg8vAqnsNzDg9lQhhmEjHDhY4zgWJBBUQJ5ZnCGAubMUQMyhJqoRRMJxYbo7Q2CedYxlQO/myqMroeEEHICIngApspxohEKI4h5DHmGEUQQw7jqAejDjBtnKz9q2w7zubi7gkugazVKHdGuWltQArkWDMCdoCqSpufg/QSPK4aV8pxW+nL96uxzMu39G+NqRe5PeekGj13Oi9BamXRmCtl1dS9X2jqel147C7W+aOJKd8dZ04dlcqsSw7KVyA9Ab/uHT/+cTht6mFRKVkMmywv0yv0mnxbMc8sSP8Apzvg0ViDtJwWxQ54Mpbny5W9qIrp2DSrmt+r+mVenu/ny+UelK6+mFR56VYtjsqfp3mxHupQZqZYdp/NGeo850x99r9j7QloyWEz8kvpK//47vuymvzQ29vf79m8MKnIaIa8bUmwRdByw6TKREIoIzE3xBrjrY7MGDUilomQ3GrNrFaIKqSZ4lkvL3tD12sn/IQCrI10xtcC7C1kH9I+xseQpYilRAwoZ5AI9IcfWFfqpRfzK1M3eeUZDRAfQDGAfc/jHTDKG1fVXiInlzcfctnwLPP9Vszs9VXvUzFy5jlZV5WzTbtN3cWkZWkhL/yS2gXm1p7lumkl24wkpv51FbYcU0EZy7SV0ucEZowkiCjvLbAVikCaGUqhyjT0c0Lj/YrElmmSWANOZ7MooHPwRCiLRaJEzBXKFGTCy49lUHNKjEigVdD6H4uTzPj9wzDCSawU0TQT2ujhjVwjgZzSj/n/eX7D/xPm/T8N/v/Ll/+Lg2fPnxw93eL85xFvyB9Rn4TzXwdAAxiMYLD/t9f/7eM/xDja1P+YBf3vKP7L2+PnttsA/IfjcQiE7nkgdH18Ey4O7pjdH7ygmX0p9n8eFA5aG3pb+0/eP/9jzFmw/13AdTBHK3/OPx7/Ic4X8qecQ9K244QG/98JXh8c/vLwwYM1/TD6KWqpv6LdOb37gT67URKterTpVxu1V9PXq3lW1d8skn++9Y83f4cDeEBAQMBnwliWuTWNu8l33G38/3X3fzGk79wFQ4S4Lwr+vwOcXIJHy4ANkLv4L4APcJ6ZSXUsz+efh1xaSOf3VxstHS6+H/nSu4s6wOns9OugxrdG7WXV5K6qc9NEn0n/ESab+s9o0P+O7v9ce1WzVNI7uAiczYI6BgQEBNwD/AvqV/+XACoAAA==
Wie bin ich dazu gekommen?
Ein Kollege zeigte einmal ein Docker-Image, mit dem er Kubernetes-Cluster testete. Er hat nichts getan: Er ist einfach untergelaufen und hat darauf gewartet, dass du ihn tötest.
„Schau, es dauert nur 700 Kilobyte! Wirklich schneller Download! ”
Und dann wurde ich neugierig, welches minimale Docker-Image ich erstellen konnte. Ich wollte eine haben, die in base64 codiert und mit einfachem Kopieren und Einfügen buchstäblich überall hin gesendet werden kann. Da das Docker-Image nur eine TAR-Datei und die TAR-Datei nur eine Datei ist, sollte alles funktionieren.
Winzige Binärdatei
Zunächst brauchte ich eine sehr kleine Linux-Binärdatei, die nichts bewirkt. Es braucht ein wenig Magie - und hier sind zwei wunderbare, informative und lesbare Artikel zum Erstellen kleiner ausführbarer Dateien:
Ich brauchte nicht "Hello World", sondern ein Programm, das nur schläft und auf x86_64 läuft. Ich habe mit einem Beispiel aus dem ersten Artikel begonnen:
SECTION .data msg: db "Hi World",10 len: equ $-msg SECTION .text global _start _start: mov edx,len mov ecx,msg mov ebx,1 mov eax,4 int 0x80 mov ebx,0 mov eax,1 int 0x80
Ausführen:
nasm -f elf64 hw.asm -o hw.o ld hw.o -o hw strip -s hw
Es ergibt sich eine Binärzahl von
504 Bytes .
Es ist jedoch nicht erforderlich, "Hello World" zu verwenden. Zuerst habe ich festgestellt, dass die Abschnitte "
.data
oder "
.text
nicht erforderlich sind und das Laden von Daten nicht erforderlich ist. Darüber hinaus befasst sich die obere Hälfte des Abschnitts
_start
mit der Textausgabe. Als Ergebnis habe ich den folgenden Code ausprobiert:
global _start _start: mov ebx,0 mov eax,1 int 0x80
Und es wurde bereits mit
352 Bytes kompiliert.
Dies ist jedoch nicht das gewünschte Ergebnis, da das Programm einfach seine Arbeit beendet und wir es zum Schlafen brauchen. Als Ergebnis zusätzlicher Untersuchungen stellte sich heraus, dass der Befehl
mov eax
das Prozessorregister mit der entsprechenden Linux-
mov eax
füllt und
int 0x80
den Aufruf selbst
int 0x80
. Dies wird
hier ausführlicher beschrieben.
Und
hier habe ich die gewünschte Liste gefunden. Syscall 1 ist
exit
, und wir brauchen
syscall 29:pause
. Das folgende Programm stellte sich heraus:
global _start _start: mov eax, 29 int 0x80
Wir haben weitere 8 Bytes gespeichert: Die Kompilierung ergab ein Ergebnis von
344 Bytes , und jetzt ist es eine geeignete Binärdatei für uns, die nichts tut und ein Signal erwartet.
In Hexen graben
Es ist Zeit, eine Kettensäge zu besorgen und sich mit der Binärdatei zu befassen ... Dafür habe ich
Hexer verwendet , was im Wesentlichen für Binärdateien mit der Fähigkeit,
Hexes direkt zu bearbeiten, vim ist. Nach langwierigen Experimenten bekam ich daraus:

… das hier:

Dieser Code macht dasselbe, aber beachten Sie, wie viele Zeilen und Leerzeichen noch übrig sind. Während meiner Arbeit ließ ich mich von einem
solchen Dokument leiten, aber im Großen und Ganzen war es ein Weg des Versuchs und Irrtums.
Die Größe hat sich also auf
136 Bytes verringert.
Weniger als 100 Bytes?
Ich wollte wissen, ob ich noch weiter gehen könnte. Nachdem ich
dies gelesen hatte, ging ich davon aus, dass es möglich sein würde, 45 Bytes zu erreichen, aber leider! - Nein. Die dort beschriebenen Tricks sind nur für 32-Bit-Binärdateien konzipiert, für 64-Bit-Binärdateien jedoch nicht.
Das Beste, was ich geschafft habe, war,
diese 64-Bit-Version des Programms in meinen Systemaufruf zu integrieren:
BITS 64 org 0x400000 ehdr: ; Elf64_Ehdr db 0x7f, "ELF", 2, 1, 1, 0 ; e_ident times 8 db 0 dw 2 ; e_type dw 0x3e ; e_machine dd 1 ; e_version dq _start ; e_entry dq phdr - $$ ; e_phoff dq 0 ; e_shoff dd 0 ; e_flags dw ehdrsize ; e_ehsize dw phdrsize ; e_phentsize dw 1 ; e_phnum dw 0 ; e_shentsize dw 0 ; e_shnum dw 0 ; e_shstrndx ehdrsize equ $ - ehdr phdr: ; Elf64_Phdr dd 1 ; p_type dd 5 ; p_flags dq 0 ; p_offset dq $$ ; p_vaddr dq $$ ; p_paddr dq filesize ; p_filesz dq filesize ; p_memsz dq 0x1000 ; p_align phdrsize equ $ - phdr _start: mov eax, 29 int 0x80 filesize equ $ - $$
Das resultierende Bild ist
127 Bytes . In diesem Zusammenhang habe ich aufgehört, die Größe zu reduzieren, aber ich akzeptiere die Vorschläge.
Kleiner Hafenarbeiter
Jetzt, da es eine Binärdatei gibt, die endloses Warten implementiert, bleibt es, sie in ein Docker-Image einzufügen.
Um jedes mögliche Byte zu speichern, habe ich eine Binärdatei mit einem Dateinamen aus einem Byte -
t
- erstellt und in die
Dockerfile
Datei
Dockerfile
, wodurch ein fast leeres Bild erstellt wurde:
FROM scratch ADD t /t
Beachten Sie, dass die
Dockerfile
keine
CMD
Dockerfile
, da dies die Größe des Bildes erhöhen würde. Zu Beginn müssen Sie den Befehl über die Argumente an
docker run
.
Als Nächstes erstellte der
docker save
eine TAR-Datei, die dann mit maximaler GZIP-Komprimierung komprimiert wurde. Das Ergebnis ist eine tragbare Docker-Image-Datei mit einer Größe von weniger als 1000 Byte:
$ docker build -tt . $ docker save t | gzip -9 - | wc -c 976
Ich habe auch versucht, die Größe der TAR-Datei durch Experimentieren mit der Docker-Manifestdatei zu reduzieren, aber vergebens: Aufgrund der Besonderheiten des TAR-Formats und des GZIP-Komprimierungsalgorithmus führten solche Änderungen nur zum Wachstum des endgültigen GZIP. Ich habe andere Komprimierungsalgorithmen ausprobiert, aber gzip erwies sich als das Beste für diese kleine Datei.
PS vom Übersetzer
Lesen Sie auch in unserem Blog: