Signature Maker Plugin for IDA Pro 9.0+
July 21, 2026 ยท View on GitHub
An IDA Pro 9.0+ zero-dependency cross-platform signature maker plugin with optional SIMD (e.g. AVX2/NEON/SSE2) speedups that works on macOS, Linux, and Windows. SigMaker includes processor-aware operand wildcarding for x86, x64, ARM, and MIPS binaries. The primary goal of this plugin is to work with future versions of IDA without needing to compile against the IDA SDK, while allowing easier community contributions.
Background reading on mahmoudimus.com:
- IDA Pro and Cython: super-charging the work-horse of reverse engineering: how the optional SIMD speedups were built.
- Growing a unique function signature without rescanning the binary: the search algorithm, with interactive visualizations.
- How do you know your Cython hot loop is fast enough?: how I confirmed those kernels are already optimal (memory-bound, not compute-bound).
Table of contents
- Installation
- SIMD Speedups
- Requirements
- What is a "sigmaker"?
- Usage
- Performance
- Using SigMaker as a library
- Acknowledgements
- Development & Releases
- Contact
Installation
sigmaker's main value proposition is its cross-platform (Windows, macOS, Linux) Python 3 support. It uses zero third party dependencies, making the code both portable and easy to install.
Quick Install
- Copy
src/sigmaker/__init__.pyinto the /plugins/ folder to the plugin directory! - Rename it to
sigmaker.py - OPTIONALLY, if you would like
SIMDspeedups, justpip install sigmaker - Restart IDA Pro.
From Releases
- Download the latest conveniently renamed
sigmaker.pyrelease from the Releases page - Copy it to your IDA Pro plugins directory
- OPTIONALLY, if you would like
SIMDspeedups, justpip install sigmaker - Restart IDA Pro
That's it!
Install with hcli
hcli is Hex-Rays' command-line tool, and it can install sigmaker from the IDA Plugin Repository. Install hcli once:
curl -LsSf https://hcli.docs.hex-rays.com/install | sh # macOS/Linux
iwr -useb https://hcli.docs.hex-rays.com/install.ps1 | iex # Windows (PowerShell)
Then authenticate (see the hcli docs) and install the plugin:
hcli plugin search sigmaker
hcli plugin install SigMaker
hcli installs the plugin under $IDAUSR/plugins/SigMaker and installs the
matching sigmaker wheel into IDA's Python environment. That wheel includes
the SIMD speedups for supported Python and platform combinations, so an HCLI
install does not require a separate pip install. IDA loads the plugin on its
next launch. Requires IDA 9.0+.
Need to find your plugin directory?
From IDA's Python console run the following command to find its plugin directory:
import idaapi, os; print(os.path.join(idaapi.get_user_idadir(), "plugins"))
Where and what is my default user directory?
The user directory is a location where IDA stores some of the global settings and which can be used for some additional customization. Default location:
- On Windows:
%APPDATA%\Hex-Rays\IDA Pro - On macOS:
~/.idapro - On Linux:
~/.idapro
SIMD Speedups
An HCLI install pulls in the matching sigmaker wheel automatically. For a
manual or standalone sigmaker.py install, run pip install sigmaker in
IDA's Python environment. On supported Windows, Linux, and macOS systems, pip
selects the wheel for the active CPython version and architecture. SigMaker
uses the extension automatically when it is available and shows its status in
the top-right menu bar:
Compatibility and updates
The speedup wheel is optional. SigMaker validates the required native entry points on the exact extension module it loaded before enabling SIMD. Newer wheels also declare an API range; older wheels without that declaration remain eligible when they provide the complete native contract. A stale, partial, or incompatible wheel is disabled before it can affect a search; the pure-Python implementation continues with the same results.
In interactive IDA, SigMaker shows one update prompt per process with the loaded extension path and the applicable command:
- HCLI install:
hcli plugin upgrade SigMaker - Manual or standalone install:
"<IDA Python from sys.exec_prefix>" -m pip install --upgrade "sigmaker==<plugin version>"
Restart IDA after updating. The manual command intentionally derives its
interpreter from sys.exec_prefix; embedded IDA hosts can report ida.exe as
sys.executable, which cannot run pip. If updating does not resolve the
problem, open an issue
and include the path from the prompt.
Library and headless users need no special setup. SigMaker never opens an IDA dialog outside the graphical plugin. An absent extension is bypassed silently; a discovered stale or incompatible extension is logged, then bypassed automatically.
SIMD Enabled

No SIMD Speedups

Requirements
- IDA Pro 9.0+
- IDA Python
- Python 3.10+
Processor support
SigMaker creates and searches byte signatures using IDA's decoded instructions. Its operand wildcarding has architecture-specific rules for:
- x86 and x64
- ARMv6-M Thumb (Cortex-M0)
- ARMv7 A32
- AArch64
- MIPS and MIPSEL
Other IDA processor modules use the generic operand wildcarding behavior. Architecture-specific rules keep stable opcode and register bytes exact while wildcarding address-bearing bytes according to IDA's operand metadata. The ARM variants above are exercised through checked-in ELF objects and real IDALIB decoding rather than processor mocks alone.
What is a "sigmaker"?
Sigmaker stands for "signature maker." It enables users to create unique binary pattern signatures that can identify specific addresses or routines within a binary, even after the binary has been updated.
In malware analysis or binary reverse engineering, a common challenge is pinpointing an important address, such as a function or global variable. However, when the binary is updated, all the effort spent identifying these locations can be lost if their addresses change.
To preserve this work, reverse engineers take advantage of the fact that most programs do not change drastically between updates. While some functions or data may be modified, much of the binary remains the same. Most often, previously identified addresses are simply relocated. This is where sigmaker comes in.
Sigmaker lets you create unique patterns to track important parts of a program, making your analysis more resilient to updates. By generating signatures for specific functions, data references, or other critical locations, you can quickly relocate these points in a new version of the binary, saving time and effort in future reverse engineering tasks.
Usage
In disassembly view, select a line you want to generate a signature for, and press
CTRL+ALT+S:

OR Right-Click and select SigMaker:

The generated signature will be printed to the output console, as well as copied to the clipboard:

| Signature type | Example preview |
|---|---|
| IDA Signature | E8 ? ? ? ? 45 33 F6 66 44 89 34 33 |
| x64Dbg Signature | E8 ?? ?? ?? ?? 45 33 F6 66 44 89 34 33 |
| C Byte Array Signature + String mask | \xE8\x00\x00\x00\x00\x45\x33\xF6\x66\x44\x89\x34\x33 x????xxxxxxxx |
| C Raw Bytes Signature + Bitmask | 0xE8, 0x00, 0x00, 0x00, 0x00, 0x45, 0x33, 0xF6, 0x66, 0x44, 0x89, 0x34, 0x33 0b1111111100001 |
Finding XREFs
Generating code Signatures by data or code xrefs and finding the shortest ones is also supported:

Signature searching
Searching for Signatures works for supported formats:

It also supports nibble wildcard searches such as 48 4? ?F 90:

Nibble wildcard search works with or without the optional SIMD speedups. Without the speedup wheel, SigMaker finds the longest exact-byte run with the standard library's C-speed byte search and verifies the remaining nibble masks in Python. The pattern must therefore contain at least one exact byte, and patterns whose best exact run is very common can be substantially slower without SIMD.
Just enter any string containing your Signature, it will automatically try to figure out what kind of Signature format is being used:

Currently, all output formats you can generate are supported.
Match(es) of your signature will be printed to console alongside the containing function name:

If the matched address is not a function name or has no function name, it falls back to just printing the address:

Signature Configuration
sigmaker also supports configurable wildcardable operands for unique signature creation:

There are also various options that be configured via the Other options button:

Performance
SigMaker's "find the shortest unique signature for the current function" search has been heavily optimized. On a real 16 MB module, a single worst-case function search once took 462 seconds (7.7 minutes). A stack of four optimizations brought the heaviest searches down to the tens-of-seconds range and typical ones to near-instant. One user reported the progress wait-box now "barely show[s] up for a 26 byte signature."
The full derivation, including the match-set math, the counting-sort index, the selectivity proof, and what is novel about the approach, is written up in ALGORITHM.md.
Benchmarks
Measured on the largest function (8486 bytes) of a 16 MB module via native idalib on Apple Silicon. The effects are cumulative across the four phases:
| Optimization | Effect | PR |
|---|---|---|
| Phase 1: seed-then-refine candidate refinement | ~13x faster function search | #33 |
| Phase 2: 2-byte bucket position index | additional ~2.48x on large databases, widening as the database grows | #35 |
| Phase 3: dynamic seed selection (1- or 2-byte) | per-anchor seed scans cut from 206 to 2 | #36 |
| Phase 4: Cython in-place refinement | per-byte refinement ~14 s to ~0.28 s (~50x); function total ~24 s to ~15.6 s | #36 |
Signature output is byte-identical before and after every optimization. The test suite cross-checks each fast path against a brute-force oracle and diffs the generated signatures across the entire test binary.
How it works
A short tour (see ALGORITHM.md for the math):
- Seed, then refine. The set of database matches can only shrink as a signature grows, so instead of rescanning the whole database for every candidate length, SigMaker scans once to seed a candidate set and then filters that set in place as each byte is appended.
- Index the database once. A counting-sort index over every adjacent byte pair lets the seed be drawn from the rarest exact run in the pattern, in time proportional to that run's frequency rather than to the database size. The same index serves both 1-byte and 2-byte runs for free, so the most selective anchor is always chosen.
- Push the hot loops into C. With the optional
pip install sigmakerSIMD wheel, the index build and the per-byte refinement run asnogilC over typed buffers with zero per-call allocation, and the raw byte scan uses AVX2/NEON/SSE2. Without the wheel, pure-Python fallbacks produce identical results.
Using SigMaker as a library
Beyond the IDA plugin, sigmaker is imported directly as a Python library by other tools (for example, batch signature-generation pipelines). The core types are usable from any IDAPython or idalib context:
import sigmaker
cfg = sigmaker.SigMakerConfig(
output_format=sigmaker.SignatureType.IDA,
wildcard_operands=True,
continue_outside_of_function=False,
wildcard_optimized=True,
ask_longer_signature=False,
)
result = sigmaker.SignatureMaker().make_signature(ea, cfg)
print(f"{result.signature:ida}") # IDA-style string
print(len(result.signature)) # byte length
# Cross-references:
xrefs = sigmaker.XrefFinder().find_xrefs(ea, cfg)
for gen in xrefs.signatures:
print(str(gen.address), f"{gen.signature:ida}")
Batch search API
Why batch search?
Signatures are usually maintained as a collection. After a binary update, you need to know which patterns still match, which became ambiguous, and which no longer match. Searching each pattern separately repeats scan setup and leaves you to compare the results by hand. Batch search accepts the collection once and returns one result per pattern in input order.
The batch layer adds little overhead:
- A current profile of
BatchSignatureSearcher.from_text()parsed a 5,003-line mixed paste into 5,000 entries in 16.0 ms median, or roughly 312,000 entries per second. - The requested segment buffer is loaded once and reused for every unique normalized pattern. This design follows real-binary profiling of signature generation where loading segments before every uniqueness check consumed 84% of total generation time.
- Duplicate normalized patterns share their match results, and file offsets are resolved only when a caller or formatter requests them. A performance regression test constructs a 100,000-hit batch result while asserting that search performs no file-offset lookups.
These are focused measurements of parsing, scan setup, and result construction. End-to-end search time still depends on the binary, search scope, patterns, and whether SIMD speedups are available.
Search a batch
Batch search is available to IDAPython and idalib scripts:
import sigmaker
text = """
print = "48 8B ?? ??"
update := E8 ? ? ? ? 48 89 C7
tick = 90;90;CC
draw = "48 89 C7"
48 8B ?? ?? 89
"""
results = sigmaker.BatchSignatureSearcher.from_text(text).search()
print(results) # text
print(f"{results:text}") # registered text formatter
print(f"{results:csv}") # registered CSV formatter
print(f"{results:json}") # registered JSON formatter
for result in results:
print(result.display_name, result.status, result.match_count)
print(result.raw_pattern, result.search_pattern, result.normalized_signature)
for hit in result.matches:
file_offset = result.file_offset_for_match(hit)
file_offset_text = (
"unavailable" if file_offset is None else f"0x{file_offset:X}"
)
print(f"{hit:ea}", f"{hit:rva}", file_offset_text)
Input format
Each non-empty line is one signature:
- Unnamed pattern: Write the pattern by itself. The result is labeled with its source line.
- Named pattern: Use
name := patternorname = pattern. - Quoted pattern: Quotes are accepted only around the right-hand side of a
named pattern, as in
update = "E8 ? ? ? ? 48". - Comments:
#and//comments are ignored outside quoted strings. Markdown fence lines are also skipped, which makes snippets from issues and notes safe to paste. - Entry boundary: Only a newline starts a new entry. A semicolon remains a
byte separator, so
tick := 90;90;CCis one signature. - Source text: SigMaker does not parse declarations, infer signatures from prose, or join a signature across multiple lines.
SignatureSearcher.from_many(text) returns the parsed per-pattern searchers if
you need to inspect names and source lines before searching.
BatchSignatureSearcher.from_text(text) uses it internally.
Each line uses the same strict parser as regular signature search. Supported forms include:
48 8B ? ?? 4? ?F
488B??9090
488B4??F90
48,8B;?;90
\x48\x8B\x00\x48\x89 xx?xx
0x48, 0x8B, 0x00, 0x48, 0x89 0b11011
(48 8B ? 90)
Compact input may omit separators only when the complete pattern consists of
two-character cells: HH, ??, H?, or ?H. For example, 488B??9090
parses as 48 8B ? 90 90.
The strict parser rejects ambiguous input instead of guessing:
- Single nibbles, such as
E 8 4. - Odd-length compact input, such as
488B?9090. - Input that mixes glued and separated cells, such as
488B ?? 90. - Long integer notation, such as
0x488B9090. - Colon, pipe, or hyphen separators.
- Declaration text and random prose.
An invalid pattern becomes an error on that entry; it does not abort the rest
of the batch. Every searchable pattern must contain at least one exact byte.
An all-wildcard pattern such as ?? ?? ?? is rejected because it matches almost
everywhere and is not a useful search key.
Results
BatchSearchResults is iterable and preserves input order. list(results)
returns the per-pattern SearchResults objects. Each entry contains:
- Identity and input
nameandsource_lineidentify the entry.raw_patterncontains the extracted user input.
- Parsed patterns
search_patternis the parsed SigMaker pattern used for display.normalized_signatureis the canonical matcher and cache pattern.signature_strremains a compatibility alias forsearch_pattern.
- Outcome
statusismatched,no_matches, orerror.matchesis the list of matching addresses.errorcontains the per-entry failure, when present.
Nibble masks such as 4? and ?F are preserved in both parsed and normalized
patterns. A full-byte wildcard displays as ? in search_pattern and as ??
in normalized_signature.
Match addresses and offsets
Each Match still behaves like an int, so existing address-based code keeps
working. It also carries optional RVA and file-offset metadata and supports:
f"{hit:ea}"for the absolute effective address.f"{hit:rva}"for the module-relative virtual address.f"{hit:fileoffset}"for the input-file offset.
Search populates the RVA directly. File offsets are lazy because IDA resolves
them one address at a time. Call result.file_offset_for_match(hit), or let a
formatter resolve the hits it emits. Each result caches both successful and
unavailable lookups.
Batch search itself performs no file-offset lookups. The text formatter resolves
only previewed hits; CSV and JSON resolve every exported hit. :rva and
:fileoffset do not fall back to :ea, since that would label an absolute
address as a derived offset. When the requested metadata is absent from the
Match, formatting returns repr(hit), which still includes the effective
address.
Scope, buffer reuse, and cancellation
- With SIMD speedups, a batch lazily copies the requested segment or all segments once, then reuses that buffer for every unique normalized pattern.
- Without SIMD speedups, nibble wildcard patterns use the same shared-buffer path. Ordinary IDA-compatible patterns continue through IDA's native search.
batch.search(scope_ea=ea)limits the scan to the containing segment, using the same fallback policy as ordinary signature search.- A caller may pass a preloaded
buf. Supplying bothbufandscope_eais rejected because SigMaker cannot prove that the buffer represents that segment. - Canceling a batch raises
UserCanceledError. Partial hits are not cached or exported as a completed entry.
GUI and headless embedding
SignatureSearcher and BatchSignatureSearcher select their default services
once from the host:
| Host | Search progress and cancellation |
|---|---|
Graphical IDA (idaapi.is_idaq() is True) | IDA wait box and Cancel button |
| idalib | Headless no-op services |
Missing or failed is_idaq() probe | Headless no-op services |
The interactive plugin also installs IDA-backed services explicitly while it
runs an action. Direct calls from IDAPython therefore retain their existing UI,
while idalib and stripped embedders do not call idaapi.user_cancelled().
Under idalib, no search-specific configuration is required. Initialize IDA before importing SigMaker, then use the same search API:
import idapro
import sigmaker
results = sigmaker.BatchSignatureSearcher.from_text(text).search()
To force headless search even inside graphical IDA, scope an empty service set to the operation:
with sigmaker.UIServices.use(sigmaker.UIServices()):
results = sigmaker.BatchSignatureSearcher.from_text(text).search()
An embedder may instead provide its own progress context or cancellation predicate. The predicate is resolved once before each scan loop and polled at the existing cancellation stride, so injection does not add a context lookup to every match:
services = sigmaker.UIServices(
cancel_requested=cancel_event.is_set,
)
with sigmaker.UIServices.use(services):
results = sigmaker.BatchSignatureSearcher.from_text(text).search()
progress is any callable that accepts the progress message and returns a
context manager. Within an explicitly constructed UIServices, omitted fields
use their headless no-op defaults.
UIServices isolates search-side UI behavior. It does not remove the plugin's
Form, action, or menu classes from the full module; producing a distributable
engine-only source file remains a separate packaging concern.
Display and export
str(results) and f"{results:text}" use the human-readable text formatter.
BatchSearchResults.display() writes that format to idaapi.msg by default, or
writes any selected formatter to a text file-like object or callable sink:
import io
buf = io.StringIO()
results.display(output=buf, formatter="json")
payload = buf.getvalue()
The built-in formats are:
textand.txtfor a human-readable summary with a bounded match preview.csvand.csvfor spreadsheets and line-oriented tooling.jsonand.jsonfor structured interchange.
The built-ins intentionally stop at common interchange formats. Project-specific
layouts belong in registered formatters, so a team can decide how to name
symbols, handle ambiguous matches, and emit EAs, RVAs, or file offsets without
changing SigMaker core. Loadable examples live in examples/.
Custom batch search formatters
Output conventions vary between reverse-engineering projects. One project may
need debugger labels, another C declarations, and another a module-relative map
for a loader. The formatter registry lets those conventions remain local while
still supporting results.format(...), f-strings, display(), and suffix-based
file export.
Register a formatter with a name and any file suffixes it owns:
import sigmaker
@sigmaker.BatchSearchFormatter.register("labels", suffixes=(".labels",))
class LabelFormatter:
def format(self, results: sigmaker.BatchSearchResults) -> str:
lines = []
for result in results:
if len(result.matches) != 1:
continue
name = result.name or result.display_name
hit = result.matches[0]
address = f"{hit:rva}" if hit.rva is not None else f"{hit:ea}"
lines.append(f"{name}: {address}")
return "\n".join(lines) + "\n"
After registration, results.format("labels") and f"{results:labels}" use the
formatter by name. Exporting to something.labels selects it by suffix.
Formatter classes are instantiated once at registration time; formatter objects
can be registered the same way.
Registration safety
Formatter registration guards existing behavior, including the built-in
text, csv, and json formats:
- Formatter names and export suffixes must be unique.
- A conflicting registration raises
ValueErrorbefore either registry is changed. The error points tooverride=Truewhen replacement is intentional. override=Trueis required to replace an existing name or rebind an existing suffix:
@sigmaker.BatchSearchFormatter.register(
"labels",
suffixes=(".labels",),
override=True,
)
class ReplacementLabelFormatter:
...
One override=True covers both the formatter name and its listed suffixes.
Existing suffixes that already point to a replaced name remain valid. This
makes replacement explicit without silently disconnecting other suffixes.
Loading personal formatters
To install a formatter permanently, paste its registration code into
$IDAUSR/idapythonrc.py. IDA sources that file during startup, so personal and
project-specific formats are available in each new IDA session.
If sigmaker is not already importable from your IDA Python environment, add
the SigMaker plugin directory to sys.path before the formatter code.
See
examples/batch_search_c_formatter.py
for a complete C-style formatter template that emits absolute EAs, RVAs, and
file offsets while keeping C output out of the built-in format list.
Stability contract
If you embed sigmaker, you can rely on the following. These guarantees are
checked before any change to the public surface:
-
Append-only configuration
SigMakerConfigfields are never reordered or removed.- New behavior arrives as new fields with safe defaults, so existing constructions keep working.
-
Stable public names
- Signature generation
SignatureMakerSigMakerConfigSignatureType- Stable members:
IDA,x64Dbg,Mask, andBitMask.
- Stable members:
Signature- Stable behavior:
__len__and__format__.
- Stable behavior:
GeneratedSignature- Stable attributes:
signature,address,status, andmatch_count.
- Stable attributes:
GenerationPolicyGenerationStatus
- Cross-reference generation
XrefFinderXrefGeneratedSignature- Stable attribute:
signatures.
- Stable attribute:
- Signature search
SignatureSearcher- Stable attributes:
input_signature,name, andsource_line.
- Stable attributes:
SearchResults- Stable attributes:
matches,signature_str,raw_pattern,search_pattern, andnormalized_signature. - Stable method:
file_offset_for_match.
- Stable attributes:
Match- Stable attributes:
address,rva, andfile_offset. __str__returns the hexadecimal address.__format__supportsea,rva, andfileoffset.
- Stable attributes:
UIServices- Stable attributes:
progressandcancel_requested. current()returns the services active in the current context.use(services)scopes services to one context and restores the previous value on exit.
- Stable attributes:
- Optional SIMD compatibility
SIMD_SPEEDUP_AVAILABLEreports whether a validated native backend was loaded during import.from sigmaker._speedups import simd_scanremains available when the matching optional speedups wheel is installed.
- Batch search
BatchSignatureSearcher- Stable attributes:
input_textandsearchers.
- Stable attributes:
BatchSearchResults- Stable behavior:
__str__and__format__.
- Stable behavior:
BatchSearchFormatter
- Signature generation
-
Stable method signatures
- Signature generation
SignatureMaker.make_signature(ea, cfg, end=None, *, progress_reporter=None, policy=GenerationPolicy.strict(), buf=None)
- Cross-reference generation
XrefFinder.find_xrefs(ea, cfg)XrefFinder.count_code_xrefs_to(ea)XrefFinder.iter_code_xrefs_to(ea)
- Signature search
SignatureSearcher.from_signature(input_signature, *, name=None, source_line=0)SignatureSearcher.from_many(text)UIServices.current()UIServices.use(services)
- Batch search
BatchSignatureSearcher.search(*, buf=None, scope_ea=None)
- Signature generation
-
Stable format specifications
- Signature formats keep producing their current output exactly:
f"{sig:ida}"f"{sig:x64dbg}"f"{sig:mask}"f"{sig:bitmask}"
- Batch search keeps the registered built-in formatter names:
textcsvjson
- Signature formats keep producing their current output exactly:
-
Byte-identical defaults
- Production defaults are unchanged across optimizations.
- A script that does not opt into a new flag gets byte-identical signatures to previous versions.
Used by
Projects that build on or embed the sigmaker library:
- mrexodia/ida-pro-mcp, an AI reverse-engineering MCP server (8.9k+ stars), vendors a stripped, engine-only copy of
sigmakerand exposes signature tools throughSigMakerConfig,SignatureType,SignatureMaker().make_signature, andXrefFinder().find_xrefs. - koyzdev/sigdrift is a batch signature-generation script that imports the library and calls
SignatureMaker().make_signature(ea, SigMakerConfig(...))andXrefFinder(), formatting results viaf"{sig:ida}"andf"{sig:mask}".
Building something on top of sigmaker? Open a PR or an issue and I will add it here.
Acknowledgements
Thank you to @A200K's IDA-Pro-SigMaker plugin, which served as inspiration and the basis for the initial port of this plugin. I would also like to acknowledge @kweatherman's sigmakerex as independent prior work within the SigMaker ecosystem. While the initial port did not draw from sigmakerex, members of the community later requested compatibility and feature parity with parts of its functionality (for example, see issue #17). As documented in sigmakerex's README credits, there is a long history of SigMaker authors and contributors, and I would like to thank and acknowledge them as well:
thanks to the previous creators of the original SigMaker tool back from the gamedeception.net days up to the current C/C++ and Python iteration authors: P4TR!CK, bobbysing, xero|hawk, ajkhoury, and zoomgod et al.
Thanks to Wojciech Mula for his SIMD programming resources.
Development & Releases
Coverage
The coverage badge reports the latest combined pure-Python unit and IDA
integration report from the primary IDA 9.3 test job. Both IDA 9.3 and IDA 9.2
jobs enforce a 90% line-and-branch coverage floor with coverage.py; the
badge is a convenient summary, while the CI gate is authoritative. The
workflow authenticates its Codecov upload with GitHub OIDC, so maintainers do
not need to copy a Codecov token into repository secrets. Forked pull requests
use Codecov's public tokenless path when available; coverage upload is
non-blocking, while the local 90% CI gate remains authoritative.
Contributing
- Fork the repository
- Create a feature branch
- Make your changes
- Test thoroughly
- Submit a pull request
The version lives in one place, __version__ in src/sigmaker/__init__.py. To keep ida-plugin.json in step with it automatically, enable the repo's git hook once per clone:
git config core.hooksPath .githooks
The pre-commit hook (.githooks/pre-commit) runs
tools/sync_plugin_version.py, which copies __version__ into both the HCLI
manifest version and its exact sigmaker==VERSION dependency, then stages the
manifest. CI checks the same contract as a backstop for commits that skip the
hook.
Contact
ping me on x @mahmoudimus or you may contact me from any one of the addresses on mahmoudimus.com.