Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,8 @@ build*/
.claude/
# IDE
.vs/
.vscode/
.vscode/*
!.vscode/settings.json
*.suo
*.user
*.sln.docstates
Expand Down
6 changes: 6 additions & 0 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -106,6 +106,12 @@ if(IDASQL_WITH_MCP)
if(MSVC AND TARGET fastmcpp_core)
target_compile_options(fastmcpp_core PRIVATE /MP)
endif()

# fastmcpp_core is a static library that gets linked into the idasql plugin
# (a shared library on Linux). Enable PIC so the link succeeds.
if(TARGET fastmcpp_core)
set_property(TARGET fastmcpp_core PROPERTY POSITION_INDEPENDENT_CODE ON)
endif()
endif()

# Add subdirectories
Expand Down
40 changes: 31 additions & 9 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -176,7 +176,7 @@ claude /install-plugin https://github.com/allthingsida/idasql-skills
$ idasql
Error: Database path required (-s)

idasql v0.0.12 - SQL interface to IDA databases
idasql v0.0.14 - SQL interface to IDA databases

Usage: idasql -s <database> [-q <query>] [-f <file>] [-i] [--export <file>]

Expand Down Expand Up @@ -211,14 +211,31 @@ Thank you for using IDA. Have a nice day!
#### Prerequisites

- CMake 3.20+
- C++17 compiler
- C++20 compiler
- IDA SDK 9.0+ (set `IDASDK` environment variable)

```bash
cmake -S src -B build -DIDASQL_WITH_MCP=OFF
cmake -S . -B build -DIDASQL_WITH_MCP=ON -DIDASQL_BUILD_EXAMPLES=OFF
cmake --build build --config Release
```

Useful CMake switches:

| Switch | Default | Description |
|--------|---------|-------------|
| `IDASQL_WITH_MCP` | `ON` | Build MCP server support via `fastmcpp`. Disable for a smaller/offline build or when you do not need `--mcp` / `.mcp`. |
| `IDASQL_BUILD_CLI` | `ON` | Build the standalone `idasql` command-line tool. |
| `IDASQL_BUILD_PLUGIN` | `ON` | Build the IDA plugin. |
| `IDASQL_BUILD_EXAMPLES` | `ON` | Build the example programs under `examples/`. |

Notes:

- Hex-Rays support is always compiled in and detected at runtime. If Hex-Rays is unavailable, decompiler-backed tables/functions are simply not registered.
- HTTP REST support is always compiled in; use `--http` from the CLI or `.http start` from the REPL/plugin CLI.
- IDAPython SQL execution is compiled in but disabled by default at runtime. Enable it per session with `PRAGMA idasql.enable_idapython = 1;`.
- `IDASQL_WITH_MCP=ON` fetches `fastmcpp`; `OFF` removes MCP support and the `--mcp` / `.mcp` commands.
- `XSQL_WITH_THINCLIENT` is forced `ON`, and `HTTPLIB_USE_OPENSSL_IF_AVAILABLE` is forced `OFF` because IDASQL uses local plain HTTP.

## Available Tables

30+ virtual tables covering functions, strings, types, cross-references, disassembly, decompilation, and more.
Expand All @@ -236,7 +253,8 @@ cmake --build build --config Release
| `blocks` | Basic blocks - start/end address, func_ea, size |
| `fchunks` | Function chunks - split/tail chunks with owner |
| `instructions` | Disassembly - address, mnemonic, operands, itype, func_addr (DELETE) |
| `heads` | All head items (code + data) - address, size, type, flags, disasm |
| `instruction_operands` | Normalized instruction operands - opnum, text, type, value; optimized by `address` and `func_addr` |
| `heads` | All head items (code + data) - optimized address lookup/range navigation |

### Strings & Bytes

Expand Down Expand Up @@ -310,14 +328,14 @@ cmake --build build --config Release

| Function | Description |
|----------|-------------|
| `grep(pattern, limit, offset)` | Unified entity search function (returns JSON) |
| `decompile(addr)` | Decompile function at address (returns pseudocode) |
| `disasm_at(addr)` | Canonical disassembly listing at address |
| `get_ui_context_json()` | Plugin-only UI context JSON (GUI runtime only) |

### Unified Entity Search

Use the `grep` table for composable SQL searches and `grep()` when JSON output is preferred.
Use the `grep` table for composable SQL searches over named functions, labels,
segments, types, and members.

```sql
-- Search anything starting with "Create"
Expand All @@ -339,8 +357,12 @@ FROM grep
WHERE pattern = 'dw%'
AND kind = 'member';

-- JSON form with pagination
SELECT grep('Create%', 20, 0);
-- Pagination
SELECT name, kind, full_name
FROM grep
WHERE pattern = 'Create%'
ORDER BY kind, name
LIMIT 20 OFFSET 20;
```

## Integration
Expand Down Expand Up @@ -394,7 +416,7 @@ The server uses a random port (8100-8199) to avoid conflicts with `--http`.

For MCP-compatible clients (Model Context Protocol, a standard for AI tool integration):

`--mcp` and `.mcp` are available only when built with `-DIDASQL_WITH_MCP=ON` (default is `OFF`).
`--mcp` and `.mcp` are available when built with `-DIDASQL_WITH_MCP=ON`, which is the default. Build with `-DIDASQL_WITH_MCP=OFF` to omit MCP support.

```bash
# Standalone mode
Expand Down
4 changes: 2 additions & 2 deletions examples/example_basic.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -116,8 +116,8 @@ int main(int argc, char* argv[]) {

// Get function at specific index
auto first_func = session.query(
"SELECT printf('0x%X', func_at_index(0)) as addr, "
" func_at(func_at_index(0)) as name"
"SELECT printf('0x%X', address) as addr, name "
"FROM funcs WHERE rowid = 0"
);
if (!first_func.empty()) {
std::cout << "First function: " << first_func.rows[0][1]
Expand Down
21 changes: 12 additions & 9 deletions examples/example_decompiler.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@ int main(int argc, char* argv[]) {
std::cout << "=== Decompiler Analysis ===\n\n";

// Try to decompile a function to check if Hex-Rays is available
auto test = session.query("SELECT decompile(func_at_index(0)) as code");
auto test = session.query("SELECT decompile((SELECT address FROM funcs WHERE rowid = 0)) as code");
if (!test.success || test.empty() || test.rows[0][0].find("Decompiler") != std::string::npos) {
std::cerr << "Warning: Hex-Rays decompiler may not be available.\n";
std::cerr << "Some queries may fail or return empty results.\n\n";
Expand All @@ -62,10 +62,11 @@ int main(int argc, char* argv[]) {

auto complex = session.query(
"SELECT "
" func_at(func_addr) as name, "
" f.name as name, "
" COUNT(*) as lines "
"FROM pseudocode "
"GROUP BY func_addr "
"FROM pseudocode p "
"JOIN funcs f ON p.func_addr = f.address "
"GROUP BY p.func_addr, f.name "
"ORDER BY lines DESC "
"LIMIT 10"
);
Expand All @@ -84,12 +85,13 @@ int main(int argc, char* argv[]) {

auto most_vars = session.query(
"SELECT "
" func_at(func_addr) as name, "
" f.name as name, "
" COUNT(*) as total_vars, "
" SUM(CASE WHEN is_arg = 1 THEN 1 ELSE 0 END) as args, "
" SUM(CASE WHEN is_arg = 0 THEN 1 ELSE 0 END) as locals "
"FROM ctree_lvars "
"GROUP BY func_addr "
"FROM ctree_lvars l "
"JOIN funcs f ON l.func_addr = f.address "
"GROUP BY l.func_addr, f.name "
"ORDER BY total_vars DESC "
"LIMIT 10"
);
Expand Down Expand Up @@ -196,8 +198,9 @@ int main(int argc, char* argv[]) {
std::cout << "\n=== Lines Containing 'if' Statements ===\n";

auto if_lines = session.query(
"SELECT func_at(func_addr) as func, line "
"FROM pseudocode "
"SELECT f.name as func, p.line "
"FROM pseudocode p "
"JOIN funcs f ON p.func_addr = f.address "
"WHERE line LIKE '%if (%' "
"LIMIT 10"
);
Expand Down
9 changes: 5 additions & 4 deletions examples/example_functions.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -98,10 +98,11 @@ int main(int argc, char* argv[]) {
std::cout << "\n=== Top 10 Functions Making Most Calls ===\n";

auto most_calls = session.query(
"SELECT func_at(func_addr) as name, COUNT(*) as calls "
"FROM instructions "
"WHERE mnemonic = 'call' "
"GROUP BY func_addr "
"SELECT f.name as name, COUNT(*) as calls "
"FROM instructions i "
"JOIN funcs f ON i.func_addr = f.address "
"WHERE i.mnemonic = 'call' "
"GROUP BY i.func_addr, f.name "
"ORDER BY calls DESC "
"LIMIT 10"
);
Expand Down
9 changes: 0 additions & 9 deletions examples/example_grep_search.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,6 @@
* example_grep_search.cpp - Grep-style unified entity search
*
* Demonstrates:
* - grep() SQL function (JSON output)
* - grep virtual table (structured rows)
* - Pattern semantics and pagination
*/
Expand Down Expand Up @@ -43,14 +42,6 @@ int main(int argc, char* argv[]) {
escaped_pattern.insert(pos, 1, '\'');
}

std::cout << "=== grep() JSON Search ===\n\n";
auto json_result = session.query(
"SELECT grep('" + escaped_pattern + "', 10, 0)"
);
if (json_result.row_count() > 0) {
std::cout << json_result.scalar() << "\n\n";
}

std::cout << "=== grep Table Search ===\n\n";

auto rows = session.query(
Expand Down
16 changes: 9 additions & 7 deletions examples/example_instructions.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -95,10 +95,11 @@ int main(int argc, char* argv[]) {
std::cout << "\n=== Functions with Most NOPs ===\n";

auto nops = session.query(
"SELECT func_at(func_addr) as name, COUNT(*) as nop_count "
"FROM instructions "
"WHERE mnemonic = 'nop' "
"GROUP BY func_addr "
"SELECT f.name as name, COUNT(*) as nop_count "
"FROM instructions i "
"JOIN funcs f ON i.func_addr = f.address "
"WHERE i.mnemonic = 'nop' "
"GROUP BY i.func_addr, f.name "
"HAVING nop_count > 5 "
"ORDER BY nop_count DESC "
"LIMIT 10"
Expand Down Expand Up @@ -135,11 +136,12 @@ int main(int argc, char* argv[]) {
// Functions with unusual push/pop ratio
auto unusual = session.query(
"SELECT "
" func_at(func_addr) as name, "
" f.name as name, "
" SUM(CASE WHEN mnemonic = 'push' THEN 1 ELSE 0 END) as pushes, "
" SUM(CASE WHEN mnemonic = 'pop' THEN 1 ELSE 0 END) as pops "
"FROM instructions "
"GROUP BY func_addr "
"FROM instructions i "
"JOIN funcs f ON i.func_addr = f.address "
"GROUP BY i.func_addr, f.name "
"HAVING pushes > 20 AND ABS(pushes - pops) > 5 "
"ORDER BY pushes DESC "
"LIMIT 10"
Expand Down
7 changes: 4 additions & 3 deletions examples/example_strings.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -142,11 +142,12 @@ int main(int argc, char* argv[]) {
std::cout << "\n=== Functions Using Most Strings (Top 10) ===\n";

auto by_func = session.query(
"SELECT func_at(x.from_ea) as func_name, COUNT(DISTINCT s.address) as str_count "
"SELECT f.name as func_name, COUNT(DISTINCT s.address) as str_count "
"FROM strings s "
"JOIN xrefs x ON s.address = x.to_ea "
"WHERE func_at(x.from_ea) IS NOT NULL "
"GROUP BY func_at(x.from_ea) "
"JOIN funcs f ON x.from_func = f.address "
"WHERE x.from_func != 0 "
"GROUP BY x.from_func, f.name "
"ORDER BY str_count DESC "
"LIMIT 10"
);
Expand Down
2 changes: 1 addition & 1 deletion ida-plugin.json
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
"IDAMetadataDescriptorVersion": 1,
"plugin": {
"name": "IDASQL",
"version": "0.0.12",
"version": "0.0.14",
"entryPoint": "idasql",
"description": "SQL interface for IDA databases. Query functions, xrefs, strings, types, and more using SQL. Supports local CLI, HTTP REST server, and optional MCP integration.",
"urls": {
Expand Down
Loading
Loading