RePythonNET-MCP
Enables static analysis and patching of .NET binaries through decompilation, IL analysis, renaming, and IL patching over the Model Context Protocol.
README
RePythonNET-MCP
An MCP (Model Context Protocol) server for static analysis and patching of .NET binaries.
Uses dnlib for IL parsing and editing, and the ILSpy/dnSpy decompiler engine for C# source reconstruction. Exposes 38 tools over a standard MCP interface, making it easy to integrate with any MCP-compatible LLM agent.
Designed to run as a standalone Docker container, and also ships with integration files for SARA — an internal automated malware reverse engineering system developed at Sekoia.io.
Features
- Decompile any .NET class or method to readable C#
- Enumerate types, methods, fields, imports, resources, entry point
- Analyse IL opcodes, call graphs, callers, string literals
- Detect P/Invoke declarations, reflection usage, large byte arrays
- Rename obfuscated classes, methods, and fields (in-memory, non-destructive)
- Patch IL instructions: replace strings, NOP out instructions, bypass checks, invert branches, patch static fields
- Save the modified binary to disk
- Upload binaries via HTTP, or load directly from a URL
- Multi-session: analyse several binaries in parallel with separate
analysis_ididentifiers
Architecture
┌─────────────────────────────────┐
│ Any MCP client / LLM agent │
│ (curl, Python, SARA, etc.) │
└──────────────┬──────────────────┘
│ JSON-RPC over HTTP
▼
┌─────────────────────────────────┐
│ pythonnet-mcp (port 8001) │
│ │
│ server.py │
│ ├── FastMCP (38 tools) │
│ ├── POST /upload │
│ └── GET /download │
│ │
│ Runtime: Python + Mono │
│ .NET libs: dnlib, ILSpy/dnSpy │
└─────────────────────────────────┘
The server is stateless across requests but holds in-memory sessions keyed by analysis_id. Each session stores the loaded dnlib module and a lazily-initialised ILSpy decompiler instance.
Quick Start
Docker (recommended)
# Create the required directories (first time only)
mkdir -p data/samples data/pythonnet_projects logs
# Build and run
docker compose -f docker-compose.yml up --build
# The server listens on http://localhost:8001
Direct (without Docker)
Prerequisites: Python 3.11+, mono-complete, and the .NET assemblies in dll/.
pip install -r requirements.txt
# Override default paths to avoid needing write access to /data/
export PYTHONNET_PROJECTS_DIR=./data/pythonnet_projects
export SAMPLES_DIR=./data/samples
mkdir -p data/pythonnet_projects data/samples
python server.py --host 0.0.0.0 --port 8001
CLI options:
| Option | Default | Description |
|---|---|---|
--host |
0.0.0.0 |
Bind address |
--port |
8001 |
Bind port |
--binary |
(none) | Optional binary to pre-load at startup |
Claude Desktop Configuration
The file scripts/wrapper.py is a wrapper to put on your host.
Then configure Claude Desktop (claude_desktop_config.json) to execute this file.
{
"mcpServers": {
"pythonnet": {
"command": "<path to python>",
"args": [
"<path to wrapper.py>"
]
}
},
}
Usage
Upload a binary
You first needs to upload the file using the /upload endpoint:
curl -F file=@malware.dll http://localhost:8001/upload
# {"path": "/data/pythonnet_projects/_uploads/malware.dll", "size_bytes": 45056}
You can then ask Claude Desktop to use this MCP service to load and analyze the binary.
You can use pythonnet_list_uploaded_files to list already uploaded files.
Call an MCP tool (raw JSON-RPC)
curl -s -X POST http://localhost:8001/mcp \
-H "Content-Type: application/json" \
-H "Accept: application/json" \
-d '{
"jsonrpc": "2.0",
"id": 1,
"method": "tools/call",
"params": {
"name": "pythonnet_load_binary",
"arguments": {
"binary_path": "/data/pythonnet_projects/_uploads/malware.dll",
"analysis_id": "session1"
}
}
}'
Session isolation: append ?session_id=<value> to the URL to keep sessions independent when running parallel analyses from multiple clients.
List available tools
curl -s -X POST http://localhost:8001/mcp \
-H "Content-Type: application/json" \
-H "Accept: application/json" \
-d '{"jsonrpc":"2.0","id":1,"method":"tools/list","params":{}}'
Download a file
# Download a patched binary produced by pythonnet_save_binary
curl "http://localhost:8001/download?path=/data/pythonnet_projects/session1/malware_patched.dll" \
-o malware_patched.dll
Downloads are restricted to files inside /data/pythonnet_projects/ and /data/samples/.
Tools Reference
All tool names are prefixed with pythonnet_. The analysis_id parameter defaults to "default" and identifies the session.
Loading
| Tool | Key Parameters | Description |
|---|---|---|
pythonnet_load_binary |
binary_path, analysis_id |
Load a .NET binary from disk. Copies it to a project directory before opening so the original is never modified. |
pythonnet_load_from_url |
url, analysis_id |
Download a .NET binary from HTTP/HTTPS and load it. |
pythonnet_unload_binary |
analysis_id |
Unload a binary and free its memory. |
pythonnet_list_uploaded_files |
extension |
List binaries in the upload staging area and the samples directory. |
pythonnet_list_active_sessions |
— | List all active and on-disk analysis sessions. |
Enumeration
| Tool | Key Parameters | Description |
|---|---|---|
pythonnet_list_all_classes |
analysis_id, namespace_filter |
List all types defined in the binary. |
pythonnet_list_all_methods |
analysis_id, class_filter |
List all methods that have a body. |
pythonnet_list_all_methods_inside_class |
class_name, analysis_id |
List all methods (including abstract/interface) inside a specific class. |
Decompilation
| Tool | Key Parameters | Description |
|---|---|---|
pythonnet_decompile_class |
class_name, analysis_id |
Decompile an entire class to C# source. |
pythonnet_decompile_method |
class_name, method_name, analysis_id, method_index |
Decompile a single method. Use method_index to disambiguate overloads. |
pythonnet_decompile_all_classes |
analysis_id |
Decompile every class in the binary at once. |
Metadata
| Tool | Key Parameters | Description |
|---|---|---|
pythonnet_get_module_info |
analysis_id |
Assembly name, version, culture, runtime version, target framework. |
pythonnet_get_entry_point |
analysis_id |
Managed entry point (Main or EntryPoint metadata). |
pythonnet_get_imports |
analysis_id |
All external assemblies and types referenced by the binary. |
pythonnet_get_fields |
class_name, analysis_id |
All fields of a class with their types and visibility. |
pythonnet_get_custom_attributes |
class_name, analysis_id, method_name |
Custom attributes on a class or one of its methods. |
pythonnet_get_resources |
analysis_id |
List all embedded resources. |
pythonnet_extract_resource |
resource_name, analysis_id |
Extract a raw embedded resource to a file. |
IL Analysis
| Tool | Key Parameters | Description |
|---|---|---|
pythonnet_get_opcodes |
class_name, method_name, analysis_id, method_index |
Dump the raw IL instruction listing of a method. |
pythonnet_get_method_calls |
class_name, method_name, analysis_id, method_index |
List all outgoing call/callvirt instructions from a method. |
pythonnet_get_callers |
class_name, method_name, analysis_id |
Find all methods that call a given method (reverse call graph). |
pythonnet_get_call_graph |
class_name, method_name, analysis_id, max_depth |
BFS call graph from a root method (default max depth: 5, internal calls only). |
pythonnet_find_dead_code |
analysis_id |
Find methods that are never called from within the binary. |
String and Name Search
| Tool | Key Parameters | Description |
|---|---|---|
pythonnet_get_strings |
analysis_id, min_length |
List all unique ldstr string literals across all method bodies. |
pythonnet_search_string |
search_string, analysis_id |
Find all methods that reference a given string literal. |
pythonnet_search_method_by_name |
method_name, analysis_id |
Search for methods by partial name match across all classes. |
Behavioral Indicators
| Tool | Key Parameters | Description |
|---|---|---|
pythonnet_find_pinvoke |
analysis_id, dll_filter |
List all P/Invoke (DllImport) declarations. |
pythonnet_find_reflection_calls |
analysis_id |
Find all uses of System.Reflection (Assembly.Load, GetMethod, Invoke, etc.). |
pythonnet_find_large_byte_arrays |
analysis_id, min_size |
Find methods that allocate large byte arrays (potential payload buffers; default min: 256 bytes). |
Renaming (Deobfuscation)
Changes are in-memory only. Call pythonnet_save_binary to persist.
| Tool | Key Parameters | Description |
|---|---|---|
pythonnet_rename_class |
class_name, new_name, analysis_id, new_namespace |
Rename a class and optionally its namespace. |
pythonnet_rename_method |
class_name, method_name, new_method_name, analysis_id, method_index |
Rename a method. |
pythonnet_rename_field |
class_name, field_name, new_field_name, analysis_id |
Rename a field. |
IL Patching
Changes are in-memory only. Call pythonnet_save_binary to persist.
| Tool | Key Parameters | Description |
|---|---|---|
pythonnet_patch_ldstr |
class_name, method_name, il_offset, new_string, analysis_id |
Replace the string operand of an ldstr instruction at a given IL offset. |
pythonnet_nop_instructions |
class_name, method_name, il_offsets, analysis_id |
Replace one or more instructions with nop. |
pythonnet_patch_return |
class_name, method_name, return_value, analysis_id |
Replace a method body with a minimal return (useful to bypass a check). |
pythonnet_patch_branch |
class_name, method_name, il_offset, mode, analysis_id |
Flip (invert), force (always/never), or NOP a conditional branch. |
pythonnet_set_field_constant |
class_name, field_name, new_value, analysis_id |
Patch the initialization value of a static field in .cctor. |
pythonnet_save_binary |
analysis_id, output_path |
Write the modified module to disk. |
Volumes and Paths
| Path (in container) | Purpose | Notes |
|---|---|---|
/data/samples/ |
Read-only input samples | Mount your binary directory here |
/data/pythonnet_projects/ |
Per-session working directories | Created automatically; writable |
/data/pythonnet_projects/_uploads/ |
HTTP upload staging area | Used by the /upload endpoint |
When running standalone, adjust the volume mounts in docker-compose.yml to your local directory layout.
Configuration
| Environment Variable | Default | Description |
|---|---|---|
WEB_SERVER_PORT |
8001 |
Port the server binds to |
PYTHONNET_PROJECTS_DIR |
/data/pythonnet_projects |
Working directory for analysis sessions (uploads, patched binaries, extracted resources) |
SAMPLES_DIR |
/data/samples |
Read-only input directory scanned by pythonnet_list_uploaded_files |
Dependencies
Python packages (see requirements.txt):
pythonnet≥ 3.0 — Python/.NET CLR interopfastmcp— MCP server frameworkmcp≥ 1.0 — MCP SDKclick,requests,aiofiles
System (installed in the Docker image): mono-complete
.NET assemblies (bundled in dll/):
| Assembly | License | Source |
|---|---|---|
dnlib.dll |
MIT | 0xd4d/dnlib |
ICSharpCode.Decompiler.dll |
MIT | icsharpcode/ILSpy |
ICSharpCode.NRefactory*.dll |
MIT | ILSpy / NRefactory |
dnSpy.Decompiler*.dll |
GPL-3.0 | dnSpy/dnSpy |
dnSpy.Contracts.Logic.dll |
GPL-3.0 | dnSpy/dnSpy |
License note: The dnSpy assemblies are GPL-3.0. If you distribute a modified version of this project that includes those files, the GPL-3.0 terms apply to the distribution. Using the service as-is (running the Docker container) is not affected.
SARA Integration
SARA is an internal automated malware reverse engineering system developed at Sekoia.io. It uses this service as its .NET analysis backend. Two additional files support that integration:
tools.py — MCP client
A lightweight HTTP client that runs inside the SARA process and proxies tool calls to this server. It:
- Auto-detects whether it is running inside Docker (uses
http://pythonnet-mcp:8001/mcp) or on the host (useshttp://localhost:8001/mcp), and respects thePYTHONNET_MCP_URLenvironment variable. - Fetches tool definitions from the server at runtime (
tools/list) with a 5-minute cache — the server is the single source of truth for schemas. - Normalises legacy relative paths (
data/samples/file.dll→/data/samples/file.dll) before forwarding requests. - Exposes
PythonnetToolRegistry, the class that SARA's service registry instantiates per analysis run.
from services.pythonnet.tools import PythonnetToolRegistry
registry = PythonnetToolRegistry(session_id="sara-abc12")
result = registry.execute_tool("pythonnet_list_all_classes", {"analysis_id": "sara-abc12"})
policy.py — Tool usage policy
Controls tool call limits and deduplication inside SARA's LangGraph analysis loop. Not needed outside SARA.
| Tool | Limit |
|---|---|
pythonnet_load_binary |
1 per session |
pythonnet_list_all_classes |
3 + lock_clears |
pythonnet_list_all_methods |
3 + lock_clears |
pythonnet_list_all_methods_inside_class |
20 + (lock_clears × 5) |
pythonnet_decompile_class |
50 + (lock_clears × 10) |
pythonnet_decompile_method |
unlimited |
pythonnet_rename_class |
20 + lock_clears |
pythonnet_rename_method |
50 + (lock_clears × 10) |
lock_clears is incremented by SARA's loop when it detects the LLM is stuck, progressively relaxing limits.
Registration in SARA
In core/services_registry.py:
def _make_pythonnet_toolset(session_id: str):
from services.pythonnet.tools import PythonnetToolRegistry
return PythonnetToolRegistry(session_id=session_id)
SERVICE_FACTORIES["pythonnet"] = _make_pythonnet_toolset
PIPELINES["pythonnet"] = {
"name": "Pythonnet",
"description": ".NET assembly decompilation and patching",
"supported_formats": ["EXE", "DLL", ".NET"],
"capabilities": ["decompile", "patch", "rename"],
"services": ["pythonnet"],
"available": _pythonnet_available,
"loader_tool": "pythonnet_load_binary",
}
Volume layout in SARA
When running inside SARA's docker-compose-sara-dev.yml:
/data/samplesis mounted read-only in the pythonnet-mcp container. The server never writes there.- All write operations (working copies, patched binaries, extracted resources) go to
/data/pythonnet_projects/{analysis_id}/.
License
The Python source files in this project are released under the MIT License — see LICENSE.
The bundled .NET assemblies in dll/ are subject to their own licenses — see the Dependencies section above, in particular the GPL-3.0 notice for the dnSpy assemblies.
Recommended Servers
playwright-mcp
A Model Context Protocol server that enables LLMs to interact with web pages through structured accessibility snapshots without requiring vision models or screenshots.
Magic Component Platform (MCP)
An AI-powered tool that generates modern UI components from natural language descriptions, integrating with popular IDEs to streamline UI development workflow.
Audiense Insights MCP Server
Enables interaction with Audiense Insights accounts via the Model Context Protocol, facilitating the extraction and analysis of marketing insights and audience data including demographics, behavior, and influencer engagement.
VeyraX MCP
Single MCP tool to connect all your favorite tools: Gmail, Calendar and 40 more.
graphlit-mcp-server
The Model Context Protocol (MCP) Server enables integration between MCP clients and the Graphlit service. Ingest anything from Slack to Gmail to podcast feeds, in addition to web crawling, into a Graphlit project - and then retrieve relevant contents from the MCP client.
Kagi MCP Server
An MCP server that integrates Kagi search capabilities with Claude AI, enabling Claude to perform real-time web searches when answering questions that require up-to-date information.
E2B
Using MCP to run code via e2b.
Neon Database
MCP server for interacting with Neon Management API and databases
Qdrant Server
This repository is an example of how to create a MCP server for Qdrant, a vector search engine.
Exa Search
A Model Context Protocol (MCP) server lets AI assistants like Claude use the Exa AI Search API for web searches. This setup allows AI models to get real-time web information in a safe and controlled way.