Найти - Пользователи
Полная версия: Прошу помощи (распарсить бинарник элементарной структуры)
Начало » Python для новичков » Прошу помощи (распарсить бинарник элементарной структуры)
1
Dumonde
Всем доброго времени суток !
Возникла необходимость срочно написать небольшой скрипт именно на Python, а т.к. знания языка у меня пока полный ноль, обращаюсь к знатокам.
На входе - бинарный файл (стоковая прошивка одного из устройств на базе Android).
Структура очень проста (с нулевой позиции):
00 00 00 00 - 'Тег' (всегда 0x00 0x00 0x00 0x00);
0F 00 00 00 - 'Длина пути и имени файла' (unsigned int);
00 20 79 01 - 'Размер файла' (unsigned int);
xx xx xx xx - 'Путь и имя файла' == 'значению' "Длина пути и имени файла"
xx xx xx xx - 'Контент' == 'значению' "Размер файла"
Пример:
0000000:  00 00 00 00 0f 00 00 00  00 20 79 01 2f 69 6d 61  '......... y./ima'
0000010:  67 65 2f 61 6d 73 73 2e  6d 62 6e 7f 45 4c 46 01  'ge/amss.mbn.ELF.'
..................
1792010:  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 0f  '................'
1792020:  00 00 00 00 80 3d 00 2f  69 6d 61 67 65 2f 62 6f  '.....=./image/bo'
1792030:  6f 74 2e 69 6d 67 41 4e  44 52 4f 49 44 21 04 f0  'ot.imgANDROID!..'
Соответственно на выходе нужно получить все каталоги и файлы (распарсить).
Знающему язык и имеющему опыт работы с бинарниками дел максимум на 15 минут.
Заранее благодарю всех откликнувшихся.
reclosedev
“Взаимоисключающе” как-то звучит:
Dumonde
Возникла необходимость срочно написать небольшой скрипт именно на Python
и
Dumonde
а т.к. знания языка у меня пока полный ноль
Не проще воспользоваться тем, в чем есть знания?

На Python будет примерно вот так:
#!/usr/bin/env python
# -*- coding: utf-8 -*-
import os
import struct
 
 
def iter_bin_image(filename):
    """ Yields name, file_size, file_data
    """
    image_size = os.path.getsize(filename)
    with open(filename, 'rb') as f:
        while f.tell() < image_size:
            tag, name_size, file_size = struct.unpack('III', f.read(12))
            name = f.read(name_size)
            yield name, file_size, f.read(file_size)
 
 
def extract_image(img_filename, output_folder='output'):
    if not os.path.exists(output_folder):
        os.makedirs(output_folder)
 
    for filename, size, data in iter_bin_image(img_filename):
        filename = filename.lstrip('/')
        filename = os.path.join(output_folder, os.path.normpath(filename))
        folder, name = os.path.split(filename)
        if not os.path.exists(folder):
            os.makedirs(folder)
        print('Saving: {} ({} bytes)'.format(filename, size))
        with open(filename, 'wb') as f:
            f.write(data)
 
 
if __name__ == "__main__":
    extract_image('data.dat')
Но тут данные одного файла читаются полностью в память. Не знаю, на сколько они большими могут быть. И ошибки никакие не проверяются.
Dumonde
Отлично, большое спасибо за столь быстрый рабочий скрипт.
reclosedev
“Взаимоисключающе” как-то звучит…
Не проще воспользоваться тем, в чем есть знания?
В данном случае не проще, т.к. есть большой скрипт именно на питоне и добавить данную потрошилку рациональнее именно в него.
reclosedev
Но тут данные одного файла читаются полностью в память. Не знаю, на сколько они большими могут быть. И ошибки никакие не проверяются.
Размер данных 100-400 Мб. Проверку попробую сам вставить по аналогии.

dehun
я честно скажу никогда раньше не занимался бинарными файлами на питонах.
стало интересно - вот что получилось.
import sys
import struct

def write_file_out(info, dirprefix):
with open(dirprefix + info['path'], 'w') as f:
f.write(info['content'])

def parse_files(binfile, prefix):
while True:
info = {}
info['name_length'] = struct.unpack('i', binfile.read(4))[0]
info['file_size'] = struct.unpack('i', binfile.read(4))[0]
info['path'] = struct.unpack('s', binfile.read(info['name_length']))
info['content'] = binfile.read(info['file_size'])
yield info

def parse_main_file(path, dirprefix):
with open(path, 'rb') as binfile:
#read tag
tag = binfile.read(4)
#parse files and write em out
try:
for fileinfo in parse_files(binfile):
write_file_out(fileinfo, dirprefix)
except:
pass

def main():
parse_main_file(sys.argv[1], sys.argv[2])

if __name__ == '__main__':
main()
а можно файлик ешё для теста? а то я написать то написал но не запускал даже - неначем =(
Dumonde
dehun
стало интересно - вот что получилось.
а можно файлик ешё для теста? а то я написать то написал но не запускал даже - неначем
Так же благодарю за оперативность. Но пока не получилось ничего извлечь с помощью данного скрипта.
Сейчас попробую под отладчиком прогнать, может быть обнаружу проблему.
На счет размера данных я выше указал, файлик 100-300 Мб. Если нужно, могу выложить чуть позже, по возвращению домой с работы.
Dumonde
Залил небольшой файлик для теста http://rghost.ru/38053132
reclosedev
Dumonde
Размер данных 100-400 Мб.
Если каждый файл может быть размером ~30 МБ, тогда лучше читать и писать частями:

#!/usr/bin/env python
# -*- coding: utf-8 -*-
import os
import struct
 
 
def extract_image(img_filename, output_folder='output'):
    if not os.path.exists(output_folder):
        os.makedirs(output_folder)
    with open(img_filename, 'rb') as img_fp:
        for filename, size in iter_bin_image(img_fp):
            filename = filename.lstrip('/')
            filename = os.path.join(output_folder, os.path.normpath(filename))
            folder, name = os.path.split(filename)
            if not os.path.exists(folder):
                os.makedirs(folder)
            print('Saving: {} ({} bytes)'.format(filename, size))
            with open(filename, 'wb') as output:
                copy_bytes(img_fp, output, size)
 
 
def iter_bin_image(img_fp):
    """
    Yields name, file_size. Allows to iter name/size
    without data consuming
    """
    image_size = os.fstat(img_fp.fileno()).st_size
    while img_fp.tell() < image_size:
        tag, name_size, file_size = struct.unpack('III', img_fp.read(12))
        name = str(img_fp.read(name_size))
        next_pos = img_fp.tell() + file_size
        yield name, file_size
        img_fp.seek(next_pos, os.SEEK_SET)
 
  
def copy_bytes(source_fp, dest_fp, bytes_to_copy, buff_size=1024*1024):
    while bytes_to_copy:
        if buff_size > bytes_to_copy:
            buff_size = bytes_to_copy
        data = source_fp.read(buff_size)
        if len(data) < buff_size:
            raise IOError('Unexpected end of file')
        dest_fp.write(data)
        bytes_to_copy -= buff_size
This is a "lo-fi" version of our main content. To view the full version with more information, formatting and images, please click here.
Powered by DjangoBB