Binärdateien in debug.cfg einbetten ohne Uuencode oder Base64

kriegaex

Aktives Mitglied
Mitglied seit
7 Nov 2006
Beiträge
2,927
Punkte für Reaktionen
3
Punkte
36
Ich habe lange nichts mehr geschrieben, aber am Wochenende hatte ich bei einer Freundin das Problem, daß zwar einerseits ein Firmware-Mod nicht infrage kam, aber andererseits gewisse Programme (z.B. Dropbear) auf der W701V laufen sollten. Also, faul wie ich bin, habe ich mir auf The-Construct schnell was zusammengeklickt.

Dabei fiel mir auf, daß außer dem Dropbear extra noch eine Busybox heruntergeladen wurde, und das nur, um uudecode nutzen und damit die privaten RSA/DSS-Schlüssel von Dropbear dekodieren zu können, welche in die debug.cfg UU-kodiert eingebettet waren. Das hat mich geärgert, und ich habe mir überlegt, wie man Binärdaten einbetten könnte, die mit den Bordmitteln der Busybox-Versionen von Standard-Firmwares entpackt werden könnten.

Mir ist eine Lösung eingefallen, welche letzten Endes darauf beruht, Zeilen wie
Code:
echo -en "\0101\0154\0145\0170\0141\0156\0144\0145\0162\0040\0113\0162\0151\0145\0147\0151"
echo -en "\0163\0143\0150\0012"
[COLOR="DimGray"]Alexander Kriegisch[/COLOR]
einzubetten, denn echo kann über oktal kodierte Escape-Sequenzen einzelne Zeichen bzw. Binärcodes darstellen.

Also schrieb ich mir ein Skript bin2oct, welches beliebige über Standardeingabe übergebene Daten in eben diese Form überführt und sie auf der Standardausgabe ausgibt. Hier ist der Einzeiler:
Code:
[COLOR="SeaGreen"]#! /bin/sh
# Convert input stream into octal hexdump with 'echo' statements
# which re-convert it back to binary form when executed[/COLOR]

cat | hexdump -bv | sed -r 's/ +$//;s/ /\\0/g;s/^......./echo -en "/;s/$/"/;s/.*""$//;'

Man speichert ihn, macht ihn mit chmod +x bin2oct ausführbar und ruft ihn dann so auf:
Code:
[COLOR="SeaGreen"]# Ausgabe eines kodierten Textes auf der Konsole[/COLOR]
echo "Mein Text" | bin2oct

[COLOR="SeaGreen"]# Umwandeln einer Binärdatei in echo-Kommandos[/COLOR]
cat dss_host_key | bin2oct > dss_host_key.sh
[COLOR="SeaGreen"]
# So würde das Ganze mit uuencode gehen[/COLOR]
uuencode dss_host_key dss_host_key > dss_host_key.uue

[COLOR="SeaGreen"]# Zurückumwandeln in Binärdatei durch Ausführen der Datei[/COLOR]
. ./dss_host_key.sh > dss_host_key_new

[COLOR="SeaGreen"]# Sind die Dateien gleich groß?[/COLOR]
ls -l dss*
[COLOR="DimGray"]-rw-r--r--    1 root     root          [COLOR="Blue"]457[/COLOR] Jun 22 10:37 dss_host_key
-rw-r--r--    1 root     root         2634 Jun 22 19:55 dss_host_key.sh
-rw-r--r--    1 root     root          663 Jun 22 19:58 dss_host_key.uue
-rw-r--r--    1 root     root          [COLOR="Blue"]457[/COLOR] Jun 22 19:56 dss_host_key_new
[/COLOR]

Das Skript kann mit jedem Linux, einer Busybox (sogar auf der Fritz!Box selbst), einem Cygwin usw. aufgerufen werden, solange nur hexdump und sed vorhanden sind. Es wäre also ein Leichtes, für The-Construct-Pseudo-Images in solchen Fällen auf den Download einer großen Busybox ins RAM einer sowieso schon mit nachgeladenen Binaries belasteten Box zu verzichten. Man bezahlt lediglich mit einem deutlich höheren Platzverbrauch für auf diese Weise kodierte Binärdaten. Im Beispiel oben ist die UU-kodierte Version des DSS-Schlüssels ca. 45% größer als das Original, die "echo-kodierte" Version hingegen 476% größer oder fast 6x so groß wie das Original bzw. 4x so groß die die UU-Version. Für kleine Dateien geht das aber, solange in der debug.cfg noch genügend Platz ist.

Etwas kleiner geht es übrigens auch noch, wenn man das Skript so modifiziert, daß es nur ein einziges, langes echo-Statement erzeugt:
Code:
cat |
    hexdump -bv |
    sed -r 's/ +$//;s/ /\\0/g;s/^......./echo -en "/;s/$/"/;s/.*""$//;' |
    tr "\n" "#" |
    sed -r 's/"#echo -en "//g;s/#//g'

Die damit erzeugte Ausgabe des DSS-Schlüssels wäre "nur" 2296 Bytes groß anstatt 2634, also 5x Original- und 3,5x UU-Größe - immerhin. Man könnte das selbstverständlih so optimieren, daß man eine Art Quoted-Printable-Version erzeugen lassen könnte, also eine Version, in der normal druckbare Zeichen nicht durch Oktal-Codes ersetzt werden, nur Sonderzeichen. Dazu habe ich aber gerade keine Lust.

Dem Betreiber von The-Construct werde ich mal per E-Mail Bescheid geben, vielleicht verwendet er das ja wahlweise. Es könnte Besitzern von RAM-mäßig begrenzten Boxen oder solchen, die viele Binaries herunterladen, helfen, solange sie keine Busybox für andere Zwecke als UU-Dekodierung brauchen.
 
Verbesserte Version, eine Art quoted printable

Update: Bugfix 19.11.2009, Beschreibung siehe #12, Fehler hier in Rot markiert. Download-Datei wurde hier aktualisiert.

Ich hatte mal wieder Zeit zum Spielen und habe das ursprüngliche Skript dahingehend verbessert, daß jetzt nur noch nicht druckbare Kontrollzeichen (ASCII 0-31) sowie in den benutzten Echo-Befehlen zu Problemen führende Zeichen wie einfaches Anführungszeichen, Backslash, Ziffern als Oktal-Codes dargestellt werden. Außerdem werden Oktalcodes nur noch mit einer führenden Null und nicht mehr in fester Länge (4-stellig) wie vorher dargestellt.

Das Skript sieht nun also folgendermaßen aus (ja, es ist durch die eingebettete Awk-Verarbeitung länger geworden):

Code:
[COLOR="Gray"]#! /bin/sh
# Convert input stream into some kind of quoted printable 'echo' statement
# with control characters escaped in octal format (e.g. '\0234') which
# re-converts itself back to binary form when executed
#
# Attention: Make sure you run this script with the same character set as
# on the router box, usually ISO 8859-1[/COLOR]

cat | hexdump -ve '/1 "[B][COLOR="Red"]%d[/COLOR][/B]\n"' | awk '

[COLOR="Gray"]# ASCII 39 is single quote[/COLOR]
BEGIN { printf "echo -en %c", 39 }
{
[COLOR="Gray"]# Special case NUL character (must not be '\0' but '\00')[/COLOR]
if ($1 == 0)
    printf "\\00"
else
{
    [COLOR="Gray"]# Octal format for control chars, backslash, single quote and numbers[/COLOR]
    if ($1 < 32 || $1 == 39 || ($1 > 47 && $1 < 58) || $1 == 92)
        printf "\\%#o", $1
    [COLOR="Gray"]# All other characters are printed literally[/COLOR]
    else
        printf "%c", $1
}
}
END { printf "%c\n", 39 }
'

Am Ende ergibt sich durch das Skript ein einzelner, potentiell sehr langer Echo-Befehl, welcher die gesamte Binärdatei wieder auf die Standardausgabe schreibt, wie gehabt - Anwendung siehe oben.

Getestet habe ich wieder direkt auf einer Fritz!Box 7170 mit Freetz 1.0 und relativ alter Firmware. Es ist also anzunehmen, daß es auf einem "großen" Linux-System ebenso einfach möglich sein sollte, das Skript laufen zu lassen, um eingebettete Echo-Befehle für Pseudo-Images oder sonstige Anwendungsfälle beim Einbetten in die debug.cfg o.ä. zu erstellen.

Was die langen Echo-Befehle betrifft, so scheint das kein Problem zu sein. Ich habe eine Busybox-Binärdatei der Größe 610.944 Bytes in einen Echo-Befehl der Größe 1.218.382 Bytes (Faktor 2,0) umgewandelt. Die entsprechende UU-kodierte Datei ist 841.770 Bytes groß (Faktor 1,4). Bei zwei RSA/DSS-Schlüsseln für Dropbear SSH habe ich mit meiner neuen Methode jeweils den Faktor 1,6 gemessen im Vergleich zu 1,45 bei Uuencode, also gar nicht mehr so weit auseinander. Es hängt einfach davon ab, wie der Anteil druckbarer vs. nicht druckbarer Zeichen in der Binärdatei ist - je mehr druckbare, desto günstiger für mein Skript.

Bei reinen Textdateien ist das Verhältnis nahe bei 1, nur Tabulatoren und Zeilenumbrüche werden vom Skript kodiert. Das wäre also sogar besser als UU-Kodierung, aber für Textdateien ist das Skript ja, fairerweise gesagt, nicht gedacht, außerdem verläßt es sich darauf, daß auf dem Quellsystem, wo es ausgeführt wird, dieselbe Zeichenkodierung vorliegt wie auf dem Zielsystem, im Normalfall auf den AVM-Boxen ISO 8859-1. Das läßt sich aber vor der Ausführung ein- und somit sicherstellen. Um das zu testen, kann man sich ja erst mal eine Binärdatei erzeugen, die sämtliche ASCII-Codes von 0 bis 255 enthält:

Code:
{
echo -en '\0000\0001\0002\0003\0004\0005\0006\0007\0010\0011\0012\0013\0014\0015\0016\0017'
echo -en '\0020\0021\0022\0023\0024\0025\0026\0027\0030\0031\0032\0033\0034\0035\0036\0037'
echo -en '\0040\0041\0042\0043\0044\0045\0046\0047\0050\0051\0052\0053\0054\0055\0056\0057'
echo -en '\0060\0061\0062\0063\0064\0065\0066\0067\0070\0071\0072\0073\0074\0075\0076\0077'
echo -en '\0100\0101\0102\0103\0104\0105\0106\0107\0110\0111\0112\0113\0114\0115\0116\0117'
echo -en '\0120\0121\0122\0123\0124\0125\0126\0127\0130\0131\0132\0133\0134\0135\0136\0137'
echo -en '\0140\0141\0142\0143\0144\0145\0146\0147\0150\0151\0152\0153\0154\0155\0156\0157'
echo -en '\0160\0161\0162\0163\0164\0165\0166\0167\0170\0171\0172\0173\0174\0175\0176\0177'
echo -en '\0200\0201\0202\0203\0204\0205\0206\0207\0210\0211\0212\0213\0214\0215\0216\0217'
echo -en '\0220\0221\0222\0223\0224\0225\0226\0227\0230\0231\0232\0233\0234\0235\0236\0237'
echo -en '\0240\0241\0242\0243\0244\0245\0246\0247\0250\0251\0252\0253\0254\0255\0256\0257'
echo -en '\0260\0261\0262\0263\0264\0265\0266\0267\0270\0271\0272\0273\0274\0275\0276\0277'
echo -en '\0300\0301\0302\0303\0304\0305\0306\0307\0310\0311\0312\0313\0314\0315\0316\0317'
echo -en '\0320\0321\0322\0323\0324\0325\0326\0327\0330\0331\0332\0333\0334\0335\0336\0337'
echo -en '\0340\0341\0342\0343\0344\0345\0346\0347\0350\0351\0352\0353\0354\0355\0356\0357'
echo -en '\0360\0361\0362\0363\0364\0365\0366\0367\0370\0371\0372\0373\0374\0375\0376\0377'
} > 0_to_255

Diese Datei jagt man durch mein Skript, speichert das Ergebnis (den Echo-Befehl) in einer Datei, führt diese aus und speichert wiederum deren Ausgabe in einer weiteren Datei, deren MD5-Prüfsumme man mit dem Original vergleicht, also etwa so, wenn man mal annimmt, daß mein Skript bin2oct_qp ("qp" wie "quoted printable") heißt:

Code:
[COLOR="Gray"]# Binary to quoted printable echo format[/COLOR]
cat 0_to_255 | ./bin2oct_qp > 0_to_255.sh

[COLOR="Gray"]# Quoted printable back to binary[/COLOR]
. ./0_to_255.sh > 0_to_255.test

[COLOR="Gray"]# Check if identical[/COLOR]
md5sum 0_to_255 0_to_255.test
[COLOR="Gray"]e2c865db4162bed963bfaa9ef6ac18f0  0_to_255
e2c865db4162bed963bfaa9ef6ac18f0  0_to_255.test[/COLOR]

Fazit: Wir sind mit der neuen Methode vom Faktor 5 - 7 gegenüber der Binärdatei auf einen Faktor von 1,6 - 2 gekommen, was eine starke Verbesserung darstellt. Damit sind wir in der gleichen Größenordnung wie Uuencode, wenngleich noch immer bei normalen Binärdaten etwas schlechter. Das macht aber nichts, denn für unseren Zweck der Einbettung von Daten in die debug.cfg o.ä. Shell-Skripten ist das völlig in Ordnung. Wir brauchn nach wie vor nur echo auf der Box, kein uudecode und schon gar keine komplett neue Busybox.
 

Anhänge

  • 0_to_255.txt
    1.5 KB · Aufrufe: 4
  • bin2oct_qp.txt
    808 Bytes · Aufrufe: 2
Zuletzt bearbeitet:
Danke für diese Mühe - mit der "bin2oct" version habe ich erfolgreich meine keys einbetten können. Die "bin2oct_gp" produziert dagegen murks, bei mir.
 
mein test-Text:
Code:
test-Textq3706n49ü1ä091qhz+18-
p*	
p*D
und das Ergebnis nach bin2oct_gp:
Code:
echo -en 'test-Textq\063\067\060\066n\064\071\037777777774\061\037777777744\060\071\061qhz+\061\070-\015\012p*\011\015\012p*D'
in einem Terminalfenster von Ubuntu 9.10 (*) produziert, Zeichenkodierung UTF-8 und Westlich probiert (die vielen 77777777 sind schon sehr suspekt...)

Tut mir leid, wenn die Posts nicht der gewünschten Form entsprechen. Eigentlich war es eher als Wink an eventuell suchende User gedacht, daß sie eben nicht unbedingt nach einem Fehlversuch mit der "_gp" Version aufgeben sollten sondern einfach das Script aus dem ersten Post nehmen können.

(*) läuft in einer Virtualbox
 
die vielen 77777777 sind schon sehr suspekt...
Und das muß man Dir erst aus den Rippen leiern?

daß sie eben nicht unbedingt nach einem Fehlversuch mit der "_gp" Version aufgeben sollten sondern einfach das Script aus dem ersten Post nehmen können.

Die Meisten würden das andere Skript verwenden oder sich zumindest melden, wenn es nicht funktioniert. Wenn man schon merkt, daß etwas nicht funktioniert, kann man auch dazu beitragen, es zu korrigieren, und sei es nur, indem man die dazu notwendigen Informationen liefert.

In Deinem Fall gehe ich davon aus, daß Dein awk oder hexdump nicht richtig funktioniert. Was ist bei Dir die Ausgabe von
Code:
echo | awk '{printf "\\%#o\n", 255}'
Und von hexdump:
Code:
echo 'ä' | hexdump -ve '/1 "%d\n"'
 
Dann ist das Problem, daß hexdump bei Dir -28 ausgibt und nicht 228.

Was kommt bei Dir mit diesem Kommando heraus?
Code:
echo 'ä' | hexdump -ve '/1 "%u\n"'
 
Code:
$ echo 'ä' | hexdump -ve '/1 "%u\n"'
228
10
 
OK, so paßt es jetzt, danke.
 
Bugfix für bin2oct_qp

Danke, das hat geholfen. Es war schwer zu debuggen, denn es lag nicht, wie vermutet, am Awk-Skript, sondern an einem Hexdump-Parameter, und zwar verhält sich die Formatangabe %d für Dezimalzahlen auf der Box unter Busybox wie erwartet, aber nicht die unter den meisten Desktop-Linuxen installierte aus dem Package bsdmainutils, bei mir getestet in Version 6.1.6ubuntu1. Der Unterschied besteht darin, daß die BSD-Version für Umlaute u.ä. Sonderzeichen mit ASCII-Code >=128 vorzeichenbehaftete Byte-Werte ausgibt, also negative Zahlen druckt. Ein "ü" (U-Umlaut, klein) wird auf dem Desktop deshalb nicht zur Zahl 252, entsprechend ihrem ASCII-Code, sondern zu -4, wie man leicht testen kann:
Code:
$ echo -n "" | hexdump -ve '/1 "%d\n"'
-4
Verwendet man statt %d einfach %u, erscheint der für die Weiterverarbeitung erwartete, vorzeichenlose Wert, und zwar identisch auf der Box und dem Desktop-Linux:
Code:
$ echo -n "" | hexdump -ve '/1 "%u\n"'
252
Somit besteht der Bugfix für bin2oct_qp einfach darin, im Skript das eine Zeichen im Format-String des Hexdump-Befehls auszutauschen. Die fehlerbereinigte Version lautet:

Code:
[COLOR="Gray"]#! /bin/sh
# Convert input stream into some kind of quoted printable 'echo' statement
# with control characters escaped in octal format (e.g. '\0234') which
# re-converts itself back to binary form when executed
#
# Attention: Make sure you run this script with the same character set as
# on the router box, usually ISO 8859-1[/COLOR]

cat | hexdump -ve '/1 "[B][COLOR="Blue"]%u[/COLOR][/B]\n"' | awk '

[COLOR="Gray"]# ASCII 39 is single quote[/COLOR]
BEGIN { printf "echo -en %c", 39 }
{
[COLOR="Gray"]# Special case NUL character (must not be '\0' but '\00')[/COLOR]
if ($1 == 0)
    printf "\\00"
else
{
    [COLOR="Gray"]# Octal format for control chars, backslash, single quote and numbers[/COLOR]
    if ($1 < 32 || $1 == 39 || ($1 > 47 && $1 < 58) || $1 == 92)
        printf "\\%#o", $1
    [COLOR="Gray"]# All other characters are printed literally[/COLOR]
    else
        printf "%c", $1
}
}
END { printf "%c\n", 39 }
'

Das Download-Skript wurde unter #2 aktualisiert.

Edit: Ah, Ralf war schneller. Das kommt davon, wenn man es neben der Arbeit macht und dann auch noch ausführlich beschreibt... ;-)
 
Zuletzt bearbeitet:
Holen Sie sich 3CX - völlig kostenlos!
Verbinden Sie Ihr Team und Ihre Kunden Telefonie Livechat Videokonferenzen

Gehostet oder selbst-verwaltet. Für bis zu 10 Nutzer dauerhaft kostenlos. Keine Kreditkartendetails erforderlich. Ohne Risiko testen.

3CX
Für diese E-Mail-Adresse besteht bereits ein 3CX-Konto. Sie werden zum Kundenportal weitergeleitet, wo Sie sich anmelden oder Ihr Passwort zurücksetzen können, falls Sie dieses vergessen haben.