Upgrade to Pro — share decks privately, control downloads, hide ads and more …

PythoNN: Василий Рябов – "Парсинг бинарных данн...

PythoNN: Василий Рябов – "Парсинг бинарных данных с помощью ctypes, или пишем на питоне как на Си"

Sobolev Nikita

July 22, 2023
Tweet

More Decks by Sobolev Nikita

Other Decks in Programming

Transcript

  1. Парсинг бинарных данных, или пишем на Python как на C

    Василий Рябов, Huawei, Нижний Новгород PythoNN meetup #1 14 июля 2023
  2. С_одержимое • Как не надо делать! (а по рукам?) •

    Как надо/можно делать (сахарок) • чистый Python • Python зовёт C • Python зовёт C++ • Python берёт C++ к себе • Какое оно внутри?
  3. Примеры прикладных задач • Обновить прошивку на флешке устройства •

    прочитать, изменить некоторые поля, записать обратно • Распарсить сетевой пакет: TCP/IP стек или свой протокол • хотя есть scapy ☺ • Сериализация/десериализация при плотной упаковке бит • Реверс инжиниринг / security checkers
  4. Разбор ELF файла • ELF – Executable & Linkable Format

    (Linux) • .exe • .o • .so, .ko, .mod • core dump
  5. Разбор ELF файла # readelf -h /usr/bin/python3 ELF Header: Magic:

    7f 45 4c 46 02 01 01 00 00 00 00 00 00 00 00 00 Class: ELF64 Data: 2's complement, little endian Version: 1 (current) OS/ABI: UNIX - System V ABI Version: 0 Type: DYN (Position-Independent Executable file) Machine: Advanced Micro Devices X86-64 Version: 0x1 ...
  6. А по-русски? • Порядок байт: • little-endian • big-endian •

    сетевой – это какой из них? • ABI – Application Binary Interface (почти как API, только жёстче)
  7. Как надо/можно делать (чистый Python) magic = b'\x7fELF’ elf_class =

    2 endianness = little-endian (1) elf_version = 1 os_abi = UNIX System V ABI (0)
  8. Как надо/можно делать (чистый Python) magic = b'\x7fELF’ elf_class =

    2 endianness = little-endian (1) elf_version = 1 os_abi = UNIX System V ABI (0)
  9. Как надо/можно делать (чистый Python) • Плюсы: • отладка на

    чистом Python • не зависим от версии Python и внешних библиотек • нет магических чисел и букв • Минусы: • если есть готовый C код, нужно его переписать на Python • ctypes.LittleEndianStructure • ctypes.BigEndianStructure
  10. Python зовёт С • Зовём готовый C код из бинарной

    библиотеки (.dll, .so) <_FuncPtr object at 0x7f66834831c0>
  11. Python зовёт С • Зовём готовый C код из бинарной

    библиотеки (.dll, .so) <_FuncPtr object at 0x7f66834831c0> readelf -Ws /usr/lib/x86_64-linux-gnu/libelf-0.186.so
  12. Python зовёт С • Зовём готовый C код из бинарной

    библиотеки (.dll, .so) <_FuncPtr object at 0x7f66834831c0> readelf -Ws /usr/lib/x86_64-linux-gnu/libelf-0.186.so • Для Windows есть Dependency Walker
  13. Python зовёт С • Компилируем готовый C код, обёрнутый в

    PyObject*, и зовём • .pyd / .so --> импортим как обычный Python модуль
  14. Python зовёт С++ • pybind11 (в LLVM это 556 строк:

    4 класса, 11 type casters) • Boost-Python (Boost – слишком большой) • SWIG (.i или .swig --> .c) (в LLVM это ~2K+ строк) • Все требуют компиляции для нескольких версий Python • Желательно создание .whl файлов (колёс) для PyPI
  15. Python зовёт С++ (pybind11) #include <pybind11/pybind11.h> int add(int i, int

    j) { return i + j; } namespace py = pybind11; PYBIND11_MODULE(cmake_example, m) { m.doc() = R"pbdoc(Pybind11 example plugin)pbdoc"); m.def("add", &add, R"pbdoc(Add two numbers)pbdoc"); }
  16. Python берёт C++ к себе • cppyy (си-пи-пи вай-вай!) •

    pip install cppyy • cppyy <-- cling (just-in-time, JIT) <-- clang (ahead-of-time, AOT) <-- LLVM • Из чистого Python можно: • #include <elf.h> • загрузить .c / .cpp файл • выполнить произвольное С/С++ выражение • Результат можно использовать в чистом Python
  17. Python берёт C++ к себе • cppyy (си-пи-пи вай-вай!) •

    pip install cppyy • cppyy <-- cling (just-in-time, JIT) <-- clang (ahead-of-time, AOT) <-- LLVM • Из чистого Python можно: • #include <elf.h> • загрузить .c / .cpp файл • выполнить произвольное С/С++ выражение • Результат можно использовать в чистом Python
  18. cppyy (что он умеет?) • Видит: • typedef’ы • классы

    • структуры • enum’ы • namespace’ы • template’ы • функции и методы • Не видит: • ни один #define  весит порядка 150 Мб
  19. Разбор ELF файла через cppyy e_ehsize = 64 e_entry =

    2273792 e_flags = 0 e_ident = <cppyy.LowLevelView object at 0x7f727c9527b0> e_machine = 62 e_phentsize = 56 e_phnum = 13 e_phoff = 64 e_shentsize = 64 e_shnum = 34 e_shoff = 5910760 e_shstrndx = 33 e_type = 3 e_version = 1
  20. Какое оно внутри? • ctypes – это только преобразование типов

    • сигнатура функции • libffi : FFI == Foreign Function Interface (на чистом C) • конвенция вызовов (calling convention) в рантайме • __cdecl • __stdcall • __fastcall (MSVC != clang) • …
  21. Так вот ты какой, ABI… • список экспортированных функций в

    .so файле • сигнатуры функций • конвенция вызовов • С++ name mangling (правило обезображивания имён) • exception propagation