
Jackalope is a customizable, distributed, coverage-guided fuzzer from googleprojectzero that hunts memory-safety bugs in black-box binaries on Windows, macOS, Linux and Android for authorized security research.
| Tool | googleprojectzero/Jackalope — customizable, distributed, coverage-guided fuzzer for black-box binaries |
| Category | Coverage-guided binary fuzzing framework written in C++ |
| Primary Use | Finding memory-safety vulnerabilities in closed-source targets during authorized research, using TinyInst instrumentation and customizable mutators |
| Safe Use | Intended for authorized vulnerability research, bug bounty programs, internal product security testing, and defensive code auditing in lab environments against software you are licensed to test |
| Telemetry Note | Fuzzing runs are local; with -delivery shmem samples never touch disk, and -dump_coverage writes coverage.txt for Lighthouse — defenders observing research see repeated process launches and crash-retry cycles in instrumented builds only |
Jackalope answers a question that has long frustrated vulnerability researchers: what do you do when the target you need to fuzz has no source code available, and the mainstream coverage-guided fuzzers assume one? Most mature options in the libFuzzer and AFL lineage want to compile instrumentation into the target, which is a non-starter for closed-source applications on Windows and macOS. The few black-box fuzzers that do exist, as the README bluntly notes, sit on codebases that are hard to customize. Jackalope's design goals are explicit about this: easy customization through pluggable components, and easy parallelization both on a single machine and across a fleet of workers.
The project comes from googleprojectzero, Google's offensive security research team, which matters for how you should read it: this is tooling built by people who fuzz real, hard targets for a living, released under Apache-2.0 with roughly 1.4k stars and an active C++ codebase. The heritage shows in the architecture. Rather than a monolithic executable with flags bolted on, Jackalope is designed to be linked as a library, with users subclassing core classes and overriding virtual methods to swap in behavior specific to their target. Stand-alone mode works, but the README is clear that the library mode is where the power lives.
Coverage collection is delegated to TinyInst, another Project Zero project that performs dynamic binary instrumentation — patching coverage counters into the target at runtime without needing source or recompilation. The -instrument_module flag tells TinyInst which module to instrument, while -target_module and -target_method focus the fuzzing loop on a specific function inside the target, which combined with -persist and -loop keeps a single process alive through thousands of iterations instead of paying process-startup cost per sample. The -cmp_coverage option enables compare coverage, a well-known technique for brute-forcing through multi-byte comparisons that would otherwise block a fuzzer's path through checksum and magic-value checks.
The component model is worth reading closely because it is the real product. Fuzzer is the orchestrator, managing corpus and coverage state, scheduling jobs to threads, and talking to the server in distributed mode; its virtual methods are the documented extension point for custom fuzzers. Mutator implements a single Mutate() method but can carry per-sample context, and notably supports the meta-mutator pattern where mutators compose other mutators to build layered mutation strategies. Instrumentation wraps target execution and coverage collection, SampleDelivery handles getting bytes into the target, and PRNG supplies randomness via Mersenne twister by default. This decomposition means a researcher with an unusual target — say, a custom file format or an IPC interface — can replace exactly one piece instead of forking the whole engine.
Sample delivery ships in two flavors, selected with -delivery: file writes each mutated sample to disk and substitutes its path for the @@ placeholder in the target command line, while shmem creates a shared memory region and passes its name instead, requiring the target (or a harness you write) to read the sample from memory. The -file_extension flag handles targets that refuse files without the right suffix. The shared-memory path is faster and avoids disk churn, but the README warns that -max_sample_size must match what the target actually expects from shared memory — a mismatch there is a classic silent failure.
Distribution is handled with a refreshingly simple model. One instance runs with -start_server and becomes the coordination point; worker machines launch fuzzers pointing at it with -server. The server collects and redistributes samples, crashes, and coverage across all connected workers, which means corpus deduplication happens centrally rather than every node rediscovering the same paths. On a single box, -nthreads fans out across cores. Sessions survive restarts via -restore or -resume, supported by both fuzzer and server processes — a practical detail that matters when a multi-day campaign spans patch cycles or machine reboots.
The mutator story is honest about its limits. Jackalope does not ship advanced mutation strategies; it provides generic mutators suitable for binary formats plus a grammar-based mutation engine documented separately under mutators/grammar/, and the README explicitly encourages users to write custom mutators for their targets. Deterministic mutation is available through -deterministic_mutations and -deterministic_only, the latter being useful in distributed setups where you want one designated client exhausting the deterministic space while others explore randomly. A dictionary of target-specific tokens can be supplied with -dict, using a line-per-entry text format with \xXX escapes for binary values.
Reliability engineering gets unusual attention, which distinguishes tools that have been used on flaky real-world software from academic prototypes. -crash_retry (default 10) attempts to reproduce each crash, and anything that will not reproduce — or that only reproduces without instrumentation — is marked flaky rather than reported as a finding. The parallel -coverage_retry and -clean_target_on_coverage flags apply the same skepticism to new coverage, and samples containing only flaky coverage are simply not saved to the corpus. This triage discipline saves enormous downstream triage time and reflects the reality that nondeterministic targets will happily generate false positives all day.
Corpus management is similarly thoughtful. -minimize_samples attempts to shrink new inputs before committing them, -dry_run processes the input corpus and exits before fuzzing, which the README recommends for corpus minimization and mass crash reproduction, and -add_all_inputs forces inclusion of samples that add no new coverage when you need to preserve a curated seed set. -keep_samples_in_memory defaults on for speed, and -track_ranges enables a read-range tracking feature documented in a separate README_ranges.md file. For triage workflows, -dump_coverage periodically exports coverage.txt in a format importable into Lighthouse, the popular coverage explorer, which closes the loop between fuzzing campaigns and reverse-engineering analysis.
Platform support is broad and specific: black-box fuzzing works on Windows, macOS, Linux, and Android, with Linux additionally supporting SanitizerCoverage mode when target source is available, documented in README_sancov.md. Building requires Python 3, CMake, and a recursive clone of the TinyInst submodule; generator arguments vary per platform (-G Xcode on macOS, Visual Studio generators on Windows, extra flags for Android cross-compilation, with -DANDROID_TARGET=VM enabling shared memory delivery when fuzzing Android inside a VM exposing /dev/shm). An example invocation from the README on macOS looks like ./fuzzer -in in -out out -t 1000 -delivery shmem -instrument_module test -target_module test -target_method __Z4fuzzPc -nargs 1 -iterations 10000 -persist -loop -cmp_coverage -- ./test -m @@, with an analogous fuzzer.exe form on Windows.
Where does this fit in an authorized workflow? Jackalope is squarely a vulnerability research instrument: you point it at software you are licensed to test — your own products, bug-bounty-eligible targets, or lab builds — and use it to find memory-safety defects before an attacker does. Its value is highest in exactly the places defenders historically have the least tooling: closed-source Windows and macOS applications where source-based fuzzers cannot reach. Reports generated from its crash directories feed directly into responsible disclosure and internal remediation.
For defenders and blue teams, the takeaway is different: Jackalope represents the current baseline of what patient, well-resourced researchers can automate against any binary target, distributed across cheap worker machines, with grammar-based mutation for structured formats and compare coverage to defeat input validation. Any parsing surface in a shipped binary should be assumed fuzzable at scale. The tool itself leaves minimal footprint — with -delivery shmem it writes nothing to disk during mutation — so its relevance to defenders is less as an observable threat and more as a calibration for how much scrutiny proprietary code must withstand.
googleprojectzero/Jackalope.Educational analysis for authorized security professionals. Use only in controlled, authorized environments.
Related coverage
0 comentários:
Post a Comment
Note: Only a member of this blog may post a comment.