Compare commits

...
248 Commits
Author SHA1 Message Date
pancake 8886365925 Release 1.2.2 2025-10-25 11:03:12 +02:00
pancake 01d8e29420 Remove the r2-5.x compat macros 2025-10-25 11:02:39 +02:00
pancake e8b556ee5d Use r_sys_sleep and simplify the overengineered retry_sleep logic 2025-10-25 10:50:05 +02:00
pancake 6c336f48f7 Fix the meson build 2025-10-24 02:35:34 +02:00
pancake 53d61831b2 Initial support for vscode stype prompt files 2025-10-24 02:31:03 +02:00
pancake cbcff6b9dd Add r2ai -E in the cli 2025-10-24 02:29:34 +02:00
pancake f04bf8162e Add the manpage for r2ai.1 2025-10-24 02:04:36 +02:00
pancake 0206ceb390 Add support for r2ai -E and ~/.config/r2ai/rc 2025-10-24 02:00:15 +02:00
pancake ff1f788c48 Minor fix 2025-10-24 01:36:58 +02:00
pancake df135b2c70 Use jan-nano by default and fix the undefined in decai 2025-10-24 01:27:24 +02:00
pancake c152777bbe Dont do rawtools by default yet 2025-10-23 19:06:09 +02:00
pancake 3e084ce797 Implement r2ai.auto.raw to use tool calling with non-tools models 2025-10-23 02:12:36 +02:00
pancake 7fce8788b5 Release 1.2.0 2025-10-21 20:49:18 +02:00
pancake 585e30470e Aim to fix the execute_js tool for r2ai -a 2025-10-21 20:48:28 +02:00
pancake 5594e64ef3 r2ai-q must be public on windows 2025-10-21 20:44:07 +02:00
pancake 904a356290 Update READMEs 2025-10-21 20:40:17 +02:00
pancake d1e2bcf911 Use the powershell http fallback when needed 2025-10-21 20:38:10 +02:00
pancake 21528f7ea8 Use ollama/gpt-oss:20b as default provider/model pair 2025-10-21 20:34:58 +02:00
pancake 2883d134aa Add a WIP decompiler prompt 2025-10-21 20:18:58 +02:00
pancake ec96ba3329 Improve the behaviour of 'r2ai -m' to show, list and select models 2025-10-21 20:12:43 +02:00
pancake f92ea17b01 Move r2ai -n into r2ai -q autoname 2025-10-21 20:07:03 +02:00
pancake 2dc92031e7 Move r2ai -v into r2ai -q varnames 2025-10-21 20:03:16 +02:00
pancake 55d54b4953 Move r2ai -s into r2ai -q signature 2025-10-21 19:58:16 +02:00
pancake 35460e639c Move r2ai -x into r2ai -q explain 2025-10-21 19:49:47 +02:00
pancake a5382c17c4 Move r2ai -V to 'r2ai -q vulns' 2025-10-21 19:41:10 +02:00
pancake f4bdb847db Fix null deref when ^C in r2ai 2025-10-21 19:40:40 +02:00
pancake a7c3d3767c Use r2 -NNH instead of r2 -H to fix build when r2 have broken plugins 2025-10-21 19:29:09 +02:00
pancake 97c47ab28a Add -m, -p, -q to choose model, provider and query from the cli
* Fix the ollama provider
2025-10-21 19:24:11 +02:00
pancake c36bc1500c Dont pass R2AIState if we can pass the RCorePluginSession 2025-10-21 18:58:16 +02:00
pancake 179cb6ca58 Remove statistics global variable 2025-10-21 18:45:49 +02:00
pancake 402c31cc96 Handle -v and -h in the r2ai executable 2025-10-21 18:32:47 +02:00
pancake be14729424 Stop using the deprecated r_cons_singleton 2025-10-21 18:28:09 +02:00
pancake aa9b8e0fba Refactoring with the aim to reduce LOCs in r2ai_http.c 2025-10-21 18:24:09 +02:00
pancake 96225cd555 Update AGENTS.md 2025-10-21 17:45:39 +02:00
pancake 321ca1bb41 Move the LLM related functions from r2ai.c into llm.c 2025-10-21 17:29:14 +02:00
pancake d94ed5a088 Fix r2ai's main and use cps everywhere 2025-10-21 16:40:57 +02:00
pancake 3537d006de Added support to query custom user prompts in r2ai 2025-10-21 00:10:28 +02:00
pancake bdcee9e736 Remove global states, requires r2-6.0 at least 2025-10-20 21:54:48 +02:00
pancake 074b7a6c9f Add AGENTS.md for r2ai.c 2025-10-20 21:25:24 +02:00
pancake 6697df8437 Use RList instead of vector for messages. Fix message count in r2ai -L 2025-10-20 21:04:30 +02:00
pancake ff0fb4c802 Add version information in decai help message and use rcons 2025-10-20 19:49:40 +02:00
pancake ca7d02e03c Refactor the api/providers logic for easier maintainance 2025-10-20 17:35:43 +02:00
pancake bd105b2b0e Fix #176 - Do not accept invalid values in "r2ai -e api=" 2025-10-20 16:57:53 +02:00
pancake 5e18525403 Use clang-format-radare2 instead of the hacky indent.py 2025-10-20 16:48:42 +02:00
pancake c9d0756e74 Fallback to powershell when curl is not found on Windows 2025-10-20 11:05:20 +02:00
Abhi d3fc79f9d1 Fix tmp dir on android(termux) 2025-10-14 09:27:50 +02:00
pancake 8f4c972beb Remove pkg-config hard dependency for r2 >= 6.0.5 2025-10-08 13:51:34 +02:00
pancake f95cda42f7 Release 1.1.2 2025-10-06 23:25:27 +02:00
pancake b241f30432 Fix leak in openai_url 2025-10-06 23:25:01 +02:00
pancake 8eb50e466b Include the r2ai plugin in the meson ci builds 2025-10-06 23:22:01 +02:00
pancake 14b5dcbfb9 Fix make install and warnings 2025-10-06 22:59:22 +02:00
cryptax e264f8f0c8 More references In the README.md 2025-10-06 15:45:10 +02:00
pancake 46c2ce6c62 Fix r2ai install when old symlinks are around 2025-10-03 22:34:36 +02:00
pancake 693ad9270c Handle ollama errors 2025-10-03 18:21:35 +02:00
pancake 8446af7385 Add description to all the config vars 2025-10-03 15:35:48 +02:00
pancake 5a156a0126 Do not use temperature for the openai provider in decai 2025-10-03 15:35:43 +02:00
pancake a28efa7a33 Add config var descriptions (fixes e??) 2025-10-03 12:46:25 +02:00
pancake bc5adb3fcb Refactor the model listing logic in decai 2025-10-02 14:00:30 +02:00
pancake 4bd1d911e0 Bring back the XAI Provider 2025-10-02 13:28:17 +02:00
pancake 41c9734c69 Bring back the mistral provider 2025-10-02 13:25:31 +02:00
pancake 1a41298dd6 Bring back the gemini provider 2025-10-02 13:22:47 +02:00
pancake c618f8ed09 Bring back the readable prompts 2025-10-02 13:20:42 +02:00
pancake 48932c5c64 Add support for listing models from openai endpoints in the decai 2025-10-02 13:17:08 +02:00
pancake 4da20a5c33 Bring back the OpenAI backend for Decai 2025-10-02 11:23:21 +02:00
pancake b7f94ad419 Release 1.1.1 2025-10-01 15:48:01 +02:00
pancake 2e45fb9e9c Fix spaces 2025-10-01 15:44:20 +02:00
pancake 9775e94455 Add support for ollamacloud to decai 2025-10-01 12:16:12 +02:00
pancake 67416766e2 Move the benchmark scripts into its own directory 2025-10-01 12:07:42 +02:00
pancake 5c1be40048 Improve benchmark2html.sh and makefile 2025-10-01 11:00:13 +02:00
pancake 49e87c7b7a Add script to generate benchmark results in html 2025-10-01 10:22:37 +02:00
pancake 11e7c288e0 Include decai.r2.js in the release page 2025-10-01 02:37:59 +02:00
pancake 909f4738f8 Release 1.1.0 2025-10-01 02:33:36 +02:00
pancake 0148a64aa7 Add decai to the release pack 2025-10-01 00:17:34 +02:00
Konstantinos Xynos d8df27a4c7 Refactored decai.r2.js to reuse providers implementations
* Update decai.r2.js to support local gpt4all
* Added support for local API server gpt4all by Nomic
* Added maxTokens support to GPT4all
* Added to GPT4all, temperature as 0.1 to enable deterministic option
* Rafactored using Claude. Needs to be tested
2025-10-01 00:09:18 +02:00
pancake 6608dbcf3c Add the Windows and macOS CI builds 2025-10-01 00:07:56 +02:00
pancake ef83ddefc9 Add r2 badge 2025-09-30 13:12:16 +02:00
pancake c19fd205e3 Make in root wraps the one in src 2025-09-30 12:51:39 +02:00
pancake eec673f8db Improved readme again 2025-09-30 12:29:27 +02:00
pancake 5e20b37e08 Remove python lint ci jobs 2025-09-30 12:15:59 +02:00
pancake 547418adf0 Move python and server stuff into the Attic 2025-09-30 12:00:23 +02:00
pancake e161577bcb Update README 2025-09-30 12:00:17 +02:00
pancake ba186d7c57 Move the getapikey logic into a separate function in r2ai.c 2025-09-26 03:08:49 +02:00
pancake 24c1e3fcb4 Suggest setting the API key with env var instead of the deprecated eval vars 2025-09-25 19:06:45 +02:00
pancake 1f001080ff Use r2-6.0.2 2025-08-20 01:38:51 +02:00
pancake a189a682b6 Disable the python ci job 2025-08-19 21:04:03 +02:00
cryptax a0299f791d Add support for gpt-5 2025-08-19 12:09:46 +02:00
cryptaxandcryptax 79214fb22c Fix to support lm-studio servers with openai style api and baseurl (#196)
Co-authored-by: cryptax <axelle@alligator>
2025-08-18 20:43:31 +02:00
cryptax 775d3243e6 removing port 2025-08-18 16:32:42 +02:00
pancake cd514ac099 Run indent 2025-08-18 12:24:58 +02:00
cryptax 6f352e1966 Support r2ai.baseurl having a custom port 2025-08-18 12:24:58 +02:00
cryptax 32458a653e Documentation for r2ai.port 2025-08-18 12:24:58 +02:00
cryptax b32fd6097f Fix Ollama calls using /chat instead of /chat/completions 2025-08-18 12:24:58 +02:00
cryptax df2fc67e2c Fixing list models for Ollama case 2025-08-18 12:24:58 +02:00
pancake b318c30865 Fix support for gpt-5 in decai 2025-08-14 14:27:08 +02:00
pancake 2610309b58 Release r2ai-1.0.0 2025-08-01 13:19:29 +02:00
pancake 0b81158d01 Rebrand r2ai.host into r2ai.baseurl in r2ai.c 2025-08-01 13:16:52 +02:00
pancake 4bcd2c582d Replace host/port configs with baseurl in decai 2025-08-01 13:08:15 +02:00
pancake 51aac5f348 Rename r2ai.base_url to r2ai.host for consistency with decai 2025-07-11 20:37:34 +02:00
pancake a437938526 Fix r2ai.c for the latest r2 2025-07-11 12:48:34 +02:00
pancake c6bba6c7dd Minor code improvements 2025-07-01 18:55:00 +02:00
pancake 3103df8210 Fix build 2025-06-27 21:38:22 +02:00
pancake b1fd4aab8e Honor use_files in system backend 2025-06-27 17:04:18 +02:00
pancake 26ca088c32 Fix build for old r2 2025-06-27 09:15:41 +02:00
pancake 710eb93bbe More build fixes 2025-06-27 00:26:20 +02:00
pancake fb1f53a176 More oldr2 fixes 2025-06-26 20:12:14 +02:00
pancake 138fd5e7d3 More build fixes for old r2s 2025-06-26 19:54:33 +02:00
pancake bd040f9a48 More build fixes 2025-06-26 19:26:27 +02:00
cryptax 1f3f04d74b documenting options in r2ai src 2025-06-26 19:23:32 +02:00
cryptax a7cff9d970 adding possibility to ask a question on a given function in direct mode 2025-06-26 19:23:27 +02:00
cryptax a1de8828ac todo to fix completely edition of commands 2025-06-26 19:23:23 +02:00
cryptax 7a1d4668d6 minor debug log message modif 2025-06-26 19:23:19 +02:00
cryptax e20f390878 Fix: auto.init_commands may be edited interactively by the user so the real edited command must be shown in the context 2025-06-26 19:23:09 +02:00
cryptax be18fe91ad fixed context reset + remove max_input_tokens: tokens are not characters we cant just truncate does not make sense 2025-06-26 19:21:56 +02:00
cryptax 6df2a231eb Fixing max output tokens were not correctly read and set in Anthropic requests 2025-06-26 19:21:51 +02:00
pancake 668eee7807 Fix build for old versions of r2 2025-06-26 19:20:19 +02:00
pancake 111974071f Fix command injection vuln in decai causing syntax errors in auto responses 2025-06-26 14:46:12 +02:00
pancake b193caec61 Add http.files and system curl option for r2ai.c 2025-06-26 14:31:56 +02:00
pancake 8994415242 Fix build with latest r2 2025-06-26 05:15:52 +02:00
pancake 7e6953dce6 Add abi version for r2 >= 5.9.9 2025-06-21 00:45:46 +02:00
cryptax 0bcd68c434 adding max_input_tokens to auto 2025-06-20 15:29:39 +02:00
pancake 74e0b23ab0 Improve the auto repl with better help and control actions 2025-06-17 12:42:55 +02:00
pancake 1044bea8dd Use r2pipe2 for auto mode to capture stderr 2025-06-17 00:30:09 +02:00
pancake 081e34a835 Improved auto prompt 2025-06-16 23:33:02 +02:00
pancake 9f22b4c3c3 Simplify the auto prompt 2025-06-16 22:56:24 +02:00
pancake 4fb8ab0f2e Add DecaiTTS, Improve reasoning, auto prompt and planning 2025-06-16 22:38:35 +02:00
sha0coder 8e5b7e0152 Dockerfile for r2 + r2ai + decai + r2ghidra + nice .radare2rc (#181) 2025-06-10 00:09:40 +02:00
pancake e40c655013 Rename ask_to_execute to yolo in r2ai-c 2025-06-09 20:12:07 +02:00
cryptax 0ebda776cf remove static lists for providers where dynamic fetch works fine 2025-06-07 12:54:36 +02:00
daniel nakov 879592f22e Add -e model=? to list models 2025-06-04 20:08:49 +02:00
pancake fd3efa6c70 Use the claude endpoint instead of hardcoding available models 2025-06-03 22:21:21 +02:00
pancake b420645c20 Add local YOLO mode in decai auto 2025-05-28 18:56:32 +02:00
pancake 211e9af847 Support broken jsons in decai auto 2025-05-28 18:55:01 +02:00
pancake 58e1eb6d14 Add 'skip' action and improve prompt in decai auto 2025-05-28 17:55:25 +02:00
pancake c72fe14f19 Fix gemini support in decai 2025-05-28 17:43:30 +02:00
pancake 10162f7302 Add decai -e files to use files instead of argv 2025-05-28 17:43:16 +02:00
pancake 949e22fdf6 Add o3 and o4 models in decai 2025-05-28 16:43:52 +02:00
pancake 2355c15cd5 gpt4.1 fix with wrong json for decai auto 2025-05-28 16:23:43 +02:00
pancake c5db441a07 List last claude models in decai 2025-05-26 12:03:42 +02:00
pancake 9853a99bcb Add skynet-like r2ai images 2025-05-15 00:59:55 +02:00
pancake 4d685520b1 Improve ollama error message when a model is not available 2025-05-14 20:02:51 +02:00
pancake a20035fd31 Workaround for the empty dir.tmp bug in r2 2025-05-14 12:56:53 +02:00
pancake 733ebf9283 Release 0.9.6 2025-05-13 15:41:50 +02:00
pancake 595c0ef2d6 Fix decaiExplain with reasoning models 2025-05-12 18:54:26 +02:00
pancake c86b58a956 Implement r2 command output for decai -x[*] 2025-05-12 18:29:44 +02:00
pancake bd1f39b379 Force lang=C in decai -s function signatures 2025-05-12 18:22:58 +02:00
pancake 71bee067b3 Massage r2ai-c-plugin for latest r2kons 2025-05-11 11:26:53 +02:00
pancake 713183d2c7 Rebrand baby into yolo 2025-05-11 11:22:47 +02:00
pancake 1f5406ae55 Implement baby mode and show colorful feedback in decai auto 2025-05-09 11:16:33 +02:00
pancake a9e5aa114b Improve auto mode for local models 2025-05-09 11:02:00 +02:00
pancake 67c1a6d129 Improve auto mode in decai 2025-05-09 00:49:40 +02:00
pancake 91bbd87c2d Emulate reasoning with non-think models via prompt engineering 2025-05-08 22:35:54 +02:00
pancake e1f8ddbff3 Add decai -e think=0,1,2 to support and filter out the think tag 2025-05-08 21:27:40 +02:00
pancake 562aeb1ea5 Fix help for decai -r 2025-05-08 18:58:44 +02:00
pancake 2444f12acc Baby mode for decai auto 2025-05-07 23:02:49 +02:00
pancake d0d58670ec Trim markdown blocks for decai auto 2025-05-07 22:45:35 +02:00
pancake 5eb8231ee1 Fix r2ai help 2025-05-07 22:32:35 +02:00
pancake 10775172d8 Install deps before linting 2025-04-30 17:42:25 +02:00
pancake 5c39cc4c3f Dont check for r2 in the root makefile and refactoring the linting 2025-04-30 11:31:44 +02:00
pancake 79ac83ff47 Missing cdpy for linting 2025-04-29 20:33:47 +02:00
pancake 6869d9e259 untab 2025-04-29 00:03:17 +02:00
pancake dba793411f Update the CI 2025-04-28 11:16:44 +02:00
pancake 4f095b5e90 Refactor old Python file into py/ and refresh the readmes 2025-04-28 11:06:17 +02:00
pancake b4b73d3ceb aout leftover 2025-04-26 18:08:16 +02:00
pancake c818e170a0 The root makefile just informs, no longer doing the python thing 2025-04-26 12:25:52 +02:00
Pau RE 88955740d7 Add system install for r2ai-plugin 2025-04-26 10:02:51 +02:00
pancake 1859d64ca7 Fix r2ai pkgname in the README 2025-04-23 20:05:59 +02:00
pancake 5af7f44498 Deprecate r2ai.api_key 2025-04-22 07:31:49 +02:00
pancake 0fb9ce8e8c Improve r2ai -e model= output 2025-04-21 23:33:34 +02:00
pancake 2d444e7159 deno fmt decai 2025-04-18 11:33:57 +02:00
pancake 6b2e327fc3 Honor dir.tmp in decai 2025-04-18 11:33:25 +02:00
pancake 6276fe6685 Add vdb implementation in Swift 2025-04-16 17:37:58 +02:00
pancake 3c67d6693f rename apiproxy 2025-04-16 17:28:01 +02:00
pancake 35a2139380 Implement an openapi proxy with vdb integration 2025-04-16 17:27:28 +02:00
pancake 594e2f4f20 Fix indent 2025-04-15 16:25:25 +02:00
emilius3m 5f281591b4 Clean model name 2025-04-15 16:07:28 +02:00
cryptax 829ffa0133 indentation fix on r2ai.c 2025-04-15 16:06:07 +02:00
cryptax 2c1b41ad57 fixing default system prompt, decreasing default max_runs 2025-04-15 16:06:07 +02:00
cryptax 91ede7cc72 fixing the programming language bug 2025-04-15 16:06:07 +02:00
cryptax 4b03d2458c adding possible model names for groq and mistral 2025-04-15 16:06:07 +02:00
pancake 57ab90b26a Add more quotes for vdb and improve the current ones 2025-04-15 13:28:59 +02:00
pancake eba9d2ca4b Fix groq url 2025-04-10 13:30:01 +02:00
pancake 165cd2bc46 Add support for groq to decai 2025-04-10 13:21:54 +02:00
Daniel Nakov c4f1ae900f Add support for reasoning_content (#158) 2025-04-09 22:07:29 -04:00
Daniel Nakov e090521bd2 Fix non-auto opts like -x -d etc (#157)
Fixes https://github.com/radareorg/r2ai/issues/155
2025-04-07 14:12:46 -04:00
pancake 094fc325c5 Release 0.9.4 2025-04-07 12:02:15 +02:00
daniel nakov b557e60b8f Fix api key checking. -e r2ai.api_key > env > .r2ai-key file 2025-04-06 18:51:50 +02:00
daniel nakov 97e62cde53 Remove adding additional empty user message 2025-04-06 18:51:50 +02:00
pancake 5b8ee5201e Handle r2ai -e model=? 2025-04-06 10:58:53 +02:00
pancake fb70b8ad32 Remove .bak file 2025-04-06 10:50:12 +02:00
pancake 67d1528895 Add C indent for r2ai 2025-04-06 10:38:33 +02:00
daniel nakov c2aba8ce86 Add support for anthropic thinking 2025-04-06 09:08:19 +02:00
pancake c5c0e13b64 Handle ? in -e r2ai.api 2025-04-02 17:37:17 +02:00
pancake 6fa0ed3d8e Add TypeScript implementation of the vectordb 2025-04-02 11:37:45 +02:00
pancake 8fdb499906 Do not log vector context in chat log to save tokens 2025-04-02 11:27:59 +02:00
pancake 24b958d040 Bring back the vector database to r2ai.c 2025-04-02 11:16:52 +02:00
pancake 1c9a9973da Fix loading API keys from file and environment 2025-04-02 09:24:12 +02:00
pancake e5dea8baa4 Honor user's message provided to r2ai.input 2025-04-02 09:12:44 +02:00
pancake 739da0859c Initial support for ollama based embeds 2025-04-02 08:57:11 +02:00
pancake 3fdfa351bf Indent markdown.c 2025-04-02 08:49:28 +02:00
pancake 6b04f5f691 Remove markdown code blocks in decai's output 2025-04-02 02:24:09 +02:00
Daniel Nakov 5b246273f2 Error handling with retry; Fix some known differences between providers; Add some more -e; Fix api key looking (#149) 2025-03-31 23:24:20 -04:00
daniel nakov 2c5634073f Add ask_to_execute support; Add mistral and grok as providers 2025-03-31 21:02:54 +02:00
Daniel Nakov d8f483e361 Add http retry with expo backoff (#147) 2025-03-31 09:51:24 -04:00
Daniel Nakov e7b154ab13 Use R2_CURL; Add javascript tool; Add Markdown renderer (#146)
* Use R2_CURL

* Add javascript tool; Add markdown rendering

* R_LOG_INFO->R_LOG_DEBUG

* Fix output formatting
2025-03-31 00:07:11 -04:00
Daniel Nakov e1f90c3c92 Use libcurl if available (#145)
* Use libcurl if available

* Fix socket version compat; Better tool handling and output
2025-03-30 11:00:22 -04:00
daniel nakov ee75d5018a Implement chat log; Add tokens to stats 2025-03-30 08:11:39 +02:00
Daniel Nakov 808b3581db Add stats display; Support for all the other providers (#143)
* Add stats display; Support for all the other providers

* Strip ANSI for LLM
2025-03-29 17:42:02 -04:00
Daniel Nakov c39c0a0757 Add .clang-format; print tool calls and responses (#142) 2025-03-29 13:12:53 -04:00
daniel nakov 042acc1be0 Auto mode, rewrite everything 2025-03-29 09:45:52 +01:00
pancake 9a12668822 Add support for openwebui to Decai 2025-03-28 16:59:58 +01:00
Steven England b943ec8be1 fix: use a normal input() statement to be cross os compliant (#135) 2025-03-25 20:21:18 +01:00
cryptax 65371a6967 Detect markdown in decai auto for proper json parsing 2025-03-25 20:20:36 +01:00
pancake 3fd244f7f0 env -> envp 2025-03-25 18:12:29 +01:00
pancake 46d55b3167 Fix #133 - Add system prompt option for Decai 2025-03-25 18:11:58 +01:00
pancake 1bf249188d Do not truncate Gemini's queries 2025-03-25 17:35:09 +01:00
pancake 38aeec0454 mv wip attic 2025-03-25 17:22:39 +01:00
pancake 7ad7bf27ed Recommend hhao/qwen2.5-coder-tools for decai 2025-03-14 10:40:53 +01:00
pancake 647bd799f4 Fix user-install for decai 2025-03-13 17:45:04 +01:00
pancake 9b5b8d2ae6 Enable the decaiDeterministic option by default 2025-03-11 19:10:40 +01:00
pancake b9a1066556 Add model listing for the gemini api in decai 2025-03-11 19:10:20 +01:00
pancake 3d7af1d8d9 Support deterministic output for Gemini 2025-03-11 19:05:55 +01:00
pancake f591d8e03f Fix decai -m 2025-03-11 18:42:51 +01:00
pancake 1268ab1567 Better prompt and decompiler rules for Decai, Swift works even with Mistral now 2025-03-11 18:38:46 +01:00
pancake fa2af78c15 Use more curlPost and improve prompt structure for better results in Decai 2025-03-11 16:29:56 +01:00
pancake 32a16dff4d Fix decai -e with empty value 2025-03-11 14:56:23 +01:00
pancake 5e8798be91 Unify the Decai language logic 2025-03-11 13:52:29 +01:00
pancake 1ae7a2977d Fix dupped instructions in decai query 2025-03-10 19:04:55 +01:00
pancake c4deedbd17 Handle ollama error messages 2025-03-10 18:10:21 +01:00
pancake 6cd8b99594 Fix arch name for debian packages (aarch64 is now arm64) 2025-03-05 21:48:09 +01:00
pancake 7ceb4147f8 Refactor decai -e config logic 2025-03-05 21:05:21 +01:00
pancake 155456912a Dont install files in r2ai-c make all 2025-03-05 20:09:36 +01:00
pancake ca8c0f3c07 Fix warning for r2-5.9.8 2025-03-05 19:44:58 +01:00
pancake 43953507cc Add debian package for r2ai-c 2025-03-05 18:04:34 +01:00
pancake edce63ea67 Add debian package for decai 2025-03-05 17:59:44 +01:00
pancake aac6092151 Improve decai -H help message 2025-03-01 20:37:22 +01:00
pancake 39ddddbc7e Add decai -k and initial refactoring to reuse logic patterns 2025-03-01 05:08:37 +01:00
pancake 3b9a325e3b Fix uaf and dblfri in r2ai.c 2025-02-27 17:16:05 +01:00
pancake ef975edb9d Update to claude 3.7 in r2ai.c 2025-02-26 17:11:33 +01:00
cryptax 060a897a09 adding example for ollama in decai 2025-02-25 22:10:41 +01:00
cryptax b687f76566 adding mention of claude 3.7 + how to use ollama models 2025-02-25 22:10:41 +01:00
daniel nakov 2c747e42d1 Fix #128 2025-02-25 22:10:25 +01:00
pancake e48a0d1166 Implement -m? in r2ai.py to suggest some model uris 2025-02-25 16:26:03 +01:00
pancake 5d16354f56 Always drop params for mistral too 2025-02-25 16:25:53 +01:00
pancake 5f577c5f7d Load mistral key from ~/.r2ai.mistral-key in r2ai.py 2025-02-25 16:25:23 +01:00
pancake 07ee07835d Fix ollama support for r2ai.py 2025-02-25 13:46:15 +01:00
pancake c6e3b5149a Use Claude's 3.7 sonnet instead of the 3.5 one by default 2025-02-25 13:28:49 +01:00
pancake 29aa5d8e60 Add 'decai -m' as an alias for 'decai -e model=' 2025-02-25 13:28:33 +01:00
pancake 6e9cb22f2f List models for claude, openai, ollama and mistral via 'decai -e model=?' 2025-02-25 13:28:06 +01:00
128 changed files with 10735 additions and 3118 deletions

No files matched your search

+146 -41
View File
@@ -1,49 +1,154 @@
# TODO: add windows
name: ci
name: CI and Release
env:
R2V: 5.9.8
R2V: 6.0.4
on:
push:
pull_request:
workflow_dispatch:
branches:
- '**'
tags:
- '[0-9]*'
# pull_request:
jobs:
build-linux:
runs-on: ubuntu-24.04
build:
name: Build (${{ matrix.os }} / ${{ matrix.build_system }})
runs-on: ${{ matrix.os }}
strategy:
fail-fast: false
matrix:
os: [ubuntu-24.04, macos-latest, windows-latest]
build_system: [make, meson]
exclude:
- os: windows-latest
build_system: make
steps:
- uses: actions/checkout@v4
- name: Installing radare2
run: |
wget "https://github.com/radareorg/radare2/releases/download/${R2V}/radare2_${R2V}_amd64.deb"
wget "https://github.com/radareorg/radare2/releases/download/${R2V}/radare2-dev_${R2V}_amd64.deb"
sudo dpkg -i "radare2_${R2V}_amd64.deb"
sudo dpkg -i "radare2-dev_${R2V}_amd64.deb"
- name: Building the C rewrite
run: make -C src
- name: Setting up the Python virtual environment
run: |
python -m venv venv
. venv/bin/activate
pip install pylint
- name: Install Python dependencies
run: |
. venv/bin/activate
pip install .
- name: Linting
run: |
. venv/bin/activate
make cilint
- name: Running r2ai from the shell
run: |
. venv/bin/activate
python -m r2ai.cli -h
- name: Running r2ai from r2pipe
run: |
. venv/bin/activate
r2 -q -c '#!pipe python -m r2ai.cli -h' /bin/ls
- name: Running r2ai from r2
run: |
. venv/bin/activate
r2 -q -i r2ai/plugin.py -c r2ai /bin/ls
- name: Checkout
uses: actions/checkout@v4
- name: Setup Meson/Ninja
if: matrix.build_system == 'meson'
run: |
python -m pip install --upgrade pip
pip install meson ninja
- name: Install radare2 on Linux
if: matrix.os == 'ubuntu-24.04'
run: |
wget "https://github.com/radareorg/radare2/releases/download/${R2V}/radare2_${R2V}_amd64.deb"
wget "https://github.com/radareorg/radare2/releases/download/${R2V}/radare2-dev_${R2V}_amd64.deb"
sudo dpkg -i "radare2_${R2V}_amd64.deb"
sudo dpkg -i "radare2-dev_${R2V}_amd64.deb"
- name: Install radare2 on macOS
if: matrix.os == 'macos-latest'
run: |
git clone --depth=1 --branch "${{env.R2V}}" https://github.com/radareorg/radare2
cd radare2
sys/install.sh
- name: Install radare2 on Windows
if: matrix.os == 'windows-latest'
shell: powershell
run: |
$url = "https://github.com/radareorg/radare2/releases/download/${{ env.R2V }}/radare2-${{ env.R2V }}-w64.zip"
$dest = "$env:TEMP\radare2.zip"
Invoke-WebRequest -Uri $url -OutFile $dest -UseBasicParsing
$prefix = 'C:\radare2'
if (Test-Path $prefix) { Remove-Item -Path $prefix -Recurse -Force }
Expand-Archive -Path $dest -DestinationPath $prefix -Force
# Move contents from subdirectory if present
$subdirs = Get-ChildItem -Path $prefix -Directory
if ($subdirs.Count -eq 1) {
$subdir = $subdirs[0]
Get-ChildItem -Path $subdir.FullName | Move-Item -Destination $prefix -Force
Remove-Item -Path $subdir.FullName -Recurse -Force
}
- name: Build with Make
if: matrix.build_system == 'make'
run: make -C src
- name: Build with Meson (Unix)
if: matrix.build_system == 'meson' && matrix.os != 'windows-latest'
run: |
cd src
meson setup build -Dr2_prefix=${{ secrets.R2_PREFIX }} || meson configure build
meson compile -C build
- name: Build with Meson (Windows)
if: matrix.build_system == 'meson' && matrix.os == 'windows-latest'
shell: powershell
run: |
cd src
$prefix = 'C:\radare2'
meson setup build --backend ninja --prefix="$prefix" -Dr2_prefix="$prefix"
if ($LASTEXITCODE -ne 0) { meson configure build }
meson compile -C build
- name: Create release artifacts (tags only)
if: startsWith(github.ref, 'refs/tags/')
shell: bash
run: |
mkdir -p release
# Collect common binary artefact names produced by Make/Meson
# Include both "r2ai.*" and "libr2ai.*" variants for DLL/dylib/so on all platforms.
find src -type f \( -iname "r2ai.dll" -o -iname "libr2ai.dll" -o -iname "r2ai.dylib" -o -iname "libr2ai.dylib" -o -iname "r2ai.so" -o -iname "libr2ai.so" -o -iname "r2ai.exe" \) -exec cp -f {} release/ \; || true
# Also check top-level build output directories
find . -maxdepth 3 -type f \( -iname "r2ai.dll" -o -iname "libr2ai.dll" -o -iname "r2ai.dylib" -o -iname "libr2ai.dylib" -o -iname "r2ai.so" -o -iname "libr2ai.so" -o -iname "r2ai.exe" \) -exec cp -f {} release/ \; || true
cp -f decai/decai.r2.js release/ || true
if command -v zip >/dev/null 2>&1; then
zip -j "release/r2ai-${{ github.ref_name }}-${{ matrix.os }}-${{ matrix.build_system }}.zip" release/*
else
powershell.exe -Command "Compress-Archive -Path release/* -DestinationPath 'release/r2ai-${{ github.ref_name }}-${{ matrix.os }}-${{ matrix.build_system }}.zip'"
fi
- name: Upload artifacts (tags only)
if: startsWith(github.ref, 'refs/tags/')
uses: actions/upload-artifact@v4
with:
name: ${{ matrix.os }}-${{ matrix.build_system }}-binary
path: release/r2ai-${{ github.ref_name }}-${{ matrix.os }}-${{ matrix.build_system }}.zip
# - name: Smoke test
# run: |
# if command -v r2 >/dev/null 2>&1; then
# r2 -q -c 'r2ai -h' -- || true
# fi
release:
name: Create Release
needs: build
if: startsWith(github.ref, 'refs/tags/')
runs-on: ubuntu-latest
permissions:
contents: write
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Verify tag is on master
run: |
git fetch origin master:refs/remotes/origin/master
if ! git branch -r --contains ${GITHUB_SHA} | grep -q "origin/master"; then
echo "Tag commit is not on origin/master — skipping release."
exit 0
fi
- name: Download artifacts
uses: actions/download-artifact@v4
with:
path: release-artifacts
- name: Add decai.r2.js to release artifacts
run: cp decai/decai.r2.js release-artifacts/
- name: Create GitHub Release
uses: softprops/action-gh-release@v1
with:
files: |
release-artifacts/**/*.zip
release-artifacts/decai.r2.js
generate_release_notes: true
-19
View File
@@ -1,19 +0,0 @@
name: Create release
on:
push:
tags:
- '*'
jobs:
release:
runs-on: ubuntu-latest
steps:
- name: Checkout code
uses: actions/checkout@v4
- name: Create GitHub release
uses: softprops/action-gh-release@v2
with:
draft: false
prerelease: false
generate_release_notes: true
+12 -1
View File
@@ -1,3 +1,14 @@
build
b
src/b
src/r2ai
src/r2ai.dSYM
venv
**/__pycache__
**/__pycache__
*.pyc
*.pyo
*.o
*.so
*.dylib
*.a
third_party
+28
View File
@@ -0,0 +1,28 @@
from transformers import AutoTokenizer
import transformers
import torch
model = "facebook/llm-compiler-13b"
model = "QuantFactory/llm-compiler-7b-GGUF"
# model = "file:///tmp/llm-compiler.gguf"
tokenizer = AutoTokenizer.from_pretrained(model)
pipeline = transformers.pipeline(
"text-generation",
model=model,
torch_dtype=torch.float16,
device_map="auto",
)
sequences = pipeline(
'%3 = alloca i32, align 4',
do_sample=True,
top_k=10,
temperature=0.1,
top_p=0.95,
num_return_sequences=1,
eos_token_id=tokenizer.eos_token_id,
max_length=200,
)
for seq in sequences:
print(f"Result: {seq['generated_text']}")
View File
File renamed without changes.
View File
File renamed without changes.
+249
View File
@@ -0,0 +1,249 @@
import Foundation
// Token representation
struct Token {
var token: String
var count: Int
var df: Int
}
// KD-Tree Node
class KDNodeData {
let point: [Float]
let text: String
var left: KDNodeData?
var right: KDNodeData?
let splitDim: Int
init(point: [Float], text: String, left: KDNodeData? = nil, right: KDNodeData? = nil, splitDim: Int) {
self.point = point
self.text = text
self.left = left
self.right = right
self.splitDim = splitDim
}
}
// Result for K-Nearest Neighbors
struct KNNResult {
let node: KDNodeData
let distanceSq: Float
}
class VectorDB {
// Core database structures
private let dimension: Int
private var root: KDNodeData?
private var tokens: [Token] = []
private var totalDocs: Int = 0
private var size: Int = 0
init(dimension: Int) {
self.dimension = dimension
}
// Validate token (similar to C implementation)
private func isValidToken(_ token: String) -> Bool {
let invalidTokens = ["pancake", "author", "radare2"]
return !invalidTokens.contains(token)
}
// Find a token in the token list
private func findToken(_ tokens: [Token], _ token: String) -> Token? {
return tokens.first { $0.token == token }
}
// Compute embedding using TF-IDF approach
private func computeEmbedding(_ text: String) -> [Float] {
// Zero the embedding vector
var embedding = [Float](repeating: 0.0, count: dimension)
// Tokenize and normalize
let words = text
.lowercased()
.replacingOccurrences(of: "[^a-z0-9\\s]", with: " ", options: .regularExpression)
.split(separator: " ")
.map { String($0) }
.filter { !$0.isEmpty }
// Local document token frequency
var docTokens: [Token] = []
for token in words {
if let existingToken = findToken(docTokens, token) {
let index = docTokens.firstIndex { $0.token == token }!
docTokens[index].count += 1
} else {
docTokens.append(Token(token: token, count: 1, df: 0))
}
}
// Increment total documents
totalDocs += 1
// Update global document frequencies
for docToken in docTokens {
if let globalToken = findToken(self.tokens, docToken.token) {
if isValidToken(docToken.token) {
let index = self.tokens.firstIndex { $0.token == docToken.token }!
self.tokens[index].count += 1
self.tokens[index].df += 1
}
} else {
self.tokens.append(Token(token: docToken.token, count: docToken.count, df: 1))
}
}
// Compute TF-IDF and update embedding
for docToken in docTokens {
// Compute term frequency
let tf = 1 + log(Float(docToken.count))
// Find global token to get document frequency
let dfValue = findToken(self.tokens, docToken.token)?.df ?? 1
// Compute inverse document frequency
let idf = log(Float(totalDocs + 1) / Float(dfValue + 1)) + 1
// Compute weight
let weight = tf * idf
// Hash-based index assignment
let hash = simpleHash(docToken.token)
let index = hash % dimension
// Add weighted value to embedding
embedding[index] += weight
}
// L2 Normalize the embedding
return normalizeVector(embedding)
}
// Simple string hash function
private func simpleHash(_ str: String) -> Int {
var hash = 0
for char in str.unicodeScalars {
hash = ((hash << 5) - hash) + Int(char.value)
hash = hash & hash // Convert to 32-bit integer
}
return abs(hash)
}
// Normalize vector to unit length
private func normalizeVector(_ vec: [Float]) -> [Float] {
let normSq = vec.reduce(0) { $0 + $1 * $1 }
if normSq > 0 {
let norm = sqrt(normSq)
return vec.map { $0 / norm }
}
return vec
}
// Compute squared Euclidean distance
private func squaredDistance(_ a: [Float], _ b: [Float]) -> Float {
guard a.count == b.count else { return Float.greatestFiniteMagnitude }
return zip(a, b).reduce(0) { $0 + pow($1.0 - $1.1, 2) }
}
// Recursive KD-Tree insertion
private func insertRecursive(_ node: KDNodeData?, point: [Float], text: String, depth: Int) -> KDNodeData {
guard let currentNode = node else {
return KDNodeData(point: point, text: text, splitDim: depth % dimension)
}
let axis = currentNode.splitDim
if point[axis] < currentNode.point[axis] {
currentNode.left = insertRecursive(currentNode.left, point: point, text: text, depth: depth + 1)
} else {
currentNode.right = insertRecursive(currentNode.right, point: point, text: text, depth: depth + 1)
}
return currentNode
}
// Insert a document into the vector database
func insert(_ text: String) {
guard !text.isEmpty else { return }
// Compute embedding
let embedding = computeEmbedding(text)
// Insert into KD-Tree
root = insertRecursive(root, point: embedding, text: text, depth: 0)
size += 1
}
// K-Nearest Neighbors search
func query(_ text: String, k: Int) -> [String] {
guard !text.isEmpty, root != nil else { return [] }
// Compute query embedding
let queryEmbedding = computeEmbedding(text)
// Initialize result set
var results: [KNNResult] = []
// Recursive KNN search
func searchRecursive(_ node: KDNodeData?, query: [Float], depth: Int) {
guard let node = node else { return }
// Compute distance and potentially insert into results
let distSq = squaredDistance(query, node.point)
if results.count < k {
results.append(KNNResult(node: node, distanceSq: distSq))
// Maintain max-heap property
results.sort { $0.distanceSq > $1.distanceSq }
} else if distSq < results[0].distanceSq {
results[0] = KNNResult(node: node, distanceSq: distSq)
results.sort { $0.distanceSq > $1.distanceSq }
}
// Decide which subtree to search first
let axis = node.splitDim
let diff = query[axis] - node.point[axis]
let first = diff < 0 ? node.left : node.right
let second = diff < 0 ? node.right : node.left
// Recursively search
searchRecursive(first, query: query, depth: depth + 1)
// Potentially search the other subtree
if results.count < k || diff * diff < results[0].distanceSq {
searchRecursive(second, query: query, depth: depth + 1)
}
}
// Perform the search
searchRecursive(root, query: queryEmbedding, depth: 0)
// Sort results and return texts
return results
.sorted { $0.distanceSq < $1.distanceSq }
.map { $0.node.text }
}
// Get total number of documents
func getSize() -> Int {
return size
}
}
// Example usage
func main() {
// Create a vector database with 64-dimensional embeddings
let db = VectorDB(dimension: 64)
// Insert some sample documents
db.insert("The quick brown fox jumps over the lazy dog")
db.insert("A quick brown fox is very quick")
db.insert("The author of radare2 is name / named pancake")
db.insert("The lazy dog sleeps all day")
// Query for similar documents
let results = db.query("who is the author of radare2?", k: 2)
print("Similar documents:", results)
}
// Run example
main()
+81
View File
@@ -0,0 +1,81 @@
R2_USER_PLUGINS=$(shell r2 -H R2_USER_PLUGINS)
PWD=$(shell pwd)
R2PM_BINDIR=$(shell r2pm -H R2PM_BINDIR)
PV=3.12
ifeq ($(shell which python${PV} > /dev/null && echo ok),ok)
PYTHON?=python${PV}
else
PYTHON?=python3
endif
PIP=$(PYTHON) -m pip
ifeq ($(R2PM_BINDIR),)
MISSING RADARE2
endif
LINTED=r2ai/code_block.py
LINTED+=r2ai/bubble.py
LINTED+=r2ai/const.py
LINTED+=r2ai/backend/kobaldcpp.py
# LINTED+=r2ai/index.py
# LINTED+=r2ai/voice.py
# LINTED+=r2ai/anthropic.py
r2ai-python py python r2aipy: venv
@./r2ai.sh
large:
. venv/bin/activate ; $(PYTHON) -m r2ai.cli -l
all.old:
@test -n "${VIRTUAL_ENV}" || (echo "Run:"; echo ". venv/bin/activate" ; exit 1)
$(PYTHON) main.py || $(MAKE) deps
venv:
$(PYTHON) -m venv venv
if [ -z "`find venv | grep llama_cpp`" ]; then . venv/bin/activate ; pip install . ; fi
deps: venv
#test -n "${VIRTUAL_ENV}" || (echo "Run: . venv/bin/activate" ; exit 1)
. venv/bin/activate && export CMAKE_ARGS="-DLLAMA_METAL=on -DLLAMA_METAL_EMBED_LIBRARY=ON" && \
pip install --force-reinstall -U --no-cache-dir .
clean:
rm -rf venv
rm -rf build
find . -name "*.egg-info" -exec rm -rf {} +
user-install:
rm -f $(R2PM_BINDIR)/r2ai
ln -fs $(PWD)/r2ai.sh $(R2PM_BINDIR)/r2ai
install: user-install
-mkdir -p /usr/local/share/man/man1/r2ai.1
-cp doc/usage/r2ai.1 /usr/local/share/man/man1/r2ai.1
install-plugin user-install-plugin:
ln -fs $(PWD)/r2ai/plugin.py $(R2_USER_PLUGINS)/r2ai.py
uninstall user-uninstall:
rm -f $(R2PM_BINDIR)/r2ai
-rm -f /usr/local/share/man/man1/r2ai.1
user-uninstall-plugin uninstall-plugin:
rm -f $(R2_USER_PLUGINS)/r2ai.py
pub:
$(PYTHON) -m build
twine check dist/*
twine upload -u __token__ --repository-url https://upload.pypi.org/legacy/ --verbose dist/*
pylint lint cilint:
. venv/bin/activate ; pylint $(LINTED)
lintall:
pylint *.py r2ai/*.py
.PHONY: lint lintall
deps-global:
export CMAKE_ARGS="-DLLAMA_METAL=on -DLLAMA_METAL_EMBED_LIBRARY=ON" && \
$(PIP) install --force-reinstall -U --break-system-packages --no-cache-dir .
+155
View File
@@ -0,0 +1,155 @@
# r2ai-python
This directory contains the original implementation of the AI support for radare2 written in Python.
It is considered _deprecated_, but it's still useful and aims to be maintained, we recommend using decai or the C rewrite as alternatives.
The Python implementation consists in a module can be used in 3 different ways:
* commandline repl using r2pipe to spawn or connect to remote radare2 instances
* radare2 core plugin that is instantiated when placing it in the radare2 plugin's directory
* Python API to be used for writing your own scripts
## API Providers
The Python implementation focused on supporting the LlamaCpp Python module, which is somehow heavy to load in many environments, but it also supports litellm which provides access to many external connectors like ollama, openai, anthropic, gemini, etc.
## Deprecation Reasons
There are several reasons why this implementation is considered deprecated and not recommended to use:
1) It's Python.
- Aka, it's slow, heavy loads in same process
- Your system probably have multiple versions installed
- The shell and radare2 python bindings are probably not the same
- Required venv to be created (at least an extra 1GB)
- Not typed, regressions and bugs appear at runtime
2) Goto 1
### Windows
On Windows you may follow the same instructions, just ensure you have the right python environment ready and create the venv to use
```cmd
git clone https://github.com/radareorg/r2ai
cd r2ai
set PATH=C:\Users\YOURUSERNAME\Local\Programs\Python\Python39\;%PATH%
python3 -m pip install .
python3 main.py
```
### Selecting the model
- List all downloaded models: `-m`
- Get a short list of models: `-MM`
- Help: `-h`
**Example selecting a remote models:**
```
[r2ai:0x00006aa0]> -m anthropic:claude-3-7-sonnet-20250219
[r2ai:0x00006aa0]> -m openai:gpt-4
```
**Example downloading a free local AI: Mistral 7B v0.2:**
Launch r2ai, select the model and ask a question. If the model isn't downloaded yet, r2ai will ask you which precise version to download.
```
[r2ai:0x00006aa0]> -m TheBloke/Mistral-7B-Instruct-v0.2-GGUF
```
Then ask your question, and r2ai will automatically download if needed:
```
[r2ai:0x00006aa0]> give me a short algorithm to test prime numbers
Select TheBloke/Mistral-7B-Instruct-v0.2-GGUF model. See -M and -m flags
[?] Quality (smaller is faster):
> Small | Size: 2.9 GB, Estimated RAM usage: 5.4 GB
Medium | Size: 3.9 GB, Estimated RAM usage: 6.4 GB
Large | Size: 7.2 GB, Estimated RAM usage: 9.7 GB
See More
[?] Quality (smaller is faster):
> mistral-7b-instruct-v0.2.Q2_K.gguf | Size: 2.9 GB, Estimated RAM usage: 5.4 GB
mistral-7b-instruct-v0.2.Q3_K_L.gguf | Size: 3.6 GB, Estimated RAM usage: 6.1 GB
mistral-7b-instruct-v0.2.Q3_K_M.gguf | Size: 3.3 GB, Estimated RAM usage: 5.8 GB
mistral-7b-instruct-v0.2.Q3_K_S.gguf | Size: 2.9 GB, Estimated RAM usage: 5.4 GB
mistral-7b-instruct-v0.2.Q4_0.gguf | Size: 3.8 GB, Estimated RAM usage: 6.3 GB
mistral-7b-instruct-v0.2.Q4_K_M.gguf | Size: 4.1 GB, Estimated RAM usage: 6.6 GB
mistral-7b-instruct-v0.2.Q4_K_S.gguf | Size: 3.9 GB, Estimated RAM usage: 6.4 GB
mistral-7b-instruct-v0.2.Q5_0.gguf | Size: 4.7 GB, Estimated RAM usage: 7.2 GB
mistral-7b-instruct-v0.2.Q5_K_M.gguf | Size: 4.8 GB, Estimated RAM usage: 7.3 GB
mistral-7b-instruct-v0.2.Q5_K_S.gguf | Size: 4.7 GB, Estimated RAM usage: 7.2 GB
mistral-7b-instruct-v0.2.Q6_K.gguf | Size: 5.5 GB, Estimated RAM usage: 8.0 GB
mistral-7b-instruct-v0.2.Q8_0.gguf | Size: 7.2 GB, Estimated RAM usage: 9.7 GB
[?] Use this model by default? ~/.r2ai.model:
> Yes
No
[?] Download to ~/.local/share/r2ai/models? (Y/n): Y
```
**Example selecting a local model served by Ollama**
Download a model and make it available through Ollama:
```
$ ollama ls
NAME ID SIZE MODIFIED
codegeex4:latest 867b8e81d038 5.5 GB 23 hours ago
```
Use it from r2ai by prefixing its name with `ollama/`
```
[r2ai:0x00002d30]> -m ollama/codegeex4:latest
[r2ai:0x00002d30]> hi
Hello! How can I assist you today?
```
### Standard/Auto mode
The standard mode is invoked by directly asking the question.
For the Auto mode, the question **must be prefixed** by `' ` (quote + space). The AI may instruct r2ai to run various commands. Those commands are run on *your host*, so you will be asked to review them before they run.
Example in "standard" mode:
```
[r2ai:0x00006aa0]> compute 4+5
4 + 5 = 9
[r2ai:0x00006aa0]> draw me a pancake in ASCII art
Sure, here's a simple ASCII pancake:
_____
( )
( )
-----
```
Example in auto mode:
```
[r2ai:0x00006aa0]>' Decompile the main
[..]
r2ai is going to execute the following command on the host
Want to edit? (ENTER to validate) pdf @ fcn.000015d0
This command will execute on this host: pdf @ fcn.000015d0. Agree? (y/N) y
```
If you wish to edit the command, you can do it inline for short one line commands, or an editor will pop up.
### r2ai Configuration settings
List all settings with `-e`
| Key | Explanation |
| ----------- | ------------------------------------- |
| debug_level | All verbose messages for level 1. Default is 2 |
| auto.max_runs | Maximum number of questions the AI is allowed to ask r2 in auto mode. |
| auto.hide_tool_output | By default false, consequently output of r2cmd, run_python etc is shown. Set to `true` to hide those internal messages. |
| chat.show_cost | Show the cost of each request to the AI if true |
+1 -1
View File
@@ -1,7 +1,7 @@
[project]
name = "r2ai"
dynamic = ["readme"]
version = "0.9.2"
version = "0.9.6"
description = "Artificial intelligence tooling for radare2"
license = {text = "MIT License"}
File renamed without changes.
View File
File renamed without changes.
File renamed without changes.
+30 -22
View File
@@ -78,18 +78,23 @@ class ChatAuto:
self._start_time = time.time()
self._last_run_time = time.time()
self.ask_to_execute = ask_to_execute
model_info = litellm.get_model_info(self.model)
self.max_tokens = min(self.max_tokens, model_info['max_tokens'])
if self.model.startswith('openai/o'):
self.is_reasoning = True
self.max_tokens = model_info['max_tokens']
self.timeout = 60 * 60
init_commands = self.interpreter.env["auto.init_commands"]
self.llama_instance = llama_instance or interpreter.llama_instance if interpreter else None
init_prompt = ""
if init_commands:
init_prompt = f"""
if not self.llama_instance:
rmodel = re.sub(r'ft:([^:]+).*', r'\1', self.model)
model_info = litellm.get_model_info(rmodel)
self.max_tokens = min(self.max_tokens, model_info['max_tokens'])
if self.model.startswith('openai/o'):
self.is_reasoning = True
self.max_tokens = model_info['max_tokens']
self.timeout = 60 * 60
init_commands = self.interpreter.env["auto.init_commands"]
if init_commands:
init_prompt = f"""
Here is some information about the binary to get you started:
> {init_commands}
{r2cmd(init_commands)}
@@ -118,7 +123,6 @@ Here is some information about the binary to get you started:
self.tools.append({ "type": "function", "function": schema })
self.functions[tool.__name__] = tool
self.tool_choice = tool_choice
self.llama_instance = llama_instance or interpreter.llama_instance if interpreter else None
#self.tool_end_message = '\nNOTE: The user saw this output, do not repeat it.'
async def process_tool_calls(self, tool_calls):
@@ -174,12 +178,10 @@ Here is some information about the binary to get you started:
else:
# inline short edit
print(f'r2ai is going to execute the following command on the host')
readline.set_startup_hook(lambda: readline.insert_text(command))
try:
new_command = input("Want to edit? (ENTER to validate) ")
finally:
readline.set_startup_hook(None)
print(f'r2ai is going to execute the following command on the host:')
print(f'> {command}')
new_command = input(f"Type a new command or press ENTER to use {command} ") or command
if answer.lower() != 'y':
answer = input(f"\033[91mThis command will execute on this host: {new_command}. Agree? (y/N)\033[0m ")
@@ -321,7 +323,10 @@ Here is some information about the binary to get you started:
"max_tokens": self.max_tokens,
"stream": stream,
}
res = create_chat_completion(self.interpreter, messages=self.messages, tools=self.tools, **args)
if self.interpreter.env["chat.rawdog"] == "true":
res = create_chat_completion(self.interpreter, messages=self.messages, tools=self.tools, **args)
else:
res = self.llama_instance.create_chat_completion(self.messages, tools=self.tools, **args)
if args['stream']:
return self.async_response_generator(res)
else:
@@ -362,9 +367,12 @@ Here is some information about the binary to get you started:
run_time = format_time(time.time() - self._last_run_time)
self._last_run_time = time.time()
total_time = format_time(time.time() - self._start_time)
if self.interpreter.env["chat.show_cost"] == "true" and self.n_runs > 0:
run_cost = litellm.completion_cost(completion_response=self._last_response, messages=self.messages, model=self.model)
self.cost += run_cost
if self.interpreter.env["chat.show_cost"] == "true" and self.n_runs > 0:
run_cost = 0
if not self.llama_instance:
run_cost = litellm.completion_cost(completion_response=self._last_response, messages=self.messages, model=self.model)
self.cost += run_cost
cb('usage', {
"model": self.model,
"run_cost": run_cost,
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
@@ -163,7 +163,7 @@ class Interpreter:
self.env["chat.rawdog"] = "false"
self.env["chat.reasoning_effort"] = "high"
self.env["chat.stream"] = "true"
self.env["chat.show_cost"] = "true"
self.env["chat.show_cost"] = "false"
self.env["chat.drop_params"] = "false"
self.env["chat.openai_store"] = "false"
self.env["auto.max_runs"] = "100"
@@ -435,13 +435,33 @@ class Interpreter:
# {"role": "user", "content": "Compose a poem that explains the concept of recursion in programming."}
# ]
from litellm import completion as litellm_completion
completion = litellm_completion(
model=self.model.replace(":", "/"),
messages=self.messages,
max_completion_tokens=maxtokens,
temperature=float(self.env["llm.temperature"]),
top_p=float(self.env["llm.top_p"]),
)
completion = None
if self.model.startswith("ollama/"):
completion = litellm_completion(
model=self.model,
messages=self.messages,
drop_params=True,
max_completion_tokens=maxtokens,
temperature=float(self.env["llm.temperature"]),
top_p=float(self.env["llm.top_p"]),
)
elif self.model.startswith("mistral/"):
completion = litellm_completion(
drop_params=True,
model=self.model.replace(":", "/"),
messages=self.messages,
max_completion_tokens=maxtokens,
temperature=float(self.env["llm.temperature"]),
top_p=float(self.env["llm.top_p"]),
)
else:
completion = litellm_completion(
model=self.model.replace(":", "/"),
messages=self.messages,
max_completion_tokens=maxtokens,
temperature=float(self.env["llm.temperature"]),
top_p=float(self.env["llm.top_p"]),
)
response = completion.choices[0].message.content
if "content" in self.messages[-1]:
File renamed without changes.
+7 -1
View File
@@ -7,7 +7,7 @@ import builtins
import traceback
import appdirs
import argparse
import litellm
from r2ai.repl import r2ai_singleton
from r2ai.utils import slurp
from r2ai.repl import runline, r2ai_repl, help_message
@@ -98,6 +98,12 @@ def main(args, commands, dorepl=True):
os.environ["OPENAI_API_KEY"] = apikey
print("[R2AI] OpenAI API key loaded from ~/.r2ai.openai-key", file=sys.stderr)
r2_mistral_file = os.path.join(home_dir, ".r2ai.mistral-key")
if os.path.isfile(r2_mistral_file):
apikey = slurp(r2_mistral_file).strip()
os.environ["MISTRAL_API_KEY"] = apikey
print("[R2AI] Mistral API key loaded from ~/.r2ai.mistral-key", file=sys.stderr)
r2_gemini_file = os.path.join(home_dir, ".r2ai.gemini-key")
if os.path.isfile(r2_gemini_file):
apikey = slurp(r2_gemini_file).strip()
File renamed without changes.
+2 -1
View File
@@ -62,7 +62,7 @@ Uncensored:
Remote:
-m openapi:http://localhost:5001
-m openai:gpt-4
-m anthropic:claude-3-5-sonnet-20241022
-m anthropic:claude-3-7-sonnet-20250219
-m bedrock:anthropic.claude-3-5-sonnet-20241022-v1
-m kobaldcpp:http://localhost:5001
"""
@@ -85,6 +85,7 @@ Anthropic:
-m anthropic:claude-3-opus-20240229
-m anthropic:claude-3-sonnet-20240229
-m anthropic:claude-3-5-sonnet-20241022
-m anthropic:claude-3-7-sonnet-20250219
groq:
-m groq:gemma-7b-it
-m groq:llama2-70b-4096
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
+11 -1
View File
@@ -34,7 +34,7 @@ def r2_cmd(x):
res = None
if r2 is not None:
oc = r2.cmd('e scr.color').strip()
r2.cmd('e scr.color=0')
r2.cmd('e scr.color=3')
res = r2.cmd(x)
r2.cmd('e scr.color=' + oc)
return res
@@ -167,6 +167,14 @@ def slurp_until(endword):
text += line
return text
def list_models():
print("ollama/llama3:latest")
print("ollama/granite-code:8b")
print("anthropic/claude-3-5-sonnet-20240620")
print("anthropic/claude-3-7-sonnet-20250219")
print("openai/gpt-4o")
print("mistral/mistral-tiny")
def set_model(model):
ai = ais[0].ai
if ai.model != model:
@@ -234,6 +242,8 @@ def runline(ai, usertext):
delete_downloaded_model(usertext[3:])
elif usertext.startswith("r2ai"):
print("ERROR: Cant run r2ai from r2ai, you must install the C plugin and run this command from radare2")
elif usertext == "-m?":
list_models()
elif usertext.startswith("-m"):
words = usertext.split(" ")
if len(words) > 1:
File renamed without changes.
File renamed without changes.
File renamed without changes.
+1
View File
@@ -32,6 +32,7 @@ def r2cmd(command: str):
The output of the r2 command
"""
r2 = get_r2_inst()
r2.cmd('e scr.color=3')
if command.startswith('r2 '):
return "You are already in r2!"
cmd = '{"cmd":' + json.dumps(command) + '}'
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
@@ -3,12 +3,17 @@ R2PM_BINDIR=$(shell r2pm -H R2PM_BINDIR)
R2_BINDIR=$(shell r2 -H R2_BINDIR)
install:
# rm -f $(R2PM_BINDIR)/r2ai-server
# ln -fs $(PWD)/r2ai-server/r2ai-server $(R2PM_BINDIR)/r2ai-server
mkdir -p $(R2_BINDIR)
cp -f r2ai-server $(R2_BINDIR)/r2ai-server || echo Maybe use user-install instead?
uninstall:
rm -f $(R2_BINDIR)/r2ai-server
user-install:
mkdir -p $(R2PM_BINDIR)
rm -f $(R2PM_BINDIR)/r2ai-server
ln -fs $(PWD)/r2ai-server $(R2PM_BINDIR)/r2ai-server
user-uninstall:
+25
View File
@@ -0,0 +1,25 @@
# r2ai-server
Start ollama, llamacpp, llamafile, r2ai, kobaldcpp and other language model webservers using a the same syntax for all of them to simplify its launching and setup.
* Use -l to list the implementations available
You can install more via r2pm
* Use -m to list or select the model. Those models can be absolute paths or model names when downloaded via r2ai, which uses the hugging face api
## Running r2ai-server
- Get usage: `r2pm -r r2ai-server`
- List available servers: `r2pm -r r2ai-server -l`
- List available models: `r2pm -r r2ai-server -m`
On Linux, models are stored in `~/.r2ai.models/`. File `~/.r2ai.model` lists the default model and other models.
**Example launching a local Mistral AI server:**
```
$ r2pm -r r2ai-server -l r2ai -m mistral-7b-instruct-v0.2.Q2_K
[12/13/24 10:35:22] INFO r2ai.server - INFO - [R2AI] Serving at port 8080 web.py:336
```
File renamed without changes.
-1
View File
@@ -1 +0,0 @@
recursive-include r2ai/ui *.tcss
+4 -91
View File
@@ -1,93 +1,6 @@
R2_USER_PLUGINS=$(shell r2 -H R2_USER_PLUGINS)
PWD=$(shell pwd)
R2PM_BINDIR=$(shell r2pm -H R2PM_BINDIR)
PV=3.12
ifeq ($(shell which python${PV} > /dev/null && echo ok),ok)
PYTHON?=python${PV}
else
PYTHON?=python3
endif
PIP=$(PYTHON) -m pip
TARGETS=all clean mrproper user-install install user-uninstall uninstall fmt indent
LINTED=r2ai/code_block.py
LINTED+=r2ai/bubble.py
LINTED+=r2ai/const.py
LINTED+=r2ai/backend/kobaldcpp.py
# LINTED+=r2ai/index.py
# LINTED+=r2ai/voice.py
# LINTED+=r2ai/anthropic.py
$(TARGETS):
$(MAKE) -C src $@
ifeq ($(R2PM_BINDIR),)
FATAL ERROR
endif
.PHONY: all all.old deps clean deps-global pub lint cilint
.PHONY: install uninstall user-install user-uninstall
all: venv
@./r2ai.sh
large:
. venv/bin/activate ; $(PYTHON) -m r2ai.cli -l
all.old:
@test -n "${VIRTUAL_ENV}" || (echo "Run:"; echo ". venv/bin/activate" ; exit 1)
$(PYTHON) main.py || $(MAKE) deps
venv:
$(PYTHON) -m venv venv
if [ -z "`find venv | grep llama_cpp`" ]; then . venv/bin/activate ; pip install . ; fi
deps: venv
#test -n "${VIRTUAL_ENV}" || (echo "Run: . venv/bin/activate" ; exit 1)
. venv/bin/activate && export CMAKE_ARGS="-DLLAMA_METAL=on -DLLAMA_METAL_EMBED_LIBRARY=ON" && \
pip install --force-reinstall -U --no-cache-dir .
clean:
rm -rf venv
rm -rf build
find . -name "*.egg-info" -exec rm -rf {} +
mrproper:
$(MAKE) clean
deps-global:
export CMAKE_ARGS="-DLLAMA_METAL=on -DLLAMA_METAL_EMBED_LIBRARY=ON" && \
$(PIP) install --force-reinstall -U --break-system-packages --no-cache-dir .
user-install:
rm -f $(R2PM_BINDIR)/r2ai-server
ln -fs $(PWD)/r2ai-server/r2ai-server $(R2PM_BINDIR)/r2ai-server
rm -f $(R2PM_BINDIR)/r2ai
ln -fs $(PWD)/r2ai.sh $(R2PM_BINDIR)/r2ai
install: user-install
-mkdir -p /usr/local/share/man/man1/r2ai.1
-cp doc/usage/r2ai.1 /usr/local/share/man/man1/r2ai.1
install-decai:
$(MAKE) -C decai user-install
install-server:
$(MAKE) -C r2ai-server user-install
install-plugin user-install-plugin:
ln -fs $(PWD)/r2ai/plugin.py $(R2_USER_PLUGINS)/r2ai.py
uninstall user-uninstall:
rm -f $(R2PM_BINDIR)/r2ai
-rm -f /usr/local/share/man/man1/r2ai.1
user-uninstall-plugin uninstall-plugin:
rm -f $(R2_USER_PLUGINS)/r2ai.py
pub:
$(PYTHON) -m build
twine check dist/*
twine upload -u __token__ --repository-url https://upload.pypi.org/legacy/ --verbose dist/*
cilint:
pylint $(LINTED)
lint:
pylint *.py r2ai/*.py
.PHONY: $(TARGETS)
+72 -283
View File
@@ -1,100 +1,79 @@
```
,______ .______ .______ ,___
: __ \ \____ |: \ : __|
| \____|/ ____|| _,_ || : |
| : \ \ . || : || |
| |___\ \__:__||___| || |
|___| : |___||___|
*
# R2AI - Augmented reversing with LLM for radare2
```console
╭─────────────────────────────────╮
│ ,______ .______ .______ ,___ │
╭──╮ │ : __ \ \____ |: \ : __| │
│ _│_ │ | \____|/ ____|| _,_ || : | │
│ O O < | : \ \ . || : || | │
│ │╷ │ | |___\ \__:__||___| || | │
│ ││ │ |___| : |___||___| │
│ ─╯│ ╰─────────────────────────────────╯
╰───╯
```
[![ci](https://github.com/radareorg/r2ai/actions/workflows/ci.yml/badge.svg)](https://github.com/radareorg/r2ai/actions/workflows/ci.yml)
Run a language model to entertain you or help answering questions about radare2 or reverse engineering in general. The language model may be local (running without Internet on your host) or remote (e.g if you have an API key). Note that models used by r2ai are pulled from external sources which may behave different or respond unreliable information. That's why there's an ongoing effort into improving the post-finetuning using memgpt-like techniques which can't get better without your help!
<p align="center">
<img src="doc/r2clippy.jpg">
</p>
[![radare2](https://img.shields.io/badge/radare2-6.0.4-green)](https://github.com/radareorg/radare2)
## Components
R2AI is structured into four independent components:
This repository contains two plugins for radare2:
* r2ai (python cli tool)
* r2-like repl using r2pipe to comunicate with r2
* supports auto solving mode
* client and server openapi protocol
* download and manage models from huggingface
* decai (r2js plugin focus on decompilation)
* adds 'decai' command to the r2 shell
* talks to local or remote services with curl
* focus on decompilation
* *r2ai-plugin*
* Native plugin written in C
* adds r2ai command inside r2
* r2ai-server
* favour *ollama* instead
* list and select models downloaded from r2ai
* simple cli tool to start local openapi webservers
* supports llamafile, llamacpp, r2ai-w and kobaldcpp
* **r2ai** - native [AI plugin](./src/README.md) for radare2
* **decai** - r2js plugin with special [focus on decompilation](./decai/README.md)
If you are looking to use radare2 with other agents via MCP:
* **r2mcp** - the [official radare2 mcp](https://github.com/radare2/radare2-mcp)
* **r2copilot** - the mcp with focus on CTF [r2copilot](https://github.com/darallium/r2-copilot)
## Features
* Support Auto mode to solve tasks using function calling
* Use local and remote language models (llama, ollama, openai, anthropic, ..)
* Support OpenAI, Anthropic, Bedrock
* Index large codebases or markdown books using a vector database
* Slurp file and perform actions on that
* Embed the output of an r2 command and resolve questions on the given data
* Define different system-level assistant role
* Set environment variables to provide context to the language model
* Configure different roles and customize prompts
* Scriptable via r2pipe via the r2ai command
* Live with repl and batch mode from cli or r2 prompt
* Scriptable via r2pipe
* Use different models, dynamically adjust query template
* Load multiple models and make them talk between them
* Support Automatic (ReAct) mode to solve tasks using function calling
* Use local and remote language models (ollama, openai, grok, anthropic, ..)
* RAG markdown, code or textfiles using its native vector database
* Embed the output of an r2 command and resolve questions on the given data
## User defined Prompts
```console
[0x00000000]> r2ai -q
explain: Explain the current function -
devices: Find and explain devices used -
libs: Group imports by Libraries -
varnames: Better variable names -
autoname: Automatically suggest a better name for this function -
vulns: Find vulnerabilities or bugs in the current function -
signature: Suggest an improved function signature -
dlopen: List libraries loaded with dlopen - Some libraries are loaded
decompile: Augmented decompilation based on LLM -
[0x00000000]>
```
## Installation
### From r2pm
The recommended way to install any of the r2ai components is via r2pm:
Install the various components via `r2pm`:
- `r2pm -ci r2ai`
- `r2pm -ci r2ai-plugin`
- `r2pm -ci decai`
- `r2pm -ci r2ai-server`
### From sources
Running `make` will setup a python virtual environment in the current directory installing all the necessary dependencies and will get into a shell to run r2ai.
The installation is now splitted into two different targets:
* `make install` will place a symlink in `$BINDIR/r2ai`
* `make install-decai` will install the decai r2js decompiler plugin
* `make install-server` will install the r2ai-server
### Windows
On Windows you may follow the same instructions, just ensure you have the right python environment ready and create the venv to use
```cmd
git clone https://github.com/radareorg/r2ai
cd r2ai
set PATH=C:\Users\YOURUSERNAME\Local\Programs\Python\Python39\;%PATH%
python3 -m pip install .
python3 main.py
```console
$ r2pm -Uci r2ai
$ r2pm -Uci decai
```
## Running r2ai
## Using r2ai
### Launch r2ai
- Adds the **r2ai** command to the radare2 shell: `r2 -qc r2ai`
- You can also run the wrapper in $PATH: `r2pm -r r2ai`
- If you installed via r2pm, you can execute it like this: `r2pm -r r2ai`
- Otherwise, `./r2ai.sh [/absolute/path/to/binary]`
Drop your API keys in environment variables or files in your home:
If you have an **API key**, put it in the adequate file:
```console
$ cat ~/.r2ai.anthropic-key
sk-ant-api03-CENSORED
$ export OPENAI_API_KEY=sk-proj-6rlSPS-zN1v...
```
| AI | API key |
| --------- | -------------------------- |
@@ -103,217 +82,27 @@ If you have an **API key**, put it in the adequate file:
| Anthropic | `$HOME/.r2ai.anthropic-key` |
| Mistral | `$HOME/.r2ai.mistral-key` |
Example using an Anthropic API key:
## Saving settings
You may customize and save your configuration settings using your OS's default settings file (e.g `~/.radare2rc` on Linux).
For example, the following configuration sets Claude 3.7 by default, with max output tokens to 64000.
```
$ cat ~/.r2ai.anthropic-key
sk-ant-api03-CENSORED
r2ai -e api=anthropic
r2ai -e model=claude-3-7-sonnet-20250219
r2ai -e max_tokens=64000
```
### Selecting the model
- List all downloaded models: `-m`
- Get a short list of models: `-MM`
- Help: `-h`
**Example selecting a remote models:**
```
[r2ai:0x00006aa0]> -m anthropic:claude-3-5-sonnet-20241022
[r2ai:0x00006aa0]> -m openai:gpt-4
```
**Example downloading a free local AI: Mistral 7B v0.2:**
Launch r2ai, select the model and ask a question. If the model isn't downloaded yet, r2ai will ask you which precise version to download.
```
[r2ai:0x00006aa0]> -m TheBloke/Mistral-7B-Instruct-v0.2-GGUF
```
Then ask your question, and r2ai will automatically download if needed:
```
[r2ai:0x00006aa0]> give me a short algorithm to test prime numbers
Select TheBloke/Mistral-7B-Instruct-v0.2-GGUF model. See -M and -m flags
[?] Quality (smaller is faster):
> Small | Size: 2.9 GB, Estimated RAM usage: 5.4 GB
Medium | Size: 3.9 GB, Estimated RAM usage: 6.4 GB
Large | Size: 7.2 GB, Estimated RAM usage: 9.7 GB
See More
[?] Quality (smaller is faster):
> mistral-7b-instruct-v0.2.Q2_K.gguf | Size: 2.9 GB, Estimated RAM usage: 5.4 GB
mistral-7b-instruct-v0.2.Q3_K_L.gguf | Size: 3.6 GB, Estimated RAM usage: 6.1 GB
mistral-7b-instruct-v0.2.Q3_K_M.gguf | Size: 3.3 GB, Estimated RAM usage: 5.8 GB
mistral-7b-instruct-v0.2.Q3_K_S.gguf | Size: 2.9 GB, Estimated RAM usage: 5.4 GB
mistral-7b-instruct-v0.2.Q4_0.gguf | Size: 3.8 GB, Estimated RAM usage: 6.3 GB
mistral-7b-instruct-v0.2.Q4_K_M.gguf | Size: 4.1 GB, Estimated RAM usage: 6.6 GB
mistral-7b-instruct-v0.2.Q4_K_S.gguf | Size: 3.9 GB, Estimated RAM usage: 6.4 GB
mistral-7b-instruct-v0.2.Q5_0.gguf | Size: 4.7 GB, Estimated RAM usage: 7.2 GB
mistral-7b-instruct-v0.2.Q5_K_M.gguf | Size: 4.8 GB, Estimated RAM usage: 7.3 GB
mistral-7b-instruct-v0.2.Q5_K_S.gguf | Size: 4.7 GB, Estimated RAM usage: 7.2 GB
mistral-7b-instruct-v0.2.Q6_K.gguf | Size: 5.5 GB, Estimated RAM usage: 8.0 GB
mistral-7b-instruct-v0.2.Q8_0.gguf | Size: 7.2 GB, Estimated RAM usage: 9.7 GB
[?] Use this model by default? ~/.r2ai.model:
> Yes
No
[?] Download to ~/.local/share/r2ai/models? (Y/n): Y
```
### Standard/Auto mode
The standard mode is invoked by directly asking the question.
For the Auto mode, the question **must be prefixed** by `' ` (quote + space). The AI may instruct r2ai to run various commands. Those commands are run on *your host*, so you will be asked to review them before they run.
Example in "standard" mode:
```
[r2ai:0x00006aa0]> compute 4+5
4 + 5 = 9
[r2ai:0x00006aa0]> draw me a pancake in ASCII art
Sure, here's a simple ASCII pancake:
_____
( )
( )
-----
```
Example in auto mode:
```
[r2ai:0x00006aa0]>' Decompile the main
[..]
r2ai is going to execute the following command on the host
Want to edit? (ENTER to validate) pdf @ fcn.000015d0
This command will execute on this host: pdf @ fcn.000015d0. Agree? (y/N) y
```
If you wish to edit the command, you can do it inline for short one line commands, or an editor will pop up.
## Running r2ai-server
- Get usage: `r2pm -r r2ai-server`
- List available servers: `r2pm -r r2ai-server -l`
- List available models: `r2pm -r r2ai-server -m`
On Linux, models are stored in `~/.r2ai.models/`. File `~/.r2ai.model` lists the default model and other models.
**Example launching a local Mistral AI server:**
```
$ r2pm -r r2ai-server -l r2ai -m mistral-7b-instruct-v0.2.Q2_K
[12/13/24 10:35:22] INFO r2ai.server - INFO - [R2AI] Serving at port 8080 web.py:336
```
## Running decai
Decai is used from `r2` (e.g `r2 ./mybinary`). Get help with `decai -h`:
```
[0x00406cac]> decai -h
Usage: decai (-h) ...
decai -H - help setting up r2ai
decai -a [query] - solve query with auto mode
decai -d [f1 ..] - decompile given functions
decai -dr - decompile function and its called ones (recursive)
decai -dd [..] - same as above, but ignoring cache
decai -dD [query]- decompile current function with given extra query
...
```
List configuration variables with `decai -e`:
```
[0x00406cac]> decai -e
decai -e api=ollama
decai -e host=http://localhost
decai -e port=11434
decai -e prompt=Rewrite this function and respond ONLY with code, NO explanations, NO markdown, Change 'goto' into if/else/for/while, Simplify as much as possible, use better variable names, take function arguments and strings from comments like 'string:'
decai -e ctxfile=
...
```
List possible APIs to discuss with AI: `decai -e api=?`:
```
[0x00406cac]> decai -e api=?
r2ai
claude
openapi
...
```
### Example using a local Mistral model and r2ai-server
For example, assuming we have a *local* Mistral AI server running on port 8080 with `r2ai-server`, we can decompile a given function with `decai -d`.
The server shows it received the question:
```
GET
CUSTOM
RUNLINE: -R
127.0.0.1 - - [13/Dec/2024 10:40:49] "GET /cmd/-R HTTP/1.1" 200 -
GET
CUSTOM
RUNLINE: -i /tmp/.pdc.txt Rewrite this function and respond ONLY with code, NO explanations, NO markdown, Change goto into if/else/for/while, Simplify as much as possible, use better variable names, take function arguments and and strings from comments like string:. Transform this pseudocode into C
```
### Example using a Mistral API key
Put the API key in `~/.r2ai.mistral-key`.
```
[0x000010d0]> decai -e api=mistral
[0x000010d0]> decai -d main
```c
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
int main(int argc, char **argv, char **envp) {
char password[40];
char input[40];
...
```
### Example with ChatGPT 4
```
[0x00406cac]> decai -e api=openai
[0x00406cac]> decai -d
#include <stdio.h>
#include <unistd.h>
void daemonize() {
daemon(1, 0);
}
...
```
## Further Reading
* There's [a chapter](https://book.rada.re/plugins/r2ai.html) in the official r2book
* Cryptax on [lmstudio+gptoss](https://cryptax.medium.com/r2ai-with-lmstudio-and-gpt-oss-08efa5ea2476) blog post
* Malware analysis [with r2ai](https://arxiv.org/pdf/2504.07574) by Cryptax and Daniel Nakov
* Analysis of [Linux/Trigona ransomware](https://cryptax.medium.com/linux-trigona-analysis-with-r2ai-3e2bd1815e52), [Linux/Prometei botnet](https://cryptax.medium.com/reversing-a-prometei-botnet-binary-with-r2-and-ai-part-one-3cdb3dc6ffab) and [W32/SkyAI](https://cryptax.medium.com/w32-skyai-uses-ai-so-do-i-d33f04d63534with) with r2ai
## Videos
- https://infosec.exchange/@radareorg/111946255058894583
## Development/Testing
Just run `make`
## TODO
* add "undo" command to drop the last message
* dump / restore conversational states (see -L command)
* Implement `~`, `|` and `>` and other r2shell features
- [Solving a crackme](https://infosec.exchange/@radareorg/111946255058894583)
- [De-obfuscation of malware Linux/Ladvix](https://asciinema.org/a/724126)
- [Analysis of the /fast option inside Linux/Trigona ransomware](https://asciinema.org/a/pBPEaJhp6cunWSKFpBUDTgPt4)
+1
View File
@@ -0,0 +1 @@
1.2.2
+5
View File
@@ -0,0 +1,5 @@
#!/bin/sh
vim decai/decai.r2.js
vim src/r2ai.h
vim src/meson.build
vim VERSION
+7
View File
@@ -4,10 +4,17 @@ SPD=$(shell r2 -H R2_LIBR_PLUGINS)
BF=a.out
all:
@echo "Run: make user-install"
test:
@echo 'You can now use 'decai' command'
r2 -i decai.r2.js -c 'decai -e deterministic=true' -caf $(BF)
fmt indent:
deno fmt
user-install:
mkdir -p "$(PD)"
cp decai.r2.js $(PD)/decai.r2.js
user-uninstall:
+113 -17
View File
@@ -2,23 +2,23 @@
The AI based Decompiler plugin for radare2
* Written in plain Javascript
* No dependencies than radare2 and curl in PATH
* Uses local ollama by default, but also r2ai-server
* Use services like Anthropic, OpenAI, HF, XAI, DeepSeek, ..
- Written in plain Javascript
- No dependencies than radare2 and curl in PATH
- Uses local ollama by default, but also r2ai-server
- Use services like Anthropic, OpenAI, HF, XAI, DeepSeek, ..
Features
* Auto mode with function calling with ANY model
* Uses the r2 pseudo decompiler by default, supports any other
* Explain purpose and auto-document functions
* Recursive decompilation to inline stubs
* Autoname functions and perform type propagation
* Find vulnerabilities, guide you and write exploits
* Choose any natural language (not just English)
* Choose output programming language (not just C)
* Chain queries to manually fine tune the results
* Customize decompilation prompt at any time
- Auto mode with function calling with ANY model
- Uses the r2 pseudo decompiler by default, supports any other
- Explain purpose and auto-document functions
- Recursive decompilation to inline stubs
- Autoname functions and perform type propagation
- Find vulnerabilities, guide you and write exploits
- Choose any natural language (not just English)
- Choose output programming language (not just C)
- Chain queries to manually fine tune the results
- Customize decompilation prompt at any time
## Installation
@@ -68,10 +68,60 @@ decai -e model=
decai -e maxinputtokens=-1
```
## Running decai
Decai is used from `r2` (e.g `r2 ./mybinary`). Get help with `decai -h`:
```
[0x00406cac]> decai -h
Usage: decai (-h) ...
decai -H - help setting up r2ai
decai -a [query] - solve query with auto mode
decai -d [f1 ..] - decompile given functions
decai -dr - decompile function and its called ones (recursive)
decai -dd [..] - same as above, but ignoring cache
decai -dD [query]- decompile current function with given extra query
...
```
List configuration variables with `decai -e`:
```
[0x00406cac]> decai -e
decai -e api=ollama
decai -e host=http://localhost
decai -e port=11434
decai -e prompt=Rewrite this function and respond ONLY with code, NO explanations, NO markdown, Change 'goto' into if/else/for/while, Simplify as much as possible, use better variable names, take function arguments and strings from comments like 'string:'
decai -e ctxfile=
...
```
List possible APIs to discuss with AI: `decai -e api=?`:
```
[0x00406cac]> decai -e api=?
r2ai
claude
openapi
...
```
### Example using a local model and ollama
For example, if Ollama serves model codegeex4:latest (`ollama ls`), set decai
API as `ollama` and model `codegeex4:latest`.
```
[0x00002d30]> decai -e api=ollama
[0x00002d30]> decai -e model=codegeex4:latest
[0x00002d30]> decai -q Explain what forkpty does in 2 lines
The `forkpty` function creates a new process with a pseudo-terminal, allowing the parent to interact with the child via standard input/output/err and controlling its terminal.
```
## Examples
See [https://github.com/radareorg/r2ai-examples](https://github.com/radareorg/r2ai-examples)
See
[https://github.com/radareorg/r2ai-examples](https://github.com/radareorg/r2ai-examples)
```c
$ cat stack-overflow/bug.c
@@ -100,4 +150,50 @@ int main(int argc, char **argv, char **envp) {
}
```
--pancake
### Example using a local Mistral model and r2ai-server
For example, assuming we have a _local_ Mistral AI server running on port 8080
with `r2ai-server`, we can decompile a given function with `decai -d`. The
server shows it received the question:
```
GET
CUSTOM
RUNLINE: -R
127.0.0.1 - - [13/Dec/2024 10:40:49] "GET /cmd/-R HTTP/1.1" 200 -
GET
CUSTOM
RUNLINE: -i /tmp/.pdc.txt Rewrite this function and respond ONLY with code, NO explanations, NO markdown, Change goto into if/else/for/while, Simplify as much as possible, use better variable names, take function arguments and and strings from comments like string:. Transform this pseudocode into C
```
### Example using a Mistral API key
Put the API key in `~/.r2ai.mistral-key`.
````
[0x000010d0]> decai -e api=mistral
[0x000010d0]> decai -d main
```c
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
int main(int argc, char **argv, char **envp) {
char password[40];
char input[40];
...
````
### Example with ChatGPT 4
```
[0x00406cac]> decai -e api=openai
[0x00406cac]> decai -d
#include <stdio.h>
#include <unistd.h>
void daemonize() {
daemon(1, 0);
}
...
```
BIN
View File
Binary file not shown.
+1316 -903
View File
File diff suppressed because it is too large. Load diff
+13
View File
@@ -0,0 +1,13 @@
PACKAGE=decai
DEPENDS=radare2
SECTION=user/shell
PRIORITY=optional
MAINTAINER=pancake <[email protected]>
VERSION=$(shell git tag | tail -n 1)
# arch
UNAMEM=$(shell uname -m)
ifeq ($(UNAMEM),x86_64)
ARCH=amd64
else
ARCH=arm64
endif
+2
View File
@@ -0,0 +1,2 @@
AI based decompiler plugin for radare2
r2js plugin for radare2 that provides several features for reverse engineers, from autonaming functions, improve decompiling outputs, find bugs or explain functions to automated crackme solving using local and remote models if API keys are provided..
+34
View File
@@ -0,0 +1,34 @@
include ./CONFIG
UNAME=$(shell uname)
SUDO?=sudo
DEPENDS=
CROSSARCH=x64
R2CFG_FLAGS?=
PWD=$(shell pwd)
PACKAGE_DIR?=${PWD}
R2_VERSION=$(shell r2 -qv)
DOCKCROSS=$(PWD)/../dockcross
R2PLUGDIR=/usr/lib/radare2/$(R2_VERSION)
all: root
$(SUDO) rm -rf control data
$(MAKE) clean
mkdir -p data
cp -rf root/* data
$(MAKE) control
$(MAKE) deb
root:
mkdir -p root/$(R2PLUGDIR)
cp -f ../../decai.r2.js root/$(R2PLUGDIR)
purge: clean
rm -rf root
summary:
echo $(VERSION)
include deb.mk
+130
View File
@@ -0,0 +1,130 @@
# Create .deb without using dpkg tools.
#
# Author: Tim Wegener <[email protected]>
#
# Use 'include deb_hand.mak' after defining the user variables in a local
# makefile.
#
# The 'data' rule must be customised in the local make file.
# This rule should make a 'data' directory containing the full file
# layout of the installed package.
#
# This makefile will create a debian-binary file a control directory and a
# a build directory in the current directory.
# Do 'make clobber' to remove these generated files.
#
# Destination:
# PACKAGE_DIR - directory where package (and support files) will be built
# defaults to the current directory
#
# Sources:
# SOURCE_DIR - directory containing files to be packaged
# ICON_SOURCE - 26x26 icon file for maemo
# DESCR - description with summary on first line
# preinst, postinst, prerm, postrm - optional control shell scripts
# These fields are used to build the control file:
# PACKAGE =
# VERSION =
# ARCH =
# SECTION =
# PRIORITY =
# MAINTAINER =
# DEPENDS =
#
# SOURCE_DIR =
# ICON_SOURCE =
# (ICON_SOURCE is optional)
# *** NO USER CHANGES REQUIRED BEYOND THIS POINT ***
ifeq ($(shell uname),Darwin)
MD5SUM=md5
else
MD5SUM=md5sum
endif
GAWK=awk
PACKAGE_DIR=$(shell pwd)
CONTROL_EXTRAS ?= ${wildcard preinst postinst prerm postrm}
${PACKAGE_DIR}/control: ${PACKAGE_DIR}/data ${CONTROL_EXTRAS} DESCR \
${ICON_SOURCE}
#rm -rf $@
mkdir -p $@
ifneq (${CONTROL_EXTRAS},)
cp ${CONTROL_EXTRAS} $@
endif
# Make control file.
echo "Package: ${PACKAGE}" > $@/control
echo "Version: ${VERSION}" >> $@/control
echo "Section: ${SECTION}" >> $@/control
echo "Priority: ${PRIORITY}" >> $@/control
echo "Architecture: ${ARCH}" >> $@/control
ifneq (${DEPENDS},)
echo "Depends: ${DEPENDS}" >> $@/control
endif
echo "Installed-Size: ${shell du -s ${PACKAGE_DIR}/data|cut -f1}" \
>> $@/control
echo "Maintainer: ${MAINTAINER}" >> $@/control
printf "Description:" >> $@/control
cat DESCR | ${GAWK} '{print " "$$0;}' >> $@/control
#ifneq (${ICON_SOURCE},)
# echo "Maemo-Icon-26:" >> $@/control
# base64 ${ICON_SOURCE} | ${GAWK} '{print " "$$0;}' >> $@/control
#endif
# Make md5sums.
cd ${PACKAGE_DIR}/data && find . -type f -exec ${MD5SUM} {} \; \
| sed -e 's| \./||' \
> $@/md5sums
${PACKAGE_DIR}/debian-binary:
echo "2.0" > $@
${PACKAGE_DIR}/clean:
rm -rf ${PACKAGE_DIR}/data ${PACKAGE_DIR}/control ${PACKAGE_DIR}/build *.deb
${PACKAGE_DIR}/build: ${PACKAGE_DIR}/debian-binary ${PACKAGE_DIR}/control \
${PACKAGE_DIR}/data
rm -rf $@
mkdir $@
cp ${PACKAGE_DIR}/debian-binary $@/
cd ${PACKAGE_DIR}/control && tar czvf $@/control.tar.gz *
cd ${PACKAGE_DIR}/data && \
COPY_EXTENDED_ATTRIBUTES_DISABLE=true \
COPYFILE_DISABLE=true \
tar cpzvf $@/data.tar.gz *
# Convert GNU ar to BSD ar that debian requires.
# Note: Order of files within ar archive is important!
${PACKAGE_DIR}/${PACKAGE}_${VERSION}_${ARCH}.deb: ${PACKAGE_DIR}/build
ar -rc $@ $</debian-binary $</control.tar.gz $</data.tar.gz
#sed -e 's|^\([^/]\+\)/ \(.*\)|\1 \2|g' $@tmp > $@fail
#rm -f $@tmp
#mv $@fail $@
.PHONY: data
data: ${PACKAGE_DIR}/data
.PHONY: control
control: ${PACKAGE_DIR}/control
.PHONY: build
build: ${PACKAGE_DIR}/build
.PHONY: clean
clean: ${PACKAGE_DIR}/clean $(EXTRA_CLEAN)
rm -f debian-binary
.PHONY: deb
deb: ${PACKAGE_DIR}/${PACKAGE}_${VERSION}_${ARCH}.deb
clobber::
rm -rf ${PACKAGE_DIR}/debian_binary ${PACKAGE_DIR}/control \
${PACKAGE_DIR}/data ${PACKAGE_DIR}/build
push:
scp *.deb radare.org:/srv/http/radareorg/cydia/debs
mrproper: clean
rm -rf root
+50
View File
@@ -0,0 +1,50 @@
FROM ubuntu:24.04
WORKDIR /root/
RUN apt update && apt install -y build-essential
RUN apt install -y git curl pkg-config
RUN apt install -y python3.12
RUN apt install -y python3.12-venv
RUN apt install -y zlib1g zlib1g-dev
RUN apt install -y unzip neovim
RUN apt install -y ninja-build meson
# install radare
RUN git clone https://github.com/radareorg/radare2.git
WORKDIR radare2/
RUN sys/install.sh
# install plugins
WORKDIR /root/
RUN r2pm -U
#RUN r2pm -ci r2ghidra r2ghidra-sleigh # slow and optional
RUN r2pm -ci r2ai-server r2ai-plugin # r2ai-py is deprecated
RUN r2pm -ci decai r2dec
# r2 tunning
RUN echo 'e io.unalloc=true' >> /root/.radare2rc
RUN echo 'e cfg.fortunes=false' >> /root/.radare2rc
RUN echo 'e emu.str=true' >> /root/.radare2rc
RUN echo 'e bin.relocs.apply=true' >> /root/.radare2rc
RUN echo 'e dir.tmp=/tmp/' >> /root/.radare2rc
RUN echo 'e scr.prompt.flag=1' >> /root/.radare2rc
RUN echo 'e cfg.editor=nvim' >> /root/.radare2rc
RUN echo 'decai -e deterministic=true' >> /root/.radare2rc
RUN echo 'decai -e lang=Python' >> /root/.radare2rc
RUN echo 'decai -e cmds=pdd,pdg' >> /root/.radare2rc
RUN echo 'decai -e prompt=..' >> /root/.radare2rc
RUN echo 'decai -e api=ollama' >> /root/.radare2rc
RUN echo 'decai -e model=qwen2.5-coder:latest' >> /root/.radare2rc
# Uncomment to have ollama inside the docker (will not use gpu)
#RUN curl -fsSL https://ollama.com/install.sh | sh
#RUN \
#  echo '#!/bin/bash'; \
#  echo 'ollama serve &'; \
#  echo 'sleep 5'; \
#  echo 'ollama pull qwen2.5-coder:latest'; >> /root/ollama.run
#
#RUN chmod +x /root/ollama.run
#RUN /root/ollama.run
+163 -86
View File
@@ -1,86 +1,163 @@
the author of radare2 is named pancake, also known as Sergi Alvarez
reopen the file in read-write mode use the `oo+` command
pancake was born in Catalonia, Sergi Alvarez, r2 author is from Barcelona
to run a script use the -i commandline flag. Some cmdline flags are also accessible from the r2 shell
you can also use `. ...` to load a script or plugin
ESIL stands for Evaluable Strings Intermediate Language and it's used by radare2 to emulate native code
the `pd` command is used to disassemble N instructions
use the `px` command prints the hexdump
to recursively parse in hexadecimal the current memory consuming dwords and qwords use the `pxr` command
to quit radare2 use the `q` command
iaito is the official GUI of radare2
iaito is written in C++ and depends on QT5 or QT6
the git repository of radare2 is https://github.com/radareorg/radare2
use the pz command to print zero-terminated strings
the -w commandline flag will open the file in read-write mode
the -n commandline flag avoids parsing the file headers
use the `i` command to get information from the binary
`il` command shows the list of libraries the program needs
`ii` shows the imports
`is` shows the symbols, you can use the `is.` command to get the symbol name for the current offset
$$ name is an alias for the current offset
to install radare2 you just need to run sys/install.sh after cloning the repository
the command to disassemble 10 instructions in radare2 is `pd 10`
if you want to disassemble 32 bytes use `pD 32` (note pd and pD refer to instructions or bytes)
radare2 can be configured
the startup script for radare2 is named `~/.radare2rc`. This file is used also to define the initial configuration options
to change any configuration use the `e` command or the `-e` commandline flag
cli and cmdline are aliases for commandline
to analyze the program use the aa command, note that there's also aaa, aaaa and aaaaa, which perform more and deeper analysis actions
to seek, move or change the current in radare2 use the `s` command
relative seeks can be done with `s+` and `s-` appending the number
you can set the default configuration options in the `~/.radare2rc` file
to disassemble the function you can use the `pdf` or `pdr` commands
to get the control flow graph of the current function in ascii art use the `agf` command
the ?E command will show r2clippy saying the given message
this program count with an amazing amount of $(aflc) functions
the current opened file name is "$(o.)"
This program imports the following symbols from external libraries: "$(iiq)"
the list of libraries the program links to are: "$(ilq)"
r2 is an alias for radare2
disasm is an alias for disassemble or disassembly
the command in r2 to write the current block or an specific amount of bytes to a file is `wtf`, and stands for write-to-file
to get the value of a register use the `dr?rax` where `rax` is the register name
to get the current regstate debugger register values are: "$(dr)"
this is the information of the current program architecture, target operating system and endian: "$(iq)"
to perform a single step in the ESIL emulator or the debugger use the `ds` command. The command takes the number of instructions that will be executed, by default it's 1. For example `ds 32` is the same as `32ds`
radare2 commands can be prefixed with a number, in that case the command after the decimals is executed many times
if the command starts with "0x", it is the same as seeking there
the current value of the program counter $(dr?PC)
to display the hexpair string of the bytes in the current offset use `p8`
the first 32 bytes in the current offset are: $(p8 32)
to step into 10 instructions use the `ds 10` command
step over won't go into calls, it will execute until is useful when you don't want to go into calls
radare started in 2006 as simple, but advanced hexadecimal editor
r2frida is a plugin for radare2 that to instrument and trace function calls on live processes using Frida
r2frida commands start with the `:` character. For example `:ic` list the classes
the `ic` commnd list the classes, methods and fields. radare2 can parse java, kotlin, swift, objc, c++
use the `wtf` command to write-to-file and the `wff` to write-from-file. think like dump and slurp
r2pipe example script: ```js\nconst msg = r2.cmd("?E Hello World");\nconsole.log(msg);\n```
the r2pipe js API provides a .cmd() and .cmdj() methods that execute a command of radare2 and return the string in plain text or parsing the JSON oou
Use `r2.cmdj()` when you want to parse the output of the command as if it was JSON. This is useful to get structured information from r2. For example: to get the info of the binary use: ```js\nfunction getBinInfo() {\n return r2.cmdj("ij")\n}\n```` neer2pipe js API provides a .cmd() and .cmdj() methods that execute a command of radare2 and return the string in plain text or parsing the JSON oou
the radare project started the year 2006 by pancake (real name is "Sergi Alvarez") and it was designed as a tool for forensics analysis
the radare2 project started as a rewrite of radare in 2009 in order to resolve all the design limitations of the original code
Sergi Alvarez was born in the city of Barcelona, so we can say that pancake is from Catalonia
the first purpose of radare was to recover some deleted files from an HFS partition on an old macppc, the tool accessed the disk in raw and searched for known strings and dumped the results as separte files to disk
the real name of pancake is Sergi Alvarez, he is a Catalan hacker and free software activist who developed many other opensource software, like r2frida, applesign, the 0xFFFF Nokia Flasher, ACR the autotools replacement, and many more
the author of radius2 is Alkali. radius2 is a fast binary emulation and symbolic execution written in Rust and based on radare2's ESIL
the `eco` command is used to change the color theme in radare2
to list all the available color themes run `eco` without any argument. But you can change the current one by passing it as argument
to edit the radare2rc file use the `ed` command. This script will be executed at startup time and can be ignored by using the `r2 -n` flag
ACR stands for AutoConfReplacement, and it is a minimalistic implementation of the GNU autoconf tool developed by pancake and used in radare2 as well as in other projects
to perform a step into the next instruction in the radare2 debugger use the `ds` command. To step over use `dso`
the command f~sym lists flags containing “sym”
set -e cfg.bigendian=true to display values in big endian when using (for example) the pv4 command
scripts specific to files or projects can be created by placing a .r2 file in the binary’s directory
to identify the architecture of a binary, you can use the iI command to inspect headers and the ie command to list entry points
the V key enters visual mode, while q exits it, allowing for interactive disassembly navigation
the `fs imports; f` command lists all flags from the 'imports' flagspace, which should be pointing to the functions imported in all the binaries loaded
use `ood` to reload the current binary in debugger mode (same as starting with 'r2 -d')
the `afl` command lists all functions detected in the binary
the iz command extracts strings from the binary
the `-e anal.hasnext=true` setting enables detection of consecutive functions when analyzing code
the `pdc` command decompiles the current function using pseudocode
use the ao command to view the current instruction with detailed operand information
to rename a function, use the command `afn new_name @ address`
the iS command lists all sections in the binary, along with their permissions and offsets
radare2's author is Sergi Alvarez, also known as pancake.
pancake is from Catalonia, Barcelona.
radare2 originated in 2006 as a forensic tool for HFS+ recovery.
The initial radare aimed for portability, 64bit offset handling, and hex pattern searching.
radare2 (r2) was born in 2009 as a refactored, modular version of radare1.
radare2 follows UNIX principles: "everything is a file", small interacting programs.
The r2pipe API allows accessing radare2 via pipes from any language.
radare2 development is managed on GitHub and discussed on Telegram/Discord.
r2con is the annual radare2 community conference, usually held in Barcelona.
what is ESIL? the meaning is Evaluable Strings Intermediate Language.
ESIL is used by radare2 to emulate native code.
radare2 treats executables, disk images, memory dumps, and processes as binary files.
Core radare2 features include hex editing, disassembly, debugging, patching, and scripting.
ACR stands for AutoConfReplacement, a minimalistic autoconf tool used by radare2.
The radare2 GitHub repository is located at radareorg/radare2.
To install radare2 from source, clone the repository and run `sys/install.sh`.
Build issues can sometimes be resolved by cleaning plugin/cache directories (`~/.local/share/radare2/`).
Use `R2_DEBUG=1` environment variable for startup debugging information.
`make purge` can help remove old radare2 installations.
Use `r2 <file>` to open a file in radare2.
Use `r2 -` to open radare2 with a temporary memory buffer (malloc plugin).
Use `r2 -w <file>` to open a file in write mode.
Use `r2 -n <file>` to open a file without parsing headers.
The `-p [prj]` flag creates a project for the analyzed file.
Reopen a file in read-write mode using `oo+`.
Reopen the current binary in debugger mode using `ood`.
Quit radare2 using the `q` command.
Run a script on startup using the `-i` command-line flag.
Load a script or plugin within r2 using `. <script_path>`.
Execute shell commands within r2 using `! <command>`.
Pipe r2 command output to shell commands using `cmd | <shell_cmd>`.
Interpret r2 command output as commands using `.cmd`. Example: `.is*`.
Repeat the last command using `..` or Enter.
Evaluate expressions using `?`. Example: `? 0x10 + 5`.
`$$` represents the current virtual address (seek).
Execute commands at specific offsets using `@`. Example: `px @ 0x1000`.
Iterate commands over multiple offsets using `@@`. Example: `wx ff @@ 10 20 30`.
Seek to an address or symbol using `s <address>`. Example: `s main`.
Seek backward using `s- <num>`. Example: `s- 16`.
Undo the last seek using `s-`.
Redo the last seek using `s+`.
Change the block size (default view size) using `b <size>`. Example: `b 256`.
Print hex dump using `px [<num_bytes>]`.
Print hex dump of words using `pxw [<num_words>]`.
Print disassembled instructions using `pd [<num_instructions>]`.
Print disassembled bytes using `pD [<num_bytes>]`.
Print zero-terminated strings using `pz`.
Print zero-terminated string at offset using `psz [<max_len>] @ <offset>`.
Get file information using `i`.
Get information about the binary format using `iI`.
List entry points using `ie`.
List imports using `ii`.
List linked libraries using `il`.
List sections using `iS`.
List symbols using `is`.
Get the symbol name at the current offset using `is.`.
Extract strings from the data section using `iz`.
Extract strings from the entire binary using `izz`.
Search for strings using `/ <string>`. Example: `/ "password"`.
Search for hex patterns using `/x <hexpairs>`. Example: `/x cafeba.e`.
Search for assembly instructions using `/c <asm_string>`. Example: `/c jmp eax`.
Assemble instructions and search for bytes using `/a <asm_string>`. Example: `/a mov eax, 1`.
Search for ROP gadgets using `/R`.
Define configuration variables using `e <var>=<value>`. Example: `e asm.arch=arm`.
Check configuration variable values using `e <var>`. Example: `e asm.bits`.
Access configuration values in expressions using `${var}`. Example: `? ${asm.bits}`.
Set `asm.pseudo=true` for pseudo-syntax disassembly.
Set `asm.describe=true` for inline opcode descriptions.
Analyze all functions and code blocks using `aa`.
Analyze functions using `af`.
Analyze function at current offset using `af`.
Analyze basic blocks within a function using `afb`.
Analyze data references using `ad`. Example: `ad@rsp`.
List analyzed functions using `afl`.
Rename a function using `afn <new_name> [@<addr>]`.
List cross-references (xrefs) *to* an address using `axt [<addr>]`.
List cross-references (xrefs) *from* an address using `axf [<addr>]`.
View instruction operand details using `ao`.
List flags matching a pattern using `f~<pattern>`. Example: `f~sym`.
List flags in a specific flagspace using `fs <space>; f`. Example: `fs imports; f`.
Enable analysis of consecutive functions using `e anal.hasnext=true`.
Start radare2 in debug mode using `r2 -d <file>`.
Attach to a process using `r2 -d <pid>`.
Step into the next instruction using `ds`.
Step over the next instruction using `dso`.
Continue execution using `dc`.
Set a breakpoint using `db <address>`.
List breakpoints using `db`.
Remove a breakpoint using `db- <address>`.
View register values using `dr`.
View register values and dereferenced pointers using `drr`.
Set `dbg.bep=entry` or `dbg.bep=main` to break at entry/main on start.
Use `dp` to list processes after a fork.
Use `dcf` to continue until a fork occurs.
Enable tracing child processes on fork using `e dbg.follow.child=true`.
Enter visual mode using `V`.
Exit visual mode using `q`.
Rotate visual modes (hex, disasm, etc.) using `p`/`P`.
Toggle cursor mode using `c`.
Move cursor using `hjkl`.
Follow jump/call address using Enter.
Step/Step Over in visual debug mode using `s`/`S`.
Toggle breakpoints using `b`.
Seek to program counter using `.`.
Add/remove comments using `;`.
Rename functions/variables in visual mode using `dr`.
Define functions/data in visual mode using `d`.
Switch between graph and panel views using Spacebar.
Iaito is the official Qt-based GUI for radare2.
`r2pm` is the radare2 package manager.
Install plugins using `r2pm -ci <plugin_name>`. Example: `r2pm -ci r2ghidra`.
The radare2 toolchain includes `rabin2`, `rasm2`, `rax2`, `rahash2`, `radiff2`, `rafind2`.
`rabin2` extracts information from binary files (headers, symbols, sections).
`rasm2` assembles and disassembles code for various architectures.
`rax2` converts between bases and evaluates expressions.
`rahash2` computes hashes and checksums.
`radiff2` compares binary files.
`rafind2` searches for patterns in files.
`r2ghidra` is a decompiler plugin using Ghidra's decompiler engine.
Use `pdg` for r2ghidra decompilation.
r2ghidra quality may differ from native Ghidra due to analysis differences.
`r2dec` is another decompiler plugin available via r2pm.
Use `pdd` for r2dec decompilation.
`pdc` is radare2's built-in pseudo-decompiler.
`pdc` combines ESIL emulation and pseudo-assembly.
`pdc` output can be verbose but works across many architectures.
`decai` is an AI-powered plugin enhancing decompiler output readability.
`decai` often uses `pdc` as a backend and feeds output to an AI model.
`decai` can explain functions (`-x`), find vulnerabilities (`-V`), suggest names (`-n`).
`r2ai` bridges radare2 with AI language models (local or remote).
`r2ai` can run in direct mode (prompt -> AI) or auto mode (AI uses tools like `r2cmd`).
`r2ai-model` is a dataset project for training LLMs about radare2.
The r2ai-model dataset uses a TSV format (Question TAB r2-command-answer).
`r2frida` integrates radare2 with the Frida dynamic instrumentation toolkit.
Use `r2frida` commands starting with `:` (previously `\`).
Trace function calls with `r2frida` using `:dt`.
Format trace output and trace onEnter/onExit using `:dtf`.
Use `:dtf` format specifiers like `z` (string), `c` (char), `x` (hex).
`r2frida` can trace functions by address, import/export names.
Use `:iE` in r2frida to enumerate exports of a loaded module.
Trace specific call destinations found using analysis commands like `/at call`.
`r2yara` integrates YARA rule matching into radare2.
Install `r2yara` using `r2pm -ci r2yara`.
`r2yara` commands start with `yr` or `yara`.
Load YARA rules from a file using `yr <file>`.
Scan the current file with loaded rules using `yrs`.
List loaded YARA rules using `yrl`.
Generate YARA rules interactively using `yrg` subcommands.
Add strings to a generated rule using `yrgs`.
Add hex patterns to a generated rule using `yrgx`.
Add the currently generated rule using `yr+`.
Flatpak releases of Iaito bundle plugins like r2dec, r2ghidra, r2frida, r2yara, decai.
Optimize text for vector databases by using concise, factual statements.
Rephrasing concepts helps improve embedding quality for semantic search.
AI embeddings represent data (like text or code) in a high-dimensional space.
Vector databases efficiently store and retrieve these embeddings.
Embeddings help capture semantic relationships (e.g., King - Man + Woman ≈ Queen).
Use `~<num>` to grep specific columns from command output. Example: `axt sym.main~[1]`.
Use `~..` to view command output in a pager (like less).
Use `~{}` to pretty-print JSON output. Example: `pdfj~{}`.
Use `.radare2rc` file in the home directory for persistent configurations.
Place `.r2` files in a binary's directory for project-specific scripts/settings.
Count the functions found with aflc
Binary file not shown.

After

Width:  |  Height:  |  Size: 14 KiB

+112
View File
@@ -0,0 +1,112 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Created with Inkscape (https://www.inkscape.org/) -->
<svg
id="svg3004"
version="1.1"
inkscape0:version="0.48.2 r9819"
width="500"
height="300"
xml:space="preserve"
sodipodi0:docname="r2.svg"
sodipodi:docname="r2ai-solid-black.svg"
inkscape:version="1.4 (e7c3feb100, 2024-10-09)"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns:xlink="http://www.w3.org/1999/xlink"
xmlns="http://www.w3.org/2000/svg"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns:inkscape0="https://www.inkscape.org/namespaces/inkscape"
xmlns:sodipodi0="https://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns:ns0="https://creativecommons.org/ns#"
xmlns:ns="https://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:ns1="https://purl.org/dc/elements/1.1/"><sodipodi:namedview
id="namedview1"
pagecolor="#ffffff"
bordercolor="#000000"
borderopacity="0.25"
inkscape:showpageshadow="2"
inkscape:pageopacity="0.0"
inkscape:pagecheckerboard="0"
inkscape:deskcolor="#d1d1d1"
showgrid="false"
showguides="true"
inkscape:zoom="1.207"
inkscape:cx="170.25684"
inkscape:cy="153.27258"
inkscape:window-width="1714"
inkscape:window-height="845"
inkscape:window-x="26"
inkscape:window-y="23"
inkscape:window-maximized="0"
inkscape:current-layer="svg3004" /><metadata
id="metadata3010"><RDF><ns0:Work
ns:about=""><ns1:format>image/svg+xml</ns1:format><ns1:type
ns:resource="https://purl.org/dc/dcmitype/StillImage" /><ns1:title /></ns0:Work></RDF></metadata><defs
id="defs3008"><pattern
inkscape:collect="always"
xlink:href="#pattern6"
preserveAspectRatio="xMidYMid"
id="pattern7"
patternTransform="translate(-3.203125e-6)" /><clipPath
clipPathUnits="userSpaceOnUse"
id="clipPath3018"><path
d="m 0,841.89 1190.55,0 L 1190.55,0 0,0 0,841.89 z"
id="path3020"
inkscape0:connector-curvature="0" /></clipPath><pattern
patternUnits="userSpaceOnUse"
width="1.2374369"
height="110.30865"
patternTransform="translate(295.04028,-128.87021)"
preserveAspectRatio="xMidYMid"
id="pattern6"><rect
style="fill-opacity:1;stroke:none;stroke-width:0.755906"
id="rect6"
width="1.2374369"
height="110.30865"
x="0"
y="0"
ry="0.61871845" /></pattern><marker
markerWidth="1.2374369"
markerHeight="110.30865"
refX="0.61871845"
refY="55.154324"
orient="auto"
id="marker7"><rect
style="fill:url(#pattern7);stroke:none"
width="1.2374369"
height="110.30865"
x="0"
y="0"
id="rect7" /></marker></defs><sodipodi0:namedview
pagecolor="#ffffff"
bordercolor="#666666"
borderopacity="1"
objecttolerance="10"
gridtolerance="10"
guidetolerance="10"
inkscape0:pageopacity="0"
inkscape0:pageshadow="2"
inkscape0:window-width="791"
inkscape0:window-height="818"
id="namedview3006"
showgrid="false"
inkscape0:zoom="0.39912236"
inkscape0:cx="715.69007"
inkscape0:cy="676.77258"
inkscape0:window-x="2687"
inkscape0:window-y="256"
inkscape0:window-maximized="0"
inkscape0:current-layer="g3012" /><path
id="path3496-8-6-5-6-6-2"
style="fill:#c8191c;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:1.30402"
d="m 325.38488,77.033582 c 0,0 -84.14563,42.868488 -84.89316,43.234548 l 12.82427,24.21005 h 72.06889 z m -142.20506,71.694888 49.85451,24.92726 12.85076,-24.2732 -13.29459,-25.13029 c -12.94186,6.3903 -34.27459,16.93531 -49.41068,24.47623 z m 70.6374,4.61097 -12.85482,24.27931 84.42248,42.80665 v -67.08596 z"
sodipodi:nodetypes="ccccccccccccccc" /><path
id="path3496-8-6-5-6-6-2-2"
style="fill:#c8191c;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:1.30402"
d="m 467.89541,76.210715 c 0,0 -84.14563,42.868485 -84.89315,43.234545 l 12.82426,24.21005 h 72.06889 z m -142.20506,71.694895 49.85451,24.92725 12.85076,-24.2732 -13.29459,-25.13029 c -12.94186,6.3903 -34.27459,16.93532 -49.41068,24.47624 z m 70.6374,4.61096 -12.85482,24.27931 84.42248,42.80665 v -67.08596 z"
sodipodi:nodetypes="ccccccccccccccc" /><path
d="M 183.41964,89.200886 V 213.41057 h 15.49313 v 7.01435 H 90.772581 v -7.01435 h 20.749649 v -67.21858 c 0,-11.98138 0.8776,-22.50487 -3.21182,-33.90204 -0.8776,-2.92232 -9.641954,-19.579945 -14.028698,-12.27479 1.17101,4.38544 1.755211,9.35247 1.755211,13.73921 0,19.57733 -13.445796,32.14422 -32.733634,32.14422 -20.165439,0 -34.192836,-14.02741 -34.192836,-34.19284 0,-21.04176 18.412827,-32.437632 37.700665,-32.437632 20.745742,0 37.403352,11.102474 47.055742,28.931102 h 0.57899 V 78.680007 c 28.05349,0.877612 56.4043,3.507829 84.46692,3.507829 v 7.01305 z"
style="fill:#1a1a1a;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:1.30402"
id="path3492-8-7"
inkscape0:connector-curvature="0" /></svg>

After

Width:  |  Height:  |  Size: 5.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 14 KiB

+112
View File
@@ -0,0 +1,112 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Created with Inkscape (https://www.inkscape.org/) -->
<svg
id="svg3004"
version="1.1"
inkscape0:version="0.48.2 r9819"
width="500"
height="300"
xml:space="preserve"
sodipodi0:docname="r2.svg"
sodipodi:docname="r2ai-solid-white.svg"
inkscape:version="1.4 (e7c3feb100, 2024-10-09)"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns:xlink="http://www.w3.org/1999/xlink"
xmlns="http://www.w3.org/2000/svg"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns:inkscape0="https://www.inkscape.org/namespaces/inkscape"
xmlns:sodipodi0="https://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns:ns0="https://creativecommons.org/ns#"
xmlns:ns="https://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:ns1="https://purl.org/dc/elements/1.1/"><sodipodi:namedview
id="namedview1"
pagecolor="#ffffff"
bordercolor="#000000"
borderopacity="0.25"
inkscape:showpageshadow="2"
inkscape:pageopacity="0.0"
inkscape:pagecheckerboard="0"
inkscape:deskcolor="#d1d1d1"
showgrid="false"
showguides="true"
inkscape:zoom="1.207"
inkscape:cx="170.25684"
inkscape:cy="153.27258"
inkscape:window-width="1714"
inkscape:window-height="845"
inkscape:window-x="26"
inkscape:window-y="23"
inkscape:window-maximized="0"
inkscape:current-layer="svg3004" /><metadata
id="metadata3010"><RDF><ns0:Work
ns:about=""><ns1:format>image/svg+xml</ns1:format><ns1:type
ns:resource="https://purl.org/dc/dcmitype/StillImage" /><ns1:title /></ns0:Work></RDF></metadata><defs
id="defs3008"><pattern
inkscape:collect="always"
xlink:href="#pattern6"
preserveAspectRatio="xMidYMid"
id="pattern7"
patternTransform="translate(-3.203125e-6)" /><clipPath
clipPathUnits="userSpaceOnUse"
id="clipPath3018"><path
d="m 0,841.89 1190.55,0 L 1190.55,0 0,0 0,841.89 z"
id="path3020"
inkscape0:connector-curvature="0" /></clipPath><pattern
patternUnits="userSpaceOnUse"
width="1.2374369"
height="110.30865"
patternTransform="translate(295.04028,-128.87021)"
preserveAspectRatio="xMidYMid"
id="pattern6"><rect
style="fill-opacity:1;stroke:none;stroke-width:0.755906"
id="rect6"
width="1.2374369"
height="110.30865"
x="0"
y="0"
ry="0.61871845" /></pattern><marker
markerWidth="1.2374369"
markerHeight="110.30865"
refX="0.61871845"
refY="55.154324"
orient="auto"
id="marker7"><rect
style="fill:url(#pattern7);stroke:none"
width="1.2374369"
height="110.30865"
x="0"
y="0"
id="rect7" /></marker></defs><sodipodi0:namedview
pagecolor="#ffffff"
bordercolor="#666666"
borderopacity="1"
objecttolerance="10"
gridtolerance="10"
guidetolerance="10"
inkscape0:pageopacity="0"
inkscape0:pageshadow="2"
inkscape0:window-width="791"
inkscape0:window-height="818"
id="namedview3006"
showgrid="false"
inkscape0:zoom="0.39912236"
inkscape0:cx="715.69007"
inkscape0:cy="676.77258"
inkscape0:window-x="2687"
inkscape0:window-y="256"
inkscape0:window-maximized="0"
inkscape0:current-layer="g3012" /><path
id="path3496-8-6-5-6-6-2"
style="fill:#c8191c;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:1.30402"
d="m 325.38488,77.033582 c 0,0 -84.14563,42.868488 -84.89316,43.234548 l 12.82427,24.21005 h 72.06889 z m -142.20506,71.694888 49.85451,24.92726 12.85076,-24.2732 -13.29459,-25.13029 c -12.94186,6.3903 -34.27459,16.93531 -49.41068,24.47623 z m 70.6374,4.61097 -12.85482,24.27931 84.42248,42.80665 v -67.08596 z"
sodipodi:nodetypes="ccccccccccccccc" /><path
id="path3496-8-6-5-6-6-2-2"
style="fill:#c8191c;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:1.30402"
d="m 467.89541,76.210715 c 0,0 -84.14563,42.868485 -84.89315,43.234545 l 12.82426,24.21005 h 72.06889 z m -142.20506,71.694895 49.85451,24.92725 12.85076,-24.2732 -13.29459,-25.13029 c -12.94186,6.3903 -34.27459,16.93532 -49.41068,24.47624 z m 70.6374,4.61096 -12.85482,24.27931 84.42248,42.80665 v -67.08596 z"
sodipodi:nodetypes="ccccccccccccccc" /><path
d="M 183.41964,89.200886 V 213.41057 h 15.49313 v 7.01435 H 90.772581 v -7.01435 h 20.749649 v -67.21858 c 0,-11.98138 0.8776,-22.50487 -3.21182,-33.90204 -0.8776,-2.92232 -9.641954,-19.579945 -14.028698,-12.27479 1.17101,4.38544 1.755211,9.35247 1.755211,13.73921 0,19.57733 -13.445796,32.14422 -32.733634,32.14422 -20.165439,0 -34.192836,-14.02741 -34.192836,-34.19284 0,-21.04176 18.412827,-32.437632 37.700665,-32.437632 20.745742,0 37.403352,11.102474 47.055742,28.931102 h 0.57899 V 78.680007 c 28.05349,0.877612 56.4043,3.507829 84.46692,3.507829 v 7.01305 z"
style="fill:#ffffff;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:1.30402"
id="path3492-8-7"
inkscape0:connector-curvature="0" /></svg>

After

Width:  |  Height:  |  Size: 5.1 KiB

+209
View File
@@ -0,0 +1,209 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Created with Inkscape (https://www.inkscape.org/) -->
<svg
id="svg3004"
version="1.1"
inkscape0:version="0.48.2 r9819"
width="500"
height="300"
xml:space="preserve"
sodipodi0:docname="r2.svg"
sodipodi:docname="r2ai.svg"
inkscape:version="1.4 (e7c3feb100, 2024-10-09)"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns:xlink="http://www.w3.org/1999/xlink"
xmlns="http://www.w3.org/2000/svg"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns:inkscape0="https://www.inkscape.org/namespaces/inkscape"
xmlns:sodipodi0="https://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns:ns0="https://creativecommons.org/ns#"
xmlns:ns="https://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:ns1="https://purl.org/dc/elements/1.1/"><sodipodi:namedview
id="namedview1"
pagecolor="#ffffff"
bordercolor="#000000"
borderopacity="0.25"
inkscape:showpageshadow="2"
inkscape:pageopacity="0.0"
inkscape:pagecheckerboard="0"
inkscape:deskcolor="#d1d1d1"
showgrid="false"
showguides="true"
inkscape:zoom="1.207"
inkscape:cx="170.25684"
inkscape:cy="153.27258"
inkscape:window-width="1714"
inkscape:window-height="845"
inkscape:window-x="26"
inkscape:window-y="23"
inkscape:window-maximized="0"
inkscape:current-layer="svg3004" /><metadata
id="metadata3010"><RDF><ns0:Work
ns:about=""><ns1:format>image/svg+xml</ns1:format><ns1:type
ns:resource="https://purl.org/dc/dcmitype/StillImage" /><ns1:title /></ns0:Work></RDF></metadata><defs
id="defs3008"><marker
style="overflow:visible"
id="Stop"
refX="0"
refY="0"
orient="auto"
inkscape:stockid="Stop"
markerWidth="2"
markerHeight="19"
viewBox="0 0 1 1"
inkscape:isstock="true"
inkscape:collect="always"
preserveAspectRatio="none"><path
style="fill:none;stroke:context-stroke;stroke-width:1"
d="M 0,4 V -4"
id="path23" /></marker><pattern
inkscape:collect="always"
xlink:href="#pattern6"
preserveAspectRatio="xMidYMid"
id="pattern7"
patternTransform="translate(-3.203125e-6)" /><clipPath
clipPathUnits="userSpaceOnUse"
id="clipPath3018"><path
d="m 0,841.89 1190.55,0 L 1190.55,0 0,0 0,841.89 z"
id="path3020"
inkscape0:connector-curvature="0" /></clipPath><pattern
patternUnits="userSpaceOnUse"
width="1.2374369"
height="110.30865"
patternTransform="translate(295.04028,-128.87021)"
preserveAspectRatio="xMidYMid"
id="pattern6"><rect
style="fill-opacity:1;stroke:none;stroke-width:0.755906"
id="rect6"
width="1.2374369"
height="110.30865"
x="0"
y="0"
ry="0.61871845" /></pattern><marker
markerWidth="1.2374369"
markerHeight="110.30865"
refX="0.61871845"
refY="55.154324"
orient="auto"
id="marker7"><rect
style="fill:url(#pattern7);stroke:none"
width="1.2374369"
height="110.30865"
x="0"
y="0"
id="rect7" /></marker><marker
style="overflow:visible"
id="Stop-3"
refX="0"
refY="0"
orient="auto"
inkscape:stockid="Stop"
markerWidth="1"
markerHeight="19"
viewBox="0 0 1 1"
inkscape:isstock="true"
inkscape:collect="always"
preserveAspectRatio="none"><path
style="fill:none;stroke:context-stroke;stroke-width:1"
d="M 0,4 V -4"
id="path23-6" /></marker><marker
style="overflow:visible"
id="Stop-3-3"
refX="0"
refY="0"
orient="auto"
inkscape:stockid="Stop"
markerWidth="1"
markerHeight="19"
viewBox="0 0 1 1"
inkscape:isstock="true"
inkscape:collect="always"
preserveAspectRatio="none"><path
style="fill:none;stroke:context-stroke;stroke-width:1"
d="M 0,4 V -4"
id="path23-6-5" /></marker><marker
style="overflow:visible"
id="Stop-6"
refX="0"
refY="0"
orient="auto"
inkscape:stockid="Stop"
markerWidth="2"
markerHeight="19"
viewBox="0 0 1 1"
inkscape:isstock="true"
inkscape:collect="always"
preserveAspectRatio="none"><path
style="fill:none;stroke:context-stroke;stroke-width:1"
d="M 0,4 V -4"
id="path23-0" /></marker></defs><rect
style="fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.755906"
id="rect5"
width="381.13055"
height="146.37111"
x="62.225395"
y="141.42136"
ry="0.6875" /><sodipodi0:namedview
pagecolor="#ffffff"
bordercolor="#666666"
borderopacity="1"
objecttolerance="10"
gridtolerance="10"
guidetolerance="10"
inkscape0:pageopacity="0"
inkscape0:pageshadow="2"
inkscape0:window-width="791"
inkscape0:window-height="818"
id="namedview3006"
showgrid="false"
inkscape0:zoom="0.39912236"
inkscape0:cx="715.69007"
inkscape0:cy="676.77258"
inkscape0:window-x="2687"
inkscape0:window-y="256"
inkscape0:window-maximized="0"
inkscape0:current-layer="g3012" /><path
id="path3496-8-6-5-6"
style="fill:#c8191c;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.96065"
d="m 409.32718,13.961811 c 0,0 -62.86723,31.580382 -63.41792,31.850056 l 9.44739,17.835078 h 53.97053 z m -105.6385,52.816247 36.72686,18.363427 9.46691,-17.881604 -9.79387,-18.512997 c -9.53402,4.707609 -25.24943,12.475918 -36.3999,18.031174 z m 52.03722,3.396805 -9.4699,17.8861 63.07118,31.534837 V 70.174863 Z"
sodipodi:nodetypes="ccccccccccccccc" /><path
id="path3496-8-6-5-6-6"
style="fill:#c8191c;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.96065"
d="m 303.71834,13.970537 c 0,0 -62.86723,31.580387 -63.41792,31.850061 l 9.44739,17.835077 h 53.97053 z m -105.6385,52.816251 36.72686,18.363428 9.46691,-17.881604 -9.79387,-18.512998 c -9.53402,4.707609 -25.24943,12.475918 -36.3999,18.031174 z m 52.03722,3.396805 -9.4699,17.8861 63.07118,31.534837 V 70.183593 Z"
sodipodi:nodetypes="ccccccccccccccc" /><path
style="fill:#ececec;fill-opacity:1;stroke:#ffffff;stroke-width:0.755906;stroke-dasharray:0, 8.31497;stroke-dashoffset:0;stroke-opacity:1;marker-mid:url(#Stop-3)"
d="m 303.83057,67.231304 -3.29247,-0.01657 -3.29246,-0.01657 -3.29247,-0.01657 -3.29246,-0.01657 -3.29247,-0.01657 -3.29247,-0.01657 -3.29246,-0.01657 -3.29247,-0.01657 -3.29246,-0.01657 -3.29247,-0.01657 -3.29247,-0.01657 -3.29246,-0.01657 -3.29247,-0.01657 -3.29246,-0.01657 -3.29247,-0.01657 -3.29247,-0.01657 -3.29246,-0.01657 -3.29247,-0.01657 -3.29246,-0.01657 -3.29247,-0.01657 -3.29246,-0.01657 -3.29247,-0.01657 -3.29247,-0.01657 -3.29246,-0.01657 -3.29247,-0.01657 -3.29246,-0.01657 -3.29247,-0.01657 -3.29247,-0.01657 -3.29246,-0.01657 -3.29247,-0.01657 -3.29246,-0.01657 -3.29247,-0.01657"
id="path7-5"
sodipodi:nodetypes="ccccccccccccccccccccccccccccccccc" /><path
style="fill:#ececec;fill-opacity:1;stroke:#ffffff;stroke-width:0.755906;stroke-dasharray:0, 8.31497;stroke-dashoffset:0;stroke-opacity:1;marker-mid:url(#Stop-3-3)"
d="m 409.35891,66.9637 -3.29247,-0.01657 -3.29246,-0.01657 -3.29247,-0.01657 -3.29246,-0.01657 -3.29247,-0.01657 -3.29247,-0.01657 -3.29246,-0.01657 -3.29247,-0.01657 -3.29246,-0.01657 -3.29247,-0.01657 -3.29247,-0.01657 -3.29246,-0.01657 -3.29247,-0.01657 -3.29246,-0.01657 -3.29247,-0.01657 -3.29247,-0.01657 -3.29246,-0.01657 -3.29247,-0.01657 -3.29246,-0.01657 -3.29247,-0.01657 -3.29246,-0.01657 -3.29247,-0.01657 -3.29247,-0.01657 -3.29246,-0.01657 -3.29247,-0.01657 -3.29246,-0.01657 -3.29247,-0.01657 -3.29247,-0.01657 -3.29246,-0.01657 -3.29247,-0.01657 -3.29246,-0.01657 L 304,66.43346"
id="path7-5-2"
sodipodi:nodetypes="ccccccccccccccccccccccccccccccccc" /><g
id="g5"><path
id="path3496-8-6-5-6-6-2"
style="fill:#c8191c;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.96065"
d="m 306.19367,160.96204 c 0,0 -61.98847,31.58039 -62.53916,31.85006 l 9.44739,17.83508 h 53.09177 z m -104.75974,52.81625 36.72686,18.36343 9.46691,-17.8816 -9.79387,-18.513 c -9.53402,4.70761 -25.24943,12.47592 -36.3999,18.03117 z m 52.03722,3.39681 -9.4699,17.8861 62.19242,31.53484 V 217.1751 Z"
sodipodi:nodetypes="ccccccccccccccc" /><path
style="opacity:1;fill:#1a1a1a;fill-opacity:1;stroke:#000000;stroke-width:0.755906;stroke-dasharray:0, 8.31497;stroke-dashoffset:0;stroke-opacity:1;marker-mid:url(#Stop)"
d="m 306.73753,215.43843 -3.29247,-0.0166 -3.29246,-0.0166 -3.29247,-0.0166 -3.29246,-0.0166 -3.29247,-0.0166 -3.29247,-0.0166 -3.29246,-0.0166 -3.29247,-0.0166 -3.29246,-0.0166 -3.29247,-0.0166 -3.29247,-0.0166 -3.29246,-0.0166 -3.29247,-0.0166 -3.29246,-0.0166 -3.29247,-0.0166 -3.29247,-0.0166 -3.29246,-0.0166 -3.29247,-0.0166 -3.29246,-0.0166 -3.29247,-0.0166 -3.29246,-0.0166 -3.29247,-0.0166 -3.29247,-0.0166 -3.29246,-0.0166 -3.29247,-0.0166 -3.29246,-0.0166 -3.29247,-0.0166 -3.29247,-0.0166 -3.29246,-0.0166 -3.29247,-0.0166 -3.29246,-0.0166 -3.29247,-0.0156"
id="path7-5-9"
sodipodi:nodetypes="ccccccccccccccccccccccccccccccccc" /></g><g
id="g4"><path
id="path3496-8-6-5-6-6-2-2"
style="fill:#c8191c;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.96065"
d="m 417.07189,160.35585 c 0,0 -61.98847,31.58039 -62.53916,31.85006 l 9.44739,17.83508 h 53.09177 z m -104.75974,52.81625 36.72686,18.36343 9.46691,-17.8816 -9.79387,-18.513 c -9.53402,4.70761 -25.24943,12.47592 -36.3999,18.03117 z m 52.03722,3.39681 -9.4699,17.8861 62.19242,31.53484 v -49.42094 z"
sodipodi:nodetypes="ccccccccccccccc" /><path
style="fill:#1a1a1a;fill-opacity:1;stroke:#000000;stroke-width:0.755906;stroke-dasharray:0, 8.31497;stroke-dashoffset:0;stroke-opacity:1;marker-mid:url(#Stop-6)"
d="m 417.61575,214.83224 -3.29247,-0.0166 -3.29246,-0.0166 -3.29247,-0.0166 -3.29246,-0.0166 -3.29247,-0.0166 -3.29247,-0.0166 -3.29246,-0.0166 -3.29247,-0.0166 -3.29246,-0.0166 -3.29247,-0.0166 -3.29247,-0.0166 -3.29246,-0.0166 -3.29247,-0.0166 -3.29246,-0.0166 -3.29247,-0.0166 -3.29247,-0.0166 -3.29246,-0.0166 -3.29247,-0.0166 -3.29246,-0.0166 -3.29247,-0.0166 -3.29246,-0.0166 -3.29247,-0.0166 -3.29247,-0.0166 -3.29246,-0.0166 -3.29247,-0.0166 -3.29246,-0.0166 -3.29247,-0.0166 -3.29247,-0.0166 -3.29246,-0.0166 -3.29247,-0.0166 -3.29246,-0.0166 -3.29247,-0.0156"
id="path7-5-9-6"
sodipodi:nodetypes="ccccccccccccccccccccccccccccccccc" /></g><path
d="M 198.25651,22.933951 V 114.43684 H 209.67 v 5.16734 h -79.6648 v -5.16734 h 15.28587 V 64.918208 c 0,-8.826454 0.64651,-16.578901 -2.36609,-24.974984 -0.64651,-2.152817 -7.10304,-14.424164 -10.33467,-9.042602 0.86266,3.230669 1.29303,6.889785 1.29303,10.121413 0,14.422241 -9.90526,23.680027 -24.11424,23.680027 -14.855492,0 -25.189206,-10.333714 -25.189206,-25.189209 0,-15.501054 13.56438,-23.896176 27.773356,-23.896176 15.28299,0 27.55433,8.178976 34.66506,21.312988 h 0.42653 V 15.183424 c 20.66647,0.646518 41.55197,2.58415 62.22516,2.58415 v 5.166377 z"
style="fill:#231f20;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.96065"
id="path3492-8"
inkscape0:connector-curvature="0" /><path
d="m 201.6106,169.92546 v 91.50289 h 11.41349 v 5.16734 h -79.6648 v -5.16734 h 15.28587 v -49.51864 c 0,-8.82645 0.64651,-16.5789 -2.36609,-24.97498 -0.64651,-2.15282 -7.10304,-14.42417 -10.33467,-9.0426 0.86266,3.23067 1.29303,6.88978 1.29303,10.12141 0,14.42224 -9.90526,23.68003 -24.11424,23.68003 -14.855492,0 -25.189206,-10.33372 -25.189206,-25.18921 0,-15.50106 13.564376,-23.89618 27.773356,-23.89618 15.28299,0 27.55433,8.17898 34.66506,21.31299 h 0.42653 v -21.74624 c 20.66647,0.64652 41.55197,2.58415 62.22516,2.58415 v 5.16638 z"
style="fill:#ffffff;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.96065"
id="path3492-8-7"
inkscape0:connector-curvature="0" /></svg>

After

Width:  |  Height:  |  Size: 12 KiB

File renamed without changes.

Before

Width:  |  Height:  |  Size: 32 KiB

After

Width:  |  Height:  |  Size: 32 KiB

+32
View File
@@ -0,0 +1,32 @@
# 🎯 Prompt: Radare2 Command Generator
You are an expert radare2 assistant. Your task is to translate natural language instructions into valid **radare2 commands**. Use a semicolon `;` to separate multiple commands in the output.
## ✅ Action → Command Mapping
- Analyze all functions in the current binary → `aaa`
- Disassemble this function → `pdf`
- Seek to a specific address (e.g., 0x8080) → `s <address>`
- Enter visual mode → `V`
- Write a string (e.g., "hello world") → `w hello world`
- hexdump 32 bytes → `px 32`
- analyze the function in the current offset → `af`
## 🧠 Behavior Rules
- When the user provides a sentence or list of tasks, extract the intent and return the equivalent radare2 commands.
- Commands should be separated by semicolons `;`.
- Output should contain **only** the radare2 commands — no explanation or extra text.
- Prioritize the mapped actions above. If unclear, infer the most likely match based on context.
## 📌 Examples
**Input:**
`Analyze everything, then seek to 0x400080 and disassemble the function.`
**Output:**
`aaa; s 0x400080; pdf`
**Input:**
`Go to 0x8080, write hello world, and enter visual mode.`
**Output:**
`s 0x8080; w hello world; V`
+30
View File
@@ -0,0 +1,30 @@
# Using kobold-ai from r2ai
KoboldCpp is a fork of llamacpp that exposes a server that r2ai can comuniate with to send queries. One of the main benefits is that it can handle large inputs on a variety of models and it's quite fast.
## Installation
```
r2pm -ci koboldcpp
```
## Running
Start your browser and do your things
```
koboldcpp --model /path/to/gguf
```
Then connect r2ai to it using the koboldcpp model:
```
r2ai '-m koboldcpp'
```
which defaults to:
```
r2ai '-m koboldcpp:http://localhost:5001'
```
+14
View File
@@ -0,0 +1,14 @@
SHELL=/bin/bash
MODELS=gpt-oss:20b gemma3:4b gemma3:12b
MODELS+=granite3-dense:8b mannix/jan-nano:latest yi-coder:latest qwen3:latest deepseek-coder-v2:latest hhao/qwen2.5-coder-tools:latest qwen2.5-coder:latest
MODELS=granite3-dense:8b mannix/jan-nano:latest yi-coder:latest qwen3:latest deepseek-coder-v2:latest hhao/qwen2.5-coder-tools:latest qwen2.5-coder:latest
LANGS=Dart Perl Ruby Javascript PHP C
all:
for a in ${MODELS} ; do \
declare -a ARGS; \
for l in ${LANGS} ; do \
ARGS+=("echo \"decai -e model=$$a;decai -e lang=$$l;s 0x100000924;af;decai -d\" | r2 -q /bin/ls"); \
done ; \
./run.sh "$${ARGS[@]}" > ${a//\//_}.html ; done
+4
View File
@@ -0,0 +1,4 @@
<b>
decai -e model=$a;decai -e lang=$l;s 0x100000924;af;decai -d
</b><br/>
Transform this pseudocode and respond ONLY with plain code (NO explanations, comments or markdown), Change 'goto' into if/else/for/while, Simplify as much as possible, use better variable names, take function arguments and strings from comments like 'string:', Reduce lines of code and fit everything in a single function, Remove all dead code
+49
View File
@@ -0,0 +1,49 @@
#!/bin/bash
# Find all directories containing BENCH.txt
dirs=$(find . -name "BENCH.txt" -type f | xargs dirname | sort | uniq)
# Start HTML output
cat <<EOF > benchmark_index.html
<html>
<table>
EOF
# Process each directory
for dir in $dirs; do
cat <<EOF >> benchmark_index.html
<tr> <td>
<div>
EOF
# Add contents of BENCH.txt
cat "$dir/BENCH.txt" >> benchmark_index.html
cat <<EOF >> benchmark_index.html
</div>
</td>
<td>
EOF
# Find and add links to HTML files
for html_file in $(ls "$dir"/*.html 2>/dev/null | sort); do
base=$(basename "$html_file" .html)
cat <<EOF >> benchmark_index.html
<a href="$dir/$base.html">$base</a>
<br />
EOF
done
cat <<EOF >> benchmark_index.html
</td>
</tr>
EOF
done
# End HTML output
cat <<EOF >> benchmark_index.html
</table>
<br />
<br />
</html>
EOF
+177
View File
@@ -0,0 +1,177 @@
#!/bin/bash
# Generate HTML output for command executions with collapsible sections and width control
if [ $# -eq 0 ]; then
echo "Usage: $0 command1 [command2 ...]"
exit 1
fi
# Timeout in seconds for each command (configurable via TIMEOUT environment variable)
TIMEOUT=${TIMEOUT:-30}
cat << 'EOF'
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Command Execution Results</title>
<style>
* {
box-sizing: border-box;
}
:root {
--max-width: 400px;
}
body {
font-family: monospace;
margin: 0;
padding: 20px;
background-color: #f5f5f5;
overflow-x: hidden;
}
.slider-container {
background: white;
padding: 15px;
margin-bottom: 20px;
box-shadow: 0 2px 4px rgba(0,0,0,0.1);
width: 100%;
}
.slider-container label {
display: block;
margin-bottom: 10px;
font-weight: bold;
}
.slider-container input {
width: 100%;
}
.commands-container {
display: flex;
flex-wrap: wrap;
gap: 15px;
align-items: flex-start;
}
.command-section {
background: white;
border-radius: 8px;
box-shadow: 0 2px 4px rgba(0,0,0,0.1);
overflow: hidden;
width: var(--max-width);
}
.command-title {
background: #e9ecef;
padding: 12px 15px;
cursor: pointer;
border: none;
width: 100%;
text-align: left;
font-family: inherit;
font-size: inherit;
border-radius: 8px 8px 0 0;
position: relative;
}
.command-title:hover {
background: #dee2e6;
}
.command-time {
position: absolute;
top: 12px;
right: 15px;
color: #6c757d;
font-size: 0.9em;
}
.command-body {
padding: 15px;
border-top: 1px solid #dee2e6;
display: none;
}
.command-output {
background: #f8f9fa;
padding: 10px;
border-radius: 4px;
white-space: pre-wrap;
word-wrap: break-word;
overflow-x: auto;
}
.command-section.expanded .command-body {
display: block;
}
</style>
</head>
<body>
<div class="slider-container">
<label for="width-slider">Maximum Width: <span id="width-value">400</span>px</label>
<input type="range" id="width-slider" min="200" max="2000" value="400" step="50">
</div>
<div class="commands-container">
EOF
# Process each command
for cmd in "$@"; do
# Extract title: if command starts with #, use the rest as title, else use the command
if [[ "$cmd" =~ ^#[[:space:]]*(.*) ]]; then
title="${BASH_REMATCH[1]}"
exec_cmd="${cmd#*#}"
exec_cmd="${exec_cmd#"${exec_cmd%%[![:space:]]*}"}" # trim leading spaces
else
title="$cmd"
exec_cmd="$cmd"
fi
# Execute command and time it
start_time=$(date +%s.%N)
tmpfile=$(mktemp)
eval "$exec_cmd" > "$tmpfile" 2>&1 &
pid=$!
(sleep $TIMEOUT && kill $pid 2>/dev/null) &
wait $pid 2>/dev/null
output=$(cat "$tmpfile")
rm "$tmpfile"
exit_code=$?
end_time=$(date +%s.%N)
# Calculate execution time
execution_time=$(echo "$end_time - $start_time" | bc -l 2>/dev/null || echo "0")
if [ $? -ne 0 ]; then
execution_time="N/A"
else
execution_time=$(printf "%.3f" "$execution_time")
fi
# Escape HTML in output
escaped_output=$(echo "$output" | sed 's/&/\&amp;/g; s/</\&lt;/g; s/>/\&gt;/g')
cat << EOF
<div class="command-section expanded">
<button class="command-title" onclick="toggleSection(this)">$title<span class="command-time">${execution_time}s</span></button>
<div class="command-body">
<div class="command-output">$escaped_output</div>
</div>
</div>
EOF
done
cat << 'EOF'
</div>
<script>
// Slider functionality
const slider = document.getElementById('width-slider');
const widthValue = document.getElementById('width-value');
slider.addEventListener('input', function() {
const value = this.value;
document.documentElement.style.setProperty('--max-width', value + 'px');
widthValue.textContent = value;
});
// Toggle functionality
function toggleSection(button) {
const section = button.parentElement;
section.classList.toggle('expanded');
}
</script>
</body>
</html>
EOF
BIN
View File
Binary file not shown.
+66
View File
@@ -0,0 +1,66 @@
#include <stdio.h>
#include <string.h>
#include <ctype.h>
#define ALPHABET_SIZE 26
// Function to perform substitution (both encryption and decryption)
void substitute(const char *input, const char *substitution, char *output, int reverse) {
char mapping[ALPHABET_SIZE];
if (reverse) {
// Create reverse substitution table for decryption
for (int i = 0; i < ALPHABET_SIZE; i++) {
mapping[substitution[i] - 'A'] = 'A' + i;
}
} else {
// Use the direct substitution table for encryption
for (int i = 0; i < ALPHABET_SIZE; i++) {
mapping[i] = substitution[i];
}
}
for (int i = 0; input[i] != '\0'; i++) {
char c = input[i];
// Handle uppercase letters
if (isupper(c)) {
output[i] = mapping[c - 'A'];
}
// Handle lowercase letters
else if (islower(c)) {
output[i] = tolower(mapping[c - 'a']);
}
// Leave non-alphabet characters unchanged
else {
output[i] = c;
}
}
output[strlen(input)] = '\0'; // Null-terminate the output string
}
int main() {
char input[256];
char output[256];
// Configurable substitution key (A-Z)
const char *substitution = "QWERTYUIOPASDFGHJKLZXCVBNM"; // Example substitution key
// Get input from the user
printf("Enter text to encrypt: ");
fgets(input, sizeof(input), stdin);
// Remove newline character from input if it exists
input[strcspn(input, "\n")] = 0;
// Perform encryption (forward substitution)
substitute(input, substitution, output, 0);
printf("Encrypted text: %s\n", output);
// Perform decryption (reverse substitution)
substitute(output, substitution, input, 1);
printf("Decrypted text: %s\n", input);
return 0;
}
-24
View File
@@ -1,24 +0,0 @@
@echo off
setlocal
:check_error
if %errorlevel% neq 0 (
echo Error encountered, exiting script.
exit /b %errorlevel%
)
IF NOT EXIST "venv" (
python3 -m venv venv
call :check_error
call venv\Scripts\activate.bat
call :check_error
pip install -r requirements.txt
call :check_error
) ELSE (
call venv\Scripts\activate.bat
call :check_error
)
python main.py
call :check_error
endlocal
+2
View File
@@ -0,0 +1,2 @@
all:
deno --allow-all server.ts
+70
View File
@@ -0,0 +1,70 @@
// proxy_server.ts
import { serve } from "https://deno.land/[email protected]/http/server.ts";
import VectorDB from "./vdb.ts"
const CONTEXT_SIZE = 5;
const VECTOR_DIMENSION = 64;
const TARGET_SERVER = "http://localhost:11434"; // 👈 change to your target
const DATA_PATH = "../doc/data/quotes.txt";
function isIntercepted(url: string) {
return url.startsWith("/api/chat");
}
const db = new VectorDB(VECTOR_DIMENSION);
const fileContent = await Deno.readTextFile(DATA_PATH);
const lines = fileContent.split("\n");
for (const line of lines) {
if (line.trim().length > 0) {
db.insert(line);
// console.log("Line:", line);
}
}
serve(async (req) => {
const url = new URL(req.url);
const targetUrl = new URL(url.pathname + url.search, TARGET_SERVER);
console.log(req);
let body = req.body;
if (req.method === "POST" && req.url.endsWith("/api/chat")) {
const obj = JSON.parse(await req.text());
const newMessages = [];
for (let msg of obj.messages) {
let content = "<prompt>" + msg.content.trim() + "</prompt>\n";
if (msg.role === "user") {
const context = db.query(msg.content, CONTEXT_SIZE);
for (const ctx of context) {
console.log("<context>" + ctx);
content += "<context>" + ctx + "</context>\n";
}
}
console.log(content);
msg.content = content;
newMessages.push(msg);
}
obj.messages = newMessages;
body = JSON.stringify(obj);
}
const proxyReq = new Request(targetUrl.toString(), {
method: req.method,
headers: req.headers,
body: body,
redirect: "manual",
});
try {
const response = await fetch(proxyReq);
const responseBody = response.body;
const responseHeaders = new Headers(response.headers);
return new Response(responseBody, {
status: response.status,
headers: responseHeaders,
});
} catch (err) {
console.error("Proxy error:", err);
return new Response("Proxy error", { status: 502 });
}
}, { port: 8000 });
+267
View File
@@ -0,0 +1,267 @@
/**
* Vector Database Implementation
* Follows the logic of the original C implementation in r2ai
* No external dependencies, works with pure strings and native TS/JS
*/
// Token representation
interface Token {
token: string;
count: number;
df: number;
}
// KD-Tree Node
interface KDNodeData {
point: Float32Array;
text: string;
left: KDNodeData | null;
right: KDNodeData | null;
splitDim: number;
}
// Result for K-Nearest Neighbors
interface KNNResult {
node: KDNodeData;
distanceSq: number;
}
class VectorDB {
// Core database structures
private dimension: number;
private root: KDNodeData | null = null;
private tokens: Token[] = [];
private totalDocs: number = 0;
private size: number = 0;
constructor(dimension: number) {
this.dimension = dimension;
}
// Validate token (similar to C implementation)
private isValidToken(token: string): boolean {
const invalidTokens = ["pancake", "author", "radare2"];
return !invalidTokens.includes(token);
}
// Find a token in the global token list
private findToken(tokens: Token[], token: string): Token | undefined {
return tokens.find((t) => t.token === token);
}
// Compute embedding using TF-IDF approach
private computeEmbedding(text: string): Float32Array {
// Zero the embedding vector
const embedding = new Float32Array(this.dimension);
// Tokenize and normalize
const tokens = text
.toLowerCase()
.replace(/[^a-z0-9\s]/g, " ")
.split(/\s+/)
.filter((token) => token.length > 0);
// Local document token frequency
const docTokens: Token[] = [];
for (const token of tokens) {
const existingToken = this.findToken(docTokens, token);
if (existingToken) {
existingToken.count++;
} else {
docTokens.push({ token, count: 1, df: 0 });
}
}
// Increment total documents
this.totalDocs++;
// Update global document frequencies
for (const docToken of docTokens) {
const globalToken = this.findToken(this.tokens, docToken.token);
if (globalToken) {
if (this.isValidToken(docToken.token)) {
globalToken.count++;
globalToken.df += 1;
}
} else {
this.tokens.push({ ...docToken, df: 1 });
}
}
// Compute TF-IDF and update embedding
for (const docToken of docTokens) {
// Compute term frequency
const tf = 1 + Math.log(docToken.count);
// Find global token to get document frequency
const globalToken = this.findToken(this.tokens, docToken.token);
const dfValue = globalToken ? globalToken.df : 1;
// Compute inverse document frequency
const idf = Math.log((this.totalDocs + 1) / (dfValue + 1)) + 1;
// Compute weight
const weight = tf * idf;
// Hash-based index assignment
const hash = this.simpleHash(docToken.token);
const index = hash % this.dimension;
// Add weighted value to embedding
embedding[index] += weight;
}
// L2 Normalize the embedding
return this.normalizeVector(embedding);
}
// Simple string hash function
private simpleHash(str: string): number {
let hash = 0;
for (let i = 0; i < str.length; i++) {
const char = str.charCodeAt(i);
hash = ((hash << 5) - hash) + char;
hash = hash & hash; // Convert to 32-bit integer
}
return Math.abs(hash);
}
// Normalize vector to unit length
private normalizeVector(vec: Float32Array): Float32Array {
const normSq = vec.reduce((sum, val) => sum + val * val, 0);
if (normSq > 0) {
const norm = Math.sqrt(normSq);
return vec.map((val) => val / norm);
}
return vec;
}
// Compute squared Euclidean distance
private squaredDistance(a: Float32Array, b: Float32Array): number {
if (a.length !== b.length) return Number.MAX_VALUE;
return a.reduce((sum, val, i) => sum + Math.pow(val - b[i], 2), 0);
}
// Recursive KD-Tree insertion
private insertRecursive(
node: KDNodeData | null,
point: Float32Array,
text: string,
depth: number,
): KDNodeData {
if (!node) {
const splitDim = depth % this.dimension;
return {
point,
text,
left: null,
right: null,
splitDim,
};
}
const axis = node.splitDim;
if (point[axis] < node.point[axis]) {
node.left = this.insertRecursive(node.left, point, text, depth + 1);
} else {
node.right = this.insertRecursive(node.right, point, text, depth + 1);
}
return node;
}
// Insert a document into the vector database
insert(text: string): void {
if (!text) return;
// Compute embedding
const embedding = this.computeEmbedding(text);
// Insert into KD-Tree
this.root = this.insertRecursive(this.root, embedding, text, 0);
this.size++;
}
// K-Nearest Neighbors search
query(text: string, k: number): string[] {
if (!text) return [];
if (!this.root) return [];
// Compute query embedding
const queryEmbedding = this.computeEmbedding(text);
// Initialize result set
const results: KNNResult[] = [];
// Recursive KNN search
const searchRecursive = (
node: KDNodeData | null,
query: Float32Array,
depth: number,
) => {
if (!node) return;
// Compute distance and potentially insert into results
const distSq = this.squaredDistance(query, node.point);
if (results.length < k) {
results.push({ node, distanceSq: distSq });
// Maintain max-heap property
results.sort((a, b) => b.distanceSq - a.distanceSq);
} else if (distSq < results[0].distanceSq) {
results[0] = { node, distanceSq: distSq };
results.sort((a, b) => b.distanceSq - a.distanceSq);
}
// Decide which subtree to search first
const axis = node.splitDim;
const diff = query[axis] - node.point[axis];
const first = diff < 0 ? node.left : node.right;
const second = diff < 0 ? node.right : node.left;
// Recursively search
searchRecursive(first, query, depth + 1);
// Potentially search the other subtree
if (results.length < k || diff * diff < results[0].distanceSq) {
searchRecursive(second, query, depth + 1);
}
};
// Perform the search
searchRecursive(this.root, queryEmbedding, 0);
// Sort results and return texts
return results
.sort((a, b) => a.distanceSq - b.distanceSq)
.map((result) => result.node.text);
}
// Get total number of documents
getSize(): number {
return this.size;
}
}
/*
// Example usage
function main() {
// Create a vector database with 64-dimensional embeddings
const db = new VectorDB(64);
// Insert some sample documents
db.insert("The quick brown fox jumps over the lazy dog");
db.insert("A quick brown fox is very quick");
db.insert("The author of radare2 is name / named pancake");
db.insert("The lazy dog sleeps all day");
// Query for similar documents
const results = db.query("who is the author of radare2?", 2);
// const results = db.query("quick brown animal", 2);
console.log("Similar documents:", results);
}
// Uncomment to run
main();
*/
export default VectorDB;
+43
View File
@@ -0,0 +1,43 @@
# R2AI Prompts
This directory contains predefined prompt files for the r2ai plugin.
## Format
Each prompt is stored in a `.r2ai` file using a simple Key: Value format.
Lines starting with `#` are comments and ignored.
Supported keys:
- `Title`: The title of the prompt (required)
- `Author`: The author of the prompt
- `Description`: A brief description
- `Command` or `Commands`: Commands to execute before sending the prompt, separated by `;` (required)
- `Prompt` or `Query`: The main prompt text sent to the LLM
- `Requires`: Requirements for the prompt (e.g., "analysis" to ensure binary analysis)
- `If-Empty`: Alternative prompt or message if the command output is empty
- `If-Command`: Additional commands to run if the main command produces output
## Variable Substitution
In the `Prompt` field, you can use:
- `${VAR}`: Replaced with the value of the environment variable `VAR`
- `$(cmd)`: Replaced with the output of the r2 command `cmd`
## Usage
- `r2ai -q`: List all available prompts
- `r2ai -q name`: Run the prompt named `name`
- `r2ai -q name extra text`: Run the prompt with additional text appended
## Conditionals
- `If-Empty`: If the command output is empty, use this as the prompt instead
- `If-Command`: If the command produces output, run these additional commands and append their output
## Future Features
- Support for r2js scripts
- More advanced conditionals
+4
View File
@@ -0,0 +1,4 @@
Title: Automatically suggest a better name for this function
Author: pancake
Command: afv;r2ai -d
Prompt: Suggest a better name for this function. Output only the suggested function name (must be one alphanumeric word) prefixed with `'afn '. Check the code below:
+4
View File
@@ -0,0 +1,4 @@
Title: Augmented decompilation based on LLM
Author: pancake
Command: pdc
Prompt: $(-e r2ai.prompt). Translate the code into $(-e r2ai.lang) programming language.
+4
View File
@@ -0,0 +1,4 @@
Title: Find and explain devices used
Author: pancake
Command: izqq~^/dev
Prompt: explain in one line the purpose of each of these devices
+6
View File
@@ -0,0 +1,6 @@
Title: List libraries loaded with dlopen
Description: Some libraries are loaded
Requires: analysis
Command: iiq~dlopen
If-Empty: This binary is not using dlopen
If-Command: e emu.str=true;pd--5@@c:axtq sym.imp.dlopen
+4
View File
@@ -0,0 +1,4 @@
Title: Explain the current function
Author: pancake
Command: r2ai -d
Prompt: Analyze function calls, comments and strings, ignore registers and memory accesses. Considering the references and involved loops; explain the purpose of this function in one or two short sentences. Output must be only the translation of the explanation in $(-e r2ai.hlang)
+4
View File
@@ -0,0 +1,4 @@
Title: Group imports by Libraries
Author: pancake
Commands: iiq;il
Query: Group the imports that belong to each library and describe its purpose and usage scope
+4
View File
@@ -0,0 +1,4 @@
Title: Suggest an improved function signature
Author: pancake
Command: ?e variables;afv;?e function name:;afd;?e current signature:?e afs
Prompt: Analyze the uses of the arguments and return value to infer the signature, identify which is the correct type for the resturn. Do NOT print the function body. Output must be *ONLY* the function signature prefixed with the `'afs ` text
+10
View File
@@ -0,0 +1,10 @@
---
description: 'Decompile this code into PHP'
version: 0.1
author: pancake
command: pdg
if-empty: exit
---
Your goal is to $(-e r2ai.prompt).
Translate the code into $(-e r2ai.lang) programming language.
+4
View File
@@ -0,0 +1,4 @@
Title: Better variable names
Author: pancake
Command: afv
Prompt: Output only the radare2 command without markdown, guess a better name and type for each local variable and function argument taking using. output an r2 script using afvn and afvt commands
+4
View File
@@ -0,0 +1,4 @@
Title: Find vulnerabilities or bugs in the current function
Author: pancake
Command: pdc
Prompt: Find vulnerabilities, do not show the code, only a short summary of the resulting analysis report including suggested good practices to address the problem and a practical exploit example.
-9
View File
@@ -1,9 +0,0 @@
# r2ai-server
Start ollama, llamacpp, llamafile, r2ai, kobaldcpp and other language model webservers using a the same syntax for all of them to simplify its launching and setup.
* Use -l to list the implementations available
You can install more via r2pm
* Use -m to list or select the model. Those models can be absolute paths or model names when downloaded via r2ai, which uses the hugging face api
+32
View File
@@ -0,0 +1,32 @@
# R2AI Plugin for radare2
## Locations
- All the source code of the plugin is in the current directory
- Radare2 headers use to be in /usr/local/include/libr
## Formatting Style
- Use the radare2 coding style rules
- Always add a whitespace before `(` in function calls
- Indent the code with tabs
- Indent comments with spaces
- Do not define variables inside the `for` parenthesis
- Do not check for null before calling free
## Test
Run `r2ai` cli tool or radare2 oneliners running the `r2ai` command.
* `r2pm -r r2ai -h` -> help message for the r2ai tool
* `r2 -qc 'r2ai -h' /bin/ls -> help message of the r2ai plugin
* `r2 -qc 'r2ai -a what arch is used here' -> testing auto mode
* `r2 -e r2ai.model=gemma3:12b -e r2ai.auto.raw=true -qc 'r2ai -a what arch is used here' -> testing auto mode
## Actions
Run the following commands to perform the action described
- FORMAT: `make fmt`
- COMPILE: `make` (with no extra arguments)
- INSTALL: `make user-install`
+76 -16
View File
@@ -1,23 +1,61 @@
EXT=$(shell r2 -H R2_LIBEXT)
CFLAGS+=$(shell pkg-config --cflags r_core)
LDFLAGS+=$(shell pkg-config --libs r_core)
R2PLUGDIR=$(shell r2 -H R2_USER_PLUGINS)
DOTEXE=
# DOTEXE=.exe
DOTLIB=.$(shell r2 -NNH R2_LIBEXT)
R2SYSPLUGDIR=$(shell r2 -NNH R2_LIBR_PLUGINS)
R2USRPLUGDIR=$(shell r2 -NNH R2_USER_PLUGINS)
R2BINDIR=$(shell r2pm -H R2PM_BINDIR)
R2MANDIR=$(shell r2pm -H R2PM_MANDIR)
R2_CFLAGS=$(shell pkg-config --cflags r_core)
R2_LDFLAGS=$(shell pkg-config --libs r_core)
ifeq ($(R2_CFLAGS),)
R2_CFLAGS=$(shell r2 -NNH R2_CFLAGS)
R2_LDFLAGS=$(shell r2 -NNH R2_LDFLAGS)
endif
CFLAGS+=$(R2_CFLAGS)
LDFLAGS+=$(R2_LDFLAGS)
CFLAGS+=-I. -fPIC
CFLAGS+=-Wall
CFLAGS+=-g
USE_R2_CURL=1
USE_LIBCURL=0
HAVE_LIBCURL=$(shell pkg-config --exists libcurl && echo 1 || echo 0)
CFLAGS+=-DUSE_R2_CURL=$(USE_R2_CURL)
CFLAGS+=-DUSE_LIBCURL=$(USE_LIBCURL)
ifeq ($(USE_LIBCURL),1)
ifeq ($(HAVE_LIBCURL),1)
CFLAGS+=$(shell pkg-config --cflags libcurl)
LDFLAGS+=$(shell pkg-config --libs libcurl)
CFLAGS+=-DHAVE_LIBCURL=1
else
CFLAGS+=-DHAVE_LIBCURL=0
endif
endif
OBJS=r2ai.o
OBJS+=llm.o
OBJS+=auto.o
OBJS+=vdb.o
OBJS+=tools.o
OBJS+=messages.o
OBJS+=anthropic.o
OBJS+=gemini.o
OBJS+=r2ai_http.o
OBJS+=openai.o
OBJS+=openapi.o
OBJS+=ollama.o
OBJS+=xai.o
OBJS+=markdown.o
OBJS+=query.o
OBJS+=rawtools.o
all: $(OBJS) r2check
$(CC) -fPIC -shared -o r2ai.$(EXT) $(OBJS) $(CFLAGS) $(LDFLAGS)
$(CC) -fPIC -shared -o r2ai$(DOTLIB) $(OBJS) $(CFLAGS) $(LDFLAGS)
$(CC) -pie -o r2ai$(DOTEXE) $(OBJS) $(CFLAGS) $(LDFLAGS) -lm main.c
try: all
$(MAKE) user-install
$(MAKE) doc-install
@@ -27,15 +65,37 @@ doc-install:
vdb.o: vdb_embed.inc.c
install user-install:
mkdir -p $(R2PLUGDIR)
rm -f $(R2PLUGDIR)/r2ai.$(EXT)
cp -f r2ai.$(EXT) $(R2PLUGDIR)
fmt format indent:
clang-format-radare2 *.c
uninstall user-uninstall:
cp -f $(R2PLUGDIR)/r2ai.$(EXT)
user-install: user-uninstall
mkdir -p $(R2USRPLUGDIR)
mkdir -p $(R2BINDIR)
mkdir -p $(R2MANDIR)/man1
mkdir -p $(HOME)/.config/r2ai/prompts
$(MAKE) user-uninstall
cp -f r2ai$(DOTLIB) $(R2USRPLUGDIR)/r2ai$(DOTLIB)
cp -f r2ai$(DOTEXE) $(R2BINDIR)/r2ai$(DOTEXE)
cp -f r2ai.1 $(R2MANDIR)/man1/r2ai.1
cp -r ../prompts/* $(HOME)/.config/r2ai/prompts/
user-uninstall:
rm -f $(R2USRPLUGDIR)/r2ai$(DOTLIB)
rm -f $(R2BINDIR)/r2ai$(DOTEXE)
rm -f $(R2MANDIR)/man1/r2ai.1
install:
$(MAKE) uninstall
mkdir -p $(R2USRPLUGDIR)
mkdir -p $(R2BINDIR)
cp -f r2ai$(DOTLIB) $(R2SYSPLUGDIR)/r2ai$(DOTLIB)
cp -f r2ai$(DOTEXE) $(R2BINDIR)/r2ai$(DOTEXE)
uninstall:
rm -f $(R2SYSPLUGDIR)/r2ai$(DOTLIB)
rm -f $(R2BINDIR)/r2ai$(DOTEXE)
r2check:
@r2 -qcq --
@r2 -NN -qcq --
clean:
rm -f *.o *.d
+250 -27
View File
@@ -1,12 +1,10 @@
# C-R2AI
# R2AI
This directory contains the C rewrite of the r2ai plugin for radare2.
The r2ai plugin for radare2.
## Installation
Requires radare2 from git if you want to use the stream HTTP APIs.
Otherwise the only requirement is a C compiler.
Requires a C compiler and modern version of radare2 if you want to use the stream HTTP APIs. It will fallback to curl or powershell if not available and `R2_CURL` environment is set to `1`.
```console
make
@@ -34,38 +32,263 @@ make user-install
[0x100003f58]> r2ai -h
Usage: r2ai [-args] [...]
| r2ai -d Decompile current function
| r2ai -d [query] Ask a question on the current function
| r2ai -dr Decompile current function (+ 1 level of recursivity)
| r2ai -a [query] Resolve question using auto mode
| r2ai -e Same as '-e r2ai.'
| r2ai -e (k(=v)) Same as '-e r2ai.'
| r2ai -h Show this help message
| r2ai -i [file] [query] read file and ask the llm with the given query
| r2ai -m show selected model, list suggested ones, choose one
| r2ai -M show suggested models for each api
| r2ai -n suggest a better name for the current function
| r2ai -r enter the repl
| r2ai -R ([text]) refresh and query embeddings (see r2ai.data)
| r2ai -s function signature
| r2ai -x explain current function
| r2ai -v suggest better variables names and types
| r2ai -V[r] find vulnerabilities in the decompiled code (-Vr uses -dr)
| r2ai [arg] send a post request to talk to r2ai and print the output
| r2ai -p [provider] set LLM provider (openai, anthropic, gemini, etc.)
| r2ai -q list available query prompts
| r2ai -q [name] (inst) run predefined prompt with optional instructions
| r2ai -r enter the chat repl
| r2ai -L show chat logs (See -Lj for json). Only for auto mode.
| r2ai -L-[N] delete the last (or N last messages from the chat history)
| r2ai -R reset the chat conversation context
| r2ai -Rq ([text]) refresh and query embeddings (see r2ai.data)
| r2ai [query] query the selected model+provider with the given query
[0x100003f58]>
```
## Query Prompts
Query prompts are predefined templates that combine radare2 commands with structured prompts to the LLM for common reverse engineering tasks. They are stored in the `prompts/` directory as `.r2ai.txt` files.
### Available Prompts
- `autoname`: Automatically rename functions and variables
- `decompile`: Decompile the current function
- `devices`: Analyze device-related code
- `dlopen`: Analyze dynamic library loading
- `explain`: Explain the purpose of the current function
- `libs`: Analyze library usage
- `signature`: Improve function signatures
- `varnames`: Rename variables based on context
- `vulns`: Find vulnerabilities in the current function
### Usage
- `r2ai -q`: List all available query prompts
- `r2ai -q [name]`: Run the specified prompt on the current function
- `r2ai -q [name] [instructions]`: Run the prompt with additional custom instructions
### Example
```console
[0x00001303]> r2ai -q explain
The main function implements a simple substitution cipher program that reads user input, encrypts it using a hardcoded alphabet, and then decrypts it back to demonstrate the algorithm.
```
Query prompts automate repetitive analysis tasks by executing predefined radare2 commands and sending context-aware prompts to the LLM.
## Configuration
These can be set with `r2ai -e <keyname>=<value>`
| Setting name | Description |
|------------------|------------------------------------------------------------------------------------------------|
| r2ai.api | Name of the provider e.g `openai`. List possibilities with `r2ai -e r2ai.api=?` |
| r2ai.model | Model name. List possibilities with `r2ai -e r2ai.model=?` |
| r2ai.baseurl | Remote LLM base URL. Specify host if necessary e.g http://127.0.0.1:11434. |
| r2ai.max_tokens | Maximum output tokens or maximum total tokens. Check the appropriate limits for your model |
| r2ai.temperature | How creative the model should be. 0=not creative, 1=very creative |
| r2ai.cmds | R2 command to issue and send output in context to model |
| r2ai.lang | Tells LLM which programming language to use for decompilation result. Only works for `r2ai -d` |
| r2ai.hlang | Tells LLM in which language to speak |
| r2ai.prompt | User prompt to send to LLM with `r2ai -d` |
| r2ai.auto.max_runs | Maximum loops when using auto mode `r2ai -a` |
| r2ai.auto.hide_tool_output | Only for auto mode `r2ai -a`. Will not show the output of the tool which ran locally |
| r2ai.auto.yolo | Set this to true if you don't want r2ai to ask you for approval before running commands sent by the LLM. This is **dangerous**. Recommendation: **leave this to false** unless you fully trust your LLM not to create havoc! |
| r2ai.http.use_files | If you get CURL argument too long error, set this to true and arguments will be stored in a file.|
## Example
See [https://github.com/radareorg/r2ai-examples](https://github.com/radareorg/r2ai-examples)
- Get the [substitute example binary](https://github.com/radareorg/r2ai/tree/master/examples)
- Initialize r2 and r2ai
```
$ r2 ./substitute
[0x000010a0]> r2ai -e api=anthropic
[0x000010a0]> r2ai -e model=claude-3-7-sonnet-20250219
[0x000010a0]> aa
```
- Decompile the main function: `r2ai -d`
```
[0x000010a0]> s main
[0x00001303]> r2ai -d
int main(int argc, char **argv, char **envp) {
char input[256];
char encrypted[512];
char *alphabet = "QWERTYUIOPASDFGHJKLZXCVBNM";
printf("Enter text to encrypt: ");
fgets(input, 256, stdin);
input[strcspn(input, "\n")] = 0;
substitute(input, alphabet, encrypted, 0);
printf("Encrypted text: %s\n", encrypted);
substitute(encrypted, alphabet, input, 1);
printf("Decrypted text: %s\n", input);
return 0;
}
```
- Ask a question on the current function: `r2ai -d query`
```
[0x00001303]> r2ai -d what is input buffer size?
Based on the disassembly provided, I can see that the input buffer size is 0x100 (256) bytes.
This is visible in the call to `fgets()` where we see:
```
esi = 0x100
rdi = rax // pointing to var_110h which is the input buffer
sym.imp.fgets() // char *fgets("", 0x00000100, 0)
```
The fgets function is being called with a second argument of 0x100, which specifies the maximum number of characters to read (including the null terminator). This means the program allocates a 256-byte buffer for user input.
```
Note that the context is **reset between each direct query**. For example, the context of the second command `r2ai -d what is input buffer size?` does not contain the request/response of the first `r2ai -d`.
- Find vulnerabilities: `r2ai -V`
```
[0x00001303]> r2ai -V
# Vulnerability Analysis
## Identified Vulnerabilities
1. **Buffer Overflow Risk**: The program uses `fgets()` with a 256-byte buffer to read user input, but doesn't validate if the substitute function's target buffer can handle this much data.
2. **Lack of Input Validation**: There appears to be no validation of the input character range, which could lead to unexpected behavior if special characters are used.
3. **Predictable Encryption**: The cipher uses a hardcoded key "QWERTYUIOPASDFGHJKLZXCVBNM", making it trivially breakable.
4. **Insufficient Memory Handling**: No verification that memory allocations succeed before use.
## Sample Exploit
```python
# Buffer overflow exploit example
from pwn import *
# Connect to the target (adjust as needed)
p = process('./cipher_program')
# Create payload: 256 'A's + return address override
payload = b"A" * 256 + p64(0x4141414141414141) # Example address
# Send the payload
p.sendlineafter(b"Enter text to encrypt: ", payload)
p.interactive()
```
## Security Recommendations
1. **Implement Buffer Size Checks**: Ensure that destination buffers are large enough for the operation and implement bounds checking.
2. **Input Validation**: Validate all user inputs for expected character ranges and length.
3. **Dynamic Key Generation**: Use cryptographically secure random number generators to create unique keys for each session.
4. **Memory Safety**: Use secure memory allocation practices with proper error handling.
5. **Modern Cryptography**: Replace the simple substitution cipher with standard cryptographic libraries (e.g., OpenSSL).
6. **Stack Protection**: Compile with stack canaries (`-fstack-protector-all`) and address space layout randomization (ASLR).
7. **Error Handling**: Implement proper error checking and graceful failure mechanisms.
8. **Secure Coding Practices**: Follow guidelines like OWASP for secure coding standards.
```
- Automatic mode: `r2ai -a query`. In the auto mode, r2ai offers use of tools.
```
[0x00001303]> r2ai -a The main implements a substitution algorithm. Give me an input that would end up as RADARE2 when substituted
[r2cmd]>
[0x00001303]> afl~main
afl~main
0x00001303 1 231 main
[Assistant]
I'll analyze the substitution algorithm in the main function to determine what input would result in "RADARE2" after substitution. Let me examine the main function code.
[r2cmd]>
[0x00001303]> pdf @ main
...
```
- See the chat logs (only in auto mode): `r2ai -L`:
```
[r2ai] Chat Logs (12 messages)
Note: System prompt is applied automatically but not stored in history
[user]: The main implements a substitution algorithm. Give me an input that would end up as RADARE2 when substituted
[assistant]: I'll analyze the substitution algorithm in the main function to determine what input would result in "RADARE2" after substitution. Let me examine the main function code.
[tool call]: r2cmd
{"command":"pdf @ main"}
```
## TODO
* add "undo" command to drop the last message
* dump / restore conversational states (see -L command)
* Implement `~`, `|` and `>` and other r2shell features
* keep context between direct commands unless explicitly reset
* modify context if tool command is user edited
## Messages API
The r2ai project includes a new API for managing messages and tool calls in conversations with AI models. This API is implemented in `messages.c` and `messages.h`.
### Key Functions
- `r2ai_messages_new()`: Create a new messages array
- `r2ai_messages_free(msgs)`: Free a messages array and all associated data
- `r2ai_messages_add(msgs, role, content, tool_call_id)`: Add a new message
- `r2ai_messages_add_tool_call(msgs, name, arguments, id)`: Add a tool call to the last message
- `r2ai_messages_last(msgs)`: Get the last message in the array
- `r2ai_messages_parse_json(msgs, json)`: Parse a JSON response into messages
- `r2ai_messages_clear(msgs)`: Clear all messages
### Example Usage
```c
[0x100003f58]> r2ai -e api=claude
[0x100003f58]> r2ai -d
int main(int argc, char **argv, char **envp) {
char buffer[32];
int result = 0;
if (argv[1] != NULL) {
strcpy_chk(buffer, argv[1], 32);
}
return result;
}
[0x100003f58]>
// Create a new messages array
R2AI_Messages *msgs = r2ai_messages_new();
// Add a user message
r2ai_messages_add(msgs, "user", "What is the disassembly of function main?", NULL);
// Add an assistant message with a tool call
R2AI_Message *msg = r2ai_messages_add(msgs, "assistant", NULL, NULL);
r2ai_messages_add_tool_call(msgs, "r2cmd", "{\"command\":\"pdf@main\"}", "tool-123");
// Free all resources when done
r2ai_messages_free(msgs);
```
## lm-studio
Install [LM Studio](https://lmstudio.ai/). On a server, LM Studio must be run as a normal user (not root) + install *FUSE*.
Then, download your preferred model(s). For example to install GPT-OSS, follow this [cookbook](https://cookbook.openai.com/articles/gpt-oss/run-locally-lmstudio).
When you launch LM Studio, go to developer options, Settings, and select server **port** (1234 by default) and check whether you need "serve on a local network" or not (accessible on localhost only, or on a local network).
In r2ai, as lm-studio uses an OpenAI-like API, configure:
- `r2ai -e api=openai`
- `r2ai -e baseurl=http://LM-STUDIO-IP:PORT`
- `r2ai -e model=?` to list available models
+288 -172
View File
@@ -1,85 +1,22 @@
#include "r2ai.h"
#if R2_VERSION_NUMBER >= 50909
static const char *modelname(const char *model_name) {
return model_name? model_name: "claude-3-5-sonnet-20241022";
return model_name? model_name: "claude-3-7-sonnet-20250219";
}
static bool handle_anthropic_stream_chunk(const char *chunk) {
if (R_STR_ISEMPTY (chunk)) {
return false;
}
R_IPI R2AI_ChatResponse *r2ai_anthropic(RCorePluginSession *cps, R2AIArgs args) {
RCore *core = cps->core;
const char *model = args.model;
char **error = args.error;
const R2AI_Tools *tools = args.tools;
R2AI_Messages *messages_input = args.messages;
if (r_str_startswith (chunk, "event:")) {
const char *event = chunk + 7;
while (*event == ' ') {
event++;
}
return !strcmp (event, "message_stop");
}
if (!r_str_startswith (chunk, "data:")) {
return false;
}
const char *data = chunk + 6;
while (*data == ' ') {
data++;
}
if (!*data) {
return false;
}
char *data_copy = strdup (data);
RJson *jres = r_json_parse (data_copy);
if (!jres) {
free (data_copy);
return false;
}
const RJson *type = r_json_get (jres, "type");
if (!type || !type->str_value) {
r_json_free (jres);
free (data_copy);
return false;
}
if (!strcmp (type->str_value, "content_block_delta")) {
const RJson *delta = r_json_get (jres, "delta");
if (delta) {
const RJson *text = r_json_get(delta, "text");
if (text && text->str_value) {
eprintf ("%s", text->str_value);
}
}
}
r_json_free (jres);
free (data_copy);
return false;
}
R_IPI char *r2ai_anthropic(const char *content, const char *model_name, char **error) {
if (error) {
*error = NULL;
}
char *apikey = r_sys_getenv ("ANTHROPIC_API_KEY");
if (!apikey) {
char *apikey_file = r_file_new ("~/.r2ai.anthropic-key", NULL);
apikey = r_file_slurp (apikey_file, NULL);
free (apikey_file);
if (!apikey) {
if (error) {
*error = strdup ("Failed to read Anthropic API key from ANTHROPIC_API_KEY env or ~/.r2ai.anthropic-key");
}
return NULL;
}
r_str_trim (apikey);
}
char *auth_header = r_str_newf ("x-api-key: %s", apikey);
// Setup HTTP headers
char *auth_header = r_str_newf ("x-api-key: %s", args.api_key);
char *anthropic_version = "anthropic-version: 2023-06-01";
const char *headers[] = {
"Content-Type: application/json",
@@ -90,129 +27,308 @@ R_IPI char *r2ai_anthropic(const char *content, const char *model_name, char **e
const char *anthropic_url = "https://api.anthropic.com/v1/messages";
// Get system message if available
const char *system_message = NULL;
// First check if it's provided in args.system_prompt
if (R_STR_ISNOTEMPTY (args.system_prompt)) {
system_message = args.system_prompt;
} else {
// If no system_prompt in args, check config as fallback
system_message = r_config_get (core->config, "r2ai.system");
}
// Create messages JSON
char *messages_json = NULL;
if (messages_input && r_list_length (messages_input->messages) > 0) {
// Convert directly to JSON without filtering
messages_json = r2ai_msgs_to_anthropic_json (messages_input);
if (!messages_json) {
if (error) {
*error = strdup ("Failed to convert messages to JSON");
}
free (auth_header);
return NULL;
}
} else {
if (error) {
*error = strdup ("No input or messages provided");
}
free (auth_header);
return NULL;
}
// Convert tools to Anthropic format if available
char *anthropic_tools_json = NULL;
if (tools && tools->n_tools > 0) {
anthropic_tools_json = r2ai_tools_to_anthropic_json (tools);
}
// Create the request JSON
PJ *pj = pj_new ();
pj_o (pj);
pj_ks (pj, "model", modelname (model_name));
pj_kn (pj, "max_tokens", 4096);
pj_ka (pj, "messages");
pj_o (pj);
pj_ks (pj, "role", "user");
pj_ks (pj, "content", content);
pj_end (pj);
pj_end (pj);
pj_ks (pj, "model", modelname (model));
pj_kn (pj, "max_tokens", args.max_tokens);
if (args.thinking_tokens >= 1024) {
pj_ko (pj, "thinking");
pj_ks (pj, "type", "enabled");
pj_kn (pj, "budget_tokens", args.thinking_tokens);
pj_end (pj);
}
// Add system message if available
if (system_message) {
pj_ks (pj, "system", system_message);
}
// Add messages
pj_k (pj, "messages");
// messages_json already contains the array itself, so we use raw
pj_raw (pj, messages_json);
// Add tools if available
if (anthropic_tools_json) {
pj_k (pj, "tools");
pj_raw (pj, anthropic_tools_json);
free (anthropic_tools_json);
}
pj_end (pj);
char *data = pj_drain (pj);
free (messages_json);
// Save the full JSON for debugging
char *tmpdir = r_file_tmpdir ();
char *req_path = r_str_newf ("%s" R_SYS_DIR "r2ai_anthropic_request.json", tmpdir);
r_file_dump (req_path, (const ut8 *)data, strlen (data), 0);
R_LOG_DEBUG ("Full request saved to %s", req_path);
free (req_path);
free (tmpdir);
R_LOG_DEBUG ("Anthropic API request data: %s", data);
// Make the API call
int code = 0;
char *res = r_socket_http_post (anthropic_url, headers, data, &code, NULL);
char *res = r2ai_http_post (core, anthropic_url, headers, data, &code, NULL);
free (data);
free (auth_header);
if (!res || code != 200) {
R_LOG_ERROR ("Anthropic API error %d", code);
if (error) {
*error = strdup (res? res : "Failed to get response from Anthropic API");
if (error && res) {
*error = strdup (res);
} else if (error) {
*error = strdup ("Failed to get response from Anthropic API");
}
free (apikey);
free (auth_header);
free (data);
free (res);
return NULL;
}
char *res_content = NULL;
RJson *jres = r_json_parse (res);
if (jres) {
const RJson *content_array = r_json_get (jres, "content");
if (content_array && content_array->type == R_JSON_ARRAY) {
const RJson *first_content = r_json_item (content_array, 0);
if (first_content) {
const RJson *text = r_json_get (first_content, "text");
if (text) {
res_content = strdup (text->str_value);
}
}
}
r_json_free (jres);
}
free (apikey);
free (auth_header);
free (data);
free (res);
return res_content;
}
// Save the response for inspection
tmpdir = r_file_tmpdir ();
char *res_path = r_str_newf ("%s" R_SYS_DIR "r2ai_anthropic_response.json", tmpdir);
r_file_dump (res_path, (const ut8 *)res, strlen (res), 0);
R_LOG_DEBUG ("Anthropic API response saved to %s", res_path);
free (res_path);
free (tmpdir);
R_IPI char *r2ai_anthropic_stream(const char *content, const char *model_name, char **error) {
if (error) {
*error = NULL;
}
char *apikey = r_sys_getenv ("ANTHROPIC_API_KEY");
if (!apikey) {
char *apikey_file = r_file_new ("~/.r2ai.anthropic-key", NULL);
apikey = r_file_slurp (apikey_file, NULL);
free(apikey_file);
if (!apikey) {
if (error) {
*error = strdup ("Failed to read Anthropic API key from ANTHROPIC_API_KEY env or ~/.r2ai.anthropic-key");
}
return NULL;
}
r_str_trim (apikey);
}
R_LOG_DEBUG ("Anthropic API response: %s", res);
char *auth_header = r_str_newf ("x-api-key: %s", apikey);
char *anthropic_version = "anthropic-version: 2023-06-01";
char *accept_header = "Accept: text/event-stream";
const char *headers[] = {
"Content-Type: application/json",
auth_header,
anthropic_version,
accept_header,
NULL
};
const char anthropic_url[] = "https://api.anthropic.com/v1/messages";
PJ *pj = pj_new ();
pj_o (pj);
pj_ks (pj, "model", modelname (model_name));
pj_kn (pj, "max_tokens", 4096);
pj_kb (pj, "stream", true);
pj_ka (pj, "messages");
pj_o (pj);
pj_ks (pj, "role", "user");
pj_ks (pj, "content", content);
pj_end (pj);
pj_end (pj);
pj_end (pj);
char *data = pj_drain(pj);
int code = 0;
char *res = r_socket_http_post (anthropic_url, headers, data, &code, NULL);
if (!res || code != 200) {
R_LOG_ERROR ("Anthropic API error %d", code);
// Parse the response
R2AI_ChatResponse *result = R_NEW0 (R2AI_ChatResponse);
if (!result) {
free (res);
if (error) {
*error = strdup ("Failed to get response from Anthropic API");
*error = strdup ("Failed to allocate memory for response");
}
free (apikey);
free (auth_header);
free (data);
return NULL;
}
char *saveptr;
char *line = strtok_r (res, "\n", &saveptr);
while (line) {
handle_anthropic_stream_chunk (line);
line = strtok_r (NULL, "\n", &saveptr);
R2AI_Message *message = NULL;
R2AI_Usage *usage = NULL;
char *response_copy = strdup (res);
if (response_copy) {
RJson *jres = r_json_parse (response_copy);
if (jres) {
// Create a new message structure
message = R_NEW0 (R2AI_Message);
const RJson *usage_json = r_json_get (jres, "usage");
if (usage_json && usage_json->type == R_JSON_OBJECT) {
usage = R_NEW0 (R2AI_Usage);
if (usage) {
const RJson *prompt_tokens = r_json_get (usage_json, "input_tokens");
const RJson *completion_tokens = r_json_get (usage_json, "output_tokens");
if (prompt_tokens && prompt_tokens->type == R_JSON_INTEGER) {
usage->prompt_tokens = prompt_tokens->num.u_value;
}
if (completion_tokens && completion_tokens->type == R_JSON_INTEGER) {
usage->completion_tokens = completion_tokens->num.u_value;
}
usage->total_tokens = usage->prompt_tokens + usage->completion_tokens;
}
}
if (message) {
message->role = strdup ("assistant");
RStrBuf *content_buf = r_strbuf_new ("");
// Check for tool calls in Anthropic response
int has_tool_use = 0;
int n_tool_calls = 0;
// Count tool_use blocks to determine array size
const RJson *content_array = r_json_get (jres, "content");
if (content_array && content_array->type == R_JSON_ARRAY) {
// First count the number of tool_use blocks
const RJson *content_item = content_array->children.first;
while (content_item) {
const RJson *type = r_json_get (content_item, "type");
if (type && type->type == R_JSON_STRING && !strcmp (type->str_value, "tool_use")) {
has_tool_use = 1;
n_tool_calls++;
}
content_item = content_item->next;
}
}
// Allocate tool_calls array if needed
if (has_tool_use && n_tool_calls > 0) {
message->tool_calls = R_NEWS0 (R2AI_ToolCall, n_tool_calls);
if (!message->tool_calls) {
if (error) {
*error = strdup ("Failed to allocate memory for tool calls");
}
r_json_free (jres);
free (response_copy);
r2ai_message_free (message);
free (res);
return NULL;
}
message->n_tool_calls = n_tool_calls;
}
// Process each content item
int tool_idx = 0;
if (content_array && content_array->type == R_JSON_ARRAY) {
R2AI_ContentBlocks *cb = R_NEW0 (R2AI_ContentBlocks);
if (!cb) {
r_json_free (jres);
free (response_copy);
r2ai_message_free (message);
free (res);
return NULL;
}
cb->n_blocks = content_array->children.count;
cb->blocks = R_NEWS0 (R2AI_ContentBlock, cb->n_blocks);
if (!cb->blocks) {
free (cb);
r_json_free (jres);
free (response_copy);
r2ai_message_free (message);
free (res);
return NULL;
}
int block_idx = 0;
const RJson *content_item = content_array->children.first;
while (content_item && block_idx < cb->n_blocks) {
const RJson *type = r_json_get (content_item, "type");
if (type && type->type == R_JSON_STRING) {
R2AI_ContentBlock *block = &cb->blocks[block_idx];
if (!strcmp (type->str_value, "text")) {
// Text content
const RJson *text = r_json_get (content_item, "text");
if (text && text->type == R_JSON_STRING) {
r_strbuf_append (content_buf, text->str_value);
block->type = strdup ("text");
block->text = strdup (text->str_value);
}
} else if (!strcmp (type->str_value, "tool_use") && tool_idx < n_tool_calls) {
// Tool call - convert from Anthropic format to OpenAI format
const RJson *name = r_json_get (content_item, "name");
const RJson *id = r_json_get (content_item, "id");
const RJson *input = r_json_get (content_item, "input");
block->type = strdup ("tool_use");
if (name && name->type == R_JSON_STRING) {
block->name = strdup (name->str_value);
R2AI_ToolCall *tc = (R2AI_ToolCall *)&message->tool_calls[tool_idx];
tc->name = strdup (name->str_value);
}
if (id && id->type == R_JSON_STRING) {
block->id = strdup (id->str_value);
R2AI_ToolCall *tc = (R2AI_ToolCall *)&message->tool_calls[tool_idx];
tc->id = strdup (id->str_value);
}
if (input && input->type == R_JSON_OBJECT) {
char *input_str = r_json_to_string (input);
if (input_str) {
R_LOG_DEBUG ("Input string: %s", input_str);
block->input = strdup (input_str);
R2AI_ToolCall *tc = (R2AI_ToolCall *)&message->tool_calls[tool_idx];
tc->arguments = strdup (input_str);
free (input_str);
}
}
tool_idx++;
} else if (!strcmp (type->str_value, "thinking")) {
const RJson *data = r_json_get (content_item, "data");
const RJson *thinking = r_json_get (content_item, "thinking");
const RJson *signature = r_json_get (content_item, "signature");
if (data && data->type == R_JSON_STRING) {
block->data = strdup (data->str_value);
}
if (thinking && thinking->type == R_JSON_STRING) {
block->thinking = strdup (thinking->str_value);
}
if (signature && signature->type == R_JSON_STRING) {
block->signature = strdup (signature->str_value);
}
block->type = strdup ("thinking");
r_strbuf_append (content_buf, "\n\x1b[90m<thinking>\n");
r_strbuf_append (content_buf, block->thinking);
r_strbuf_append (content_buf, "\n</thinking>\x1b[0m\n");
}
block_idx++;
}
content_item = content_item->next;
}
message->content_blocks = cb;
}
// Store the content
message->content = r_strbuf_drain (content_buf);
// If there's no content and no tool calls, clean up
if (!message->content && !message->n_tool_calls) {
r2ai_message_free (message);
message = NULL;
}
}
r_json_free (jres);
}
free (response_copy);
}
eprintf ("\n");
// Assign message and usage to result
result->message = message;
result->usage = usage;
free (apikey);
free (auth_header);
free (data);
// Free the HTTP response body
free (res);
return NULL;
return result;
}
#endif
R_IPI char *r2ai_anthropic_stream(RCore *core, R2AIArgs args) {
(void)core;
(void)args;
// Not implemented yet
return NULL;
}
+440 -87
View File
@@ -1,101 +1,454 @@
#include "r2ai.h"
static const char Gprompt_auto[] = \
"# Function Calling\n" \
"\n" \
"Respond ONLY using JSON. You are a smart assistant designed to process user queries and decide if a local function needs to be executed. Follow these steps:\n" \
"1. Analyze the user input to determine if it requires invoking a local function or just returning a direct response.\n" \
"2. If it requires a function call:\n" \
" - Use the key \"action\": \"execute_function\".\n" \
" - Provide the \"function_name\" as a string.\n" \
" - Include \"parameters\" as a dictionary of key-value pairs matching the required function arguments.\n" \
" - Optionally, provide a \"response\" to summarize your intent.\n" \
"3. If no function is required:\n" \
" - Use the key \"action\": \"reply\".\n" \
" - Include \"response\" with the direct answer to the user query.\n" \
"\n" \
"Return the result as a JSON object.\n" \
"\n" \
"## Here is an example:\n" \
"\n" \
"User Query: \"Count how many functions.\"\n" \
"Response:\n" \
"{\n" \
" \"action\": \"execute_function\",\n" \
" \"function_name\": \"r2cmd\",\n" \
" \"parameters\": {\n" \
" \"r2cmd\": \"aflc\",\n" \
" }\n" \
" \"response\": \"Count how many functions do we have\"\n" \
"}\n" \
"\n" \
"# Now, analyze the following user input:\n";
// Stats are now stored in R2AI_State->stats, no longer need global variable
R_IPI void cmd_r2ai_a(RCore *core, const char *user_query) {
RList *replies = r_list_newf (free);
while (true) {
RStrBuf *sb = r_strbuf_new ("");
r_strbuf_append (sb, Gprompt_auto);
if (!r_list_empty (replies)) {
r_strbuf_append (sb, "\n## Executed function results:\n");
char *r;
RListIter *iter;
r_list_foreach (replies, iter, r) {
r_strbuf_appendf (sb, "%s\n", r);
// Helper function to format time duration
static char *format_time_duration(time_t seconds) {
if (seconds < 60) {
return r_str_newf ("%" PFMT64d "s", (long long)seconds);
}
if (seconds < 3600) {
return r_str_newf ("%" PFMT64d "m%" PFMT64d "s", (long long) (seconds / 60), (long long) (seconds % 60));
}
return r_str_newf ("%" PFMT64d "h%" PFMT64d "m%" PFMT64d "s",
(long long) (seconds / 3600),
(long long) ((seconds % 3600) / 60),
(long long) (seconds % 60));
}
// Initialize timing and cost tracking for a run
static void r2ai_stats_init_run(R2AI_State *state, int n_run) {
time_t run_start = time (NULL);
if (n_run == 1) {
// First run, initialize total timing
state->stats.total_cost = 0.0;
state->stats.run_cost = 0.0;
state->stats.total_start_time = run_start;
state->stats.total_tokens = 0;
state->stats.run_tokens = 0;
state->stats.total_prompt_tokens = 0;
state->stats.run_prompt_tokens = 0;
state->stats.total_completion_tokens = 0;
state->stats.run_completion_tokens = 0;
}
state->stats.start_time = run_start;
}
// Print a simple run indicator at the start
static void r2ai_print_run_end(RCorePluginSession *cps, const R2AI_Usage *usage, int n_run, int max_runs) {
RCore *core = cps->core;
R2AI_State *state = cps->data;
(void)n_run;
(void)max_runs;
time_t run_time = time (NULL) - state->stats.start_time;
time_t total_time = time (NULL) - state->stats.total_start_time;
if (usage) {
state->stats.run_tokens = usage->total_tokens;
state->stats.run_prompt_tokens = usage->prompt_tokens;
state->stats.run_completion_tokens = usage->completion_tokens;
state->stats.total_tokens += usage->total_tokens;
state->stats.total_prompt_tokens += usage->prompt_tokens;
state->stats.total_completion_tokens += usage->completion_tokens;
}
if (r_config_get_b (core->config, "r2ai.chat.show_cost") == true) {
// TODO: calculate cost
state->stats.run_cost = 0.0 * run_time;
state->stats.total_cost += state->stats.run_cost;
}
// Format times for display
char *run_time_str = format_time_duration (run_time);
char *total_time_str = format_time_duration (total_time);
// Print detailed stats
r_cons_printf (core->cons, "\x1b[1;34m%s | total: %d in: %d out: %d | run: %d in: %d out: %d | %s / %s\x1b[0m\n",
r_config_get (core->config, "r2ai.model"),
state->stats.total_tokens,
state->stats.total_prompt_tokens,
state->stats.total_completion_tokens,
state->stats.run_tokens,
state->stats.run_prompt_tokens,
state->stats.run_completion_tokens,
run_time_str,
total_time_str);
r_cons_newline (core->cons);
r_cons_flush (core->cons);
free (run_time_str);
free (total_time_str);
}
const char *Gprompt_auto = "You are a reverse engineer and you are using radare2 to analyze a binary.\n"
"The user will ask questions about the binary and you will respond with the answer to the best of your ability.\n"
"\n"
"# Guidelines\n"
"- Understand the Task: Grasp the main objective, goals, requirements, constraints, and expected output.\n"
"- Reasoning Before Conclusions**: Encourage reasoning steps before any conclusions are reached.\n"
"- Assume the user is always asking you about the binary, unless they're specifically asking you for radare2 help.\n"
"- The binary has already been loaded. You can interact with the binary using the r2cmd tool.\n"
"- `this` or `here` might refer to the current address in the binary or the binary itself.\n"
"- If you need more information, try to use the r2cmd tool to run commands before answering.\n"
"- You can use the r2cmd tool multiple times if you need or you can pass a command with pipes if you need to chain commands.\n"
"- If you're asked to decompile a function, make sure to return the code in the language you think it was originally written and rewrite it to be as easy as possible to be understood. Make sure you use descriptive variable and function names and add comments.\n"
"- Don't just regurgitate the same code, figure out what it's doing and rewrite it to be more understandable.\n"
"- If you need to run a command in r2 before answering, you can use the r2cmd tool\n"
"- Do not repeat commands if you already know the answer.\n"
"- Formulate a plan. Think step by step. Analyze the binary as much as possible before answering.\n"
"- You must keep going until you have a final answer.\n"
"- Double check that final answer. Make sure you didn't miss anything.\n"
"- Make sure you call tools and functions correctly.\n";
// Helper function to process messages and handle tool calls recursively
R_API void process_messages(RCorePluginSession *cps, R2AI_Messages *messages, const char *system_prompt, int n_run) {
RCore *core = cps->core;
R2AI_State *state = cps->data;
char *error = NULL;
bool interrupted = false;
const int max_runs = r_config_get_i (core->config, "r2ai.auto.max_runs");
if (n_run > max_runs) {
r_cons_printf (core->cons, "\x1b[1;31m[r2ai] Max runs reached\x1b[0m\n");
r_cons_flush (core->cons);
return;
}
if (!system_prompt) {
system_prompt = Gprompt_auto;
const char *init_commands = r_config_get (core->config, "r2ai.auto.init_commands");
if (R_STR_ISNOTEMPTY (init_commands)) {
char *edited_command = NULL;
char *cmd_output = execute_tool (core, "r2cmd", r_str_newf ("{\"command\":\"%s\"}", init_commands), &edited_command);
if (R_STR_ISNOTEMPTY (cmd_output)) {
R2AI_Message init_msg = {
.role = "system",
.content = r_str_newf ("Here is some information about the binary to get you started:\n>%s\n%s", edited_command, cmd_output)
};
r2ai_msgs_add (messages, &init_msg);
free (cmd_output);
}
free (edited_command);
}
r_strbuf_appendf (sb, "## User prompt\n%s\n", user_query);
}
char *q = r_strbuf_drain (sb);
char *error = NULL;
char *res = r2ai (core, q, &error, true);
free (q);
{
RJson *jres = r_json_parse (res);
if (!jres) {
R_LOG_ERROR ("Invalid json");
r_cons_printf ("%s\n", res);
free (res);
break;
r2ai_stats_init_run (state, n_run);
r_cons_printf (core->cons, "\x1b[35m[DEBUG] About to call r2ai_llmcall with n_run=%d\x1b[0m\n", n_run);
r_cons_flush (core->cons);
// Set up args for r2ai_llmcall call with tools directly
R2AIArgs args = {
.messages = messages,
.error = &error,
.dorag = true,
.tools = r2ai_get_tools (), // Always send tools in auto mode
.system_prompt = system_prompt
};
R2AI_ChatResponse *response = r2ai_llmcall (cps, args);
if (!response) {
return;
}
const R2AI_Message *message = response->message;
const R2AI_Usage *usage = response->usage;
if (!message) {
R_LOG_ERROR ("No message in response");
free (response);
return;
}
// Debug logging for rawtools
R_LOG_DEBUG ("[DEBUG] Model response - Role: %s", message->role? message->role: "null");
if (message->content) {
R_LOG_DEBUG ("[DEBUG] Content: %s", message->content);
}
if (message->reasoning_content) {
R_LOG_DEBUG ("[DEBUG] Reasoning: %s", message->reasoning_content);
}
if (message->tool_calls && message->n_tool_calls > 0) {
R_LOG_DEBUG ("[DEBUG] Tool calls: %d", message->n_tool_calls);
for (int i = 0; i < message->n_tool_calls; i++) {
const R2AI_ToolCall *tc = &message->tool_calls[i];
R_LOG_DEBUG ("[DEBUG] Tool %d: %s - %s", i, tc->name? tc->name: "null", tc->arguments? tc->arguments: "null");
}
}
// Process the response - we need to add it to our messages array
if (message->content || message->reasoning_content) {
r_cons_printf (core->cons, "\x1b[31m[Assistant]\x1b[0m\n\n");
if (message->reasoning_content) {
r_cons_printf (core->cons, "\x1b[90m<thinking>\n%s\n</thinking>\x1b[0m\n", message->reasoning_content);
r_cons_newline (core->cons);
r_cons_flush (core->cons);
}
if (message->content) {
r_cons_printf (core->cons, "%s", message->content);
r_cons_newline (core->cons);
r_cons_flush (core->cons);
}
}
// Add the response to our messages array
// This creates a copy, so we can safely free the original later
r2ai_msgs_add (messages, message);
// Check for tool calls and process them
if (message->tool_calls && message->n_tool_calls > 0) {
R_LOG_DEBUG ("Found %d tool call(s)", message->n_tool_calls);
// Process each tool call
for (int i = 0; i < message->n_tool_calls; i++) {
const R2AI_ToolCall *tool_call = &message->tool_calls[i];
if (!tool_call->name || !tool_call->arguments || !tool_call->id) {
R_LOG_DEBUG ("Skipping invalid tool call %d", i);
continue;
}
const RJson *action = r_json_get (jres, "action");
if (action) {
const char *action_str = action->str_value;
if (!strcmp (action_str, "execute_function") || !strcmp (action_str, "r2cmd")) {
const RJson *parameters = r_json_get (jres, "parameters");
if (!parameters) {
goto badjson;
}
const RJson *r2cmd = r_json_get (parameters, "r2cmd");
if (!r2cmd) {
goto badjson;
}
R_LOG_INFO ("[r2cmd] Running: %s", r2cmd);
char *res2 = r_core_cmd_str (core, r2cmd->str_value);
PJ *pj = pj_new ();
pj_o (pj);
pj_ks (pj, "action", "function_response");
pj_ks (pj, "r2cmd", r2cmd->str_value);
pj_ks (pj, "response", res2);
pj_end (pj);
char *reply = pj_drain (pj);
r_list_append (replies, reply);
free (res2);
} else if (!strcmp (action_str, "reply")) {
const RJson *res = r_json_get (jres, "response");
if (!res) {
goto badjson;
}
r_cons_printf ("%s\n", res->str_value);
}
R_LOG_DEBUG ("Tool call %d: %s with args: %s", i, tool_call->name, tool_call->arguments);
// Don't log the full arguments which might get truncated
char *tool_name = strdup (tool_call->name);
char *tool_args = strdup (tool_call->arguments);
if (!tool_name || !tool_args) {
R_LOG_ERROR ("Failed to allocate memory for tool call");
free (tool_name);
free (tool_args);
continue;
}
char *cmd_output = NULL;
if (interrupted) {
cmd_output = strdup ("<user interrupted>");
} else {
r_cons_printf ("%s\n", res);
char *edited_command = NULL;
cmd_output = execute_tool (core, tool_name, tool_args, &edited_command);
if (edited_command) {
// Update the last message's tool call arguments with the edited command
R2AI_Message *last_msg = r_list_get_n (messages->messages, r_list_length (messages->messages) - 1);
if (last_msg && last_msg->tool_calls && last_msg->n_tool_calls > 0) {
for (int j = 0; j < last_msg->n_tool_calls; j++) {
R2AI_ToolCall *tc = (R2AI_ToolCall *)&last_msg->tool_calls[j];
if (tc->id && strcmp (tc->id, tool_call->id) == 0) {
// For r2cmd, update the command in arguments
if (strcmp (tool_name, "r2cmd") == 0) {
char *args_dup = strdup (tc->arguments);
RJson *args_json = r_json_parse (args_dup);
if (args_json) {
RJson *cmd_json = (RJson *)r_json_get (args_json, "command");
if (cmd_json && cmd_json->str_value) {
// Update the command field
free ((char *)cmd_json->str_value);
cmd_json->str_value = strdup (edited_command);
// Serialize back to JSON
char *new_args = r_json_to_string (args_json);
if (new_args) {
free ((void *)tc->arguments);
tc->arguments = new_args;
}
}
r_json_free (args_json);
}
}
break;
}
}
}
}
free (edited_command);
}
badjson:
r_json_free (jres);
free (tool_name);
free (tool_args);
if (strcmp (cmd_output, "R2AI_SIGINT") == 0) {
r_cons_printf (core->cons, "\n\n\x1b[1;31m[r2ai] Processing interrupted after tool execution\x1b[0m\n\n");
r_cons_flush (core->cons);
free (cmd_output);
cmd_output = strdup ("<user interrupted>");
interrupted = true;
}
// Create a tool call response message
R2AI_Message tool_response = {
.role = "tool",
.tool_call_id = strdup (tool_call->id),
.content = cmd_output
};
// Add the tool response to our messages array
r2ai_msgs_add (messages, &tool_response);
R_LOG_DEBUG ("[DEBUG] Added tool response to messages: %s", cmd_output? cmd_output: "null");
r_cons_printf (core->cons, "\x1b[32m[DEBUG] Tool result: %s\x1b[0m\n", cmd_output? cmd_output: "no output");
free (cmd_output);
}
free (res);
r2ai_print_run_end (cps, usage, n_run, max_runs);
// Check if we should continue with recursion
if (!interrupted && message->tool_calls && message->n_tool_calls > 0) {
R_LOG_DEBUG ("[DEBUG] Recursing to process_messages with n_run=%d", n_run + 1);
process_messages (cps, messages, system_prompt, n_run + 1);
} else {
R_LOG_DEBUG ("[DEBUG] Auto mode loop ending - no more tool calls or interrupted");
}
} else {
r2ai_print_run_end (cps, usage, n_run, max_runs);
}
// Free the response struct itself since r2ai_message_free doesn't do it anymore
free (response);
}
R_IPI void cmd_r2ai_a(RCorePluginSession *cps, const char *user_query) {
RCore *core = cps->core;
R2AI_State *state = cps->data;
r_cons_printf (core->cons, "\x1b[35m[DEBUG] cmd_r2ai_a called with query: %s\x1b[0m\n", user_query);
r_cons_flush (core->cons);
// Get conversation
R2AI_Messages *messages = r2ai_conversation_get (state);
if (!messages) {
R_LOG_ERROR ("Conversation not initialized");
return;
}
// Add user query to the conversation (no system prompt)
// If this is the first message in a new conversation, clear previous history
if (r_list_length (messages->messages) == 0 || r_config_get_b (core->config, "r2ai.auto.reset_on_query")) {
r2ai_msgs_clear (messages);
}
// Add user query
R2AI_Message user_msg = {
.role = "user",
.content = (char *)user_query
};
r2ai_msgs_add (messages, &user_msg);
process_messages (cps, messages, NULL, 1);
}
// Helper function to display content with length indication for long content
static void print_content_with_length(RCore *core, const char *content, const char *empty_msg, bool always_show_length) {
if (!content || *content == '\0') {
r_cons_printf (core->cons, "%s\n", empty_msg? empty_msg: "<no content>");
return;
}
size_t content_len = strlen (content);
const size_t max_display = 200;
if (content_len > max_display) {
// Truncate long content and show length
char *truncated = r_str_ndup (content, max_display);
r_cons_printf (core->cons, "%s... \x1b[1;37m(length: %zu chars)\x1b[0m\n",
truncated, content_len);
free (truncated);
} else if (always_show_length) {
// Always show length for certain types (like tool responses)
r_cons_printf (core->cons, "%s \x1b[1;37m(length: %zu chars)\x1b[0m\n",
content, content_len);
} else {
r_cons_printf (core->cons, "%s\n", content);
}
}
// Add this function right after cmd_r2ai_a
R_IPI void cmd_r2ai_logs(RCorePluginSession *cps) {
RCore *core = cps->core;
R2AI_State *state = cps->data;
// Get conversation
R2AI_Messages *messages = r2ai_conversation_get (state);
if (!messages || r_list_length (messages->messages) == 0) {
r_cons_printf (core->cons, "No conversation history available\n");
return;
}
const char *input = r_core_cmd_str (core, "r2ai");
bool json_mode = input && strstr (input, "-Lj");
free ((char *)input);
if (json_mode) {
PJ *pj = pj_new ();
if (!pj) {
return;
}
pj_a (pj);
for (int i = 0; i < r_list_length (messages->messages); i++) {
const R2AI_Message *msg = r_list_get_n (messages->messages, i);
pj_o (pj);
pj_ks (pj, "role", msg->role? msg->role: "unknown");
pj_ks (pj, "content", msg->content? msg->content: "");
if (msg->tool_calls && msg->n_tool_calls > 0) {
pj_ka (pj, "tool_calls");
for (int j = 0; j < msg->n_tool_calls; j++) {
const R2AI_ToolCall *tc = &msg->tool_calls[j];
pj_o (pj);
if (tc->name) {
pj_ks (pj, "name", tc->name);
}
if (tc->arguments) {
pj_ks (pj, "arguments", tc->arguments);
}
pj_end (pj);
}
pj_end (pj);
}
pj_end (pj);
}
pj_end (pj);
char *json_str = pj_drain (pj);
r_cons_printf (core->cons, "%s\n", json_str);
free (json_str);
return;
}
r_cons_printf (core->cons, "\x1b[1;34m[r2ai] Chat Logs (%d messages)\x1b[0m\n",
r_list_length (messages->messages));
r_cons_printf (core->cons, "\x1b[1;33mNote: System prompt is applied automatically but not stored in history\x1b[0m\n\n");
// Display each message in the conversation
for (int i = 0; i < r_list_length (messages->messages); i++) {
const R2AI_Message *msg = r_list_get_n (messages->messages, i);
const char *role = msg->role;
// Format based on role
if (!strcmp (role, "user")) {
r_cons_printf (core->cons, "\x1b[1;32m[user]:\x1b[0m ");
print_content_with_length (core, msg->content, "<no content>", false);
} else if (!strcmp (role, "assistant")) {
r_cons_printf (core->cons, "\x1b[1;36m[assistant]:\x1b[0m ");
print_content_with_length (core, msg->content, "<no content>", false);
// Show tool calls if present
if (msg->tool_calls && msg->n_tool_calls > 0) {
for (int j = 0; j < msg->n_tool_calls; j++) {
const R2AI_ToolCall *tc = &msg->tool_calls[j];
r_cons_printf (core->cons, " \x1b[1;35m[tool call]:\x1b[0m %s\n",
tc->name? tc->name: "<unnamed>");
if (tc->arguments) {
r_cons_printf (core->cons, " %s\n", tc->arguments);
}
}
}
} else if (!strcmp (role, "tool")) {
r_cons_printf (core->cons, "\x1b[1;35m[tool]:\x1b[0m ");
print_content_with_length (core, msg->content, "<no result>", true);
// Don't show the tool call ID as requested
} else {
// Other roles (system, etc.)
r_cons_printf (core->cons, "\x1b[1;37m[%s]:\x1b[0m ", role);
print_content_with_length (core, msg->content, "<no content>", false);
}
r_cons_newline (core->cons);
r_cons_flush (core->cons);
}
}
Loaded 100 of 128 files, more files were not shown because too many files have changed in this diff. Show more