Wie kann ich eine Bitmaske auf / dev / zero setzen, damit ich andere Bytes als Null erhalten kann?

20

Wie kann ich eine Bitmaske /dev/zeroaufsetzen, damit ich eine Quelle nicht nur für 0x00, sondern auch für ein beliebiges Byte zwischen 0x01 und 0xFF haben kann?

Eduard Florinescu
quelle
8
Warum fragst du? Bitte bearbeiten Sie die Frage, um sie zu motivieren.
Basile Starynkevitch
1
Sie können diese Antwort als Referenz verwenden: stackoverflow.com/questions/12634503/how-to-use-xor-in-bash
Romeo Ninov
Ich gab eine Antwort auf diese Frage, aber als ich sie noch einmal las, glaubte ich, dass ich sie missverstanden hatte. Möchten Sie jeden 0x00in einen bestimmten Wert oder in einen zufälligen Wert im 0x00-0xFFBereich übersetzen?
Kos
1
@kos jeweils auf einen bestimmten Wert wie 444444...kein Zufallswert
Eduard Florinescu

Antworten:

18

Der folgende bashCode ist so eingestellt, dass er mit dem Byte arbeitet, das in der Binärdatei wiedergegeben wird . Allerdings können Sie es leicht zu handhaben ändern ocatal , dezimal oder hex einfach durch den Wechsel radix r Wert 2 zu 8, 10oder 16jeweils und Einstellung b=entsprechend.

r=2; b=01111110
printf -vo '\\%o' "$(($r#$b))"; </dev/zero tr '\0' "$o"

BEARBEITEN - Es wird der gesamte Bereich der Bytewerte verarbeitet: hex 00 - FF (als ich 00-7F unten schrieb, habe ich nur Einzelbyte-UTF-8-Zeichen in Betracht gezogen).

Wenn Sie zum Beispiel nur 4 Bytes (Zeichen im UTF-8-Bereich 'ASCII'-only hex 00-7F) möchten , können Sie dies in head umleiten :... | head -c4

Ausgabe (4 Zeichen):

~~~~

Um die Ausgabe im 8-Bit-Format anzuzeigen, leiten Sie sie weiter in xxd(oder einen anderen Byte-Dump * von 1 und 0 ):
z. b=10000000und Rohrleitungen zu:... | head -c4 | xxd -b

0000000: 10000000 10000000 10000000 10000000                    ....
Peter.O
quelle
1
Wollten Sie o=$(printf ...)für die zweite Zeile schreiben ?
Jwodder
1
@jwodder: Nein, die zweite Zeile ist wie gezeigt korrekt. Die Option printf-v bewirkt, dass die Ausgabe die unmittelbar nach ihr benannte Variable direkt setzt. In diesem Fall lautet der Variablenname o(für Oktal ). Beachten Sie, dass die -vOption für die Shell- Version von printf(nicht für die Version / usr / bin / printf ) gilt.
Peter.O
2
@jwodder Im Allgemeinen stellt die -vOption auch sicher, dass die Variable genau auf das eingestellt wird, was Sie angegeben haben. $(...)transformiert zuerst die Ausgabe. Das ist der Grund, warum o=$(printf '\n')nicht die erwartete Wirkung erzielt wird, wohingegen dies der printf -vo '\n'Fall ist. (Es spielt hier keine Rolle, da die Ausgabe hier in einer Form vorliegt, die von einer solchen Umwandlung nicht betroffen ist. Wenn Sie die -vOption jedoch nicht kennen, ist dies möglicherweise hilfreich.)
hvd
18

Das kann man nicht so einfach machen.

Sie können ein eigenes Kernelmodul schreiben, das ein solches Gerät bereitstellt. Das empfehle ich nicht.

Sie könnten ein winziges C-Programm schreiben, das einen unendlichen Strom gleicher Bytes auf eine Pipe (oder auf stdout) oder ein FIFO schreibt .

Sie könnten tr (1) verwenden , um aus /dev/zerojedem 0-Byte etwas anderes zu lesen und es zu übersetzen.

Sie könnten vielleicht yes (1) verwenden , zumindest wenn Sie es sich leisten können, Zeilenumbrüche zu haben (oder es weiterzuleiten tr -d '\n'...)

Basile Starynkevitch
quelle
10
Oder verwenden Sie yes 1 | tr -d $'\n'für diese Angelegenheit.
Kojiro
3
@kojiro: Das wird scheitern, wenn Sie versuchen, yeseinen Strom von \nZeichen. Eine Alternative, die behandelt wird, \nist: yes '' | tr '\n' "$c"- Wo $ckann ein beliebiges Zeichen des gesamten ASCII-Zeichenbereichs sein.
Peter.O
1
@ Peter.O Ich bin mir nicht sicher, wie Sie meinen Kommentar interpretiert haben, um etwas anderes als den wörtlichen, statischen Ausdruck zu bedeuten yes 1 | tr -d $'\n'. Ich nehme an, Sie könnten eine Shell verwenden, die die $''Backslash-Behandlung nicht ausführt, oder Sie könnten versuchen, ein Gebietsschema zu finden, das sich ändert tr -d $'\n', aber ich habe es noch nicht gefunden.
Kojiro
@kojiro: Sie yes 1 | tr -d $'\n'werden ganz gerne einen 1Zeichenstrom und fast jeden anderen Einzelbyte -Wert drucken, aber es kann keinen \nZeichenstrom drucken . Das OP möchte alle Bytewerte "zwischen 0x01 und 0xFF" verarbeiten können
Peter.O
1
loop() { if [ "$1" = $'\n' ]; then yes "$1"; else yes "$1" | tr -d $'\n' ; fi;
PSkocik
13

Wenn Sie dies im wahrsten Sinne des Wortes erreichen möchten, können Sie einen LD_PRELOAD-Hook verwenden . Die Grundidee ist, eine Funktion aus der C-Bibliothek neu zu schreiben und anstelle der normalen zu verwenden.

Hier ist ein einfaches Beispiel, in dem wir die Funktion read () überschreiben, um den Ausgabepuffer mit 0x42 zu XOR zu verknüpfen.

#define _GNU_SOURCE
#include <string.h>
#include <errno.h>
#include <sys/types.h>
#include <dlfcn.h> 
#include <unistd.h>

static int dev_zero_fd = -1;

int open64(const char *pathname, int flags)
{
    static int (*true_open64)(const char*, int) = NULL;
    if (true_open64 == NULL) {
        if ((true_open64 = dlsym(RTLD_NEXT, "open64")) == NULL) {
            perror("dlsym");
            return -1;
        }        
    }
    int ret = true_open64(pathname, flags);
    if (strcmp(pathname, "/dev/zero") == 0) {
        dev_zero_fd = ret;
    }
    return ret;
}


ssize_t read(int fd, void *buf, size_t count)
{
    static ssize_t (*true_read)(int, void*, size_t) = NULL;
    if (true_read == NULL) {
        if ((true_read = dlsym(RTLD_NEXT, "read")) == NULL) {
            perror("dlsym");
            return -1;
        }        
    }    

    if (fd == dev_zero_fd) {
        int i;
        ssize_t ret = true_read(fd, buf, count);    
        for (i = 0; i < ret; i++) {
            *((char*)buf + i) ^= 0x42;
        }
        return ret;
    }

    return true_read(fd, buf, count);    
}

Eine naive Implementierung würde XOR 0x42 für jede gelesene Datei bedeuten, was unerwünschte Konsequenzen hätte. Um dieses Problem zu lösen, habe ich auch die open () - Funktion eingebunden, sodass sie den Dateideskriptor abruft, der mit / dev / zero verknüpft ist. Dann führen wir das XOR in unserer read () - Funktion nur aus, wenn fd == dev_zero_fd.

Verwendung:

$ gcc hook.c -ldl -shared -o hook.so
$ LD_PRELOAD=$(pwd)/hook.so bash #this spawns a hooked shell
$ cat /dev/zero
BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB
yoann
quelle
3
In Anbetracht Ihrer Implementierung könnten Sie eine symbolische Verknüpfung von / dev / capbee zu / dev / zero haben, nach / dev / capbee suchen und / dev / zero in Ruhe lassen. // dev / zero ist nicht dasselbe wie / dev / zero.
Robert Jacobs
1
@ RobertJacobs In der Tat. Wir könnten sogar symlinks / dev / 0x01, / dev / 0x02, / dev / 0x03, ... nach / dev / zero generieren und den Dateinamen analysieren, um die anzuwendende Bitmaske zu bestimmen.
Yoann
11

In Bezug auf die Geschwindigkeit war die schnellste, die ich gefunden habe:

$ PERLIO=:unix perl -e '$s="\1" x 65536; for(;;){print $s}' | pv -a > /dev/null
[4.02GiB/s]

Zum Vergleich:

$ tr '\0' '\1' < /dev/zero | pv -a > /dev/null
[ 765MiB/s]
$ busybox tr '\0' '\1' < /dev/zero | pv -a > /dev/null
[ 399MiB/s]

$ yes $'\1' | tr -d '\n' | pv -a > /dev/null
[26.7MiB/s]

$ dash -c 'while:; do echo -n "\ 1"; erledigt '| pv -a> / dev / null
[225 KB / s]
$ bash -c 'while:; do echo -ne "\ 1"; erledigt '| pv -a> / dev / null
[180 KB / s]

$ < /dev/zero pv -a > /dev/null
[5.56GiB/s]
$ cat /dev/zero | pv -a > /dev/null
[2.82GiB/s]
Stéphane Chazelas
quelle
In meinem Debian perlergeben sich 2,13GiB, während sich < /dev/zero8,73GiB ergeben. Was kann die Leistung beeinträchtigen?
Cuonglm
@ Cuonglm, ja, ich sehe einige Unterschiede zwischen den Systemen, aber perlist durchweg schneller als die anderen Lösungen. Ich bekomme den gleichen Durchsatz wie mit dem gleichwertigen kompilierten C-Programm. Der Benchmark bezieht sich sowohl auf die Anwendung als auch auf den Scheduler des Systems. Was den Unterschied am meisten ausmacht, ist die Größe der Puffer, die geschrieben werden.
Stéphane Chazelas
@cuonglm Das Rohr verlangsamt es auch. Ich denke cat /dev/zero| pv -a >/dev/null, dass Sie ungefähr 2 GiBs pro Sekunde auch geben werden (es tut auf meinem System, während < /dev/zero), gibt mir um 6GiBps.
PSkocik
@ StéphaneChazelas Darf ich fragen, auf welchem ​​System bist du, Stéphane Chazelas? Die Ergebnisse auf meinem unterscheiden sich ziemlich (ich kann ungefähr 2.1GiB aus der Perl-Version herausbekommen). Ich bin auf Linux ProBook 3.13.0-24-generic #47-Ubuntu SMP Fri May 2 23:30:00 UTC 2014 x86_64 x86_64 x86_64 GNU/LinuxIntel i5 Core drin.
PSkocik
1
@PSkocik, Linux 3.16.0-4-amd64 # 1 SMP Debian 3.16.7-ckt9-3 (23.04.2015) x86_64 GNU / Linux, Intel (R) Core (TM) 2 Duo-CPU T9600 bei 2,80 GHz. Der neuere Kernel scheint einen Unterschied zu machen (es sei denn, es ist der neuere Perl: v5.20.2)
Stéphane Chazelas
7

Es ist irgendwie sinnlos zu versuchen, eine Bitmaske / x oder null Bytes zu erstellen, nicht wahr? Ein Byte zu nehmen und xormit Null zu belegen, ist ein No-Op.

Erstellen Sie einfach eine Schleife, die die gewünschten Bytes enthält, und setzen Sie sie hinter eine Pipe oder Named Pipe. Es verhält sich so ziemlich wie ein Zeichengerät (verschwendet im Leerlauf keine CPU-Zyklen):

mkfifo pipe
while : ; do echo -n "a"; done > pipe &

Und wenn Sie es optimieren möchten, können Sie den folgenden C-Code verwenden:

#include <stdio.h>
#include <string.h>

int main(int argc, char **argv) { 
  char c = argc == 1+1 ? argv[1][0] : 'y';

  char buff[BUFSIZ];
  memset(buff, c, BUFSIZ);

  for(;;){ 
    write(1, buff, sizeof(buff)); 
  }
}

kompilieren & ausführen

$ CFLAGS=-O3 make loop
./loop "$the_byte_you_want" > pipe

Leistungstest:

./loop 1 | pv -a >/dev/null 

2,1 GB / s auf meinem Computer (sogar etwas schneller als cat /dev/zero | pv -a >/dev/null)

PSkocik
quelle
Ich habe ursprünglich versucht, Putchar in C zu verwenden, aber es war langsam.
PSkocik
Warum aus Neugier argc == 1+1statt agrc == 2?
Setzen Sie Monica iamnotmaynard am
@iamnotmaynard Um mich daran zu erinnern, dass es 1 für die ausführbare Befehlszeile plus 1 Argument ist. :-D
PSkocik
Ah. Das war meine Vermutung, aber ich wollte sicherstellen, dass es keinen geheimen Grund gab.
Setzen Sie Monica iamnotmaynard am
"Ein Byte zu nehmen und es mit Null zu xoren, ist ein No-Op." Das ist nicht wahr: 0 XOR X == X.
Jacwah
5

Lesen Sie Nullen, übersetzen Sie jede Null in Ihr Muster!

Wir lesen Null-Bytes aus /dev/zeround trwenden eine Bitmaske auf jedes der Bytes an, indem wir jedes Null-Byte übersetzen:

$ </dev/zero tr '\000' '\176' | head -c 10
~~~~~~~~~~$

Oktal 176 ist der ASCII-Code von ~, also erhalten wir 10 ~. (Das $am Ende der Ausgabe zeigt in meiner Shell an, dass es kein Zeilenende gab - es könnte für Sie anders aussehen)

Lassen Sie uns also 0xFFBytes erstellen : Hex 0xFFist oktal 0377. Die führende Null wird in der trBefehlszeile weggelassen. Am Ende hexdumpwird verwendet, um die Ausgabe lesbar zu machen.

$ </dev/zero tr '\000' '\377' | head -c 10 | hexdump
0000000 ffff ffff ffff ffff ffff               
000000a

Sie müssen hier die Oktalcodes der Zeichen anstelle des Hexadezimals verwenden. Es ist also der Bereich von \000bis oktal \377(genauso wie 0xFF).
Verwenden Sie ascii -xund ascii -o, um eine Tabelle der Zeichen mit hexadezimalen oder oktalen Indexnummern zu erhalten.
(Für eine Tabelle mit Dezimal- und Hexadezimalzahl nur ascii).

Ziemlich schnell

Es läuft ziemlich schnell, verglichen mit der Verwendung von Nullen: Es cat /dev/zeroist nur viermal so schnell, während es die E / A-Pufferung perfekt nutzen trkann , was nicht möglich ist.

$ </dev/zero tr '\000' '\176' | pv -a >/dev/null
[ 913MB/s]

$ </dev/zero cat | pv -a >/dev/null        
[4.37GB/s]
Volker Siegel
quelle
3

Hängt davon ab, was Sie mit den Daten machen möchten und wie flexibel Sie sie verwenden möchten.

Im schlimmsten Fall, wenn Sie Geschwindigkeit benötigen, können Sie das Gleiche wie mit / dev / zero tun und einfach die Geräte / dev / one, / dev / two, ... / dev / fourtytwo ... und so weiter kompilieren.

In den meisten Fällen ist es besser, die Daten direkt dort zu erstellen, wo sie benötigt werden, also innerhalb eines Programms / Skripts als Konstante. Mit mehr Informationen könnten Ihnen die Leute besser helfen.

Gast
quelle
1

Endlosschleife

Ersetzen Sie \u00mit dem gewünschten Byte.

while true ; do printf "\u00" ; done | yourapp

C ++ Code:

#include<cstdio>

int main(){
 char out=Byte;
 while(true)
 fwrite(&out,sizeof(out),1,stdout);
}

Kompilieren: Ersetzen Sie Bytemit dem gewünschten Wert.

g++ -O3 -o bin file.cpp -D Byte=0x01

Verwenden

./bin | yourapp

ncomputer
quelle