ida-pro-mcp-xjoker 1.0.1__tar.gz → 1.0.2__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- ida_pro_mcp_xjoker-1.0.2/PKG-INFO +117 -0
- ida_pro_mcp_xjoker-1.0.2/README.md +95 -0
- {ida_pro_mcp_xjoker-1.0.1 → ida_pro_mcp_xjoker-1.0.2}/pyproject.toml +5 -2
- {ida_pro_mcp_xjoker-1.0.1 → ida_pro_mcp_xjoker-1.0.2}/src/ida_pro_mcp/ida_mcp/api_analysis.py +3 -1
- {ida_pro_mcp_xjoker-1.0.1 → ida_pro_mcp_xjoker-1.0.2}/src/ida_pro_mcp/ida_mcp/api_core.py +1 -1
- {ida_pro_mcp_xjoker-1.0.1 → ida_pro_mcp_xjoker-1.0.2}/src/ida_pro_mcp/ida_mcp/auth.py +40 -3
- {ida_pro_mcp_xjoker-1.0.1 → ida_pro_mcp_xjoker-1.0.2}/src/ida_pro_mcp/ida_mcp/framework.py +3 -1
- {ida_pro_mcp_xjoker-1.0.1 → ida_pro_mcp_xjoker-1.0.2}/src/ida_pro_mcp/ida_mcp/server_manager.py +1 -1
- {ida_pro_mcp_xjoker-1.0.1 → ida_pro_mcp_xjoker-1.0.2}/src/ida_pro_mcp/ida_mcp/sync.py +10 -14
- ida_pro_mcp_xjoker-1.0.2/src/ida_pro_mcp/ida_mcp/tests/__init__.py +14 -0
- {ida_pro_mcp_xjoker-1.0.1 → ida_pro_mcp_xjoker-1.0.2}/src/ida_pro_mcp/ida_mcp/tests/test_api_resources.py +1 -1
- {ida_pro_mcp_xjoker-1.0.1 → ida_pro_mcp_xjoker-1.0.2}/src/ida_pro_mcp/ida_mcp/ui.py +2 -3
- {ida_pro_mcp_xjoker-1.0.1 → ida_pro_mcp_xjoker-1.0.2}/src/ida_pro_mcp/ida_mcp/utils.py +5 -6
- {ida_pro_mcp_xjoker-1.0.1 → ida_pro_mcp_xjoker-1.0.2}/src/ida_pro_mcp/ida_mcp.py +86 -38
- ida_pro_mcp_xjoker-1.0.2/src/ida_pro_mcp_xjoker.egg-info/PKG-INFO +117 -0
- ida_pro_mcp_xjoker-1.0.1/PKG-INFO +0 -405
- ida_pro_mcp_xjoker-1.0.1/README.md +0 -384
- ida_pro_mcp_xjoker-1.0.1/src/ida_pro_mcp/ida_mcp/tests/__init__.py +0 -14
- ida_pro_mcp_xjoker-1.0.1/src/ida_pro_mcp_xjoker.egg-info/PKG-INFO +0 -405
- {ida_pro_mcp_xjoker-1.0.1 → ida_pro_mcp_xjoker-1.0.2}/LICENSE +0 -0
- {ida_pro_mcp_xjoker-1.0.1 → ida_pro_mcp_xjoker-1.0.2}/setup.cfg +0 -0
- {ida_pro_mcp_xjoker-1.0.1 → ida_pro_mcp_xjoker-1.0.2}/src/ida_pro_mcp/__init__.py +0 -0
- {ida_pro_mcp_xjoker-1.0.1 → ida_pro_mcp_xjoker-1.0.2}/src/ida_pro_mcp/__main__.py +0 -0
- {ida_pro_mcp_xjoker-1.0.1 → ida_pro_mcp_xjoker-1.0.2}/src/ida_pro_mcp/ida_mcp/__init__.py +0 -0
- {ida_pro_mcp_xjoker-1.0.1 → ida_pro_mcp_xjoker-1.0.2}/src/ida_pro_mcp/ida_mcp/api_debug.py +0 -0
- {ida_pro_mcp_xjoker-1.0.1 → ida_pro_mcp_xjoker-1.0.2}/src/ida_pro_mcp/ida_mcp/api_memory.py +0 -0
- {ida_pro_mcp_xjoker-1.0.1 → ida_pro_mcp_xjoker-1.0.2}/src/ida_pro_mcp/ida_mcp/api_modify.py +0 -0
- {ida_pro_mcp_xjoker-1.0.1 → ida_pro_mcp_xjoker-1.0.2}/src/ida_pro_mcp/ida_mcp/api_python.py +0 -0
- {ida_pro_mcp_xjoker-1.0.1 → ida_pro_mcp_xjoker-1.0.2}/src/ida_pro_mcp/ida_mcp/api_resources.py +0 -0
- {ida_pro_mcp_xjoker-1.0.1 → ida_pro_mcp_xjoker-1.0.2}/src/ida_pro_mcp/ida_mcp/api_stack.py +0 -0
- {ida_pro_mcp_xjoker-1.0.1 → ida_pro_mcp_xjoker-1.0.2}/src/ida_pro_mcp/ida_mcp/api_types.py +0 -0
- {ida_pro_mcp_xjoker-1.0.1 → ida_pro_mcp_xjoker-1.0.2}/src/ida_pro_mcp/ida_mcp/cache.py +0 -0
- {ida_pro_mcp_xjoker-1.0.1 → ida_pro_mcp_xjoker-1.0.2}/src/ida_pro_mcp/ida_mcp/config.py +0 -0
- {ida_pro_mcp_xjoker-1.0.1 → ida_pro_mcp_xjoker-1.0.2}/src/ida_pro_mcp/ida_mcp/http.py +0 -0
- {ida_pro_mcp_xjoker-1.0.1 → ida_pro_mcp_xjoker-1.0.2}/src/ida_pro_mcp/ida_mcp/port_utils.py +0 -0
- {ida_pro_mcp_xjoker-1.0.1 → ida_pro_mcp_xjoker-1.0.2}/src/ida_pro_mcp/ida_mcp/rpc.py +0 -0
- {ida_pro_mcp_xjoker-1.0.1 → ida_pro_mcp_xjoker-1.0.2}/src/ida_pro_mcp/ida_mcp/tests/test_api_analysis.py +0 -0
- {ida_pro_mcp_xjoker-1.0.1 → ida_pro_mcp_xjoker-1.0.2}/src/ida_pro_mcp/ida_mcp/tests/test_api_core.py +0 -0
- {ida_pro_mcp_xjoker-1.0.1 → ida_pro_mcp_xjoker-1.0.2}/src/ida_pro_mcp/ida_mcp/tests/test_api_memory.py +0 -0
- {ida_pro_mcp_xjoker-1.0.1 → ida_pro_mcp_xjoker-1.0.2}/src/ida_pro_mcp/ida_mcp/tests/test_api_modify.py +0 -0
- {ida_pro_mcp_xjoker-1.0.1 → ida_pro_mcp_xjoker-1.0.2}/src/ida_pro_mcp/ida_mcp/tests/test_api_stack.py +0 -0
- {ida_pro_mcp_xjoker-1.0.1 → ida_pro_mcp_xjoker-1.0.2}/src/ida_pro_mcp/ida_mcp/tests/test_api_types.py +0 -0
- {ida_pro_mcp_xjoker-1.0.1 → ida_pro_mcp_xjoker-1.0.2}/src/ida_pro_mcp/ida_mcp/zeromcp/__init__.py +0 -0
- {ida_pro_mcp_xjoker-1.0.1 → ida_pro_mcp_xjoker-1.0.2}/src/ida_pro_mcp/ida_mcp/zeromcp/jsonrpc.py +0 -0
- {ida_pro_mcp_xjoker-1.0.1 → ida_pro_mcp_xjoker-1.0.2}/src/ida_pro_mcp/ida_mcp/zeromcp/mcp.py +0 -0
- {ida_pro_mcp_xjoker-1.0.1 → ida_pro_mcp_xjoker-1.0.2}/src/ida_pro_mcp/idalib_server.py +0 -0
- {ida_pro_mcp_xjoker-1.0.1 → ida_pro_mcp_xjoker-1.0.2}/src/ida_pro_mcp/idalib_session_manager.py +0 -0
- {ida_pro_mcp_xjoker-1.0.1 → ida_pro_mcp_xjoker-1.0.2}/src/ida_pro_mcp/server.py +0 -0
- {ida_pro_mcp_xjoker-1.0.1 → ida_pro_mcp_xjoker-1.0.2}/src/ida_pro_mcp/test.py +0 -0
- {ida_pro_mcp_xjoker-1.0.1 → ida_pro_mcp_xjoker-1.0.2}/src/ida_pro_mcp_xjoker.egg-info/SOURCES.txt +0 -0
- {ida_pro_mcp_xjoker-1.0.1 → ida_pro_mcp_xjoker-1.0.2}/src/ida_pro_mcp_xjoker.egg-info/dependency_links.txt +0 -0
- {ida_pro_mcp_xjoker-1.0.1 → ida_pro_mcp_xjoker-1.0.2}/src/ida_pro_mcp_xjoker.egg-info/entry_points.txt +0 -0
- {ida_pro_mcp_xjoker-1.0.1 → ida_pro_mcp_xjoker-1.0.2}/src/ida_pro_mcp_xjoker.egg-info/requires.txt +0 -0
- {ida_pro_mcp_xjoker-1.0.1 → ida_pro_mcp_xjoker-1.0.2}/src/ida_pro_mcp_xjoker.egg-info/top_level.txt +0 -0
|
@@ -0,0 +1,117 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: ida-pro-mcp-xjoker
|
|
3
|
+
Version: 1.0.2
|
|
4
|
+
Summary: Vibe reversing with IDA Pro (enhanced fork)
|
|
5
|
+
Author: mrexodia, can1357, IDA Pro MCP Contributors
|
|
6
|
+
Maintainer: xjoker
|
|
7
|
+
Project-URL: Repository, https://github.com/xjoker/ida-pro-mcp
|
|
8
|
+
Project-URL: Issues, https://github.com/xjoker/ida-pro-mcp/issues
|
|
9
|
+
Keywords: ida,mcp,llm,plugin
|
|
10
|
+
Classifier: Development Status :: 5 - Production/Stable
|
|
11
|
+
Classifier: Intended Audience :: Developers
|
|
12
|
+
Classifier: Programming Language :: Python :: 3
|
|
13
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
14
|
+
Classifier: Operating System :: MacOS
|
|
15
|
+
Classifier: Operating System :: Microsoft :: Windows
|
|
16
|
+
Requires-Python: >=3.11
|
|
17
|
+
Description-Content-Type: text/markdown
|
|
18
|
+
License-File: LICENSE
|
|
19
|
+
Requires-Dist: idapro>=0.0.7
|
|
20
|
+
Requires-Dist: tomli-w>=1.0.0
|
|
21
|
+
Dynamic: license-file
|
|
22
|
+
|
|
23
|
+
# IDA Pro MCP (Enhanced Fork)
|
|
24
|
+
|
|
25
|
+
[中文文档](https://github.com/xjoker/ida-pro-mcp/blob/main/README_zh.md) | English
|
|
26
|
+
|
|
27
|
+
[](https://pypi.org/project/ida-pro-mcp-xjoker/)
|
|
28
|
+
[](https://pypi.org/project/ida-pro-mcp-xjoker/)
|
|
29
|
+
|
|
30
|
+
An enhanced fork of [mrexodia/ida-pro-mcp](https://github.com/mrexodia/ida-pro-mcp) - MCP Server for LLM-assisted reverse engineering in IDA Pro.
|
|
31
|
+
|
|
32
|
+
## What's Different from Original
|
|
33
|
+
|
|
34
|
+
| Feature | Original | This Fork |
|
|
35
|
+
|---------|----------|-----------|
|
|
36
|
+
| **Multi-instance Support** | ❌ Port conflict crashes | ✅ Auto port increment (13337→13346) |
|
|
37
|
+
| **Web Configuration** | ❌ None | ✅ Bilingual UI at `/config.html` |
|
|
38
|
+
| **API Key Auth** | ❌ None | ✅ Bearer token + env var support |
|
|
39
|
+
| **Server Startup** | Manual hotkey | ✅ Auto-start on IDA launch |
|
|
40
|
+
| **Hotkey Conflicts** | Occupies Ctrl+Alt+M | ✅ No hotkey, menu-only |
|
|
41
|
+
| **Config Persistence** | None | ✅ Saved per IDB database |
|
|
42
|
+
|
|
43
|
+
### Key Enhancements
|
|
44
|
+
|
|
45
|
+
- **Port Conflict Auto-Retry**: Multiple IDA instances automatically use different ports
|
|
46
|
+
- **Web Config UI**: `http://localhost:13337/config.html` with English/中文 interface
|
|
47
|
+
- **API Key Authentication**: Secure remote access with Bearer token
|
|
48
|
+
- **Bug Fixes**: Thread safety, regex handling, type parsing errors fixed
|
|
49
|
+
|
|
50
|
+
## Installation
|
|
51
|
+
|
|
52
|
+
```bash
|
|
53
|
+
pip install ida-pro-mcp-xjoker
|
|
54
|
+
ida-pro-mcp --install
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
Restart IDA Pro completely after installation.
|
|
58
|
+
|
|
59
|
+
## Quick Start
|
|
60
|
+
|
|
61
|
+
1. Open a binary in IDA Pro
|
|
62
|
+
2. MCP server starts automatically on `http://127.0.0.1:13337`
|
|
63
|
+
3. Configure your MCP client:
|
|
64
|
+
|
|
65
|
+
```bash
|
|
66
|
+
# Claude Code
|
|
67
|
+
claude mcp add ida-pro-mcp http://127.0.0.1:13337/mcp
|
|
68
|
+
|
|
69
|
+
# With API Key authentication
|
|
70
|
+
claude mcp add --transport http ida-pro-mcp http://127.0.0.1:13337/mcp \
|
|
71
|
+
--header "Authorization: Bearer your-api-key"
|
|
72
|
+
```
|
|
73
|
+
|
|
74
|
+
4. Open web config at `http://127.0.0.1:13337/config.html` to customize settings
|
|
75
|
+
|
|
76
|
+
## Requirements
|
|
77
|
+
|
|
78
|
+
- Python 3.11+
|
|
79
|
+
- IDA Pro 8.3+ (9.0 recommended), **IDA Free not supported**
|
|
80
|
+
- Any [MCP-compatible client](https://modelcontextprotocol.io/clients)
|
|
81
|
+
|
|
82
|
+
## API Overview
|
|
83
|
+
|
|
84
|
+
**71 MCP Tools** including:
|
|
85
|
+
|
|
86
|
+
| Category | Tools |
|
|
87
|
+
|----------|-------|
|
|
88
|
+
| Analysis | `decompile`, `disasm`, `xrefs_to`, `callees`, `callers`, `basic_blocks` |
|
|
89
|
+
| Memory | `get_bytes`, `get_string`, `get_int`, `patch` |
|
|
90
|
+
| Types | `declare_type`, `set_type`, `infer_types`, `read_struct` |
|
|
91
|
+
| Modify | `set_comments`, `rename`, `patch_asm` |
|
|
92
|
+
| Search | `find_bytes`, `find_insns`, `find_regex` |
|
|
93
|
+
| Debug | `dbg_*` (20+ debugger tools, enable with `?ext=dbg`) |
|
|
94
|
+
| Python | `py_eval` - execute Python in IDA context |
|
|
95
|
+
|
|
96
|
+
**24 MCP Resources** for read-only access:
|
|
97
|
+
- `ida://idb/metadata`, `ida://cursor`, `ida://structs`, `ida://xrefs/from/{addr}`, etc.
|
|
98
|
+
|
|
99
|
+
## Headless Mode
|
|
100
|
+
|
|
101
|
+
```bash
|
|
102
|
+
# SSE transport
|
|
103
|
+
ida-pro-mcp --transport http://127.0.0.1:8744/sse
|
|
104
|
+
|
|
105
|
+
# With idalib (no GUI)
|
|
106
|
+
idalib-mcp --host 127.0.0.1 --port 8745 /path/to/binary
|
|
107
|
+
```
|
|
108
|
+
|
|
109
|
+
## Links
|
|
110
|
+
|
|
111
|
+
- [Original Project](https://github.com/mrexodia/ida-pro-mcp) by mrexodia
|
|
112
|
+
- [Changelog](https://github.com/xjoker/ida-pro-mcp/blob/main/CHANGELOG.md)
|
|
113
|
+
- [Issues](https://github.com/xjoker/ida-pro-mcp/issues)
|
|
114
|
+
|
|
115
|
+
## License
|
|
116
|
+
|
|
117
|
+
MIT - Same as original project
|
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
# IDA Pro MCP (Enhanced Fork)
|
|
2
|
+
|
|
3
|
+
[中文文档](https://github.com/xjoker/ida-pro-mcp/blob/main/README_zh.md) | English
|
|
4
|
+
|
|
5
|
+
[](https://pypi.org/project/ida-pro-mcp-xjoker/)
|
|
6
|
+
[](https://pypi.org/project/ida-pro-mcp-xjoker/)
|
|
7
|
+
|
|
8
|
+
An enhanced fork of [mrexodia/ida-pro-mcp](https://github.com/mrexodia/ida-pro-mcp) - MCP Server for LLM-assisted reverse engineering in IDA Pro.
|
|
9
|
+
|
|
10
|
+
## What's Different from Original
|
|
11
|
+
|
|
12
|
+
| Feature | Original | This Fork |
|
|
13
|
+
|---------|----------|-----------|
|
|
14
|
+
| **Multi-instance Support** | ❌ Port conflict crashes | ✅ Auto port increment (13337→13346) |
|
|
15
|
+
| **Web Configuration** | ❌ None | ✅ Bilingual UI at `/config.html` |
|
|
16
|
+
| **API Key Auth** | ❌ None | ✅ Bearer token + env var support |
|
|
17
|
+
| **Server Startup** | Manual hotkey | ✅ Auto-start on IDA launch |
|
|
18
|
+
| **Hotkey Conflicts** | Occupies Ctrl+Alt+M | ✅ No hotkey, menu-only |
|
|
19
|
+
| **Config Persistence** | None | ✅ Saved per IDB database |
|
|
20
|
+
|
|
21
|
+
### Key Enhancements
|
|
22
|
+
|
|
23
|
+
- **Port Conflict Auto-Retry**: Multiple IDA instances automatically use different ports
|
|
24
|
+
- **Web Config UI**: `http://localhost:13337/config.html` with English/中文 interface
|
|
25
|
+
- **API Key Authentication**: Secure remote access with Bearer token
|
|
26
|
+
- **Bug Fixes**: Thread safety, regex handling, type parsing errors fixed
|
|
27
|
+
|
|
28
|
+
## Installation
|
|
29
|
+
|
|
30
|
+
```bash
|
|
31
|
+
pip install ida-pro-mcp-xjoker
|
|
32
|
+
ida-pro-mcp --install
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
Restart IDA Pro completely after installation.
|
|
36
|
+
|
|
37
|
+
## Quick Start
|
|
38
|
+
|
|
39
|
+
1. Open a binary in IDA Pro
|
|
40
|
+
2. MCP server starts automatically on `http://127.0.0.1:13337`
|
|
41
|
+
3. Configure your MCP client:
|
|
42
|
+
|
|
43
|
+
```bash
|
|
44
|
+
# Claude Code
|
|
45
|
+
claude mcp add ida-pro-mcp http://127.0.0.1:13337/mcp
|
|
46
|
+
|
|
47
|
+
# With API Key authentication
|
|
48
|
+
claude mcp add --transport http ida-pro-mcp http://127.0.0.1:13337/mcp \
|
|
49
|
+
--header "Authorization: Bearer your-api-key"
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
4. Open web config at `http://127.0.0.1:13337/config.html` to customize settings
|
|
53
|
+
|
|
54
|
+
## Requirements
|
|
55
|
+
|
|
56
|
+
- Python 3.11+
|
|
57
|
+
- IDA Pro 8.3+ (9.0 recommended), **IDA Free not supported**
|
|
58
|
+
- Any [MCP-compatible client](https://modelcontextprotocol.io/clients)
|
|
59
|
+
|
|
60
|
+
## API Overview
|
|
61
|
+
|
|
62
|
+
**71 MCP Tools** including:
|
|
63
|
+
|
|
64
|
+
| Category | Tools |
|
|
65
|
+
|----------|-------|
|
|
66
|
+
| Analysis | `decompile`, `disasm`, `xrefs_to`, `callees`, `callers`, `basic_blocks` |
|
|
67
|
+
| Memory | `get_bytes`, `get_string`, `get_int`, `patch` |
|
|
68
|
+
| Types | `declare_type`, `set_type`, `infer_types`, `read_struct` |
|
|
69
|
+
| Modify | `set_comments`, `rename`, `patch_asm` |
|
|
70
|
+
| Search | `find_bytes`, `find_insns`, `find_regex` |
|
|
71
|
+
| Debug | `dbg_*` (20+ debugger tools, enable with `?ext=dbg`) |
|
|
72
|
+
| Python | `py_eval` - execute Python in IDA context |
|
|
73
|
+
|
|
74
|
+
**24 MCP Resources** for read-only access:
|
|
75
|
+
- `ida://idb/metadata`, `ida://cursor`, `ida://structs`, `ida://xrefs/from/{addr}`, etc.
|
|
76
|
+
|
|
77
|
+
## Headless Mode
|
|
78
|
+
|
|
79
|
+
```bash
|
|
80
|
+
# SSE transport
|
|
81
|
+
ida-pro-mcp --transport http://127.0.0.1:8744/sse
|
|
82
|
+
|
|
83
|
+
# With idalib (no GUI)
|
|
84
|
+
idalib-mcp --host 127.0.0.1 --port 8745 /path/to/binary
|
|
85
|
+
```
|
|
86
|
+
|
|
87
|
+
## Links
|
|
88
|
+
|
|
89
|
+
- [Original Project](https://github.com/mrexodia/ida-pro-mcp) by mrexodia
|
|
90
|
+
- [Changelog](https://github.com/xjoker/ida-pro-mcp/blob/main/CHANGELOG.md)
|
|
91
|
+
- [Issues](https://github.com/xjoker/ida-pro-mcp/issues)
|
|
92
|
+
|
|
93
|
+
## License
|
|
94
|
+
|
|
95
|
+
MIT - Same as original project
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
[project]
|
|
2
2
|
name = "ida-pro-mcp-xjoker"
|
|
3
|
-
version = "1.0.
|
|
3
|
+
version = "1.0.2"
|
|
4
4
|
description = "Vibe reversing with IDA Pro (enhanced fork)"
|
|
5
5
|
readme = "README.md"
|
|
6
6
|
requires-python = ">=3.11"
|
|
@@ -9,6 +9,9 @@ authors = [
|
|
|
9
9
|
{ name = "can1357" },
|
|
10
10
|
{ name = "IDA Pro MCP Contributors" },
|
|
11
11
|
]
|
|
12
|
+
maintainers = [
|
|
13
|
+
{ name = "xjoker" },
|
|
14
|
+
]
|
|
12
15
|
keywords = ["ida", "mcp", "llm", "plugin"]
|
|
13
16
|
classifiers = [
|
|
14
17
|
"Development Status :: 5 - Production/Stable",
|
|
@@ -50,7 +53,7 @@ dev = [
|
|
|
50
53
|
|
|
51
54
|
[tool.commitizen]
|
|
52
55
|
name = "cz_conventional_commits"
|
|
53
|
-
version = "1.0.
|
|
56
|
+
version = "1.0.2"
|
|
54
57
|
version_files = [
|
|
55
58
|
"pyproject.toml:^version"
|
|
56
59
|
]
|
{ida_pro_mcp_xjoker-1.0.1 → ida_pro_mcp_xjoker-1.0.2}/src/ida_pro_mcp/ida_mcp/api_analysis.py
RENAMED
|
@@ -13,6 +13,7 @@ import ida_idaapi
|
|
|
13
13
|
import ida_xref
|
|
14
14
|
import ida_ua
|
|
15
15
|
import ida_name
|
|
16
|
+
import ida_idp
|
|
16
17
|
from .rpc import tool
|
|
17
18
|
from .sync import idasync, tool_timeout
|
|
18
19
|
from .cache import decompile_cache, xrefs_cache
|
|
@@ -503,7 +504,8 @@ def callees(
|
|
|
503
504
|
break
|
|
504
505
|
current_ea = next_ea
|
|
505
506
|
continue
|
|
506
|
-
|
|
507
|
+
# Use architecture-independent call instruction detection
|
|
508
|
+
if ida_idp.is_call_insn(insn):
|
|
507
509
|
op0 = insn.ops[0]
|
|
508
510
|
if op0.type in (ida_ua.o_mem, ida_ua.o_near, ida_ua.o_far):
|
|
509
511
|
target = op0.addr
|
|
@@ -10,7 +10,7 @@ import ida_nalt
|
|
|
10
10
|
|
|
11
11
|
from .rpc import tool
|
|
12
12
|
from .sync import idasync
|
|
13
|
-
from .cache import function_cache
|
|
13
|
+
from .cache import function_cache
|
|
14
14
|
|
|
15
15
|
# Cached strings list: [(ea, text), ...]
|
|
16
16
|
_strings_cache: list[tuple[int, str]] | None = None
|
|
@@ -3,11 +3,14 @@
|
|
|
3
3
|
Provides authentication middleware for MCP server with support for:
|
|
4
4
|
- Bearer token authentication (Authorization: Bearer <key>)
|
|
5
5
|
- X-API-Key header authentication
|
|
6
|
+
- Environment variable expansion (${ENV_VAR} syntax)
|
|
6
7
|
- Timing-attack resistant comparison
|
|
7
8
|
"""
|
|
8
9
|
|
|
9
10
|
import hmac
|
|
10
11
|
import logging
|
|
12
|
+
import os
|
|
13
|
+
import re
|
|
11
14
|
from typing import Optional, Callable
|
|
12
15
|
|
|
13
16
|
logger = logging.getLogger(__name__)
|
|
@@ -18,6 +21,33 @@ AUTH_EXEMPT_PATHS = frozenset({
|
|
|
18
21
|
"/config.html",
|
|
19
22
|
})
|
|
20
23
|
|
|
24
|
+
# Pattern to match ${ENV_VAR} syntax
|
|
25
|
+
_ENV_VAR_PATTERN = re.compile(r"^\$\{([A-Za-z_][A-Za-z0-9_]*)\}$")
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
def resolve_env_var(value: Optional[str]) -> Optional[str]:
|
|
29
|
+
"""Resolve environment variable reference in ${VAR} format.
|
|
30
|
+
|
|
31
|
+
Args:
|
|
32
|
+
value: The value to resolve, may be a literal or ${ENV_VAR} reference
|
|
33
|
+
|
|
34
|
+
Returns:
|
|
35
|
+
The resolved value (from environment) or the original value if not a reference
|
|
36
|
+
"""
|
|
37
|
+
if not value:
|
|
38
|
+
return value
|
|
39
|
+
|
|
40
|
+
match = _ENV_VAR_PATTERN.match(value.strip())
|
|
41
|
+
if match:
|
|
42
|
+
env_name = match.group(1)
|
|
43
|
+
env_value = os.environ.get(env_name)
|
|
44
|
+
if env_value:
|
|
45
|
+
return env_value
|
|
46
|
+
else:
|
|
47
|
+
logger.warning(f"Environment variable '{env_name}' not found, using literal value")
|
|
48
|
+
return None # Env var not set, disable auth
|
|
49
|
+
return value
|
|
50
|
+
|
|
21
51
|
|
|
22
52
|
def check_api_key(provided_key: Optional[str], expected_key: Optional[str]) -> bool:
|
|
23
53
|
"""Compare API keys using constant-time comparison to prevent timing attacks.
|
|
@@ -100,23 +130,29 @@ class AuthMiddleware:
|
|
|
100
130
|
|
|
101
131
|
Args:
|
|
102
132
|
api_key: The expected API key (None = no authentication)
|
|
133
|
+
Supports ${ENV_VAR} syntax to reference environment variables
|
|
103
134
|
enabled: Whether authentication is enabled
|
|
104
135
|
"""
|
|
105
|
-
self.
|
|
136
|
+
self._api_key_raw = api_key # Store original value (may be ${ENV_VAR})
|
|
106
137
|
self._enabled = enabled and api_key is not None
|
|
107
138
|
|
|
108
139
|
@property
|
|
109
140
|
def enabled(self) -> bool:
|
|
110
141
|
return self._enabled
|
|
111
142
|
|
|
143
|
+
@property
|
|
144
|
+
def _api_key(self) -> Optional[str]:
|
|
145
|
+
"""Get the resolved API key (expands ${ENV_VAR} references)."""
|
|
146
|
+
return resolve_env_var(self._api_key_raw)
|
|
147
|
+
|
|
112
148
|
def update_key(self, api_key: Optional[str], enabled: bool = True) -> None:
|
|
113
149
|
"""Update the API key configuration.
|
|
114
150
|
|
|
115
151
|
Args:
|
|
116
|
-
api_key: New API key
|
|
152
|
+
api_key: New API key (supports ${ENV_VAR} syntax)
|
|
117
153
|
enabled: Whether to enable authentication
|
|
118
154
|
"""
|
|
119
|
-
self.
|
|
155
|
+
self._api_key_raw = api_key
|
|
120
156
|
self._enabled = enabled and api_key is not None
|
|
121
157
|
|
|
122
158
|
def authenticate(self, path: str, headers: dict) -> bool:
|
|
@@ -160,6 +196,7 @@ __all__ = [
|
|
|
160
196
|
"check_api_key",
|
|
161
197
|
"extract_api_key_from_headers",
|
|
162
198
|
"is_path_exempt",
|
|
199
|
+
"resolve_env_var",
|
|
163
200
|
"AuthMiddleware",
|
|
164
201
|
"create_auth_check",
|
|
165
202
|
"AUTH_EXEMPT_PATHS",
|
|
@@ -340,6 +340,7 @@ def get_functions_with_calls() -> list[str]:
|
|
|
340
340
|
"""
|
|
341
341
|
import idaapi
|
|
342
342
|
import idautils
|
|
343
|
+
import ida_idp
|
|
343
344
|
|
|
344
345
|
result = []
|
|
345
346
|
for func_ea in idautils.Functions():
|
|
@@ -352,7 +353,8 @@ def get_functions_with_calls() -> list[str]:
|
|
|
352
353
|
for head in idautils.Heads(func.start_ea, func.end_ea):
|
|
353
354
|
insn = idaapi.insn_t()
|
|
354
355
|
if idaapi.decode_insn(insn, head) > 0:
|
|
355
|
-
|
|
356
|
+
# Use architecture-independent call instruction detection
|
|
357
|
+
if ida_idp.is_call_insn(insn):
|
|
356
358
|
has_call = True
|
|
357
359
|
break
|
|
358
360
|
|
{ida_pro_mcp_xjoker-1.0.1 → ida_pro_mcp_xjoker-1.0.2}/src/ida_pro_mcp/ida_mcp/server_manager.py
RENAMED
|
@@ -11,7 +11,7 @@ import threading
|
|
|
11
11
|
from dataclasses import dataclass, field
|
|
12
12
|
from typing import Optional, Callable, TYPE_CHECKING
|
|
13
13
|
|
|
14
|
-
from .config import ServerInstanceConfig, McpConfig, get_config
|
|
14
|
+
from .config import ServerInstanceConfig, McpConfig, get_config
|
|
15
15
|
from .auth import AuthMiddleware
|
|
16
16
|
from .port_utils import try_serve_with_port_retry
|
|
17
17
|
|
|
@@ -107,11 +107,14 @@ def _sync_wrapper(ff):
|
|
|
107
107
|
raise IDASyncError(error_str)
|
|
108
108
|
|
|
109
109
|
call_stack.put((ff.__name__))
|
|
110
|
+
# Enable batch mode for all synchronized operations
|
|
111
|
+
old_batch = idc.batch(1)
|
|
110
112
|
try:
|
|
111
113
|
res_container.put(ff())
|
|
112
114
|
except Exception as x:
|
|
113
115
|
res_container.put(x)
|
|
114
116
|
finally:
|
|
117
|
+
idc.batch(old_batch)
|
|
115
118
|
call_stack.get()
|
|
116
119
|
|
|
117
120
|
idaapi.execute_sync(runned, idaapi.MFF_WRITE)
|
|
@@ -132,21 +135,14 @@ def _normalize_timeout(value: object) -> float | None:
|
|
|
132
135
|
|
|
133
136
|
|
|
134
137
|
def sync_wrapper(ff, timeout_override: float | None = None):
|
|
135
|
-
"""Wrapper to enable
|
|
138
|
+
"""Wrapper to enable timeout and cancellation during IDA synchronization.
|
|
139
|
+
|
|
140
|
+
Note: Batch mode is handled in _sync_wrapper to ensure it's always
|
|
141
|
+
applied consistently for all synchronized operations.
|
|
142
|
+
"""
|
|
136
143
|
# Capture cancel event from thread-local before execute_sync
|
|
137
144
|
cancel_event = get_current_cancel_event()
|
|
138
145
|
|
|
139
|
-
def _run_with_batch(inner_ff):
|
|
140
|
-
def _wrapped():
|
|
141
|
-
old_batch = idc.batch(1)
|
|
142
|
-
try:
|
|
143
|
-
return inner_ff()
|
|
144
|
-
finally:
|
|
145
|
-
idc.batch(old_batch)
|
|
146
|
-
|
|
147
|
-
_wrapped.__name__ = inner_ff.__name__
|
|
148
|
-
return _wrapped
|
|
149
|
-
|
|
150
146
|
timeout = timeout_override
|
|
151
147
|
if timeout is None:
|
|
152
148
|
timeout = _get_tool_timeout_seconds()
|
|
@@ -172,8 +168,8 @@ def sync_wrapper(ff, timeout_override: float | None = None):
|
|
|
172
168
|
sys.setprofile(old_profile)
|
|
173
169
|
|
|
174
170
|
timed_ff.__name__ = ff.__name__
|
|
175
|
-
return _sync_wrapper(
|
|
176
|
-
return _sync_wrapper(
|
|
171
|
+
return _sync_wrapper(timed_ff)
|
|
172
|
+
return _sync_wrapper(ff)
|
|
177
173
|
|
|
178
174
|
|
|
179
175
|
def idasync(f):
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
"""IDA Pro MCP Test Package.
|
|
2
|
+
|
|
3
|
+
This package contains test modules for each API module.
|
|
4
|
+
Tests are registered via the @test decorator from the framework module.
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
# Import all test modules to register tests when the package is imported
|
|
8
|
+
from . import test_api_core as test_api_core
|
|
9
|
+
from . import test_api_analysis as test_api_analysis
|
|
10
|
+
from . import test_api_memory as test_api_memory
|
|
11
|
+
from . import test_api_modify as test_api_modify
|
|
12
|
+
from . import test_api_types as test_api_types
|
|
13
|
+
from . import test_api_stack as test_api_stack
|
|
14
|
+
from . import test_api_resources as test_api_resources
|
|
@@ -145,7 +145,7 @@ def test_resource_struct_name():
|
|
|
145
145
|
def test_resource_struct_name_not_found():
|
|
146
146
|
"""struct_name_resource handles non-existent structure"""
|
|
147
147
|
try:
|
|
148
|
-
|
|
148
|
+
struct_name_resource("NonExistentStruct12345")
|
|
149
149
|
# Should return error or empty
|
|
150
150
|
except IDAError:
|
|
151
151
|
pass # Expected for non-existent struct
|
|
@@ -10,7 +10,6 @@ from typing import Optional, TYPE_CHECKING
|
|
|
10
10
|
|
|
11
11
|
from .config import (
|
|
12
12
|
ServerInstanceConfig,
|
|
13
|
-
McpConfig,
|
|
14
13
|
get_config,
|
|
15
14
|
save_config,
|
|
16
15
|
reload_config,
|
|
@@ -34,9 +33,9 @@ class ServerConfigForm(idaapi.Form):
|
|
|
34
33
|
instance_id = "" if is_new else config.instance_id
|
|
35
34
|
host = "127.0.0.1" if is_new else config.host
|
|
36
35
|
port = 13337 if is_new else config.port
|
|
37
|
-
|
|
36
|
+
_ = False if is_new else config.auth_enabled # Reserved for future use
|
|
38
37
|
api_key = "" if is_new else (config.api_key or "")
|
|
39
|
-
|
|
38
|
+
_ = False if is_new else config.auto_start # Reserved for future use
|
|
40
39
|
|
|
41
40
|
form_template = r"""STARTITEM 0
|
|
42
41
|
BUTTON YES* Save
|
|
@@ -22,6 +22,7 @@ from typing import (
|
|
|
22
22
|
|
|
23
23
|
import ida_funcs
|
|
24
24
|
import ida_hexrays
|
|
25
|
+
import ida_idp
|
|
25
26
|
import ida_kernwin
|
|
26
27
|
import ida_nalt
|
|
27
28
|
import ida_typeinf
|
|
@@ -1022,7 +1023,8 @@ def get_callees(addr: str) -> list[dict]:
|
|
|
1022
1023
|
while current_ea < func_end:
|
|
1023
1024
|
insn = idaapi.insn_t()
|
|
1024
1025
|
idaapi.decode_insn(insn, current_ea)
|
|
1025
|
-
|
|
1026
|
+
# Use architecture-independent call instruction detection
|
|
1027
|
+
if ida_idp.is_call_insn(insn):
|
|
1026
1028
|
target = idc.get_operand_value(current_ea, 0)
|
|
1027
1029
|
target_type = idc.get_operand_type(current_ea, 0)
|
|
1028
1030
|
if target_type in [idaapi.o_mem, idaapi.o_near, idaapi.o_far]:
|
|
@@ -1064,11 +1066,8 @@ def get_callers(addr: str, limit: int = 50) -> list[Function]:
|
|
|
1064
1066
|
continue
|
|
1065
1067
|
insn = idaapi.insn_t()
|
|
1066
1068
|
idaapi.decode_insn(insn, caller_addr)
|
|
1067
|
-
|
|
1068
|
-
|
|
1069
|
-
idaapi.NN_callfi,
|
|
1070
|
-
idaapi.NN_callni,
|
|
1071
|
-
]:
|
|
1069
|
+
# Use architecture-independent call instruction detection
|
|
1070
|
+
if not ida_idp.is_call_insn(insn):
|
|
1072
1071
|
continue
|
|
1073
1072
|
callers[func["addr"]] = func
|
|
1074
1073
|
|