@keithzetterstrom

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

Сигнатуры, записанные в шестнадцатеричном коде, являются регулярными выражениями. Например: df26.*cc08d.*2b
Проблема в том, что при переводе сигнатуры из шестн. системы в байтовую строку создается определенный порядок байт (\xdf&.*\x0c\xc0\x8d.*+.*), который может отличаться от записи в файле. Проще говоря, мы не знаем, где должны стоять "\x". Подскажите, пожалуйста, есть ли какой-либо способ решения этой проблемы?
  • Вопрос задан
  • 1389 просмотров
Пригласить эксперта
Ответы на вопрос 2
@TopestKek
"df26.*cc08d.*2b" это явно не hex число, это строка.
>>> s = b'df26.*cc08d.*2b'
>>> [s[i] for i in range(len(s))]
[100, 102, 50, 54, 46, 42, 99, 99, 48, 56, 100, 46, 42, 50, 98]

Вот они, циферки) В каждой из них по 2 байта. Да, мы однозначно не знаем как именно они разобьются на байты. А еще мы можем их перевести в биты
>>> [bin(s[i]) for i in range(len(s))]
['0b1100100', '0b1100110', '0b110010', '0b110110', '0b101110', '0b101010', '0b1100011', '0b1100011', '0b110000', '0b111000', '0b1100100', '0b101110', '0b101010', '0b110010', '0b1100010']

А если их еще и слепить, то у нас будет однозначная строка для поиска
>>> ''.join(["{0:b}".format(s[i]) for i in range(len(s))])
'110010011001101100101101101011101010101100011110001111000011100011001001011101010101100101100010'
Ответ написан
Комментировать
@insolor
Если файлы не слишком большие (полностью помещаются в память), можно грузить в память, и матчить целиком.

Пример, если достаточно найти одно совпадение:

import re

pattern = b'\x80.A.+\x80.Z' # cmp reg8, 'A' ..... cmp reg8, 'Z'

with open("file.bin", 'rb') as f:
    match = re.search(pattern, f.read())
    if match:
        print(hex(match.start()), match.group(0))  # выводим смещение от начала файла и найденное совпадение
    else:
        print('Nothing found')


Вывод:

0x2cb3b4 b'\x80\xf9A|\x05\x80\xf9Z'

Если нужно найти все совпадения:

with open("file.bin", 'rb') as f:
    for match in re.finditer(pattern, f.read()):
        print(hex(match.start()), match.group(0))


Вывод:

0x2cb3b4 b'\x80\xf9A|\x05\x80\xf9Z'
0x444184 b'\x80\xf9A|\x05\x80\xf9Z'


Тестировалось на файле размера порядка 10 МБ, нужные мне совпадения находит мгновенно.

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

Нужно использовать два буфера, т.к. начало совпадения может оказаться внутри одного буфера, а конец внутри другого. На каждом этапе два соседних буфера складываются, поиск происходит внутри объединенного буфера.

Пример реализации:

block_size = 512*1024**2  # 512 мегабайт

block1 = b''

with open('file.bin', 'rb') as f:
    while True:
        block2 = f.read(block_size)
        if not block2:  # Достигнут конец файла
            match = None
            break
        match = re.search(pattern, block1 + block2)  # Не тестировалось на больших файлах, сцепление длинных байтовых строк в цикле может тормозить!
        if match:
            break
        block1 = block2

if match:
    print(hex(match.start()), match.group(0))
else:
    print('Nothing found')
Ответ написан
Комментировать
Ваш ответ на вопрос

Войдите, чтобы написать ответ

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