Skip to content

Releases: BinFlip/nsis-rs

v0.3.0

04 Jun 02:45

Choose a tag to compare

[0.3.0] - 2026-06-03

Added

  • NsisInstaller::builder() returning a NsisInstallerBuilder for configuring
    a parse, plus NsisInstallerBuilder::max_decompressed_size() to set the
    decompression budget. NsisInstaller::from_bytes() is retained as a
    convenience that parses with default limits.
  • NsisInstaller::max_decompressed_size() accessor and the
    NsisInstaller::DEFAULT_MAX_DECOMPRESSED_SIZE constant (64 MiB).
  • decompress::DecodeLimit, an enum that makes the three real decode intents
    explicit: Exact(n) (known size — stop at n, ignore trailing input),
    Capped(n) (unknown size — decode to end-of-stream, error if it exceeds
    n), and Truncate(n) (unknown size — decode to end-of-stream, stop at n
    without error). Includes a size() accessor.
  • Error::OutputTooLarge { limit }, returned when a Capped stream would
    expand past its budget.

Changed

  • Breaking: decompression budgets are now configurable instead of guessed.
    The previous max(compressed_size * 10, 64 MiB) heuristic — used for embedded
    files, the solid file-data stream, and uninstaller overlays — is replaced by a
    single budget threaded from NsisInstaller::builder().max_decompressed_size().
  • Breaking: decompress::decompress_block now takes a single limit: DecodeLimit argument in place of the previous max_output: usize and
    expected_size: Option<usize> parameters.
  • Breaking: decompress::{decompress_deflate, decompress_bzip2, decompress_lzma} now take limit: DecodeLimit in place of their
    max_output / expected_size parameters.
  • Breaking: Error gained the OutputTooLarge variant; exhaustive matches
    on Error must handle it.
  • Over-budget extracted artifacts (files, uninstallers) now fail with
    Error::OutputTooLarge instead of being silently truncated, so callers no
    longer receive partial data reported as success.
  • The LZMA decoder is now bounded during decompression via a size-limited
    writer rather than decoding fully and truncating afterward.

Fixed

  • Extract LZMA non-solid embedded files correctly. Per-file LZMA streams carry
    no stored uncompressed size and terminate with an end-of-stream marker;
    passing a fixed expected size made lzma-rs reject the marker
    ("Expected unpacked size of N but decompressed to M"), so every file in
    affected installers was dropped. Unknown-size streams now rely on the EOS
    marker.
  • Apply the same end-of-stream fix to the uninstaller-overlay decode path,
    which carried the identical latent defect.

v0.2.1

21 May 19:59

Choose a tag to compare

Fixed

  • Handle packed NSIS installers whose FirstHeader is structurally valid but
    not 512-byte aligned relative to the PE overlay, as seen in Adload samples.
  • Report out-of-bounds EW_EXTRACTFILE payloads as iterator errors instead
    of exposing malformed file records as empty extracted files.

v0.2.0

20 May 21:41

Choose a tag to compare

Added

  • NsisInstaller::format_entry(&Entry) -> String for rendering an opcode
    mnemonic plus decoded parameters as a script-like line.
  • NsisInstaller::format_entry_params(&Entry) -> String for consumers that
    need reusable opcode-aware parameter formatting without reimplementing the
    string/variable/jump decoding rules.
  • NsisInstaller::script_analysis() for script-level control-flow analysis,
    including roots, functions, basic blocks, edges, entry-to-block mapping, and
    diagnostics for invalid targets.
  • Symbol-aware formatting via format_entry_with_analysis() and
    format_entry_params_with_analysis(), which annotate jump and call targets
    with names from ScriptAnalysis.

Changed

  • The dump example now uses the crate-provided instruction formatting API.
  • The dump example uses script analysis symbols when rendering control-flow
    targets.
  • Opcode metadata now matches NSIS operand layouts more closely for stack,
    UI, execution, registry, and file I/O instructions.

Fixed

  • EW_PUSHPOP formatting now treats param0 as a variable for Pop and as a
    string for Push, preventing variable IDs from being decoded as offsets into
    unrelated strings such as ProgramFilesDir.
  • Corrected high-level accessor layouts for ShellExecOp and RegDelete.
  • Corrected several opcode parameter layouts and types, including
    EW_GETFULLPATHNAME, EW_INTOP, EW_INTFMT, EW_FINDWINDOW,
    EW_SENDMESSAGE, EW_GETDLGITEM, EW_SHELLEXEC, EW_DELREG, EW_FOPEN,
    EW_FSEEK, and EW_FINDFIRST.

v0.1.2

05 May 02:01

Choose a tag to compare

Added

  • fmt::Display implementations for the public diagnostic enums so
    consumers no longer need format!("{:?}", ...):
    • CompressionMethod"deflate", "bzip2", "lzma", "none".
    • CompressionMode"solid", "non-solid".
    • StringEncoding"ANSI", "Unicode", "Park".
    • NsisVersion"NSIS 1", "NSIS 2", "NSIS 3", "NSIS Park".
    • RegValueType"REG_SZ", "REG_EXPAND_SZ", "REG_BINARY",
      "REG_DWORD", "REG_MULTI_SZ", "REG_UNKNOWN(N)".
  • Callback enum (src/installer/callback.rs) covering the ten
    common-header callback slots (OnInit, OnInstSuccess,
    OnInstFailed, OnUserAbort, OnGuiInit, OnGuiEnd,
    OnMouseOverSection, OnVerifyInstDir, OnSelChange,
    OnRebootFailed). Provides:
    • Callback::ALL — all ten variants in common-header order.
    • Callback::name() — canonical NSIS script name (e.g. ".onInit").
    • Callback::index() — slot index (0..10) into the common-header
      callback array.
    • fmt::Display — delegates to name().
  • NsisInstaller::callback(Callback) -> Option<usize> — generic
    accessor that returns the entry index for any callback slot,
    complementing the existing per-callback on_init() / on_inst_success()
    / etc. methods.
  • All standard EW_* opcode constants are re-exported at the crate root,
    so upstream analysis crates can match opcode numbers without importing
    the internal opcode module.
  • Section::contains_entry(usize) -> bool — returns true when the
    given entry index falls within the section's [code, code+code_size)
    range. Treats negative code/code_size as zero, so consumers no
    longer need defensive .max(0) casts before doing range checks.
  • NsisInstaller::section_contains_entry(section_idx, entry_idx) -> bool
    — convenience wrapper that resolves the section by index and applies
    Section::contains_entry.

Changed

  • The clippy panic-prevention lint set
    (unwrap_used, expect_used, panic, arithmetic_side_effects,
    indexing_slicing) is now declared deny in
    Cargo.toml [lints.clippy], so the policy holds regardless of the
    consuming workspace. Previously only missing_docs and unsafe_code
    were denied (in src/lib.rs).
  • Triage and clearance of the 310 lint violations exposed by the new
    policy. Affected files (paths relative to src/):
    addressmap.rs, decompress/{bzip2,lzma,mod}.rs,
    header/{blockheader,commonheader,firstheader,mod}.rs,
    installer/{analysis,files,nsisinstaller}.rs,
    nsis/{ctlcolors,entry,langtable,page,section}.rs,
    opcode/mod.rs,
    strings/{ansi,mod,park,unicode}.rs,
    util.rs.
    Patterns applied:
    • &[u8] indexing → .get(...) with Option/Result propagation.
    • Offset/size arithmetic → checked_add / checked_sub /
      saturating_*.
    • .unwrap() / .expect() on parse steps → ?-propagated
      Error arms.
      Tests retain the convenience of unwrap/expect/panic via the
      cfg_attr(test, allow(...)) escape hatch in src/lib.rs.

Fixed

  • Carried forward from in-progress work on main: park opcode mapping
    correction and uninstaller data extraction (see commit 1778513).

v0.1.1

07 Apr 00:41

Choose a tag to compare

fix: park opcode mapping
fix: uninstaller data extraction

v0.1.0 — Initial release

05 Apr 16:19

Choose a tag to compare

Parse and inspect NSIS installer binaries from pure safe Rust.

Highlights:

Complete NSIS format support: deflate, LZMA, bzip2 × solid/non-solid
ANSI, Unicode, and Park string encodings
Zero-copy file extraction with decompression
Malware analysis iterators: plugin calls, exec commands, registry ops,
shortcuts, uninstallers with recursive parsing
71 opcodes with typed parameter annotations
All callbacks exposed (onInit is the #1 malware indicator)
175 tests across 8 self-built fixtures
No unsafe code, no panics, fully documented