Как я могу подсчитать, сколько раз последовательность байтов встречается в файле?

Вам нужно 'ldd busybox' и скопировать общие библиотеки, на которые он ссылается, в chroot. Используйте 'cp -L src dst' на libs, потому что они обычно упрощены.

16
16.12.2016, 21:08
8 ответов

Это запрошенное однострочное решение (для последних оболочек, которые имеют «подстановку процесса»):

grep -o "ef be ad de" <(hexdump -v -e '/1 "%02x "' infile.bin) | wc -l

Если «подстановка процесса» <(…) недоступна, просто используйте grep как filter:

hexdump -v -e '/1 "%02x "' infile.bin  | grep -o "ef be ad de" | wc -l

Ниже приводится подробное описание каждой части решения.

Байтовые значения из шестнадцатеричных чисел:

Первую проблему легко решить:

Эти escape-последовательности \ Xnn работают только в оболочке Fish.

Измените верхний X на нижний x и используйте printf (для большинства оболочек):

$ printf -- '\xef\xbe\xad\xde'

Или используйте:

$ /usr/bin/printf -- '\xef\xbe\xad\xde'

Для тех оболочек, которые предпочитают не реализовывать представление '\ x'.

Конечно, преобразование шестнадцатеричного числа в восьмеричное будет работать (почти) в любой оболочке:

$ "$sh" -c 'printf '\''%b'\'' "$(printf '\''\\0%o'\'' $((0xef)) $((0xbe)) $((0xad)) $((0xde)) )"'

Где «$ sh» - любая (разумная) оболочка. Но правильно процитировать его довольно сложно.

Двоичные файлы.

Наиболее надежным решением является преобразование файла и последовательности байтов (обоих) в некоторую кодировку, которая не имеет проблем с нечетными значениями символов, например (новая строка) 0x0A или (нулевой байт) 0x00 . И тем и другим довольно сложно правильно управлять с помощью инструментов, разработанных и адаптированных для обработки «текстовых файлов».

Преобразование, подобное base64, может показаться допустимым, но оно представляет проблему, заключающуюся в том, что каждый входной байт может иметь до трех выходных представлений в зависимости от того, является ли он первым, вторым или третьим байтом позиции mod 24 (биты).

$ echo "abc" | base64
YWJjCg==

$ echo "-abc" | base64
LWFiYwo=

$ echo "--abc" | base64
LS1hYmMK

$ echo "---abc" | base64        # Note that YWJj repeats.
LS0tYWJjCg==

Шестнадцатеричное преобразование.

Вот почему наиболее надежным преобразованием должно быть преобразование, которое начинается на границе каждого байта, как в простом HEX-представлении.
Мы можем получить файл с шестнадцатеричным представлением файла любым из следующих инструментов:

$ od -vAn -tx1 infile.bin | tr -d '\n'   > infile.hex
$ hexdump -v -e '/1 "%02x "' infile.bin  > infile.hex
$ xxd -c1 -p infile.bin | tr '\n' ' '    > infile.hex

Последовательность байтов для поиска в данном случае уже шестнадцатеричная.
:

$ var="ef be ad de"

Но это также может быть преобразовано. Пример передачи шестнадцатеричного-двоичного-шестнадцатеричного цикла туда и обратно:

$ echo "ef be ad de" | xxd -p -r | od -vAn -tx1
ef be ad de

Строка поиска может быть установлена ​​из двоичного представления. Любой из трех представленных выше вариантов od, hexdump или xxd эквивалентен. Просто убедитесь, что вы включили пробелы, чтобы обеспечить совпадение на границах байтов (сдвиг на полубайты не разрешен):

$ a="$(printf "\xef\xbe\xad\xde" | hexdump -v -e '/1 "%02x "')"
$ echo "$a"
ef be ad de

Если двоичный файл выглядит следующим образом:

$ cat infile.bin | xxd
00000000: 5468 6973 2069 7320 efbe adde 2061 2074  This is .... a t
00000010: 6573 7420 0aef bead de0a 6f66 2069 6e70  est ......of inp
00000020: 7574 200a dead beef 0a66 726f 6d20 6120  ut ......from a 
00000030: 6269 0a6e 6172 7920 6669 6c65 2e0a 3131  bi.nary file..11
00000040: 3232 3131 3232 3131 3232 3131 3232 3131  2211221122112211
00000050: 3232 3131 3232 3131 3232 3131 3232 3131  2211221122112211
00000060: 3232 0a

Затем простой поиск с помощью grep выдаст список совпадающих последовательностей:

$ grep -o "$a" infile.hex | wc -l
2

Одна строка?

Все это можно выполнить в одной строке:

$ grep -o "ef be ad de" <(xxd -c 1 -p infile.bin | tr '\n' ' ') | wc -l

Например, для поиска 11221122 в том же файле потребуются следующие два шага:

$ a="$(printf '11221122' | hexdump -v -e '/1 "%02x "')"
$ grep -o "$a" <(xxd -c1 -p infile.bin | tr '\n' ' ') | wc -l
4

Чтобы "увидеть" совпадения:

$ grep -o "$a" <(xxd -c1 -p infile.bin | tr '\n' ' ')
3131323231313232
3131323231313232
3131323231313232
3131323231313232

$ grep "$a" <(xxd -c1 -p infile.bin | tr '\n' ' ')

… 0a 3131323231313232313132323131323231313232313132323131323231313232 313132320a


Буферизация

Есть опасения, что grep будет буферизовать весь файл, и, если файл большой, создаст большой . Для этого мы можем использовать небуферизованное решение sed:

a='ef be ad de'
hexdump -v -e '/1 "%02x "' infile.bin  | 
    sed -ue 's/\('"$a"'\)/\n\1\n/g' | 
        sed -n '/^'"$a"'$/p' |
            wc -l

Первый sed не буферизован ( -u ) и используется только для вставки двух новых строк в поток для каждой соответствующей строки. Второй sed будет печатать только (короткие) совпадающие строки. Wc -l подсчитает совпадающие строки.

Это приведет к буферизации только некоторых коротких строк. Соответствующая строка (строки) во втором sed. Это должно быть довольно мало используемых ресурсов.

Или, что несколько сложнее для понимания, но та же идея в одном sed:

a='ef be ad de'
hexdump -v -e '/1 "%02x "' infile.bin  |
    sed -u '/\n/P;//!s/'"$a"'/\n&\n/;D' |
        wc -l
16
27.01.2020, 19:48
PERLIO=:raw perl -nE '$c++ while m/\xef\xbe\xad\xde/g; END{say $c}' file

Который рассматривает входной файл(ы) как двоичный (без перевода для linefeeds или кодировок, см. perlrun), затем циклирует входной файл(ы), не печатая приращение счетчика для всех совпадений заданного шестигранника (или любой другой формы, см. perlre).

6
27.01.2020, 19:48

Самый простой перевод, который я вижу:

$ echo $'\xef\xbe\xad\xde' > hugohex
$ echo $'\xef\xbe\xad\xde\xef\xbe\xad\xde' >> hugohex
$ grep -F -a -o -e $'\xef\xbe\xad\xde' hugohex|wc -l
3

Где я использовал $ '\ xef' как bash-цитирование ANSI (первоначально ksh93 , теперь поддерживается zsh , bash , mksh , FreeBSD sh ) версией Fish \ Xef и использовал grep -o ... | wc -l для подсчета экземпляров. grep -o выводит каждое совпадение в отдельной строке. Флаг -a заставляет grep вести себя с двоичными файлами так же, как с текстовыми файлами. -F предназначен для фиксированных строк, поэтому вам не нужно экранировать операторы регулярных выражений.

Как и в случае с fish , вы не можете использовать этот подход, хотя если последовательность, которую нужно искать, включает байты 0 или 0xa (новая строка в ASCII).

6
27.01.2020, 19:48

С флагом GNU grep -P (perl-regexp)

LC_ALL=C grep -oaP '\xef\xbe\xad\xde' file | wc -l

LC_ALL = C , чтобы избежать проблем в многобайтовых локали, где grep в противном случае попытался бы интерпретировать последовательности байтов как символы.

-a обрабатывает двоичные файлы, эквивалентные текстовым файлам (вместо обычного поведения, когда grep только выводит, есть ли хотя бы одно совпадение или нет)

7
27.01.2020, 19:48

Вы можете использовать метод Python bytes.count , чтобы получить общее количество неперекрывающихся подстрок в байтовой строке.

python -c "print(open('./myexecutable', 'rb').read().count(b'\xef\xbe\xad\xde'))"

Этот однострочный файл загружает в память весь файл, поэтому он не самый эффективный, но работает и более разборчив, чем Perl; D

4
27.01.2020, 19:48
tr "$(printf \\0xef)\n" \\n\\0 < infile |
grep -c "^$(printf "\0xbe\0xad\0xde")"
1
27.01.2020, 19:48

С помощью GNU awk вы можете:

LC_ALL=C awk -v 'RS=\xef\xbe\xad\xde' 'END{print NR - (NR && RT == "")}'

Если какой-либо из байтов является операторами ERE, их нужно экранировать (с помощью \\ ). Как 0x2e , то есть . необходимо ввести как \\. или \ x2e . Помимо этого, он должен работать с произвольными байтовыми значениями, включая 0 и 0xa.

Обратите внимание, что это не так просто, как просто NR-1 , потому что есть несколько особых случаев:

  • когда вход пуст, NR равен 0, NR-1 даст -1.
  • когда ввод заканчивается разделителем записей, после этого пустая запись не создается. Мы проверяем это с помощью RT == "" .

Также обратите внимание, что в худшем случае (если файл не содержит поискового запроса) файл будет загружен в память целиком).

5
27.01.2020, 19:48

Я думаю, вы можете использовать Perl, попробуйте:

perl -0777ne 'CORE::say STDOUT s/\xef\xbe\xad\xde//g' file_name  

Команда замены sдает количество сделанных замен, -0777 означает не рассматривать новую строку как специальный символ,e-выполнить команду, sayнапечатать то, что идет дальше, затем напечатать символ новой строки, nНе до конца разобрался, но без -из документации не работает:

causes Perl to assume the following loop around your program, which makes it iterate over filename arguments somewhat like sed -n or awk: LINE: while (<>) { ... # your program goes here }

2
27.01.2020, 19:48

Теги

Похожие вопросы