200kB файл для поиска 8! (40320 перестановок) в Python или IDA
Я работаю над разборкой прошивки (процессор Siemens C165 - https://www.infineon.com/dgdl/Infineon-C165-DS-v02_00-en%5B8%5D.pdf?fileId=db3a304412b407950112b43a49a66fd7) в IDA.
У меня есть прошивка, поэтому я также могу прочитать ее через Python.
Мне нужно найти строку перестановки
0, 1, 2, 3, 4, 5, 6, 7 (0-7)
Написал эту простую программу:
from itertools import permutations
l = list(permutations(range(0,8)))
print(len(l))
with open("firm.ori", 'rb') as f:
s = f.read()
for i in l:
hexstr = '\\x'.join('{:02x}'.format(x) for x in i)
hexstrfinal = "\\0x" + hexstr
#print hexstrfinal
if(s.find(b'hexstrfinal')>0):
print "found"
Тем не менее, он не находит ничего
Я думал, что последовательность будет рядом друг с другом, но, возможно, нет.
Хочется просто быть уверенным, что программа правильная.
Ну, на самом деле 0-7 должно быть клевом, значит ли это, что мне нужно искать, например, как одну комбинацию:
0x01, 0x23, 0x45, 0x67
Выше это байты.
Может кто-нибудь подтвердить это и посоветовать, как это искать?
Обновление 1:
Пробовал 2-й вариант
from itertools import permutations
l = list(permutations(range(0,8)))
print(len(l))
with open("firm.ori", 'rb') as f:
s = f.read()
for item in l:
str1 = ''.join(str(e) for e in item)
n = 2
out = [str1[i:i+n] for i in range(0, len(str1), n)]
hexstr = '\\x'.join(e for e in out)
hexstrfinal = "\\x" + hexstr
#print hexstrfinal
if(s.find(b'hexstrfinal')>0):
print "found"
Но тоже нет хитов...
Есть идеи, что я делаю не так?
2 ответа
В вашем коде есть несколько недоразумений и большая неэффективность. Давайте начнем с недоразумений.
поскольку firm.ori
открывается в двоичном режиме (rb
), результат s = f.read()
это bytes
объект. Несмотря на то, что методы похожи на строку, это не строка! Содержит байты, а не символы. Когда вы отображаете это, \x...
выходы не указывают на то, что bytes
объект, содержащий обратную косую черту ASCII и xes. Вместо этого каждый \x...
является escape-последовательностью, используемой для представления шестнадцатеричного значения данного байта, которое не соответствует печатному символу ASCII.
Внутри вашего цикла вы имеете дело исключительно со строками: hexstr = '\\x'.join('{:02x}'.format(x) for x in i)
берет вашу перестановку и форматирует ее, чтобы выглядеть как строковое представление bytes
объект. Надеюсь, вы поняли из предыдущего параграфа, почему это не сработает.
s.find(b'hexstrfinal')
ищет буквенный массив ASCII b'hexstrfinal'
не для переменной с именем hexstrfinal
, Последний не будет работать, конечно, потому что hexstrfinal
имеет тип str
не bytes
, Если бы вы должны были преобразовать его в bytes
используя простой hexstrfinal.encode('ascii')
, вы получите b'\\x...'
, что совсем не то, что вы хотите. Правильный путь будет
s.find(hexstrfinal.encode('ascii').decode('unicode-escape').encode('latin1'))
Надеюсь, вы поймете, почему излишне неэффективно преобразовывать строку три раза, чтобы получить bytes
ты хочешь. Каждый раз, когда вы начинаете использовать строки в качестве опоры для манипулирования числами, самое время оценить ваш подход. Это начинает наше обсуждение неэффективности в вашем коде.
В настоящее время вы пытаетесь перебрать все возможные перестановки 0-7 вместо того, чтобы искать перестановки, которые на самом деле есть. Учитывая, что размер файла составляет всего 200 КБ, было бы неразумно ожидать, что в нем появятся все или даже большинство перестановок. Кроме того, вы ищете во всем файле для каждой возможной перестановки. Для размера файла N
а также K
перестановки, ваш код работает в O(N * K)
время, в то время как это можно сделать за один проход через файл, или O(N)
, При наличии соответствующих структур данных даже цикл, написанный на простом Python, скорее всего будет работать быстрее, чем оптимизированная версия текущего кода.
Стратегия проста. Итерация по s
, Если текущий персонаж и следующие семь составляют правильную перестановку, начните вечеринку. В противном случае продолжайте искать:
N = 8
allowed = set(range(N))
for index, b in enumerate(s):
if b in allowed and set(s[index:index + N]) == allowed:
print(f'Found sequence {s[index:index + N]} at offset {index}')
Здесь возможен целый ряд возможных оптимизаций, и вы могли бы сделать все это более эффективно с помощью numpy или scipy.
Вещи также станут более сложными, если вы разрешите повторения в последовательности. В этом случае вам придется отсортировать последовательности:
allowed = sorted(...)
N = len(allowed)
for index, b in enumerate(s):
if b in allowed and sorted(s[index:index + N]) == allowed:
print(f'Found sequence {s[index:index + N]} at offset {index}')
Если вы собираетесь искать кусочки, все становится еще сложнее. Я бы бросил чек полностью b in allowed
и просто напишите пользовательскую проверку, которая может применяться на каждом полушаге:
N = 8
def build_set(items):
output = set()
for b in items:
output.add(b & 0xF)
output.add((b >> 4) & 0xF)
return output
def matches(seq):
if len(seq) == N // 2:
return build_set(seq) == allowed
elif len(seq) == N // 2 + 1:
check = build_set(seq[1:-1])
check.add(seq[0] & 0xF)
check.add((seq[-1] >> 4) & 0xF)
return check == allowed
else:
return False
allowed = set(range())
for index, b in enumerate(s):
if matches(s[index:index + N // 2]):
print(f'Found sequence {s[index:index + N // 2]} at offset {index}.0')
if matches(s[index:index + N // 2 + 1]):
print(f'Found sequence {s[index:index + N // 2 + 1]]} at offset {index}.5')
Вот, build_set
просто разбивает грызть в набор. matches
проверяет либо массив из 8 полубайтов, выровненный по байту (4 элемента), либо массив из 8 полубайтов, смещенный на половину байта (5 элементов). Оба случая сообщаются независимо.
Не понятно, что вы пытаетесь найти, но...
Каждая перестановка (0,1,2,3,4,5,6,7)
будет кортеж из семи предметов, подобный этому
t = (7, 6, 4, 1, 3, 5, 0, 2)
Вы можете сделать строки из двух элементов, как это
>>> a = [''.join(map(str,thing)) for thing in zip(t,t[1:])]
>>> a
['76', '64', '41', '13', '35', '50', '02']
Затем сделайте целые числа из строк и передайте его bytes
>>> b = bytes(map(int,a))
>>> b
b'L@)\r#2\x02'
Тогда ищи его
>>> b in s
????
Если это не находит это, это не там.
Вот десятибайтовый объект (похожий на ваш файл)
>>> b = b'\xcbl\x7f|_k\x00\x9f\xa2\xcc'
Просто так получилось:
>>> bytes([203, 108, 127, 124, 95, 107, 0, 159, 162, 204])
Поиск 3-символьных (или 3-целых) последовательностей
>>> bytes([127,94,107]) in b
False
>>> bytes([127,95,107]) in b
False
>>> bytes([124,95,107]) in b
True
>>>
Когда я использую двоичные файлы, я действительно думаю, что целые числа, а не символы.