Skip to main content

Malstruct

Malstruct is a powerful declarative and symmetrical parser and builder for binary data that was forked from construct as of release 2.10.70.

Instead of writing imperative code to parse a piece of data, you declaratively define a data structure that describes your data. As this data structure is not code, you can use it in one direction to parse data into Pythonic objects, and in the other direction, to build objects into binary data.

The library provides both simple, atomic constructs (such as integers of various sizes), as well as composite ones which allow you form hierarchical and sequential structures of increasing complexity. Construct features bit and byte granularity, easy debugging and testing, an easy-to-extend subclass system, and lots of primitive constructs to make your work easier:

  • Fields: raw bytes or numerical types

  • Structs and Sequences: combine simpler constructs into more complex ones

  • Bitwise: splitting bytes into bit-grained fields

  • Adapters: change how data is represented

  • Arrays/Ranges: duplicate constructs

  • Meta-constructs: use the context (history) to compute the size of data

  • If/Switch: branch the computational path based on the context

  • On-demand (lazy) parsing: read and parse only what you require

  • Pointers: jump from here to there in the data stream

  • Tunneling: prefix data with a byte count or compress it

Example

A Struct is a collection of ordered, named fields:

>>> format = Struct(
...     "signature" / Const(b"BMP"),
...     "width" / Int8ub,
...     "height" / Int8ub,
...     "pixels" / Array(this.width * this.height, Byte),
... )
>>> format.build(dict(width=3,height=2,pixels=[7,8,9,11,12,13]))
b'BMP\x03\x02\x07\x08\t\x0b\x0c\r'
>>> format.parse(b'BMP\x03\x02\x07\x08\t\x0b\x0c\r')
Container(signature=b'BMP')(width=3)(height=2)(pixels=[7, 8, 9, 11, 12, 13])

A Sequence is a collection of ordered fields, and differs from Array and GreedyRange in that those two are homogenous:

>>> format = Sequence(PascalString(Byte, "utf8"), GreedyRange(Byte))
>>> format.build([u"lalaland", [255,1,2]])
b'\nlalaland\xff\x01\x02'
>>> format.parse(b"\x004361789432197")
['', [52, 51, 54, 49, 55, 56, 57, 52, 51, 50, 49, 57, 55]]

Malware Analysis

Helpers and utilities have been added to Malstruct to aid in malware analysis and configuration parser development, from simple windows structure extensions to constructs/adapters to aid in processing binary file types (e.g. PE, ELF, and Mach-O).

For example, when attempting to extract a referenced string from a 64-bit PE file the following can assist:

>>> spec = FocusLast(
    "re" / RegexSearch(
        re.compile(
            # test64.exe @ 0x14000101d
            br"""
                \x45\x33\xc9                    # xor     r9d, r9d; lpNumberOfCharsWritten
                \x41\xb8(?P<size>.{4})          # mov     r8d, 0Eh; nNumberOfCharsToWrite
                \x48\x8d\x15(?P<ro>.{4})(?P<e>) # lea     rdx, aHelloWorld; "Hello, World!\n"
                \x48\x8b\x4c\x24.               # mov     rcx, [rsp+48h+hConsoleOutput]; hConsoleOutput
                \xff\x15.{4}                    # call    cs:WriteConsoleA
                \x33\xc9                        # xor     ecx, ecx; uExitCode
            """,
            re.DOTALL | re.VERBOSE
        ),
        size=Int32ul,
        ro=Int32ul,
        e=Tell
    ),
    PEPointer64(this.re.ro, this.re.e, String(this.re.size))
)
>>> spec.parse(data, pe=pe)
'Hello, World!\n'

Alternatively to using PEPointer64, users can leverage the PEMemoryAddress adapter to perform the internal memory conversion calculation as follows:

>>> spec = FocusLast(
    "re" / RegexSearch(
        re.compile(
            # test64.exe @ 0x14000101d
            br"""
                \x45\x33\xc9                    # xor     r9d, r9d; lpNumberOfCharsWritten
                \x41\xb8(?P<size>.{4})          # mov     r8d, 0Eh; nNumberOfCharsToWrite
                \x48\x8d\x15(?P<ro>.{4})(?P<e>) # lea     rdx, aHelloWorld; "Hello, World!\n"
                \x48\x8b\x4c\x24.               # mov     rcx, [rsp+48h+hConsoleOutput]; hConsoleOutput
                \xff\x15.{4}                    # call    cs:WriteConsoleA
                \x33\xc9                        # xor     ecx, ecx; uExitCode
            """,
            re.DOTALL | re.VERBOSE
        ),
        size=Int32ul,
        ro=Int32ul,
        e=PEMemoryAddress(Tell)
    ),
    PEPointer(this.re.ro + this.re.e, String(this.re.size))
)
>>> spec.parse(data, pe=pe)
'Hello, World!\n'

PEcon

Included in malstruct is the pecon (PE file reconstruction utility) package. Please see the pecon API documentation for more information.

Changelog

The format is based on Keep a Changelog, and this project adheres to Calendar Versioning with the schema MAJOR.MINOR.YYYY0M0D.

3.0.20260803 - 2026-08-03

Added

  • HexEncoded adapter for processing hex-encoded strings. (WARNING: Must be adapted on a parsed string, e.g. GreedyString())

  • PostFixed Subconstruct for parsing data buffers that are postfixed with a data size, e.g. a 16-byte buffer post-fixed with an 8-bit size of 3 will yield the first 3 bytes of the buffer.

3.0.20260518 - 2026-05-18

Added

  • Alias VarIntl to VarInt

  • Add VarIntb for big-endian parsing

  • PEImport, PEImportPointer, and PEImportSymbol to process imported APIs from memory address references in PE files

3.0.20260429 - 2026-04-29

Changed

  • Split out core functionality across adapters, alignment, analysis, bytes_, conditional, exceptions, expr, helpers, integers, lazy, mappings, miscellaneous, stream, strings, and transforms

  • Moved binary file analysis to malstruct.binaryfiles

  • Moved remaining malstruct.utils functionality to base level

  • Added pecon utility as an installed package

  • Move from “flat” layout to “src” layout

  • Use pyproject.toml configuration file for packaging

Removed

  • Removed usage of __all__ in init

  • Removed usage of compilation feature and benchmarks

  • Removed py3compat functionality

  • Removed pefileutils and elffileutils

  • Removed functionality from machoutils unrelated to malstructs/adapters

2.10.71

Changed

  • Reverted default behavior changed by https://github.com/construct/construct/pull/1015
    • OffsettedEnd, Prefixed, FixedSize, NullTerminated, NullStriped, ProcessXor use offsets relative to the last occurrence of these subconstructs

    • To use offsets relative to the beginning of the stream set absolute=True when constructing these constructs

  • Moved optional dependencies to required dependencies

Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

malstruct-3.0.20260803.tar.gz (127.4 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

malstruct-3.0.20260803-py3-none-any.whl (114.5 kB view details)

Uploaded Python 3

File details

Details for the file malstruct-3.0.20260803.tar.gz.

File metadata

  • Download URL: malstruct-3.0.20260803.tar.gz
  • Upload date:
  • Size: 127.4 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/7.0.0 CPython/3.13.14

File hashes

Hashes for malstruct-3.0.20260803.tar.gz
Algorithm Hash digest
SHA256 f50c728f4679f4397051fcaf573099152269d226023613a8a865573e459bef3a
MD5 1d24a0b21829aba7fb7312f706933009
BLAKE2b-256 e4908a1ae2345270f4c57d1fd0903d1f734a53ce6fb96b3f51c73811cb8a053d

See more details on using hashes here.

Provenance

The following attestation bundles were made for malstruct-3.0.20260803.tar.gz:

Publisher: main.yml on ciphertechsolutions/malstruct

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file malstruct-3.0.20260803-py3-none-any.whl.

File metadata

File hashes

Hashes for malstruct-3.0.20260803-py3-none-any.whl
Algorithm Hash digest
SHA256 732e7a9e01a5c57d3b0b493144d21670e3a686002225c63bb04fdc08e2a2fa65
MD5 ea9b112250e81e11537860a2660ff3b2
BLAKE2b-256 bc9b693d2d7d4455df7bb2e361b6e7dd74ac384bd03a9367738315d8693ecd87

See more details on using hashes here.

Provenance

The following attestation bundles were made for malstruct-3.0.20260803-py3-none-any.whl:

Publisher: main.yml on ciphertechsolutions/malstruct

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

Release history Release notifications | RSS feed

This release

3.0.20260803 This release

2 files

3.0.20260518

2 files

3.0.20260429

2 files

2.10.71

2 files

2.10.70

2 files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page