Chcę policzyć, ile razy pewna sekwencja bajtów dzieje się w pliku, który mam. Na przykład chcę dowiedzieć się, ile razy liczba \0xdeadbeef
występuje w pliku wykonywalnym. Teraz robię to za pomocą grep:
#/usr/bin/fish
grep -c \Xef\Xbe\Xad\Xde my_executable_file
(Bajty są zapisywane w odwrotnej kolejności, ponieważ mój procesor to little-endian)
Mam jednak dwa problemy z moim podejściem:
- Te
\Xnn
sekwencje ucieczki działają tylko w skorupkach ryb. - grep faktycznie liczy liczbę linii, które zawierają moją magiczną liczbę. Jeśli wzorzec występuje dwa razy w tej samej linii, będzie liczony tylko raz.
Czy istnieje sposób na rozwiązanie tych problemów? Jak mogę uruchomić ten jeden liner w powłoce Bash i dokładnie zliczyć liczbę wystąpień wzoru w pliku?
bash
grep
escape-characters
hugomg
źródło
źródło
grep -o
11221122
, co należy zwrócić na danych wejściowych, takich jak112211221122
? 1 lub 2?Odpowiedzi:
Jest to wymagane rozwiązanie jednowierszowe (dla najnowszych powłok, które mają „podstawianie procesów”):
Jeśli nie
<(…)
jest dostępne „podstawienie procesu” , użyj grep jako filtru:Poniżej znajduje się szczegółowy opis każdej części rozwiązania.
Wartości bajtów z liczb szesnastkowych:
Twój pierwszy problem jest łatwy do rozwiązania:
Zmień górną
X
na dolnąx
i użyj printf (dla większości powłok):Albo użyj:
Dla tych powłok, które zdecydują się nie implementować reprezentacji „\ x”.
Oczywiście, przełożenie hex na ósemkowe będzie działać na (prawie) dowolnej powłoce:
Gdzie „$ sh” jest dowolną (rozsądną) powłoką. Ale trudno jest poprawnie go podać.
Pliki binarne.
Najbardziej niezawodnym rozwiązaniem jest transformacja pliku i sekwencji bajtów (oba) na kodowanie, które nie ma problemów z nieparzystymi wartościami znaków, takimi jak (nowa linia)
0x0A
lub (bajt zerowy)0x00
. Oba są dość trudne do prawidłowego zarządzania za pomocą narzędzi zaprojektowanych i przystosowanych do przetwarzania „plików tekstowych”.Transformacja taka jak base64 może wydawać się poprawna, ale przedstawia problem polegający na tym, że każdy bajt wejściowy może mieć maksymalnie trzy reprezentacje wyjściowe, w zależności od tego, czy jest to pierwszy, drugi czy trzeci bajt pozycji mod 24 (bitów).
Przekształcenie heksadecymalne.
Właśnie dlatego najbardziej niezawodną transformacją powinna być taka, która rozpoczyna się na każdej granicy bajtów, podobnie jak prosta reprezentacja HEX.
Możemy uzyskać plik z reprezentacją szesnastkową pliku za pomocą dowolnego z tych narzędzi:
W tym przypadku sekwencja bajtów do przeszukiwania jest już szesnastkowa.
:
Ale można go również przekształcić. Oto przykład szesnastkowego pojemnika szesnastkowego w obie strony:
Wyszukiwany ciąg może być ustawiony z reprezentacji binarnej. Każda z trzech opcji przedstawionych powyżej od, hexdump lub xxd są równoważne. Pamiętaj tylko, aby uwzględnić spacje, aby upewnić się, że dopasowanie znajduje się na granicach bajtów (niedozwolone jest przesunięcie końcówki):
Jeśli plik binarny wygląda następująco:
Następnie proste wyszukiwanie grep wyświetli listę pasujących sekwencji:
Jedna linia?
Wszystko to można wykonać w jednym wierszu:
Na przykład wyszukiwanie
11221122
w tym samym pliku wymaga dwóch następujących kroków:Aby „zobaczyć” dopasowania:
… 0a 3131323231313232313132323131323231313232313132323131323231313232 313132320a
Buforowanie
Istnieje obawa, że grep zbuforuje cały plik, a jeśli plik jest duży, spowoduje duże obciążenie komputera. W tym celu możemy użyć niebuforowanego rozwiązania sed:
Pierwszy sed jest niebuforowany (
-u
) i służy tylko do wstrzykiwania dwóch nowych wierszy do strumienia na pasujący ciąg. Drugised
wydrukuje tylko (krótkie) pasujące linie. Wc -l policzy pasujące linie.Spowoduje to buforowanie tylko niektórych krótkich linii. Pasujące łańcuchy w drugim sed. Powinno to być dość niskie w wykorzystywanych zasobach.
Lub nieco bardziej skomplikowany do zrozumienia, ale ten sam pomysł w jednym sed:
źródło
grep
końcu załadujesz go do pamięci (tutaj dwukrotnie większy niż oryginalny plik + 1 ze względu na kodowanie szesnastkowe), więc ostatecznie będzie więcej nad głową niżpython
podejście lub tenperl
z-0777
. Potrzebujesz takżegrep
implementacji, która obsługuje wiersze o dowolnej długości (te, które obsługują,-o
zazwyczaj tak robią). W przeciwnym razie dobra odpowiedź.od -An -tx1 | tr -d '\n'
lubhexdump -v -e '/1 " %02x"'
z ciągiem wyszukiwania uniknąć również zawierające przestrzenie, ale nie widzę taką poprawkęxxd
.sed -u
(jeśli jest dostępny) służy do rozpakowywania. Oznacza to, że będzie czytał jeden bajt na raz na wejściu i od razu wysyła swoje wyjście bez buforowania. W anycase nadal będzie musiał załadować całą linię do obszaru wzorów, więc tutaj nie pomoże.Z GNU
grep
„s-P
flag (Perl-regexp)LC_ALL=C
polega na unikaniu problemów w ustawieniach wielobajtowych, w których wgrep
innym przypadku próbowano by interpretować sekwencje bajtów jako znaki.-a
traktuje pliki binarne równoważne plikom tekstowym (zamiast normalnego zachowania, gdziegrep
wypisuje tylko, czy jest co najmniej jedno dopasowanie, czy nie)źródło
grep
, aby dopasować?-a
opcję, w przeciwnym razie grep odpowieBinary file file.bin matches
na każdy plik, który grep rozpozna jako binarny.Który traktuje pliki wejściowe jako pliki binarne (brak tłumaczenia dla źródeł i kodowania, patrz Perlrun ), a następnie zapętla pliki wejściowe, nie drukując, zwiększając licznika dla wszystkich dopasowań danego heksadecymalnego (lub dowolnej postaci, patrz perlre ) .
źródło
-0ooo
).$/
z nieco innym kompromisem (użycie pamięci proporcjonalne do maksymalnej odległości między takimi sekwencjami):perl -nE 'BEGIN { $/ = "\xef\xbe\xad\xde" } chomp; $c++ unless eof && length; END { say $c }'
Dzięki GNU
awk
możesz:Jeśli którykolwiek z bajtów jest operatorem ERE, trzeba by go jednak uciec (za pomocą
\\
). Podobnie jak0x2e
to.
, które należy wprowadzić jako\\.
lub\\\x2e
. Poza tym powinien działać z dowolnymi wartościami bajtów, w tym 0 i 0xa.Pamiętaj, że nie jest to tak proste, jak tylko
NR-1
dlatego, że istnieje kilka specjalnych przypadków:RT==""
.Zauważ też, że w najgorszym przypadku (jeśli plik nie zawiera wyszukiwanego terminu), plik zostanie w całości załadowany do pamięci).
źródło
Najbardziej proste tłumaczenie, jakie widzę, to:
Gdzie użyłem
$'\xef'
jako bash ANSI powołujący (pierwotnieksh93
funkcję, teraz obsługiwane przezzsh
,bash
,mksh
, FreeBSDsh
) wersja Fisha\Xef
i wykorzystywanegrep -o ... | wc -l
do liczenia wystąpień.grep -o
wypisuje każde dopasowanie w osobnej linii.-a
Flag sprawia grep zachowywać w plikach binarnych w ten sam sposób to robi na plikach tekstowych.-F
jest dla stałych ciągów, więc nie musisz uciekać operatorom wyrażeń regularnych.Podobnie jak w twoim
fish
przypadku, nie możesz użyć tego podejścia, jeśli szukana sekwencja zawiera bajty 0 lub 0xa (nowa linia w ASCII).źródło
printf '%b' $(printf '\\%o ' $((0xef)) $((0xbe)) $((0xad)) $((0xde))) > hugohex'
byłoby najbardziej przenośną metodą „czystej powłoki”. Oczywiście:printf "efbeadde" | xxd -p -r > hugohex
wydaje się najbardziej praktyczną metodą.Możesz użyć
bytes.count
metody Pythona, aby uzyskać całkowitą liczbę nie nakładających się podciągów w bajtowaniu.Ten jednowierszowy ładuje cały plik do pamięci, więc nie jest najbardziej wydajny, ale działa i jest bardziej czytelny niż Perl; D
źródło
239I$ 190I$ 173I$ 222I$ HXA ERfile$Y 0UC <:S^EQA$; %C$> QC=
(gd & r)mmap()
plik w Pythonie ; to zmniejszy zatwierdzenie pamięci.źródło
Myślę, że możesz użyć Perla, spróbuj:
Polecenie Zamień
s
podaje liczbę wykonanych zamian, -0777 oznacza, że nie traktuj nowej linii jako znaku specjalnego,e
- wykonaj komendę,say
aby wydrukować, co będzie dalej, wypisz znak nowej linii,n
nie do końca zrozumiałem, ale nie działa bez - z dokumenty:źródło