summaryrefslogtreecommitdiff
path: root/docs/dev
diff options
context:
space:
mode:
authorBrian Potchik <brian@vector35.com>2025-11-06 00:20:24 -0500
committerBrian Potchik <brian@vector35.com>2025-11-06 00:20:24 -0500
commit6ca568ccbac91e22c9191fd945c42311a89ff9d9 (patch)
treea477eaf83b30033d3ca23be2d314fd3731585b56 /docs/dev
parent7f97cc3da7c73bb06d891870ebbfc306421892de (diff)
Add Container Browser documentation.
Diffstat (limited to 'docs/dev')
-rw-r--r--docs/dev/containertransforms.md218
-rw-r--r--docs/dev/index.md1
2 files changed, 219 insertions, 0 deletions
diff --git a/docs/dev/containertransforms.md b/docs/dev/containertransforms.md
new file mode 100644
index 00000000..f1a5a7f4
--- /dev/null
+++ b/docs/dev/containertransforms.md
@@ -0,0 +1,218 @@
+# Container Transforms
+
+Container Transforms are specialized transforms that enable Binary Ninja to extract and navigate files within container formats such as ZIP archives, disk images, and other multi-file structures. Unlike simple encoding transforms (Base64, Hex, etc.), container transforms can produce multiple output files and interact with the Container Browser UI.
+
+You can list all available container transforms (those with detection support) using:
+
+```python
+>>> [x.name for x in Transform if getattr(x, "supports_detection", False)]
+['Gzip', 'Zlib', 'Zip', 'CaRT', 'IntelHex', 'SRec', 'TiTxt', 'IMG4', 'LZFSE']
+```
+
+## Overview
+
+The Transform API provides the foundation for creating custom container decoders. Container transforms differ from standard transforms in that they:
+
+1. Support **context-aware decoding** via `perform_decode_with_context()`
+2. Can produce **multiple output files** from a single input
+3. Support **password protection** and other interactive parameters
+4. Integrate with the **Container Browser** UI for file selection
+
+## Basic Transform Structure
+
+All transforms, including container transforms, inherit from the `Transform` base class. Here's a minimal example:
+
+```python
+from binaryninja import Transform, TransformType, TransformCapabilities
+
+class MyContainerTransform(Transform):
+ transform_type = TransformType.DecodeTransform
+ capabilities = TransformCapabilities.TransformSupportsContext | TransformCapabilities.TransformSupportsDetection
+ name = "MyContainer"
+ long_name = "My Container Format"
+ group = "Container"
+
+ def can_decode(self, input):
+ """Check if this transform can decode the input"""
+ # Check for magic bytes or other signatures
+ head = input.read(0, 4)
+ return head == b"MYCN" # Your format's magic bytes
+
+ def perform_decode_with_context(self, context, params):
+ """Context-aware extraction for multi-file containers"""
+ # Implementation details below
+ pass
+
+# Register the transform
+MyContainerTransform.register()
+```
+
+## Container Extraction Protocol
+
+Container transforms typically operate in **two phases**:
+
+### Phase 1: Discovery
+
+During discovery, the transform enumerates all available files and populates `context.available_files`:
+
+```python
+def perform_decode_with_context(self, context, params):
+ # Parse the container format
+ container = parse_my_format(context.input)
+
+ # Phase 1: Discovery
+ if not context.has_available_files:
+ file_list = [entry.name for entry in container.entries]
+ context.set_available_files(file_list)
+ return False # More user interaction needed
+```
+
+Returning `False` indicates that the Container Browser should present these files to the user for selection.
+
+### Phase 2: Extraction
+
+Once the user selects files, the transform extracts them and creates child contexts:
+
+```python
+def perform_decode_with_context(self, context, params):
+ container = parse_my_format(context.input)
+
+ # Phase 1: Discovery (as above)
+ if not context.has_available_files:
+ # ... discovery code ...
+ return False
+
+ # Phase 2: Extraction
+ requested = context.requested_files
+ if not requested:
+ return False # No files selected yet
+
+ complete = True
+ for filename in requested:
+ try:
+ data = container.extract(filename)
+ context.create_child(DataBuffer(data), filename)
+ except Exception as e:
+ # Create child with error status
+ context.create_child(
+ DataBuffer(b""),
+ filename,
+ result=TransformResult.TransformFailure,
+ message=str(e)
+ )
+ complete = False
+
+ return complete # True if all files extracted successfully
+```
+
+## Complete Example: ZipPython
+
+Binary Ninja includes a reference implementation of a ZIP container transform in `api/python/transform.py`.
+
+
+
+## Transform Results and Error Handling
+
+Use `TransformResult` values to communicate extraction status:
+
+- `TransformResult.TransformSuccess`: Extraction completed successfully
+- `TransformResult.TransformNotAttempted`: Extraction not attempted
+- `TransformResult.TransformFailure`: Generic extraction failure
+- `TransformResult.TransformRequiresPassword`: File is encrypted and needs a password
+
+Set results on individual child contexts:
+
+```python
+context.create_child(
+ data=databuffer.DataBuffer(extracted_data),
+ filename="file.bin",
+ result=TransformResult.TransformSuccess,
+ message="" # Optional success message
+)
+```
+
+## Working with Passwords
+
+Container transforms should integrate with Binary Ninja's password management system:
+
+```python
+# Get passwords from settings
+passwords = Settings().get_string_list('files.container.defaultPasswords')
+
+# Check for password in transform parameters
+if "password" in params:
+ p = params["password"]
+ pwd = p.decode("utf-8", "replace") if isinstance(p, (bytes, bytearray)) else str(p)
+ passwords.insert(0, pwd)
+
+# Try each password
+for password in passwords:
+ try:
+ content = extract_with_password(container, filename, password)
+ break # Success!
+ except PasswordError:
+ continue # Try next password
+```
+
+When a file requires a password that wasn't provided, use `TransformResult.TransformRequiresPassword` to signal the UI to prompt the user.
+
+## Metadata and Virtual Paths
+
+Container transforms automatically create metadata that tracks the extraction chain:
+
+```python
+# After opening a file extracted through containers:
+>>> bv.parent_view.auto_metadata['container']
+{
+ 'chain': [
+ {'transform': 'Zip'},
+ {'transform': 'Base64'}
+ ],
+ 'virtualPath': 'Zip(/path/to/archive.zip)::Base64(encoded_file)::extracted'
+}
+```
+
+You can also add custom metadata to child contexts:
+
+```python
+child = context.create_child(data, filename)
+if child.metadata_obj:
+ child.metadata_obj["custom_field"] = "value"
+```
+
+## Testing Container Transforms
+
+When testing your container transform, you can use the Python API directly:
+
+```python
+from binaryninja import TransformSession
+
+# Test with a file
+session = TransformSession("test_container.bin")
+
+# Process and check results
+if session.process():
+ print(f"Extraction complete: {session.current_context.filename}")
+else:
+ print("User interaction required")
+ ctx = session.current_context
+ if ctx.parent and ctx.parent.has_available_files:
+ print(f"Available files: {ctx.parent.available_files}")
+```
+
+For interactive testing in the UI:
+
+1. **Full Mode**: Settings → `files.container.mode` → "Full"
+ - Opens your container and shows all extracted files immediately
+2. **Interactive Mode**: Settings → `files.container.mode` → "Interactive"
+ - Requires clicking through each level of the container hierarchy
+
+## API Reference
+
+For complete API documentation, see:
+
+- [`Transform`](https://api.binary.ninja/binaryninja.transform-module.html#binaryninja.transform.Transform) - Base transform class
+- [`TransformContext`](https://api.binary.ninja/binaryninja.transform-module.html#binaryninja.transform.TransformContext) - Container extraction context
+- [`TransformSession`](https://api.binary.ninja/binaryninja.transform-module.html#binaryninja.transform.TransformSession) - Multi-stage extraction workflow
+- [`TransformResult`](https://api.binary.ninja/binaryninja.enums-module.html#binaryninja.enums.TransformResult) - Extraction result codes
+- [`TransformCapabilities`](https://api.binary.ninja/binaryninja.enums-module.html#binaryninja.enums.TransformCapabilities) - Transform capability flags
diff --git a/docs/dev/index.md b/docs/dev/index.md
index 01c355a4..94772b25 100644
--- a/docs/dev/index.md
+++ b/docs/dev/index.md
@@ -11,6 +11,7 @@ The Binary Ninja API is available through a [Core API](#core-api), through the [
The Python API is the most common third-party API and is used in many [public plugins](https://github.com/vector35/community-plugins). Here's a list of the most important Python API documentation resources:
- [Writing Python Plugins](plugins.md)
+ - [Container Transforms](containertransforms.md) - Creating custom container/archive decoders
- [Applying Annotations](annotation.md)
- [Script Cookbook](cookbook.md) with common examples and concepts explained
- [Python API Reference](https://api.binary.ninja/) (available offline via the Help menu)