diff --git a/.agents/skills/reverse-engineer-anything/SKILL.md b/.agents/skills/reverse-engineer-anything/SKILL.md
index de9f6b7c1..b0b5b8b33 100644
--- a/.agents/skills/reverse-engineer-anything/SKILL.md
+++ b/.agents/skills/reverse-engineer-anything/SKILL.md
@@ -2,7 +2,7 @@
name: reverse-engineer-anything
description: Reverse engineer native, managed, Electron/JavaScript, packaged, firmware, and browser targets with REA. Use shipped-artifact or requested runtime evidence to explain features, compare versions, decompile code, or guide a reconstruction. Skip REA for ordinary source-repository architecture analysis.
metadata:
- version: "41"
+ version: "42"
---
# REA
@@ -31,6 +31,49 @@ Diagnose the observed blocker, continue useful supported work, and resume the
original task after repair. Client approval, shell PATH, registration and engine
availability have different remedies; avoid repeating setup for all of them.
+1. Diagnose without changing REA or client configuration. `npx` may download
+ the package and write to npm's cache. For Codex, run
+ `npx -y rea-agents@latest doctor --client codex --json`. Substitute the current
+ supported client: `claude_code`, `claude_desktop`, `codex`, `cursor`,
+ `gemini_cli`, `windsurf`, `devin`, `opencode`, `antigravity`, `copilot_cli`,
+ `commandcode`, `vscode`, `grok_build`, `omp`, or `grok_bot`. `grok_bot` has no local
+ file registration: doctor reports the Grok Bot chat step, and setup does not
+ write `mcp.json`. If the client is unknown, use `doctor --json` and
+ inspect its registration results before choosing a setup scope.
+2. Distinguish the reason. Missing, malformed, or stale registration needs a
+ scoped configuration repair. An aligned registration with no tools in the
+ active session needs a restart/reconnection; doctor cannot prove that the
+ current agent has connected. A missing provider affects only tasks requiring
+ that provider: static JavaScript inspection needs neither Hopper nor Ghidra,
+ and Android inspection has separate bring-your-own JADX/Java prerequisites.
+3. For a configuration repair, prepare the read-only plan:
+ `npx -y rea-agents@latest setup --client codex --dry-run --json`.
+ Use the current client's ID, show its exact proposed paths, backups, and
+ changes, and obtain approval before setup writes configuration or installs
+ Hopper. Setup normally installs the matching bundled skill too; include that
+ replacement in the reviewed plan. After approval, apply the same scope with
+ `npx -y rea-agents@latest setup --client codex --yes`. Add `--install-hopper`
+ only if that separate installation was needed and explicitly approved.
+4. Restart/reconnect the affected agent. Verify that REA tools actually appear
+ in the session, then resume the original investigation. If they remain
+ absent, inspect the client's MCP launch error rather than repeating setup.
+
+REA setup never installs or upgrades Node.js, npm, Homebrew, Java, Ghidra, IDA,
+JADX, Binwalk, or Unblob. Use existing prerequisites; do not install unrelated
+software to repair
+MCP registration. For an unsupported client, use manual stdio registration
+with a version-pinned `rea-agents` package or continue through the CLI.
+
+A concrete CLI fallback for an operator-supplied JavaScript tree or ASAR is:
+
+```bash
+npx -y rea-agents@latest analyze-javascript-application /absolute/path/to/app --json
+```
+
+No MCP registration or native engine is required. Read the returned Evidence,
+graph, limitations, and unknowns with the same care as an MCP result; the CLI
+returns the Evidence record directly. This fallback does not establish that
+MCP is configured. Native CLI tasks still require their selected engine.
Prefer an existing `rea` CLI. When none is installed and REA package acquisition
is within the authorized scope, use `npx -y rea-agents@latest` in its place.
Configuration repair starts with a read-only setup plan; show paths, backups and
@@ -61,6 +104,10 @@ explicit path or endpoint and do not need it.
consult the NativeAOT workflow in `references/native-and-artifacts.md` for
optional metadata recovery and its supported host/layout boundary.
- User-owned browser page already open: `list_browser_targets`.
+- Browser action the agent must perform (click, type, navigate):
+ `capture_browser_scenario` with declared actions. See
+ [runtime observation](references/runtime-observation.md) for connect/launch
+ and the focused follow-up browser tools.
- Fiddler Everywhere traffic or a native `.saz` capture: read the
[Fiddler workflow](references/runtime-observation.md#fiddler-everywhere-traffic-debugging).
Inspect saved SAZ with `inspect_web_network_capture` and `format: "saz"`;
@@ -122,6 +169,14 @@ explicit path or endpoint and do not need it.
absent metadata does not prove a binary is not Go, and unparsed text records
remain explicit. See the
[Go binary guide](https://github.com/morluto/rea/blob/main/docs/go-binaries.md).
+- Embedded cargo-auditable Rust dependency metadata: `inspect_rust_binary`
+ with the explicit local ELF, PE, or thin Mach-O file. This built-in
+ target-free inspection requires no Rust installation or disassembler. Preserve
+ package order, dependency indices, and the section file range. A missing
+ `.dep-v0` section is null metadata, not proof the binary has no dependencies.
+ A rustc commit hash is an inference from a read-only `/rustc/<40-hex>/` path.
+ See the
+ [Rust binary guide](https://github.com/morluto/rea/blob/main/docs/rust-binaries.md).
- Explicit local SQLite database snapshot: `inspect_sqlite_database`. Inspect
with Node.js 24.x >=24.15 or 26+ providing native SQLite limits. Read schema
@@ -272,7 +327,8 @@ as complete while required questions remain open.
[references/javascript-applications.md](references/javascript-applications.md)
- Android APK declarations, classes, methods, and static references:
[references/android-applications.md](references/android-applications.md)
-- Passive browser/Electron observation and static/runtime reconciliation:
+- Browser/Electron observation (passive and scenario) and static/runtime
+ reconciliation:
[references/runtime-observation.md](references/runtime-observation.md)
- Evidence paging, comparisons, residual unknowns, and verification:
[references/evidence-workflows.md](references/evidence-workflows.md)
diff --git a/.agents/skills/reverse-engineer-anything/references/evidence-workflows.md b/.agents/skills/reverse-engineer-anything/references/evidence-workflows.md
index c651287f3..539bfb6b2 100644
--- a/.agents/skills/reverse-engineer-anything/references/evidence-workflows.md
+++ b/.agents/skills/reverse-engineer-anything/references/evidence-workflows.md
@@ -65,3 +65,7 @@ Use `inspect_analysis_view` with `view: {kind: "api-surface"}` on retained
method, origin and path template. Format matches are inferences, not handler
identity or schemas. Group pages cite parent record ordinals; consult record
coverage and exclusions before claiming completeness. No requests are replayed.
+When the connected schema advertises `include_query_parameter_names`, select
+it in the view for distinct observed names with original record ordinals. Query
+values are omitted; the names establish neither required parameters nor a
+complete server schema. Keep the original parent Evidence for exact URLs.
diff --git a/.agents/skills/reverse-engineer-anything/references/native-and-artifacts.md b/.agents/skills/reverse-engineer-anything/references/native-and-artifacts.md
index 8aab2e44f..506f40d4d 100644
--- a/.agents/skills/reverse-engineer-anything/references/native-and-artifacts.md
+++ b/.agents/skills/reverse-engineer-anything/references/native-and-artifacts.md
@@ -11,6 +11,18 @@ unparsed module-text line. A missing record is an unknown, not proof of a
non-Go binary; observed metadata does not authenticate its claims. Function,
source and type recovery remain a separate native-analysis question.
+## Rust dependency metadata
+
+Use `inspect_rust_binary` for an explicit local ELF, PE, or thin Mach-O when
+the question concerns the cargo-auditable crate list embedded in `.dep-v0`
+(Mach-O: `__DATA,.dep-v0`). No `open_binary`, Rust installation, or target
+execution is required. The CLI equivalent is `inspect-rust-binary PATH`.
+Preserve artifact SHA-256, package order, dependency indices, and the section
+file range. A missing section is null metadata, not proof the binary has no
+dependencies. An omitted package kind stays null. A rustc commit hash is
+inferred from a read-only `/rustc/<40-hex>/` path and does not by itself
+identify the compiler. See [Rust binaries](https://github.com/morluto/rea/blob/main/docs/rust-binaries.md).
+
## SQLite snapshots
Use `inspect_sqlite_database` with an explicit local database snapshot for its
diff --git a/.agents/skills/reverse-engineer-anything/references/runtime-observation.md b/.agents/skills/reverse-engineer-anything/references/runtime-observation.md
index 2d660d675..ba197ecb8 100644
--- a/.agents/skills/reverse-engineer-anything/references/runtime-observation.md
+++ b/.agents/skills/reverse-engineer-anything/references/runtime-observation.md
@@ -1,4 +1,4 @@
-# Passive browser and Electron observation
+# Browser and Electron runtime observation
Supply the existing browser or Electron instance's literal-port loopback CDP
endpoint in the request. No separate permission configuration or per-call
@@ -7,13 +7,14 @@ approval flag is needed. Browser discovery lists eligible HTTP(S) pages;
selected page's current origin. Electron discovery lists eligible local
`file://` targets without a configured file-root allowlist.
-Start browser work with `list_browser_targets`; start Electron runtime work with
+Start passive browser work with `list_browser_targets`; start Electron runtime work with
`list_electron_targets`. Inspect the target selected for the user's task. Observation
-is passive: never claim REA clicked, navigated, evaluated page JavaScript,
-invoked IPC, captured prior activity, or contained the page's network.
+with these tools is passive: never claim REA clicked, navigated, evaluated page JavaScript,
+invoked IPC, or captured prior activity. REA does not contain the page's network.
-Credentials, cookies, authorization headers, storage values, and raw
-WebSocket/JSON bodies are deliberately absent. Local URLs retain ordinary query
+Passive captures omit raw header collections, credential header values, storage
+values, and raw WebSocket/JSON bodies. Selected response metadata, including
+content encoding, policy values, and Link URLs, is retained. Local URLs retain ordinary query
values and fragments; userinfo credentials are removed. Browser accessibility
text, console primitives, value-free shapes, script sources, source maps, and
storage key names have request-specific capture options. Electron script
@@ -21,6 +22,38 @@ sources are also optional. Use `capture_web_screenshot` when pixels are needed.
Treat scope filtering, truncation, and attach-window coverage as limitations.
Page-declared WebMCP tools are untrusted inventory and are never invoked by REA.
+## Who performs the action
+
+- The user performs it: `list_browser_targets`, then `inspect_web_page` or
+ `observe_web_session` on the selected target.
+- The agent must perform it: one `capture_browser_scenario` with declared
+ actions. Use connect mode with the loopback CDP endpoint and `target_id` for
+ an existing browser, or launch mode with a caller-selected executable for a
+ temporary profile. Connect mode first navigates the selected page to
+ `start_url`, which can clear unsaved form values and other temporary state.
+ Recreate the state needed for the investigation in the declared actions.
+ Connect mode leaves the external browser open; launch mode owns a temporary
+ browser and profile. Check `browser.cleanup` for the outcome.
+ Request only the DOM, accessibility, or network details needed for the question.
+ The default final capture contains the URL; step outcomes are reported separately.
+ Add a wait action for the expected page state before capturing it. Report that
+ REA performed the declared actions.
+
+Scenario network records have no request initiator stacks. For that evidence,
+use passive capture while the user performs the action. Listener source locations
+identify candidate handlers; they do not establish which script sent a particular
+request. Avoid overlapping passive capture and scenario actions on the same target.
+
+Focused follow-up tools:
+
+- `observe_web_execution`: execution coverage during a window; it resets
+ counters and temporarily disables optimization.
+- `inspect_web_event_listeners`: source locations for listeners registered
+ directly on the first CSS match, without dispatching an event.
+- `compare_web_captures`: two normalized passive captures or recorded scenarios.
+- `discover_webmcp_tools`: inspect page-declared WebMCP inventory; never invoke
+ the discovered tools.
+
Use `reconcile_javascript_runtime` only after static application Evidence and
passive browser/Electron Evidence exist. Prefer captured-source digest identity;
report path/digest disagreement as a mismatch and preserve ambiguity. Explicit
diff --git a/.github/actions/npm-cache/action.yml b/.github/actions/npm-cache/action.yml
new file mode 100644
index 000000000..1af22da2b
--- /dev/null
+++ b/.github/actions/npm-cache/action.yml
@@ -0,0 +1,42 @@
+name: Cache npm package downloads
+description: Restore npm's download cache and save it only from trusted default-branch events.
+outputs:
+ can-save:
+ description: Whether this run may update the shared npm download cache.
+ value: ${{ steps.context.outputs.can-save }}
+ path:
+ description: The npm download-cache directory.
+ value: ${{ steps.context.outputs.path }}
+runs:
+ using: composite
+ steps:
+ - id: context
+ shell: bash
+ env:
+ DEFAULT_BRANCH: ${{ github.event.repository.default_branch }}
+ LOCKFILES_HASH: ${{ hashFiles('package-lock.json', 'npm-shrinkwrap.json', '.npmrc') }}
+ run: |
+ cache_path="$(npm config get cache)"
+ runtime_arch="$(node -p 'process.arch')"
+ can_save=false
+ if [[ -n "${DEFAULT_BRANCH}" && "${GITHUB_REF}" == "refs/heads/${DEFAULT_BRANCH}" && ( "${GITHUB_EVENT_NAME}" == "push" || "${GITHUB_EVENT_NAME}" == "schedule" || "${GITHUB_EVENT_NAME}" == "workflow_dispatch" ) ]]; then
+ can_save=true
+ fi
+ cache_key="npm-download-${RUNNER_OS}-${runtime_arch}-${LOCKFILES_HASH}"
+ {
+ printf 'can-save=%s\n' "${can_save}"
+ printf 'path=%s\n' "${cache_path}"
+ printf 'key=%s\n' "${cache_key}"
+ } >> "${GITHUB_OUTPUT}"
+ - name: Restore and save npm package downloads
+ if: steps.context.outputs.can-save == 'true'
+ uses: actions/cache@0057852bfaa89a56745cba8c7296529d2fc39830 # v4.3.0
+ with:
+ path: ${{ steps.context.outputs.path }}
+ key: ${{ steps.context.outputs.key }}
+ - name: Restore npm package downloads without saving
+ if: steps.context.outputs.can-save != 'true'
+ uses: actions/cache/restore@0057852bfaa89a56745cba8c7296529d2fc39830 # v4.3.0
+ with:
+ path: ${{ steps.context.outputs.path }}
+ key: ${{ steps.context.outputs.key }}
diff --git a/.github/workflows/ci-timings.yml b/.github/workflows/ci-timings.yml
index 73a9ba4b9..74f27af32 100644
--- a/.github/workflows/ci-timings.yml
+++ b/.github/workflows/ci-timings.yml
@@ -12,12 +12,13 @@ concurrency:
permissions:
contents: read
actions: read
+ pull-requests: read
jobs:
report:
name: Collect recent completed workflow attempts
runs-on: ubuntu-latest
- timeout-minutes: 5
+ timeout-minutes: 10
steps:
# Consume only API data and execute batch code from the trusted default branch.
- uses: actions/checkout@11d5960a326750d5838078e36cf38b85af677262 # v4.4.0
@@ -27,6 +28,18 @@ jobs:
- uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4.4.0
with:
node-version-file: .nvmrc
+ - name: Cache npm package downloads
+ id: npm-cache
+ continue-on-error: true
+ uses: ./.github/actions/npm-cache
+ - name: Diagnose missing and startup-failed PR CI
+ continue-on-error: true
+ env:
+ GH_TOKEN: ${{ github.token }}
+ CI_REPOSITORY: ${{ github.repository }}
+ CI_REPOSITORY_ID: ${{ github.repository_id }}
+ CI_DEFAULT_BRANCH: ${{ github.event.repository.default_branch }}
+ run: node scripts/ci/pr-ci-health.mjs
- name: Record queue, execution, and matched step timing
continue-on-error: true
env:
@@ -44,3 +57,28 @@ jobs:
if-no-files-found: error
overwrite: true
retention-days: 14
+ - name: Install dependencies for static failure attribution
+ id: failure-deps
+ if: ${{ !cancelled() && hashFiles('.cache/ci-job-timings/batch-manifest.json') != '' }}
+ continue-on-error: true
+ run: npm ci
+ - name: Attribute supported static failures against main
+ if: ${{ !cancelled() && steps.failure-deps.outcome == 'success' && hashFiles('.cache/ci-job-timings/batch-manifest.json') != '' }}
+ continue-on-error: true
+ env:
+ GH_TOKEN: ${{ github.token }}
+ CI_REPOSITORY: ${{ github.repository }}
+ CI_DEFAULT_BRANCH: ${{ github.event.repository.default_branch }}
+ run: node scripts/ci/failure-report.mjs
+ - name: Upload CI health and failure diagnostics
+ if: ${{ !cancelled() }}
+ continue-on-error: true
+ uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2
+ with:
+ name: ci-health-diagnostics
+ path: |
+ .cache/ci-pr-health/
+ .cache/ci-failures/
+ include-hidden-files: true
+ if-no-files-found: warn
+ retention-days: 14
diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml
index 8b47fcf89..b35b6a6e1 100644
--- a/.github/workflows/ci.yml
+++ b/.github/workflows/ci.yml
@@ -42,6 +42,7 @@ jobs:
tier: ${{ steps.plan.outputs.tier }}
qualification_run_id: ${{ steps.qualification.outputs.run_id }}
code: ${{ steps.plan.outputs.code }}
+ reverse_tools: ${{ steps.plan.outputs.reverse_tools }}
sqlite: ${{ steps.plan.outputs.sqlite }}
runtime: ${{ steps.plan.outputs.runtime }}
package: ${{ steps.plan.outputs.package }}
@@ -122,11 +123,28 @@ jobs:
- uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4.4.0
with:
node-version-file: .nvmrc
- cache: npm
+ - name: Cache npm package downloads
+ id: npm-cache
+ uses: ./.github/actions/npm-cache
- run: npm ci
+ - name: Restore static task cache
+ if: needs.changes.outputs.code == 'true'
+ id: static-cache
+ uses: actions/cache/restore@0057852bfaa89a56745cba8c7296529d2fc39830 # v4.3.0
+ with:
+ path: .turbo/cache/
+ key: rea-static-v1-${{ runner.os }}-${{ runner.arch }}-${{ hashFiles('.nvmrc', '.npmrc', 'package-lock.json') }}-${{ github.sha }}
+ # Turbo validates each task's inputs after restoring this snapshot.
+ restore-keys: rea-static-v1-${{ runner.os }}-${{ runner.arch }}-${{ hashFiles('.nvmrc', '.npmrc', 'package-lock.json') }}-
- name: Validate workflow syntax and expressions
if: needs.changes.outputs.workflows == 'true'
run: go run github.com/rhysd/actionlint/cmd/actionlint@v1.7.7 -shellcheck= -pyflakes=
+ - name: Record static task source and execution identity
+ continue-on-error: true
+ if: needs.changes.outputs.code == 'true'
+ env:
+ CI_PR_HEAD_SHA: ${{ github.event.pull_request.head.sha }}
+ run: npm run ci:static-context
- if: needs.changes.outputs.code == 'true'
run: npm run check:ci
- if: needs.changes.outputs.code == 'true'
@@ -136,6 +154,15 @@ jobs:
run: npm run test:python
- if: needs.changes.outputs.code != 'true'
run: npm run format:check
+ - name: Save validated static task cache
+ if: >-
+ success() && needs.changes.outputs.code == 'true' &&
+ steps.npm-cache.outputs.can-save == 'true' &&
+ steps.static-cache.outputs.cache-hit != 'true'
+ uses: actions/cache/save@0057852bfaa89a56745cba8c7296529d2fc39830 # v4.3.0
+ with:
+ path: .turbo/cache/
+ key: ${{ steps.static-cache.outputs.cache-primary-key }}
- name: Upload complete static diagnostics
if: always() && needs.changes.outputs.code == 'true'
uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2
@@ -146,6 +173,34 @@ jobs:
if-no-files-found: ignore
retention-days: 7
+ test-source:
+ name: Source tests
+ needs: changes
+ if: needs.changes.outputs.code == 'true' && needs.changes.outputs.tier == 'fast'
+ runs-on: ubuntu-latest
+ timeout-minutes: 10
+ steps:
+ - uses: actions/checkout@11d5960a326750d5838078e36cf38b85af677262 # v4.4.0
+ - uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4.4.0
+ with:
+ node-version-file: .nvmrc
+ - name: Cache npm package downloads
+ id: npm-cache
+ uses: ./.github/actions/npm-cache
+ - run: npm ci
+ - name: Run source tests without waiting for build
+ run: npm run test:fast -- --reporter=default --reporter=blob
+ env:
+ VITEST_BLOB_LABEL: source
+ - uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2
+ if: ${{ !cancelled() }}
+ with:
+ name: vitest-report-source
+ path: .vitest/blob/*
+ include-hidden-files: true
+ if-no-files-found: error
+ retention-days: 7
+
test-shard:
name: Test shard (${{ matrix.shard }})
needs: [changes, build]
@@ -153,15 +208,21 @@ jobs:
runs-on: ubuntu-latest
timeout-minutes: 30
strategy:
- fail-fast: false
+ # Failed PRs release the remaining matrix slots; qualification retains
+ # every shard's diagnostics and coverage. Passing PRs run the full suite.
+ fail-fast: ${{ github.event_name == 'pull_request' && needs.changes.outputs.tier == 'fast' }}
matrix:
- shard: [1/6, 2/6, 3/6, 4/6, 5/6, 6/6]
+ # Fast feedback spends the same six test jobs on one source group and
+ # five built groups; full qualification retains six complete shards.
+ shard: ${{ fromJSON(needs.changes.outputs.tier == 'fast' && '["1/5","2/5","3/5","4/5","5/5"]' || '["1/6","2/6","3/6","4/6","5/6","6/6"]') }}
steps:
- uses: actions/checkout@11d5960a326750d5838078e36cf38b85af677262 # v4.4.0
- uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4.4.0
with:
node-version-file: .nvmrc
- cache: npm
+ - name: Cache npm package downloads
+ id: npm-cache
+ uses: ./.github/actions/npm-cache
- run: npm ci
- uses: actions/download-artifact@d3f86a106a0bac45b974a628896c90dbdf5c8093 # v4.3.0
with:
@@ -172,21 +233,42 @@ jobs:
run: npm run test:ci:shard:run -- --shard=${{ matrix.shard }}
env:
REA_CI_TEST_HISTORY: .cache/ci-test-history/test-results.json
- - name: Run complete portable suite for fast feedback
+ - name: Run built-dependent tests for fast feedback
if: needs.changes.outputs.tier == 'fast'
- run: npm run test:run -- --reporter=default --reporter=blob --shard=${{ matrix.shard }}
+ run: npm run test:built -- --reporter=default --reporter=blob --shard=${{ matrix.shard }}
env:
+ VITEST_BLOB_LABEL: built
REA_CI_TEST_HISTORY: .cache/ci-test-history/test-results.json
- uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2
if: ${{ !cancelled() }}
with:
- name: vitest-report-${{ strategy.job-index }}
+ name: vitest-report-built-${{ strategy.job-index }}
path: .vitest/blob/*
include-hidden-files: true
if-no-files-found: error
overwrite: true
retention-days: 7
+ reverse-tools-quality:
+ name: Reverse tools coverage and mutation
+ needs: [changes, static]
+ if: needs.changes.outputs.reverse_tools == 'true'
+ runs-on: ubuntu-latest
+ timeout-minutes: 60
+ steps:
+ - uses: actions/checkout@11d5960a326750d5838078e36cf38b85af677262 # v4.4.0
+ - uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4.4.0
+ with:
+ node-version-file: .nvmrc
+ - name: Cache npm package downloads
+ id: npm-cache
+ uses: ./.github/actions/npm-cache
+ - run: npm ci
+ - name: Run reverse tools tests with coverage
+ run: npm run test:coverage:reverse-tools
+ - name: Report reverse tools mutation results
+ run: npm run test:mutation:reverse-tools
+
build:
name: Build package
needs: changes
@@ -212,7 +294,9 @@ jobs:
- uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4.4.0
with:
node-version-file: .nvmrc
- cache: npm
+ - name: Cache npm package downloads
+ id: npm-cache
+ uses: ./.github/actions/npm-cache
- run: npm ci
- name: Reuse portable build outputs across runs
uses: ./.github/actions/portable-build-cache
@@ -271,7 +355,7 @@ jobs:
test:
name: Portable tests
- needs: [changes, test-shard]
+ needs: [changes, test-source, test-shard]
if: ${{ !cancelled() && needs.changes.outputs.tier != 'reused' }}
runs-on: ubuntu-latest
timeout-minutes: 10
@@ -286,13 +370,18 @@ jobs:
if: needs.changes.outputs.code == 'true' && success()
with:
node-version-file: .nvmrc
- cache: npm
+ - name: Cache npm package downloads
+ if: needs.changes.outputs.code == 'true' && success()
+ id: npm-cache
+ uses: ./.github/actions/npm-cache
- if: needs.changes.outputs.code == 'true' && success()
run: npm ci
- uses: actions/download-artifact@d3f86a106a0bac45b974a628896c90dbdf5c8093 # v4.3.0
if: >-
!cancelled() && needs.changes.outputs.code == 'true' &&
- (needs.test-shard.result == 'success' || needs.test-shard.result == 'failure')
+ (needs.test-shard.result == 'success' || needs.test-shard.result == 'failure' ||
+ (needs.changes.outputs.tier == 'fast' &&
+ (needs.test-source.result == 'success' || needs.test-source.result == 'failure')))
with:
path: .vitest/blob
pattern: vitest-report-*
@@ -320,7 +409,8 @@ jobs:
if: >-
!cancelled() && needs.changes.outputs.code == 'true' &&
needs.changes.outputs.tier == 'fast' &&
- (needs.test-shard.result == 'success' || needs.test-shard.result == 'failure')
+ (needs.test-shard.result == 'success' || needs.test-shard.result == 'failure' ||
+ needs.test-source.result == 'success' || needs.test-source.result == 'failure')
run: >-
npm exec -- vitest --merge-reports
--reporter=default
@@ -333,6 +423,8 @@ jobs:
if: needs.changes.outputs.code == 'true' && !cancelled() && hashFiles('test-timings.json') != ''
env:
SHARD_RESULT: ${{ needs.test-shard.result }}
+ SOURCE_REQUIRED: ${{ needs.changes.outputs.tier == 'fast' }}
+ SOURCE_RESULT: ${{ needs.test-source.result }}
MERGE_RESULT: ${{ needs.changes.outputs.tier == 'full' && steps.merge.outcome || steps.merge-fast.outcome }}
CI_VERIFY_TEST_INVENTORY: "true"
CI_TEST_SELECTION: ${{ needs.changes.outputs.test_selection }}
@@ -365,7 +457,13 @@ jobs:
env:
CODE_REQUIRED: ${{ needs.changes.outputs.code }}
SHARD_RESULT: ${{ needs.test-shard.result }}
- run: test "${CODE_REQUIRED}" != "true" || test "${SHARD_RESULT}" = "success"
+ SOURCE_REQUIRED: ${{ needs.changes.outputs.tier == 'fast' }}
+ SOURCE_RESULT: ${{ needs.test-source.result }}
+ run: |
+ if [ "${CODE_REQUIRED}" = "true" ]; then
+ test "${SHARD_RESULT}" = "success" || exit 1
+ test "${SOURCE_REQUIRED}" != "true" || test "${SOURCE_RESULT}" = "success"
+ fi
conformance-fixtures:
name: Source-built conformance fixtures
@@ -450,7 +548,9 @@ jobs:
- uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4.4.0
with:
node-version-file: .nvmrc
- cache: npm
+ - name: Cache npm package downloads
+ id: npm-cache
+ uses: ./.github/actions/npm-cache
- name: Verify native runtime identity
env:
EXPECTED_CI_PLATFORM: ${{ matrix.platform }}
@@ -472,11 +572,6 @@ jobs:
- run: npm run verify:windows-native:artifact
- name: Verify NativeAOT diagnostic applicability on this host
run: npm run test:focused -- tests/evaluation/package/nativeAotDiagnostics.test.ts
- - name: Resolve npm cache before verifier HOME isolation
- id: npm-cache
- run: |
- cache_path="$(npm config get cache)"
- echo "path=${cache_path}" >> "${GITHUB_OUTPUT}"
- run: npm run verify:package
env:
npm_config_cache: ${{ steps.npm-cache.outputs.path }}
@@ -506,7 +601,9 @@ jobs:
- uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4.4.0
with:
node-version-file: .nvmrc
- cache: npm
+ - name: Cache npm package downloads
+ id: npm-cache
+ uses: ./.github/actions/npm-cache
- run: npm ci
- uses: actions/download-artifact@d3f86a106a0bac45b974a628896c90dbdf5c8093 # v4.3.0
with:
@@ -554,7 +651,9 @@ jobs:
- uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4.4.0
with:
node-version-file: .nvmrc
- cache: npm
+ - name: Cache npm package downloads
+ id: npm-cache
+ uses: ./.github/actions/npm-cache
- run: npm ci
- name: Run the public CLI/MCP reconstruction journey
env:
@@ -583,7 +682,9 @@ jobs:
- uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4.4.0
with:
node-version-file: .nvmrc
- cache: npm
+ - name: Cache npm package downloads
+ id: npm-cache
+ uses: ./.github/actions/npm-cache
- run: npm ci
- uses: actions/download-artifact@d3f86a106a0bac45b974a628896c90dbdf5c8093 # v4.3.0
with:
@@ -703,7 +804,9 @@ jobs:
- uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4.4.0
with:
node-version-file: .nvmrc
- cache: npm
+ - name: Cache npm package downloads
+ id: npm-cache
+ uses: ./.github/actions/npm-cache
- run: npm ci
- run: npm run docs:check
- name: Generation leaves authored files unchanged
@@ -792,7 +895,9 @@ jobs:
changes,
static,
build,
+ reverse-tools-quality,
test-shard,
+ test-source,
sqlite-runtime,
test,
conformance-fixtures,
diff --git a/.github/workflows/pages.yml b/.github/workflows/pages.yml
index 6b8a05b46..baa6ba785 100644
--- a/.github/workflows/pages.yml
+++ b/.github/workflows/pages.yml
@@ -23,6 +23,8 @@ jobs:
- uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4.4.0
with:
node-version-file: .nvmrc
- cache: npm
+ - name: Cache npm package downloads
+ id: npm-cache
+ uses: ./.github/actions/npm-cache
- run: npm ci
- run: npm run docs:build
diff --git a/.github/workflows/real-binary-layout.yml b/.github/workflows/real-binary-layout.yml
index 0218543f0..06fcc290a 100644
--- a/.github/workflows/real-binary-layout.yml
+++ b/.github/workflows/real-binary-layout.yml
@@ -32,7 +32,9 @@ jobs:
- uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4.4.0
with:
node-version-file: .nvmrc
- cache: npm
+ - name: Cache npm package downloads
+ id: npm-cache
+ uses: ./.github/actions/npm-cache
- run: npm ci
- uses: ./.github/actions/portable-build-cache
with:
diff --git a/.github/workflows/real-browser.yml b/.github/workflows/real-browser.yml
index 3c3fe955a..aa7e6ebb2 100644
--- a/.github/workflows/real-browser.yml
+++ b/.github/workflows/real-browser.yml
@@ -25,7 +25,9 @@ jobs:
- uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4.4.0
with:
node-version-file: .nvmrc
- cache: npm
+ - name: Cache npm package downloads
+ id: npm-cache
+ uses: ./.github/actions/npm-cache
- run: npm ci
- uses: ./.github/actions/portable-build-cache
with:
diff --git a/.github/workflows/real-cutter.yml b/.github/workflows/real-cutter.yml
new file mode 100644
index 000000000..91121f207
--- /dev/null
+++ b/.github/workflows/real-cutter.yml
@@ -0,0 +1,76 @@
+name: Real upstream Cutter verification
+
+on:
+ workflow_dispatch:
+ repository_dispatch:
+ types: [real-cutter-upstream]
+
+permissions:
+ contents: read
+
+jobs:
+ windows-native-artifact:
+ name: Build Windows bridge security boundary
+ runs-on: ubuntu-latest
+ steps:
+ - uses: actions/checkout@11d5960a326750d5838078e36cf38b85af677262 # v4.4.0
+ - uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4.4.0
+ with:
+ node-version-file: .nvmrc
+ - name: Install the cross compiler used by the Windows native lane
+ run: |
+ sudo apt-get update
+ sudo apt-get install --no-install-recommends -y g++-mingw-w64-x86-64
+ - run: npm run build:windows-native
+ - uses: actions/upload-artifact@v4
+ with:
+ name: cutter-windows-native
+ path: native/windows/build/
+ if-no-files-found: error
+
+ cutter-upstream:
+ name: Cutter upstream smoke (${{ matrix.platform }})
+ needs: windows-native-artifact
+ runs-on: ${{ matrix.runner }}
+ timeout-minutes: 20
+ strategy:
+ fail-fast: false
+ matrix:
+ include:
+ - platform: linux
+ runner: [self-hosted, cutter-upstream, linux]
+ - platform: macOS
+ runner: [self-hosted, cutter-upstream, macOS]
+ - platform: Windows
+ runner: [self-hosted, cutter-upstream, Windows]
+ steps:
+ - uses: actions/checkout@11d5960a326750d5838078e36cf38b85af677262 # v4.4.0
+ - uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4.4.0
+ with:
+ node-version-file: .nvmrc
+ - name: Cache npm package downloads
+ id: npm-cache
+ uses: ./.github/actions/npm-cache
+ - run: npm ci
+ - if: matrix.platform == 'Windows'
+ uses: actions/download-artifact@v4
+ with:
+ name: cutter-windows-native
+ path: native/windows/build/
+ - if: matrix.platform == 'Windows'
+ run: npm run verify:windows-native:artifact
+ - if: matrix.platform == 'Windows'
+ run: npm run build:cached
+ - if: matrix.platform == 'Windows'
+ run: npm run verify:windows-native
+ - name: Verify host and real Cutter bridge
+ env:
+ EXPECTED_PLATFORM: ${{ matrix.platform }}
+ run: npm run verify:cutter
+ - uses: actions/upload-artifact@v4
+ if: always()
+ with:
+ name: cutter-upstream-${{ matrix.platform }}
+ path: cutter-smoke.json
+ if-no-files-found: ignore
+ retention-days: 30
diff --git a/.github/workflows/real-evm-interface.yml b/.github/workflows/real-evm-interface.yml
index 8588a269f..3a47b8f90 100644
--- a/.github/workflows/real-evm-interface.yml
+++ b/.github/workflows/real-evm-interface.yml
@@ -26,7 +26,9 @@ jobs:
- uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4.4.0
with:
node-version-file: .nvmrc
- cache: npm
+ - name: Cache npm package downloads
+ id: npm-cache
+ uses: ./.github/actions/npm-cache
- run: npm ci
- uses: ./.github/actions/portable-build-cache
with:
diff --git a/.github/workflows/real-go-binary.yml b/.github/workflows/real-go-binary.yml
index c7d74b001..d99622067 100644
--- a/.github/workflows/real-go-binary.yml
+++ b/.github/workflows/real-go-binary.yml
@@ -48,7 +48,9 @@ jobs:
- uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4.4.0
with:
node-version-file: .nvmrc
- cache: npm
+ - name: Cache npm package downloads
+ id: npm-cache
+ uses: ./.github/actions/npm-cache
- uses: actions/setup-go@40f1582b2485089dde7abd97c1529aa768e1baff # v5.6.0
with:
go-version: "1.26.1"
diff --git a/.github/workflows/real-hopper-linux.yml b/.github/workflows/real-hopper-linux.yml
index d5e5f8c33..417af2ca5 100644
--- a/.github/workflows/real-hopper-linux.yml
+++ b/.github/workflows/real-hopper-linux.yml
@@ -19,7 +19,9 @@ jobs:
- uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4.4.0
with:
node-version-file: .nvmrc
- cache: npm
+ - name: Cache npm package downloads
+ id: npm-cache
+ uses: ./.github/actions/npm-cache
- run: npm ci
- name: Pack CLI for setup verification
shell: bash
@@ -40,7 +42,9 @@ jobs:
- uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4.4.0
with:
node-version-file: .nvmrc
- cache: npm
+ - name: Cache npm package downloads
+ id: npm-cache
+ uses: ./.github/actions/npm-cache
- run: npm ci
- name: Install packed CLI in isolation
shell: bash
diff --git a/.github/workflows/real-hopper.yml b/.github/workflows/real-hopper.yml
index b3ceeb7d9..da1c60c9f 100644
--- a/.github/workflows/real-hopper.yml
+++ b/.github/workflows/real-hopper.yml
@@ -19,7 +19,9 @@ jobs:
- uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4.4.0
with:
node-version-file: .nvmrc
- cache: npm
+ - name: Cache npm package downloads
+ id: npm-cache
+ uses: ./.github/actions/npm-cache
- run: npm ci
- name: Install packed CLI in isolation
shell: bash
diff --git a/.github/workflows/real-javascript-recovery.yml b/.github/workflows/real-javascript-recovery.yml
index a5043b62e..786d4a2d4 100644
--- a/.github/workflows/real-javascript-recovery.yml
+++ b/.github/workflows/real-javascript-recovery.yml
@@ -27,7 +27,9 @@ jobs:
- uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4.4.0
with:
node-version-file: .nvmrc
- cache: npm
+ - name: Cache npm package downloads
+ id: npm-cache
+ uses: ./.github/actions/npm-cache
- run: npm ci
- uses: ./.github/actions/portable-build-cache
with:
diff --git a/.github/workflows/real-packet-capture.yml b/.github/workflows/real-packet-capture.yml
index 8daebfd8a..12cb3d23d 100644
--- a/.github/workflows/real-packet-capture.yml
+++ b/.github/workflows/real-packet-capture.yml
@@ -47,7 +47,9 @@ jobs:
- uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4.4.0
with:
node-version-file: .nvmrc
- cache: npm
+ - name: Cache npm package downloads
+ id: npm-cache
+ uses: ./.github/actions/npm-cache
- run: sudo apt-get update && sudo DEBIAN_FRONTEND=noninteractive apt-get install -y tshark util-linux python3
- run: npm ci
- run: npm run build:cached
diff --git a/.github/workflows/real-recorded-crash.yml b/.github/workflows/real-recorded-crash.yml
index 0463df27d..58cf90aed 100644
--- a/.github/workflows/real-recorded-crash.yml
+++ b/.github/workflows/real-recorded-crash.yml
@@ -29,7 +29,9 @@ jobs:
- uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4.4.0
with:
node-version-file: .nvmrc
- cache: npm
+ - name: Cache npm package downloads
+ id: npm-cache
+ uses: ./.github/actions/npm-cache
- run: npm ci
- uses: ./.github/actions/portable-build-cache
with:
diff --git a/.github/workflows/real-signal-recording.yml b/.github/workflows/real-signal-recording.yml
index 5c1380e2d..1ddc2d5e0 100644
--- a/.github/workflows/real-signal-recording.yml
+++ b/.github/workflows/real-signal-recording.yml
@@ -40,7 +40,9 @@ jobs:
- uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4.4.0
with:
node-version-file: .nvmrc
- cache: npm
+ - name: Cache npm package downloads
+ id: npm-cache
+ uses: ./.github/actions/npm-cache
- run: sudo apt-get update && sudo apt-get install -y sigrok-cli util-linux python3
- run: npm ci
- run: npm run build:cached
diff --git a/.github/workflows/real-web-network-captures.yml b/.github/workflows/real-web-network-captures.yml
index 3ee59d2c8..7901114f0 100644
--- a/.github/workflows/real-web-network-captures.yml
+++ b/.github/workflows/real-web-network-captures.yml
@@ -26,7 +26,9 @@ jobs:
- uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4.4.0
with:
node-version-file: .nvmrc
- cache: npm
+ - name: Cache npm package downloads
+ id: npm-cache
+ uses: ./.github/actions/npm-cache
- name: Use pinned package manager
run: npm install --global npm@11.16.0
- run: npm ci
diff --git a/.github/workflows/real-web-runtime.yml b/.github/workflows/real-web-runtime.yml
index f5b9f201a..0ceb9b057 100644
--- a/.github/workflows/real-web-runtime.yml
+++ b/.github/workflows/real-web-runtime.yml
@@ -21,12 +21,14 @@ jobs:
- uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4.4.0
with:
node-version-file: .nvmrc
- cache: npm
+ - name: Cache npm package downloads
+ id: npm-cache
+ uses: ./.github/actions/npm-cache
- run: npm ci
- uses: ./.github/actions/portable-build-cache
with:
build_cache_artifact: ${{ inputs.build_cache_artifact }}
- - run: npm run verify:browser:runtime
+ - run: env -u DBUS_SESSION_BUS_ADDRESS npm run verify:browser:runtime
env:
REA_BROWSER_EXECUTABLE: /usr/bin/google-chrome
REA_BROWSER_NO_SANDBOX: "true"
diff --git a/.github/workflows/real-web-source-map.yml b/.github/workflows/real-web-source-map.yml
index 2a1e65dc0..eb1ecde34 100644
--- a/.github/workflows/real-web-source-map.yml
+++ b/.github/workflows/real-web-source-map.yml
@@ -21,7 +21,9 @@ jobs:
- uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4.4.0
with:
node-version-file: .nvmrc
- cache: npm
+ - name: Cache npm package downloads
+ id: npm-cache
+ uses: ./.github/actions/npm-cache
- run: npm ci
- uses: ./.github/actions/portable-build-cache
with:
diff --git a/.github/workflows/sqlite-runtime.yml b/.github/workflows/sqlite-runtime.yml
index fc379d1dc..1ea6964b7 100644
--- a/.github/workflows/sqlite-runtime.yml
+++ b/.github/workflows/sqlite-runtime.yml
@@ -27,7 +27,9 @@ jobs:
with:
node-version: ${{ matrix.node }}
check-latest: true
- cache: npm
+ - name: Cache npm package downloads
+ id: npm-cache
+ uses: ./.github/actions/npm-cache
- run: npm ci
- uses: actions/download-artifact@d3f86a106a0bac45b974a628896c90dbdf5c8093 # v4.3.0
with:
diff --git a/.gitignore b/.gitignore
index 0d006e13f..e4dc40814 100644
--- a/.gitignore
+++ b/.gitignore
@@ -33,6 +33,8 @@ codedb.snapshot
htmlcov/
.coverage
coverage/
+.stryker-tmp/
+reports/mutation/
.vitest/
.vitest-reports/
test-results.xml
diff --git a/README.md b/README.md
index 53a86c5fc..0088640d0 100644
--- a/README.md
+++ b/README.md
@@ -1,6 +1,7 @@
**English** · [简体中文](docs/readme/README_zh.md) · [繁體中文](docs/readme/README_zh-TW.md) · [日本語](docs/readme/README_ja.md) · [한국어](docs/readme/README_ko.md) · [Türkçe](docs/readme/README_tr.md) · [Русский](docs/readme/README_ru.md) · [Tiếng Việt](docs/readme/README_vi.md) · [ไทย](docs/readme/README_th.md) · [Deutsch](docs/readme/README_de.md) · [Español](docs/readme/README_es.md) · [Français](docs/readme/README_fr.md) · [Українська](docs/readme/README_uk.md) · [Polski](docs/readme/README_pl.md) · [Português (Brasil)](docs/readme/README_pt-BR.md) · [العربية](docs/readme/README_ar.md) · [فارسی](docs/readme/README_fa.md) · [Bahasa Indonesia](docs/readme/README_id.md) · [Italiano](docs/readme/README_it.md)
+· [Ελληνικά](docs/readme/README_el.md)
# REA: Reverse Engineer Anything
@@ -23,6 +24,12 @@
npx rea-agents setup
+> [!IMPORTANT]
+> Get REA from [morluto/rea](https://github.com/morluto/rea),
+> [rea.tools](https://rea.tools/), or the [rea-agents npm package](https://www.npmjs.com/package/rea-agents).
+> Follow the installation and release links from these official sources. A similar
+> repository name or copied README does not establish that a download is official.
+
@@ -155,6 +162,7 @@ Additional tools and host support depend on the target:
| Native binaries | Pseudocode, assembly, strings, symbols, calls and references | Hopper, Ghidra or IDA; [native analysis](https://rea.tools/guides/native/) |
| Offline ELF layout | Sections, segments, original symbols/relocations and static mitigation candidates | Caller-supplied pwntools on Linux x64; [binary diagnostics](docs/binary-diagnostics.md) |
| Go binaries | Embedded compiler version, modules, replacements and build settings | Built-in static ELF/PE/thin Mach-O inspection; no Go installation required |
+| Rust binaries | Embedded cargo-auditable crate names, versions, sources and dependency edges | Built-in static ELF/PE/thin Mach-O inspection; [Rust binaries](docs/rust-binaries.md) |
| WASM artifact | Validation, sections, exact import/export WAT forms and decoded WAT | Caller-supplied WABT 1.0.42; `inspect_wasm_artifact` |
| EVM bytecode | Dispatch selectors, byte offsets, inferred arguments and mutability | Local raw/hex carrier on Linux x64; [offline EVM guide](docs/evm-bytecode.md) |
| SQLite databases | Tables, columns, indexes, views, triggers and selected ordinary-table rows | Local database/WAL snapshot; Node.js 24.x >=24.15 or 26+ with native SQLite limits |
@@ -179,6 +187,11 @@ Static JavaScript and .NET inspection read the supplied files without running
the application. Runtime capture runs or interacts with the selected target
using your user permissions; each runtime guide describes its effects.
+For offline native-binary inspection, REA also supports caller-supplied GNU
+objdump and Rizin. See the [reverse-engineering tools guide](docs/reverse-engineering-tools.md)
+for provider setup, CLI commands, and Evidence limitations. REA does not install
+or update either provider.
+
Native formats and host support vary by provider. See
diff --git a/bridge/cutter/rea_bridge.py b/bridge/cutter/rea_bridge.py
new file mode 100644
index 000000000..473f1d5fe
--- /dev/null
+++ b/bridge/cutter/rea_bridge.py
@@ -0,0 +1,816 @@
+"""REA's experimental local bridge plugin for Cutter.
+
+Install this file in Cutter's user plugin directory. The bridge is REA-owned
+IPC, not an upstream Cutter transport. It binds to loopback and requires a
+per-process random token stored in a user-only descriptor file.
+"""
+
+from __future__ import annotations
+
+import json
+import hashlib
+import hmac
+import os
+import secrets
+import socket
+import stat
+import tempfile
+import threading
+import time
+import uuid
+from pathlib import Path
+from typing import Any
+
+if os.name == "nt":
+ import ctypes
+ from ctypes import wintypes
+
+import cutter
+
+try:
+ from PySide6.QtCore import QObject, Signal
+except ImportError:
+ from PySide2.QtCore import QObject, Signal
+
+
+MAX_FRAME_BYTES = 16 * 1024 * 1024
+SOCKET_TIMEOUT_SECONDS = 20
+AUTH_FRAME_TIMEOUT_SECONDS = 1
+MAX_DESCRIPTOR_BYTES = 64 * 1024
+MAX_COMPLETION_RECEIPTS = 64
+
+
+def _windows_api() -> tuple[Any, Any]:
+ kernel32 = ctypes.WinDLL("kernel32", use_last_error=True)
+ advapi32 = ctypes.WinDLL("advapi32", use_last_error=True)
+ kernel32.GetCurrentProcess.restype = wintypes.HANDLE
+ kernel32.LocalFree.argtypes = [wintypes.HLOCAL]
+ kernel32.LocalFree.restype = wintypes.HLOCAL
+ kernel32.CloseHandle.argtypes = [wintypes.HANDLE]
+ kernel32.CloseHandle.restype = wintypes.BOOL
+ kernel32.GetFileAttributesW.argtypes = [wintypes.LPCWSTR]
+ kernel32.GetFileAttributesW.restype = wintypes.DWORD
+ kernel32.CreateDirectoryW.argtypes = [wintypes.LPCWSTR, wintypes.LPVOID]
+ kernel32.CreateDirectoryW.restype = wintypes.BOOL
+ kernel32.CreateFileW.argtypes = [wintypes.LPCWSTR, wintypes.DWORD, wintypes.DWORD,
+ wintypes.LPVOID, wintypes.DWORD, wintypes.DWORD, wintypes.HANDLE]
+ kernel32.CreateFileW.restype = wintypes.HANDLE
+ kernel32.WriteFile.argtypes = [wintypes.HANDLE, wintypes.LPVOID, wintypes.DWORD,
+ ctypes.POINTER(wintypes.DWORD), wintypes.LPVOID]
+ kernel32.WriteFile.restype = wintypes.BOOL
+ kernel32.FlushFileBuffers.argtypes = [wintypes.HANDLE]
+ kernel32.FlushFileBuffers.restype = wintypes.BOOL
+ kernel32.MoveFileExW.argtypes = [wintypes.LPCWSTR, wintypes.LPCWSTR, wintypes.DWORD]
+ kernel32.MoveFileExW.restype = wintypes.BOOL
+ kernel32.DeleteFileW.argtypes = [wintypes.LPCWSTR]
+ kernel32.DeleteFileW.restype = wintypes.BOOL
+ advapi32.OpenProcessToken.argtypes = [wintypes.HANDLE, wintypes.DWORD, ctypes.POINTER(wintypes.HANDLE)]
+ advapi32.OpenProcessToken.restype = wintypes.BOOL
+ advapi32.GetTokenInformation.argtypes = [wintypes.HANDLE, ctypes.c_int, wintypes.LPVOID,
+ wintypes.DWORD, ctypes.POINTER(wintypes.DWORD)]
+ advapi32.GetTokenInformation.restype = wintypes.BOOL
+ advapi32.ConvertSidToStringSidW.argtypes = [wintypes.LPVOID, ctypes.POINTER(wintypes.LPWSTR)]
+ advapi32.ConvertSidToStringSidW.restype = wintypes.BOOL
+ advapi32.GetNamedSecurityInfoW.argtypes = [wintypes.LPWSTR, ctypes.c_int, wintypes.DWORD,
+ ctypes.POINTER(wintypes.LPVOID), ctypes.POINTER(wintypes.LPVOID),
+ ctypes.POINTER(wintypes.LPVOID), ctypes.POINTER(wintypes.LPVOID),
+ ctypes.POINTER(wintypes.LPVOID)]
+ advapi32.GetNamedSecurityInfoW.restype = wintypes.DWORD
+ advapi32.GetSecurityDescriptorControl.argtypes = [wintypes.LPVOID, ctypes.POINTER(wintypes.WORD),
+ ctypes.POINTER(wintypes.DWORD)]
+ advapi32.GetSecurityDescriptorControl.restype = wintypes.BOOL
+ advapi32.EqualSid.argtypes = [wintypes.LPVOID, wintypes.LPVOID]
+ advapi32.EqualSid.restype = wintypes.BOOL
+ advapi32.GetAclInformation.argtypes = [wintypes.LPVOID, wintypes.LPVOID, wintypes.DWORD, ctypes.c_int]
+ advapi32.GetAclInformation.restype = wintypes.BOOL
+ advapi32.GetAce.argtypes = [wintypes.LPVOID, wintypes.DWORD, ctypes.POINTER(wintypes.LPVOID)]
+ advapi32.GetAce.restype = wintypes.BOOL
+ advapi32.IsValidSid.argtypes = [wintypes.LPVOID]
+ advapi32.IsValidSid.restype = wintypes.BOOL
+ advapi32.GetLengthSid.argtypes = [wintypes.LPVOID]
+ advapi32.GetLengthSid.restype = wintypes.DWORD
+ advapi32.ConvertStringSecurityDescriptorToSecurityDescriptorW.argtypes = [
+ wintypes.LPCWSTR, wintypes.DWORD, ctypes.POINTER(wintypes.LPVOID), ctypes.POINTER(wintypes.DWORD),
+ ]
+ advapi32.ConvertStringSecurityDescriptorToSecurityDescriptorW.restype = wintypes.BOOL
+ return kernel32, advapi32
+
+
+def _windows_user_sid_text() -> str:
+ kernel32, advapi32 = _windows_api()
+ token = wintypes.HANDLE()
+ if not advapi32.OpenProcessToken(kernel32.GetCurrentProcess(), 0x0008, ctypes.byref(token)):
+ raise ctypes.WinError(ctypes.get_last_error())
+ try:
+ required = wintypes.DWORD()
+ advapi32.GetTokenInformation(token, 1, None, 0, ctypes.byref(required))
+ if required.value == 0:
+ raise ctypes.WinError(ctypes.get_last_error())
+ buffer = ctypes.create_string_buffer(required.value)
+ if not advapi32.GetTokenInformation(token, 1, buffer, required, ctypes.byref(required)):
+ raise ctypes.WinError(ctypes.get_last_error())
+
+ class SID_AND_ATTRIBUTES(ctypes.Structure):
+ _fields_ = [("Sid", wintypes.LPVOID), ("Attributes", wintypes.DWORD)]
+
+ class TOKEN_USER(ctypes.Structure):
+ _fields_ = [("User", SID_AND_ATTRIBUTES)]
+
+ sid = ctypes.cast(buffer, ctypes.POINTER(TOKEN_USER)).contents.User.Sid
+ text = wintypes.LPWSTR()
+ if not advapi32.ConvertSidToStringSidW(sid, ctypes.byref(text)):
+ raise ctypes.WinError(ctypes.get_last_error())
+ try:
+ return text.value
+ finally:
+ kernel32.LocalFree(text)
+ finally:
+ kernel32.CloseHandle(token)
+
+
+def _verify_windows_private_path(path: Path, directory: bool) -> None:
+ kernel32, advapi32 = _windows_api()
+ attributes = kernel32.GetFileAttributesW(str(path))
+ if attributes == 0xFFFFFFFF:
+ raise ctypes.WinError(ctypes.get_last_error())
+ if attributes & 0x400 or bool(attributes & 0x10) != directory:
+ raise RuntimeError("Cutter bridge path is a reparse point or has the wrong object type")
+
+ class ACL_SIZE_INFORMATION(ctypes.Structure):
+ _fields_ = [("AceCount", wintypes.DWORD), ("AclBytesInUse", wintypes.DWORD), ("AclBytesFree", wintypes.DWORD)]
+
+ class ACE_HEADER(ctypes.Structure):
+ _fields_ = [("AceType", wintypes.BYTE), ("AceFlags", wintypes.BYTE), ("AceSize", wintypes.WORD)]
+
+ class SID_HEADER(ctypes.Structure):
+ _fields_ = [("Revision", wintypes.BYTE), ("SubAuthorityCount", wintypes.BYTE),
+ ("IdentifierAuthority", wintypes.BYTE * 6)]
+
+ class ACCESS_ALLOWED_ACE(ctypes.Structure):
+ _fields_ = [("Header", ACE_HEADER), ("Mask", wintypes.DWORD), ("SidStart", wintypes.DWORD)]
+
+ owner = wintypes.LPVOID()
+ dacl = wintypes.LPVOID()
+ descriptor = wintypes.LPVOID()
+ result = advapi32.GetNamedSecurityInfoW(
+ str(path), 1, 0x1 | 0x4, ctypes.byref(owner), None, ctypes.byref(dacl), None, ctypes.byref(descriptor),
+ )
+ if result != 0:
+ raise ctypes.WinError(result)
+ try:
+ control = wintypes.WORD()
+ revision = wintypes.DWORD()
+ if not advapi32.GetSecurityDescriptorControl(descriptor, ctypes.byref(control), ctypes.byref(revision)):
+ raise ctypes.WinError(ctypes.get_last_error())
+ if not control.value & 0x1000 or not dacl:
+ raise RuntimeError("Cutter bridge ACL must be present and protected from inheritance")
+
+ token = wintypes.HANDLE()
+ if not advapi32.OpenProcessToken(kernel32.GetCurrentProcess(), 0x0008, ctypes.byref(token)):
+ raise ctypes.WinError(ctypes.get_last_error())
+ try:
+ required = wintypes.DWORD()
+ advapi32.GetTokenInformation(token, 1, None, 0, ctypes.byref(required))
+ token_user = ctypes.create_string_buffer(required.value)
+ if not advapi32.GetTokenInformation(token, 1, token_user, required, ctypes.byref(required)):
+ raise ctypes.WinError(ctypes.get_last_error())
+
+ class SID_AND_ATTRIBUTES(ctypes.Structure):
+ _fields_ = [("Sid", wintypes.LPVOID), ("Attributes", wintypes.DWORD)]
+
+ class TOKEN_USER(ctypes.Structure):
+ _fields_ = [("User", SID_AND_ATTRIBUTES)]
+
+ current_sid = ctypes.cast(token_user, ctypes.POINTER(TOKEN_USER)).contents.User.Sid
+ if not advapi32.EqualSid(owner, current_sid):
+ raise RuntimeError("Cutter bridge path owner does not match the current user")
+ finally:
+ kernel32.CloseHandle(token)
+
+ info = ACL_SIZE_INFORMATION()
+ if not advapi32.GetAclInformation(dacl, ctypes.byref(info), ctypes.sizeof(info), 2) or info.AceCount != 2:
+ raise RuntimeError("Cutter bridge ACL must allow only the current user and SYSTEM")
+ expected_user = _windows_user_sid_text()
+ user_allowed = False
+ system_allowed = False
+ for index in range(info.AceCount):
+ ace_pointer = wintypes.LPVOID()
+ if not advapi32.GetAce(dacl, index, ctypes.byref(ace_pointer)):
+ raise ctypes.WinError(ctypes.get_last_error())
+ header = ctypes.cast(ace_pointer, ctypes.POINTER(ACE_HEADER)).contents
+ if header.AceType != 0 or header.AceSize < ctypes.sizeof(ACE_HEADER) + ctypes.sizeof(wintypes.DWORD) + ctypes.sizeof(SID_HEADER):
+ raise RuntimeError("Cutter bridge ACL contains a malformed or unsupported ACE")
+ ace_address = ctypes.cast(ace_pointer, ctypes.c_void_p).value
+ if ace_address is None:
+ raise RuntimeError("Cutter bridge ACL returned a null ACE")
+ sid_pointer = ctypes.c_void_p(ace_address + ctypes.sizeof(ACE_HEADER) + ctypes.sizeof(wintypes.DWORD))
+ sid_header = ctypes.cast(sid_pointer, ctypes.POINTER(SID_HEADER)).contents
+ sid_size = ctypes.sizeof(SID_HEADER) + sid_header.SubAuthorityCount * ctypes.sizeof(wintypes.DWORD)
+ if sid_size > header.AceSize - ctypes.sizeof(ACE_HEADER) - ctypes.sizeof(wintypes.DWORD) or not advapi32.IsValidSid(sid_pointer) or advapi32.GetLengthSid(sid_pointer) != sid_size:
+ raise RuntimeError("Cutter bridge ACL contains an invalid SID")
+ ace = ctypes.cast(ace_pointer, ctypes.POINTER(ACCESS_ALLOWED_ACE)).contents
+ sid_text = wintypes.LPWSTR()
+ if ace.Mask != 0x001F01FF or ace.Header.AceFlags != 0x03:
+ raise RuntimeError("Cutter bridge ACL contains an unsupported ACE")
+ if not advapi32.ConvertSidToStringSidW(sid_pointer, ctypes.byref(sid_text)):
+ raise ctypes.WinError(ctypes.get_last_error())
+ try:
+ if sid_text.value == expected_user:
+ user_allowed = True
+ elif sid_text.value == "S-1-5-18":
+ system_allowed = True
+ else:
+ raise RuntimeError("Cutter bridge ACL allows an unrelated principal")
+ finally:
+ kernel32.LocalFree(sid_text)
+ if not user_allowed or not system_allowed:
+ raise RuntimeError("Cutter bridge ACL must allow only the current user and SYSTEM")
+ finally:
+ kernel32.LocalFree(descriptor)
+
+
+def _windows_security_attributes() -> tuple[Any, Any]:
+ kernel32, advapi32 = _windows_api()
+ sid = _windows_user_sid_text()
+ sddl = f"O:{sid}G:{sid}D:P(A;OICI;FA;;;{sid})(A;OICI;FA;;;SY)"
+ descriptor = wintypes.LPVOID()
+ if not advapi32.ConvertStringSecurityDescriptorToSecurityDescriptorW(sddl, 1, ctypes.byref(descriptor), None):
+ raise ctypes.WinError(ctypes.get_last_error())
+
+ class SECURITY_ATTRIBUTES(ctypes.Structure):
+ _fields_ = [("nLength", wintypes.DWORD), ("lpSecurityDescriptor", wintypes.LPVOID), ("bInheritHandle", wintypes.BOOL)]
+
+ attributes = SECURITY_ATTRIBUTES(ctypes.sizeof(SECURITY_ATTRIBUTES), descriptor, False)
+ return kernel32, (descriptor, attributes)
+
+
+def _ensure_windows_bridge_directory(directory: Path) -> None:
+ directory.parent.mkdir(parents=True, exist_ok=True)
+ if not directory.exists():
+ kernel32, (descriptor, attributes) = _windows_security_attributes()
+ try:
+ if not kernel32.CreateDirectoryW(str(directory), ctypes.byref(attributes)):
+ error = ctypes.get_last_error()
+ if error != 183:
+ raise ctypes.WinError(error)
+ finally:
+ kernel32.LocalFree(descriptor)
+ _verify_windows_private_path(directory, True)
+
+
+def _ensure_posix_bridge_directory(directory: Path) -> None:
+ directory.mkdir(mode=0o700, parents=True, exist_ok=True)
+ if not hasattr(os, "O_NOFOLLOW") or not hasattr(os, "O_DIRECTORY"):
+ raise RuntimeError("Cutter bridge directory requires no-follow directory handles")
+ descriptor = os.open(directory, os.O_RDONLY | os.O_DIRECTORY | os.O_NOFOLLOW)
+ try:
+ directory_info = os.fstat(descriptor)
+ if directory_info.st_uid != os.getuid():
+ raise RuntimeError("Cutter bridge directory must be owned by the current user")
+ os.fchmod(descriptor, 0o700)
+ directory_info = os.fstat(descriptor)
+ if stat.S_IMODE(directory_info.st_mode) != 0o700:
+ raise RuntimeError("Cutter bridge directory must have mode 0700")
+ finally:
+ os.close(descriptor)
+
+
+def _write_windows_descriptor(directory: Path, destination: Path, payload: bytes) -> None:
+ if len(payload) > MAX_DESCRIPTOR_BYTES:
+ raise RuntimeError("Cutter bridge descriptor exceeds its byte limit")
+ kernel32, (security_descriptor, attributes) = _windows_security_attributes()
+ temporary = directory / f".{destination.name}.{uuid.uuid4().hex}.tmp"
+ handle = wintypes.HANDLE()
+ try:
+ handle = kernel32.CreateFileW(str(temporary), 0x40000000 | 0x00020000 | 0x00040000,
+ 0x1 | 0x2 | 0x4, ctypes.byref(attributes), 1, 0x80, None)
+ if handle == wintypes.HANDLE(-1).value:
+ raise ctypes.WinError(ctypes.get_last_error())
+ _verify_windows_private_path(temporary, False)
+ if payload:
+ data = ctypes.create_string_buffer(payload)
+ written = wintypes.DWORD()
+ if not kernel32.WriteFile(handle, data, len(payload), ctypes.byref(written), None) or written.value != len(payload):
+ raise ctypes.WinError(ctypes.get_last_error())
+ if not kernel32.FlushFileBuffers(handle):
+ raise ctypes.WinError(ctypes.get_last_error())
+ kernel32.CloseHandle(handle)
+ handle = wintypes.HANDLE()
+ if not kernel32.MoveFileExW(str(temporary), str(destination), 0x1 | 0x8):
+ raise ctypes.WinError(ctypes.get_last_error())
+ finally:
+ if handle and handle != wintypes.HANDLE(-1).value:
+ kernel32.CloseHandle(handle)
+ kernel32.DeleteFileW(str(temporary))
+ kernel32.LocalFree(security_descriptor)
+
+
+def _bridge_directory() -> Path:
+ configured = os.environ.get("REA_CUTTER_BRIDGE_DIR")
+ if configured:
+ return Path(configured).expanduser()
+ local_app_data = os.environ.get("LOCALAPPDATA")
+ if os.name == "nt" and local_app_data:
+ return Path(local_app_data) / "REA" / "CutterBridge"
+ return Path.home() / ".cache" / "rea" / "cutter-bridge"
+
+
+class _RequestDispatcher(QObject):
+ dispatch = Signal(object, object)
+
+ def __init__(self) -> None:
+ super().__init__()
+ self.dispatch.connect(self._handle)
+ self.generation = 0
+ self.completed_commands: dict[str, str] = {}
+ self._receipt_lock = threading.Lock()
+ self.current_file = self._read_current_file()
+ try:
+ self.cutter_version = str(cutter.core().getVersionInformation())
+ except Exception:
+ self.cutter_version = None
+
+ @staticmethod
+ def _read_current_file() -> str | None:
+ try:
+ value = cutter.core().currentlyOpenFile()
+ return str(value) if value else None
+ except Exception:
+ return None
+
+ def _handle(self, request: dict[str, Any], reply: dict[str, Any]) -> None:
+ kind = request.get("kind")
+ request_id = request.get("request_id")
+ if kind in ("command", "ack"):
+ try:
+ valid_request_id = (
+ isinstance(request_id, str)
+ and str(uuid.UUID(request_id)) == request_id.lower()
+ )
+ except (ValueError, AttributeError):
+ valid_request_id = False
+ if not valid_request_id:
+ reply.update({
+ "ok": False,
+ "error": "invalid-command-request-id",
+ "execution_state": "not_started",
+ })
+ request["_event"].set()
+ return
+ if kind == "ack":
+ with self._receipt_lock:
+ acknowledged = self.completed_commands.pop(request_id, None) is not None
+ reply.update({"ok": True, "acknowledged": acknowledged})
+ request["_event"].set()
+ return
+ if request["_cancelled"].is_set():
+ reply.update({
+ "ok": False,
+ "error": "request-cancelled-before-execution",
+ "execution_state": "not_started",
+ })
+ if kind == "command":
+ self._record_command_completion(request_id, "not_started")
+ request["_event"].set()
+ return
+ self._refresh_document_identity()
+ if kind == "status":
+ with self._receipt_lock:
+ completed_commands = [
+ {"request_id": receipt_id, "execution_state": state}
+ for receipt_id, state in self.completed_commands.items()
+ ]
+ reply.update({
+ "ok": True,
+ "session_id": request.get("session_id"),
+ "current_file": self.current_file,
+ "document_generation": self.generation,
+ "cutter_version": self.cutter_version,
+ "identity_status": "partial",
+ "completed_commands": completed_commands,
+ })
+ request["_event"].set()
+ return
+ if kind != "command":
+ reply.update({"ok": False, "error": "unsupported-request-kind"})
+ request["_event"].set()
+ return
+ if request.get("expected_generation") != self.generation:
+ reply.update({
+ "ok": False,
+ "error": "active-document-generation-changed",
+ "execution_state": "not_started",
+ })
+ self._record_command_completion(request_id, "not_started")
+ request["_event"].set()
+ return
+ command = request.get("command")
+ if not isinstance(command, str) or not command.strip():
+ reply.update({
+ "ok": False,
+ "error": "invalid-command",
+ "execution_state": "not_started",
+ })
+ self._record_command_completion(request_id, "not_started")
+ request["_event"].set()
+ return
+ reply["execution_state"] = "running"
+ try:
+ output = cutter.cmdj(command) if request.get("json") is True else cutter.cmd(command)
+ command_error = None
+ except Exception as error:
+ output = None
+ command_error = str(error)
+ self._refresh_document_identity()
+ if command_error is None:
+ reply.update({
+ "ok": True,
+ "output": output,
+ "current_file": self.current_file,
+ "document_generation": self.generation,
+ "cutter_version": self.cutter_version,
+ "identity_status": "partial",
+ "execution_state": "complete",
+ })
+ self._record_command_completion(request_id, "complete")
+ else:
+ reply.update({
+ "ok": False,
+ "error": command_error,
+ "current_file": self.current_file,
+ "document_generation": self.generation,
+ "cutter_version": self.cutter_version,
+ "identity_status": "partial",
+ "execution_state": "unknown",
+ "message": "The command raised an exception; it may have produced partial effects. Do not retry automatically.",
+ })
+ self._record_command_completion(request_id, "failed")
+ request["_event"].set()
+
+ def _record_command_completion(
+ self, request_id: str, execution_state: str
+ ) -> None:
+ with self._receipt_lock:
+ if request_id in self.completed_commands or len(self.completed_commands) < MAX_COMPLETION_RECEIPTS:
+ self.completed_commands[request_id] = execution_state
+
+ def can_accept_command(self) -> bool:
+ with self._receipt_lock:
+ return len(self.completed_commands) < MAX_COMPLETION_RECEIPTS
+
+ def _refresh_document_identity(self) -> None:
+ current_file = self._read_current_file()
+ if current_file != self.current_file:
+ self.current_file = current_file
+ self.generation += 1
+
+
+class _BridgeServer(threading.Thread):
+ def __init__(self, dispatcher: _RequestDispatcher) -> None:
+ super().__init__(name="rea-cutter-bridge", daemon=True)
+ self.dispatcher = dispatcher
+ self.server = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
+ self.server.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
+ self.server.bind(("127.0.0.1", 0))
+ self.server.listen(8)
+ self.server.settimeout(0.5)
+ self.port = self.server.getsockname()[1]
+ self.session_id = str(uuid.uuid4())
+ self.token = secrets.token_urlsafe(32)
+ self.poisoned = False
+ self.connection_ordinal = 0
+ self.directory = _bridge_directory()
+ self.descriptor = self.directory / f"cutter-{os.getpid()}-{self.session_id}.json"
+
+ def publish(self) -> None:
+ if os.name == "nt":
+ _ensure_windows_bridge_directory(self.directory)
+ else:
+ _ensure_posix_bridge_directory(self.directory)
+ if os.name == "nt" and (self.directory.is_symlink() or not self.directory.is_dir()):
+ raise RuntimeError("Cutter bridge directory must be a real directory")
+ payload = {
+ "session_id": self.session_id,
+ "pid": os.getpid(),
+ "host": "127.0.0.1",
+ "port": self.port,
+ "token": self.token,
+ "document_generation": self.dispatcher.generation,
+ "current_file": self.dispatcher.current_file,
+ "cutter_version": self.dispatcher.cutter_version,
+ "identity_status": "partial",
+ }
+ if os.name == "nt":
+ _write_windows_descriptor(self.directory, self.descriptor, json.dumps(payload).encode("utf-8"))
+ return
+ descriptor, temporary_path = tempfile.mkstemp(prefix=f".{self.descriptor.name}.", dir=self.directory)
+ try:
+ os.fchmod(descriptor, 0o600)
+ descriptor_info = os.fstat(descriptor)
+ if descriptor_info.st_uid != os.getuid() or stat.S_IMODE(descriptor_info.st_mode) & 0o077:
+ raise RuntimeError("Cutter bridge descriptor must be owned by the current user with mode 0600")
+ with os.fdopen(descriptor, "w", encoding="utf-8") as stream:
+ stream.write(json.dumps(payload))
+ stream.flush()
+ os.fsync(stream.fileno())
+ os.replace(temporary_path, self.descriptor)
+ except Exception:
+ try:
+ os.close(descriptor)
+ except OSError:
+ pass
+ try:
+ os.unlink(temporary_path)
+ except OSError:
+ pass
+ raise
+
+ def run(self) -> None:
+ try:
+ self.publish()
+ except Exception:
+ self.server.close()
+ return
+ while True:
+ try:
+ connection, _ = self.server.accept()
+ except socket.timeout:
+ if getattr(self, "stopping", False):
+ break
+ continue
+ except OSError:
+ break
+ self.connection_ordinal += 1
+ connection_ordinal = self.connection_ordinal
+ with connection:
+ connection.settimeout(AUTH_FRAME_TIMEOUT_SECONDS)
+ authenticated = False
+ try:
+ hello = self._receive_frame(connection)
+ if not isinstance(hello, dict) or hello.get("kind") != "rea-auth-v2" or not isinstance(hello.get("nonce"), str) or len(hello["nonce"]) != 64:
+ self._send(connection, {"ok": False, "error": "authentication-failed"})
+ continue
+ client_nonce = hello["nonce"]
+ server_nonce = f"{connection_ordinal:016x}{secrets.token_hex(24)}"
+ server_proof = hmac.new(self.token.encode(), f"rea-cutter-server-v2:{self.session_id}:{client_nonce}:{server_nonce}".encode(), hashlib.sha256).hexdigest()
+ self._send(connection, {"session_id": self.session_id, "nonce": server_nonce, "proof": server_proof})
+ authenticated_frame = self._receive_frame(connection)
+ request_bytes = authenticated_frame.get("request") if isinstance(authenticated_frame, dict) else None
+ expected_proof = hmac.new(self.token.encode(), f"rea-cutter-client-v2:{self.session_id}:{client_nonce}:{server_nonce}:{request_bytes}".encode(), hashlib.sha256).hexdigest()
+ if not isinstance(request_bytes, str) or not isinstance(authenticated_frame.get("proof"), str) or not hmac.compare_digest(authenticated_frame["proof"], expected_proof):
+ self._send(connection, {"ok": False, "error": "authentication-failed"})
+ continue
+ request = json.loads(request_bytes)
+ if not isinstance(request, dict) or request.get("session_id") != self.session_id:
+ self._send(connection, {"ok": False, "error": "authentication-failed"})
+ continue
+ authenticated = True
+ connection.settimeout(SOCKET_TIMEOUT_SECONDS)
+ if self.poisoned and request.get("kind") not in ("status", "ack"):
+ self._send_authenticated(connection, {
+ "ok": False,
+ "error": "session-poisoned-after-ui-timeout",
+ "execution_state": "not_started",
+ }, client_nonce, server_nonce)
+ continue
+ if (
+ request.get("kind") == "command"
+ and not self.dispatcher.can_accept_command()
+ ):
+ self._send_authenticated(connection, {
+ "ok": False,
+ "error": "completion-receipt-capacity-exceeded",
+ "execution_state": "not_started",
+ }, client_nonce, server_nonce)
+ continue
+ request["session_id"] = self.session_id
+ event = threading.Event()
+ cancelled = threading.Event()
+ reply: dict[str, Any] = {}
+ request["_event"] = event
+ request["_cancelled"] = cancelled
+ self.dispatcher.dispatch.emit(request, reply)
+ if not event.wait(SOCKET_TIMEOUT_SECONDS):
+ cancelled.set()
+ self.poisoned = True
+ self._send_authenticated(connection, {
+ "ok": False,
+ "error": "ui-thread-request-timeout",
+ "execution_state": "unknown",
+ "message": "The UI operation may still complete after this timeout; restart Cutter before issuing further commands.",
+ }, client_nonce, server_nonce)
+ else:
+ if request.get("kind") == "status" and reply.get("ok") is True:
+ reply["completed_connection"] = f"{connection_ordinal - 1:016x}"
+ self._send_authenticated(connection, reply, client_nonce, server_nonce)
+ except socket.timeout:
+ if authenticated:
+ self._send_authenticated(connection, {
+ "ok": False,
+ "error": "request-timeout",
+ "execution_state": "unknown",
+ "message": "The command may have produced partial or persistent effects. Do not retry automatically.",
+ }, client_nonce, server_nonce)
+ except Exception as error:
+ self._send(connection, {"ok": False, "error": str(error)})
+ self.server.close()
+ try:
+ self.descriptor.unlink(missing_ok=True)
+ except OSError:
+ pass
+
+ @staticmethod
+ def _receive_frame(connection: socket.socket) -> dict[str, Any]:
+ data = bytearray()
+ deadline = time.monotonic() + AUTH_FRAME_TIMEOUT_SECONDS
+ while b"\n" not in data:
+ if len(data) >= MAX_FRAME_BYTES:
+ raise ValueError("frame-limit-exceeded")
+ remaining = deadline - time.monotonic()
+ if remaining <= 0:
+ raise socket.timeout("authentication frame timed out")
+ connection.settimeout(remaining)
+ part = connection.recv(min(65536, MAX_FRAME_BYTES + 1 - len(data)))
+ if not part:
+ raise ValueError("incomplete authentication frame")
+ data.extend(part)
+ line, remainder = bytes(data).split(b"\n", 1)
+ if len(line) > MAX_FRAME_BYTES or remainder:
+ raise ValueError("invalid framed request")
+ value = json.loads(line)
+ return value if isinstance(value, dict) else {}
+
+ def _send_authenticated(self, connection: socket.socket, value: dict[str, Any], client_nonce: str, server_nonce: str) -> None:
+ encoded = self._encode_authenticated_response(value, client_nonce, server_nonce)
+ if encoded is None:
+ value = self._truncated_response(value)
+ encoded = self._encode_authenticated_response(value, client_nonce, server_nonce)
+ if encoded is None:
+ # This fixed-size fallback is checked through the same signed framing path.
+ value = {
+ "ok": False,
+ "error": "response-limit-exceeded",
+ "execution_state": value.get("execution_state", "unknown"),
+ "output": None,
+ "output_truncated": True,
+ "message": "The result exceeded the bridge frame limit; command completion state is preserved.",
+ }
+ encoded = self._encode_authenticated_response(value, client_nonce, server_nonce)
+ if encoded is None:
+ return
+ try:
+ connection.sendall(encoded)
+ except OSError:
+ pass
+
+ def _encode_authenticated_response(self, value: dict[str, Any], client_nonce: str, server_nonce: str) -> bytes | None:
+ if not _within_response_limit(value):
+ return None
+ prefix = b'{"response":"'
+ suffix = b'","proof":"' + (b"0" * 64) + b'"}\n'
+ frame_size = len(prefix) + len(suffix)
+ response_parts: list[bytes] = []
+ escaped_parts: list[bytes] = []
+ for fragment in json.JSONEncoder(ensure_ascii=False, separators=(",", ":")).iterencode(value):
+ raw = fragment.encode("utf-8")
+ escaped = json.dumps(fragment, ensure_ascii=False)[1:-1].encode("utf-8")
+ frame_size += len(escaped)
+ if frame_size > MAX_FRAME_BYTES:
+ return None
+ response_parts.append(raw)
+ escaped_parts.append(escaped)
+ proof = hmac.new(self.token.encode(), digestmod=hashlib.sha256)
+ proof.update(f"rea-cutter-response-v2:{self.session_id}:{client_nonce}:{server_nonce}:".encode())
+ for part in response_parts:
+ proof.update(part)
+ return prefix + b"".join(escaped_parts) + b'","proof":"' + proof.hexdigest().encode() + b'"}\n'
+
+ @staticmethod
+ def _truncated_response(value: dict[str, Any]) -> dict[str, Any]:
+ response: dict[str, Any] = {
+ "ok": value.get("ok") is True,
+ "output": None,
+ "execution_state": value.get("execution_state", "unknown"),
+ "output_truncated": True,
+ "message": "The command result exceeded the bridge frame limit; completion state is preserved and output was omitted.",
+ }
+ for key in ("current_file", "document_generation", "cutter_version", "identity_status"):
+ if key in value:
+ response[key] = value[key]
+ if value.get("ok") is not True and isinstance(value.get("error"), str):
+ response["error"] = value["error"]
+ return response
+
+ @staticmethod
+ def _send(connection: socket.socket, value: dict[str, Any]) -> None:
+ if not _within_response_limit(value):
+ value = {
+ "ok": False,
+ "error": "response-limit-exceeded",
+ "execution_state": value.get("execution_state", "unknown"),
+ "output_truncated": True,
+ "message": "The command completed or may have completed, but its result exceeded the bridge response limit. Do not retry automatically.",
+ }
+ parts: list[bytes] = []
+ encoded_size = 0
+ for part in json.JSONEncoder(ensure_ascii=False).iterencode(value):
+ encoded_part = part.encode("utf-8")
+ encoded_size += len(encoded_part)
+ if encoded_size > MAX_FRAME_BYTES:
+ fallback = {
+ "ok": False,
+ "error": "response-limit-exceeded",
+ "execution_state": value.get("execution_state", "unknown"),
+ "output_truncated": True,
+ "message": "The command completed or may have completed, but its result exceeded the bridge response limit. Do not retry automatically.",
+ }
+ parts = [json.dumps(fallback, separators=(",", ":")).encode("utf-8")]
+ break
+ parts.append(encoded_part)
+ encoded = b"".join(parts)
+ try:
+ connection.sendall(encoded + b"\n")
+ except OSError:
+ # A disconnected caller must not terminate the bridge thread or
+ # prevent later commands from being served.
+ pass
+
+ def stop(self) -> None:
+ self.stopping = True
+ try:
+ self.server.close()
+ except OSError:
+ pass
+
+
+class ReaCutterPlugin(cutter.CutterPlugin):
+ name = "REA Local Bridge"
+ description = "Local REA analysis bridge for the active Cutter session"
+ version = "1.0"
+ author = "REA"
+
+ def __init__(self) -> None:
+ super().__init__()
+ self._dispatcher: _RequestDispatcher | None = None
+ self._bridge: _BridgeServer | None = None
+
+ def setupPlugin(self) -> None:
+ return
+
+ def setupInterface(self, main: Any) -> None:
+ self._dispatcher = _RequestDispatcher()
+ self._bridge = _BridgeServer(self._dispatcher)
+ self._bridge.start()
+
+ def terminate(self) -> None:
+ if self._bridge is not None:
+ self._bridge.stop()
+ self._bridge.join(timeout=2)
+ self._bridge = None
+ self._dispatcher = None
+
+
+def create_cutter_plugin() -> ReaCutterPlugin:
+ return ReaCutterPlugin()
+
+
+def _within_response_limit(value: Any) -> bool:
+ pending = [value]
+ seen: set[int] = set()
+ size = 0
+ while pending:
+ current = pending.pop()
+ if isinstance(current, str):
+ size += 2
+ if current.isascii():
+ size += len(current)
+ size += current.count('"') + current.count("\\")
+ size += sum(5 for character in current if ord(character) < 0x20)
+ else:
+ size += 4 * len(current)
+ size += current.count('"') + current.count("\\")
+ size += sum(5 for character in current if ord(character) < 0x20)
+ elif isinstance(current, dict):
+ identity = id(current)
+ if identity in seen:
+ return False
+ seen.add(identity)
+ size += 2 + 4 * len(current)
+ if size > MAX_FRAME_BYTES:
+ return False
+ pending.extend(current.keys())
+ pending.extend(current.values())
+ elif isinstance(current, (list, tuple)):
+ identity = id(current)
+ if identity in seen:
+ return False
+ seen.add(identity)
+ size += 2 + 2 * len(current)
+ if size > MAX_FRAME_BYTES:
+ return False
+ pending.extend(current)
+ else:
+ size += 16
+ if size > MAX_FRAME_BYTES:
+ return False
+ return True
diff --git a/bridge/ghidra/ReaGhidraBridge.java b/bridge/ghidra/ReaGhidraBridge.java
index 84e809ccb..e184dc206 100644
--- a/bridge/ghidra/ReaGhidraBridge.java
+++ b/bridge/ghidra/ReaGhidraBridge.java
@@ -81,6 +81,7 @@ import ghidra.program.model.listing.Function;
import ghidra.program.model.listing.FunctionIterator;
import ghidra.program.model.listing.Instruction;
import ghidra.program.model.listing.InstructionIterator;
+import ghidra.program.model.listing.Listing;
import ghidra.program.model.listing.Variable;
import ghidra.program.model.mem.MemoryBlock;
import ghidra.program.model.mem.MemoryBlockSourceInfo;
@@ -405,7 +406,9 @@ public final class ReaGhidraBridge extends HeadlessScript {
case "xrefs" -> xrefs(request.params);
case "analyze_function" -> {
FunctionAnalysisObservation observation = analyzeFunctionObservation(request.params);
- yield functionObservation(observation.dossier(), observation.limitations());
+ JsonObject result = functionObservation(observation.dossier(), observation.limitations());
+ result.add("relationship_coverage", observation.relationshipCoverage());
+ yield result;
}
case "set_address_name", "set_addresses_names" -> {
if (!descriptor.transport.equals("unix-socket"))
@@ -1016,12 +1019,19 @@ public final class ReaGhidraBridge extends HeadlessScript {
for (Function item : ordered) {
result.add(canonicalAddress(item.getEntryPoint()));
}
- return functionObservation(
- result,
- noReturnCallLimitations(
+ if (callers) {
+ JsonObject observation = functionObservation(result, noReturnCallLimitations(
currentProgram.getListing().getInstructions(function.getBody(), true)
- )
+ ));
+ observation.add("relationship_coverage", callRelationshipCoverage(null,
+ unrepresentedIncomingCalls(function, observed)));
+ monitor.checkCancelled();
+ return observation;
+ }
+ CallBoundaryObservation calls = callBoundaryObservation(
+ function, currentProgram.getListing().getInstructions(function.getBody(), true), observed, false, null
);
+ return callRelationshipObservation(result, calls);
}
private JsonObject procedureInfo(JsonObject params) throws Exception {
@@ -1071,18 +1081,10 @@ public final class ReaGhidraBridge extends HeadlessScript {
result.addProperty("direction", direction);
result.add("references", edges);
result.addProperty("reference_kinds_available", true);
- JsonArray unresolvedCalls = new JsonArray();
- if (direction.equals("outgoing")) for (Instruction instruction : scanInstructions(function, Integer.MAX_VALUE).instructions) {
- if (!instruction.getFlowType().isCall()) continue;
- boolean resolved = false;
- for (Reference reference : instruction.getReferencesFrom()) if (reference.getReferenceType().isCall()) resolved = true;
- if (!resolved) {
- JsonObject site = new JsonObject();
- site.addProperty("address", canonicalAddress(instruction.getAddress()));
- site.addProperty("reason", "Instruction decoder identifies a call but ReferenceManager supplies no call destination");
- unresolvedCalls.add(site);
- }
- }
+ JsonArray unresolvedCalls = direction.equals("outgoing")
+ ? callBoundaryObservation(
+ function, currentProgram.getListing().getInstructions(function.getBody(), true), null, true, references
+ ).unresolvedCalls() : new JsonArray();
result.add("unresolved_calls", unresolvedCalls);
return result;
}
@@ -1490,19 +1492,28 @@ public final class ReaGhidraBridge extends HeadlessScript {
InstructionScan scan = scanInstructions(function, Integer.MAX_VALUE);
DecompileResults decompilation = decompile(function);
String pseudocode = decompiledText(decompilation);
+ JsonObject availability = new JsonObject();
+ availability.add("pseudocode", facetAvailability(
+ pseudocode != null,
+ function.isExternal() ? "Ghidra does not decompile external functions."
+ : function.getBody().isEmpty() ? "Ghidra has no function body to decompile."
+ : "Ghidra completed decompilation without returning pseudocode."
+ ));
if (pseudocode == null) {
pseudocode = "";
}
- List incomingReferences = collectReferences(function, "incoming")
- .stream()
+ List bodyIncoming = collectReferences(function, "incoming");
+ List incomingReferences = bodyIncoming.stream()
.filter(reference -> !function.getBody().contains(reference.getFromAddress()))
.toList();
List outgoingReferences = collectReferences(function, "outgoing");
JsonArray incoming = referenceEdges(incomingReferences);
JsonArray outgoing = referenceEdges(outgoingReferences);
- JsonArray comments = comments(scan.instructions);
- JsonArray callers = procedureIdentities(function.getCallingFunctions(monitor));
- JsonArray callees = procedureIdentities(function.getCalledFunctions(monitor));
+ JsonArray comments = comments(function);
+ Set callerFunctions = function.getCallingFunctions(monitor);
+ JsonArray callers = procedureIdentities(callerFunctions);
+ Set calleeFunctions = function.getCalledFunctions(monitor);
+ JsonArray callees = procedureIdentities(calleeFunctions);
JsonArray referencedStrings = referencedStrings(outgoingReferences);
JsonArray referencedNames = referencedNames(outgoingReferences);
JsonArray blocks = basicBlockValues(function);
@@ -1536,6 +1547,15 @@ public final class ReaGhidraBridge extends HeadlessScript {
nativeApiBoundary(function, decompilation)
);
result.add("native_value_flow", nativeValueFlow(decompilation));
+ // Successful full queries distinguish collected empty sets from missing facts.
+ // Availability does not claim that Ghidra resolved every runtime relationship.
+ for (String facet : List.of("comments", "callers", "callees", "references", "strings_names", "cfg")) {
+ availability.add(facet, facetAvailability(true, null));
+ }
+ availability.add("assembly", facetAvailability(
+ !scan.truncated(), "The function instruction scan was truncated."
+ ));
+ result.add("facet_availability", availability);
JsonArray limitations = new JsonArray();
limitations.add(
"Unresolved computed or indirect flows without target addresses are not represented as reference edges."
@@ -1563,8 +1583,10 @@ public final class ReaGhidraBridge extends HeadlessScript {
"Swift String literal decoding is implemented for AArch64 register pairs only; no Swift literals were decoded for this architecture."
);
}
- List boundaryLimitations = noReturnCallLimitations(scan.instructions);
- for (String limitation : boundaryLimitations) {
+ CallBoundaryObservation calls = callBoundaryObservation(
+ function, scan.instructions, calleeFunctions, true, outgoingReferences);
+ result.add("unresolved_calls", calls.unresolvedCalls());
+ for (String limitation : calls.limitations()) {
limitations.add(limitation);
}
if (decompilation != null && decompilation.getErrorMessage() != null &&
@@ -1572,7 +1594,14 @@ public final class ReaGhidraBridge extends HeadlessScript {
limitations.add("Ghidra decompiler diagnostic: " + decompilation.getErrorMessage());
}
result.add("limitations", limitations);
- return new FunctionAnalysisObservation(result, boundaryLimitations);
+ monitor.checkCancelled();
+ int missingCallers = 0;
+ for (Reference reference : bodyIncoming) {
+ monitor.checkCancelled();
+ if (unrepresentedCaller(reference, callerFunctions)) missingCallers++;
+ }
+ return new FunctionAnalysisObservation(result, calls.limitations(),
+ callRelationshipCoverage(calls, missingCallers));
}
private JsonObject nativeValueFlow(DecompileResults decompilation) throws Exception {
@@ -3276,54 +3305,202 @@ public final class ReaGhidraBridge extends HeadlessScript {
return result;
}
+ private static JsonObject facetAvailability(boolean available, String reason) {
+ JsonObject result = new JsonObject();
+ result.addProperty("available", available);
+ if (!available) result.addProperty("reason", reason);
+ return result;
+ }
+
+ private JsonObject callRelationshipObservation(JsonElement value, CallBoundaryObservation calls)
+ throws Exception {
+ monitor.checkCancelled();
+ JsonObject result = functionObservation(value, calls.limitations());
+ result.add("relationship_coverage", callRelationshipCoverage(calls, null));
+ return result;
+ }
+
+ private static JsonObject callRelationshipCoverage(
+ CallBoundaryObservation calls, Integer missingCallers) {
+ JsonObject coverage = completeResolvedCallCoverage();
+ Integer omitted = calls == null ? null : calls.unrepresentedCalleeCalls();
+ JsonArray directions = new JsonArray();
+ if (missingCallers != null && missingCallers > 0) directions.add("callers");
+ String callerLimitation = missingCallers != null && missingCallers > 0
+ ? "Ghidra's caller inventory omits sources of " + missingCallers +
+ " call reference(s) into this function's body." : null;
+ if ((calls != null && omitted == null) || (calls == null && missingCallers == null)) {
+ coverage.addProperty("status", "unknown");
+ String reason = calls == null ? "No call inventory was examined."
+ : "The callee inventory was not examined alongside decoded call targets.";
+ coverage.addProperty("reason", reason + (callerLimitation == null ? "" : " " + callerLimitation));
+ if (calls == null && missingCallers == null) directions.add("callers");
+ directions.add("callees");
+ coverage.add("limited_directions", directions);
+ return coverage;
+ }
+ JsonArray limitations = new JsonArray();
+ if (callerLimitation != null) limitations.add(callerLimitation);
+ if (omitted != null && omitted > 0) {
+ limitations.add("Ghidra's callee inventory omits targets of " + omitted +
+ " call reference(s) or decoded call(s) without destinations.");
+ directions.add("callees");
+ }
+ if (!limitations.isEmpty()) {
+ coverage.addProperty("status", "partial");
+ coverage.add("limitations", limitations);
+ coverage.add("limited_directions", directions);
+ }
+ return coverage;
+ }
+
+ private static JsonObject completeResolvedCallCoverage() {
+ JsonObject coverage = new JsonObject();
+ coverage.addProperty("status", "complete");
+ JsonArray diagnostics = new JsonArray();
+ diagnostics.add(
+ "Only resolved Ghidra call references are returned; unresolved computed or indirect calls remain unknown, while function classifications distinguish thunks and externals."
+ );
+ coverage.add("diagnostics", diagnostics);
+ return coverage;
+ }
+
+ private boolean unrepresentedCaller(Reference reference, Set callers) {
+ if (!reference.getReferenceType().isCall()) return false;
+ Function caller = containingFunction(reference.getFromAddress());
+ return caller == null || !callers.contains(caller);
+ }
+
+ private int unrepresentedIncomingCalls(Function function, Set callers)
+ throws Exception {
+ int[] omitted = { 0 };
+ visitReferences(function, "incoming", reference -> {
+ if (unrepresentedCaller(reference, callers)) omitted[0]++;
+ });
+ return omitted[0];
+ }
+
private List noReturnCallLimitations(Iterable instructions)
throws Exception {
List limitations = new ArrayList<>();
for (Instruction instruction : instructions) {
monitor.checkCancelled();
- if (!instruction.getFlowType().isCall() || !instruction.getFlowType().isTerminal()) {
- continue;
- }
- Address[] destinations = instruction.getFlows();
- StringBuilder limitation = new StringBuilder();
- limitation.append("Ghidra Listing reports a terminal call at ")
- .append(canonicalAddress(instruction.getAddress()));
- if (destinations.length == 0) {
- limitation.append(" with no resolved callee; its no-return basis is unknown.");
- }
- else {
- limitation.append(" to ");
- for (int index = 0; index < destinations.length; index++) {
- if (index > 0) limitation.append(", ");
- Address destination = destinations[index];
- limitation.append(canonicalAddress(destination));
- Function target = currentProgram.getFunctionManager().getFunctionAt(destination);
- if (target == null) {
- limitation.append(" (callee identity unknown)");
- }
- else {
- limitation.append(" ").append(procedureName(target));
- limitation.append(" (external=").append(target.isExternal());
- limitation.append(", thunk=").append(target.isThunk());
- limitation.append(", hasNoReturn=").append(target.hasNoReturn()).append(")");
- }
- }
- limitation.append("; fallthrough is excluded by Ghidra's flow model.");
- Function target = destinations.length == 1
- ? currentProgram.getFunctionManager().getFunctionAt(destinations[0])
- : null;
- if (target != null && target.hasNoReturn()) {
- limitation.append(" This records Ghidra's FunctionManager flag, not an independent verification that the callee cannot return.");
- }
- else {
- limitation.append(" The reason for the terminal flow is not established by this observation.");
- }
- }
- limitations.add(limitation.toString());
+ String limitation = terminalCallLimitation(instruction);
+ if (limitation != null) limitations.add(limitation);
}
return limitations;
}
+ private CallBoundaryObservation callBoundaryObservation(
+ Function function, Iterable instructions, Set returnedCallees,
+ boolean includeUnresolvedSites, List observedReferences)
+ throws Exception {
+ List limitations = new ArrayList<>();
+ CallBoundaryReferences calls = new CallBoundaryReferences(returnedCallees, includeUnresolvedSites);
+ if (observedReferences == null) visitReferences(function, "outgoing", calls::observe);
+ else for (Reference reference : observedReferences) {
+ monitor.checkCancelled();
+ calls.observe(reference);
+ }
+ for (Instruction instruction : instructions) {
+ monitor.checkCancelled();
+ String limitation = terminalCallLimitation(instruction);
+ if (limitation != null) limitations.add(limitation);
+ if (!instruction.getFlowType().isCall()) continue;
+ boolean destination = false;
+ for (Reference reference : instruction.getReferencesFrom()) {
+ if (!reference.getReferenceType().isCall()) continue;
+ destination = true;
+ break;
+ }
+ if (!destination) {
+ calls.unrepresented++;
+ calls.unresolved(instruction.getAddress(),
+ "Instruction decoder identifies a call but ReferenceManager supplies no call destination");
+ }
+ }
+ return new CallBoundaryObservation(List.copyOf(limitations),
+ returnedCallees == null ? null : calls.unrepresented, calls.unresolvedCalls());
+ }
+
+ private final class CallBoundaryReferences {
+ private final Set returnedCallees;
+ private final TreeMap unresolved;
+ private int unrepresented;
+
+ CallBoundaryReferences(Set returnedCallees, boolean includeUnresolvedSites) {
+ this.returnedCallees = returnedCallees;
+ unresolved = includeUnresolvedSites ? new TreeMap<>() : null;
+ }
+
+ void observe(Reference reference) {
+ if (!reference.getReferenceType().isCall()) return;
+ // Reference identity follows the same containing-function owner as referenceEdge.
+ Function target = containingFunction(reference.getToAddress());
+ if (returnedCallees != null && (target == null || !returnedCallees.contains(target)))
+ unrepresented++;
+ if (target == null) unresolved(reference.getFromAddress(),
+ "A call destination has no known containing Ghidra procedure");
+ }
+
+ void unresolved(Address address, String reason) {
+ if (unresolved != null) unresolved.put(address, reason);
+ }
+
+ JsonArray unresolvedCalls() {
+ if (unresolved == null) return null;
+ JsonArray result = new JsonArray();
+ for (var item : unresolved.entrySet()) {
+ JsonObject site = new JsonObject();
+ site.addProperty("address", canonicalAddress(item.getKey()));
+ site.addProperty("reason", item.getValue());
+ result.add(site);
+ }
+ return result;
+ }
+ }
+
+ private String terminalCallLimitation(Instruction instruction) {
+ if (!instruction.getFlowType().isCall() || !instruction.getFlowType().isTerminal())
+ return null;
+ Address[] destinations = instruction.getFlows();
+ StringBuilder limitation = new StringBuilder();
+ limitation.append("Ghidra Listing reports a terminal call at ")
+ .append(canonicalAddress(instruction.getAddress()));
+ if (destinations.length == 0) {
+ limitation.append(" with no resolved callee; its no-return basis is unknown.");
+ }
+ else {
+ limitation.append(" to ");
+ for (int index = 0; index < destinations.length; index++) {
+ if (index > 0) limitation.append(", ");
+ Address destination = destinations[index];
+ limitation.append(canonicalAddress(destination));
+ Function target = currentProgram.getFunctionManager().getFunctionAt(destination);
+ if (target == null) {
+ limitation.append(" (callee identity unknown)");
+ }
+ else {
+ limitation.append(" ").append(procedureName(target));
+ limitation.append(" (external=").append(target.isExternal());
+ limitation.append(", thunk=").append(target.isThunk());
+ limitation.append(", hasNoReturn=").append(target.hasNoReturn()).append(")");
+ }
+ }
+ limitation.append("; fallthrough is excluded by Ghidra's flow model.");
+ Function target = destinations.length == 1
+ ? currentProgram.getFunctionManager().getFunctionAt(destinations[0])
+ : null;
+ if (target != null && target.hasNoReturn()) {
+ limitation.append(" This records Ghidra's FunctionManager flag, not an independent verification that the callee cannot return.");
+ }
+ else {
+ limitation.append(" The reason for the terminal flow is not established by this observation.");
+ }
+ }
+ return limitation.toString();
+ }
+
private static String renderAssembly(List instructions) {
return String.join("\n", renderAssemblyLines(instructions));
}
@@ -3347,15 +3524,27 @@ public final class ReaGhidraBridge extends HeadlessScript {
private List collectReferences(
Function function,
String direction) throws Exception {
+ TreeMap observed = new TreeMap<>();
+ visitReferences(function, direction, reference -> addReference(observed, reference));
+ List result = new ArrayList<>(observed.values());
+ result.sort(REFERENCE_ORDER);
+ return result;
+ }
+
+ @FunctionalInterface
+ private interface ReferenceVisitor {
+ void accept(Reference reference) throws Exception;
+ }
+
+ private void visitReferences(Function function, String direction, ReferenceVisitor visitor)
+ throws Exception {
// References can target instruction interiors or originate in embedded
// data. Use actual body ownership, including an external's exact entry.
- AddressSet scope = new AddressSet(function.getBody());
- scope.add(function.getEntryPoint());
+ AddressSet scope = functionObservationScope(function);
boolean outgoing = direction.equals("outgoing");
AddressIterator addresses = outgoing
? currentProgram.getReferenceManager().getReferenceSourceIterator(scope, true)
: currentProgram.getReferenceManager().getReferenceDestinationIterator(scope, true);
- TreeMap observed = new TreeMap<>();
while (addresses.hasNext()) {
monitor.checkCancelled();
Address address = addresses.next();
@@ -3363,7 +3552,7 @@ public final class ReaGhidraBridge extends HeadlessScript {
for (Reference reference : currentProgram.getReferenceManager()
.getReferencesFrom(address)) {
monitor.checkCancelled();
- addReference(observed, reference);
+ if (!reference.isEntryPointReference()) visitor.accept(reference);
}
}
else {
@@ -3371,19 +3560,14 @@ public final class ReaGhidraBridge extends HeadlessScript {
.getReferencesTo(address);
while (iterator.hasNext()) {
monitor.checkCancelled();
- addReference(observed, iterator.next());
+ Reference reference = iterator.next();
+ if (!reference.isEntryPointReference()) visitor.accept(reference);
}
}
}
- List result = new ArrayList<>(observed.values());
- result.sort(REFERENCE_ORDER);
- return result;
}
private static void addReference(TreeMap observed, Reference reference) {
- if (reference.isEntryPointReference()) {
- return;
- }
String key = canonicalAddress(reference.getFromAddress()) + "\u0000" +
canonicalAddress(reference.getToAddress()) + "\u0000" +
reference.getReferenceType().getName() + "\u0000" +
@@ -3392,6 +3576,12 @@ public final class ReaGhidraBridge extends HeadlessScript {
observed.putIfAbsent(key, reference);
}
+ private static AddressSet functionObservationScope(Function function) {
+ AddressSet scope = new AddressSet(function.getBody());
+ scope.add(function.getEntryPoint());
+ return scope;
+ }
+
private JsonArray referenceEdges(List references) {
JsonArray result = new JsonArray();
for (Reference reference : references) {
@@ -3545,23 +3735,28 @@ public final class ReaGhidraBridge extends HeadlessScript {
return result;
}
- private static JsonArray comments(List instructions) {
+ private JsonArray comments(Function function) throws Exception {
+ Listing listing = currentProgram.getListing();
+ AddressSet scope = functionObservationScope(function);
+ AddressIterator addresses = listing.getCommentAddressIterator(scope, true);
TreeMap observed = new TreeMap<>();
- for (Instruction instruction : instructions) {
+ while (addresses.hasNext()) {
+ monitor.checkCancelled();
+ Address address = addresses.next();
for (CommentType type : CommentType.values()) {
- String text = instruction.getComment(type);
+ String text = listing.getComment(type, address);
if (text == null || text.isEmpty()) {
continue;
}
String kind = type == CommentType.EOL ? "inline" : "comment";
JsonObject item = new JsonObject();
- item.addProperty("address", canonicalAddress(instruction.getAddress()));
+ item.addProperty("address", canonicalAddress(address));
item.addProperty("kind", kind);
item.addProperty("text", text);
- String key = canonicalAddress(instruction.getAddress()) + "\u0000" +
+ String key = canonicalAddress(address) + "\u0000" +
kind + "\u0000" + type.name() + "\u0000" + text;
observed.put(key, item);
- }
+ }
}
JsonArray result = new JsonArray();
observed.values().forEach(result::add);
@@ -4497,7 +4692,8 @@ public final class ReaGhidraBridge extends HeadlessScript {
private record InventoryItem(Address address, String value, JsonObject facts) {}
private record AddressNameRequest(String inputAddress, Address address, String name) {}
private record FunctionEntry(Function function, InventoryItem item) {}
- private record FunctionAnalysisObservation(JsonObject dossier, List limitations) {}
+ private record FunctionAnalysisObservation(JsonObject dossier, List limitations, JsonObject relationshipCoverage) {}
+ private record CallBoundaryObservation(List limitations, Integer unrepresentedCalleeCalls, JsonArray unresolvedCalls) {}
private record SwiftStringLiteralDecode(JsonArray literals, JsonArray omissions) {}
private record SwiftLiteralLayout(
String representation,
diff --git a/bridge/hopper_bridge.py b/bridge/hopper_bridge.py
index 92a473c06..4188fe0da 100644
--- a/bridge/hopper_bridge.py
+++ b/bridge/hopper_bridge.py
@@ -644,7 +644,7 @@ def _analyze_function(document, params):
"end": _hex(_basic_block_end(procedure, block)),
"successors": sorted(set(successors), key=lambda value: int(value, 16)),
})
- pseudo = _pseudocode(document, procedure) or ""
+ pseudo = _pseudocode(document, procedure)
assembly_lines = _assembly(procedure).splitlines()
callers = sorted((_procedure_identity(item) for item in procedure.getAllCallerProcedures()), key=lambda item: int(item["address"], 16))
callees = sorted((_procedure_identity(item) for item in procedure.getAllCalleeProcedures()), key=lambda item: int(item["address"], 16))
@@ -706,7 +706,7 @@ def _analyze_function(document, params):
"signature": procedure.signatureString(),
"locals": _procedure_locals(procedure),
},
- "pseudocode": pseudo,
+ "pseudocode": pseudo or "",
"assembly": assembly_lines,
"comments": comments,
"callers": callers, "callees": callees,
@@ -718,6 +718,12 @@ def _analyze_function(document, params):
"basic_blocks": blocks,
"native_api": None,
"native_value_flow": None,
+ "facet_availability": {
+ "pseudocode": {"available": True} if pseudo is not None else _unavailable("Hopper did not supply pseudocode."),
+ **{facet: {"available": True} for facet in (
+ "assembly", "comments", "callers", "callees", "references", "strings_names", "cfg"
+ )},
+ },
"limitations": [
"Native CallReference classifications are observed where available; detailed reference flags remain unknown.",
"Hopper's public Python API does not expose equivalent external or thunk classification in this dossier.",
diff --git a/docs/.vitepress/config.ts b/docs/.vitepress/config.ts
index 5d7fcc2f3..dd562d813 100644
--- a/docs/.vitepress/config.ts
+++ b/docs/.vitepress/config.ts
@@ -29,6 +29,7 @@ export default defineConfig({
items: [
{ text: "Native binaries", link: "/native-investigation" },
{ text: "Go build metadata", link: "/go-binaries" },
+ { text: "Rust dependencies", link: "/rust-binaries" },
{
text: "JavaScript and Electron",
link: "/javascript-artifact-reconstruction",
@@ -62,6 +63,7 @@ export default defineConfig({
{ text: "Ghidra DOS analysis", link: "/ghidra-dos" },
{ text: "Ghidra raw binary", link: "/raw-binary" },
{ text: "Ghidra NativeAOT", link: "/ghidra-nativeaot" },
+ { text: "Ghidra PowerPC", link: "/ghidra-ppc" },
{ text: "Ghidra semantics", link: "/ghidra-provider" },
],
},
diff --git a/docs/android-analysis.md b/docs/android-analysis.md
index 50087f464..668bcbffb 100644
--- a/docs/android-analysis.md
+++ b/docs/android-analysis.md
@@ -33,10 +33,40 @@ unverified.
## Supply tools
Use a **full JDK 17 or newer** (including its compiler module) and
-**jadx-headless-mcp 0.7.1**. REA runs its packaged Java metadata bridge in
-source-file mode against the unmodified engine JAR. REA does not install Java,
-an Android SDK, JADX, a device service or an emulator. Set the path in the shell
-or MCP server environment:
+[**jadx-headless-mcp 0.7.1**](https://github.com/1013503897/jadx-headless-mcp/releases/download/v0.7.1/jadx-headless-mcp-0.7.1-all.jar).
+Verify the downloaded JAR's SHA-256 against
+[REA's pinned release metadata](https://github.com/morluto/rea/blob/main/src/android/JadxRelease.ts)
+before using it. The following download and verification commands work for npm
+package consumers as well as repository checkouts; the repository-only
+`npm run fixtures:android` helper is not included in the npm package.
+
+On macOS or Linux, choose a destination and verify the pinned digest:
+
+```sh
+mkdir -p "$HOME/.local/share/rea/android" &&
+cd "$HOME/.local/share/rea/android" &&
+curl -fL -o jadx-headless-mcp-0.7.1-all.jar https://github.com/1013503897/jadx-headless-mcp/releases/download/v0.7.1/jadx-headless-mcp-0.7.1-all.jar &&
+if command -v sha256sum >/dev/null; then
+ echo '6e5eacf500b64292bfb73c49797c1958f6ee44646e43e868039ae7feb573ff75 jadx-headless-mcp-0.7.1-all.jar' | sha256sum -c -
+else
+ echo '6e5eacf500b64292bfb73c49797c1958f6ee44646e43e868039ae7feb573ff75 jadx-headless-mcp-0.7.1-all.jar' | shasum -a 256 -c -
+fi &&
+export REA_JADX_MCP_JAR="$PWD/jadx-headless-mcp-0.7.1-all.jar"
+```
+
+On Windows, run the equivalent in PowerShell:
+
+```powershell
+$jar = Join-Path $HOME '.local\share\rea\android\jadx-headless-mcp-0.7.1-all.jar'
+New-Item -ItemType Directory -Force (Split-Path $jar) | Out-Null
+Invoke-WebRequest -Uri 'https://github.com/1013503897/jadx-headless-mcp/releases/download/v0.7.1/jadx-headless-mcp-0.7.1-all.jar' -OutFile $jar
+if ((Get-FileHash -Algorithm SHA256 $jar).Hash.ToLowerInvariant() -ne '6e5eacf500b64292bfb73c49797c1958f6ee44646e43e868039ae7feb573ff75') { throw 'JADX JAR SHA-256 does not match REA pinned metadata' }
+$env:REA_JADX_MCP_JAR = $jar
+```
+
+REA runs its packaged Java metadata bridge in source-file mode against the
+unmodified engine JAR. REA does not install Java, an Android SDK, JADX, a device
+service or an emulator. Set the path in the shell or MCP server environment:
```sh
export REA_JADX_MCP_JAR=/absolute/path/jadx-headless-mcp-0.7.1-all.jar
diff --git a/docs/architecture.mermaid b/docs/architecture.mermaid
index 753c57024..90dd41ab1 100644
--- a/docs/architecture.mermaid
+++ b/docs/architecture.mermaid
@@ -38,6 +38,10 @@ graph TD
JS_CODEC["src/javascript/sourceMaps/ pinned upstream map codec owned heap-limited Node process, complete point evidence"]
INSPECTOR["src/inspector/ passive V8 Inspector discovery and capture script and target locations"]
DOTNET["src/dotnet/ Managed static metadata provider PE/CLI metadata inventory, members, IL"]
+ OBJDUMP["src/objdump/ GNU objdump command profile"]
+ RIZIN["src/rizin/ Static command profile and persistent debugger sessions"]
+ GDB["src/gdb/ Persistent GDB/MI sessions owned process lifecycle and target state"]
+
FIRMWARE["src/firmware/ Binwalk / Unblob adapters owned processes, report normalization, private extraction"]
ANDROID["src/android/ Static APK adapter owned SDK stdio, pinned headless JADX, normalization"]
REFERENCE["src/reference/ Filesystem reference-source reader bounded traversal, validation, errors"]
@@ -50,7 +54,7 @@ graph TD
end
subgraph "Contracts Layer"
- CONTRACTS["src/contracts/ Caller-visible schemas for analysis, managed, browser, Electron, application, and session tools"]
+ CONTRACTS["src/contracts/ Caller-visible schemas for analysis, reverse engineering, managed, browser, Electron, application, and session tools"]
ERRORS["src/contracts/errorSchemas.ts Shared analysis error schemas CLI diagnostics, generated error catalog"]
end
@@ -122,6 +126,14 @@ graph TD
INSPECTOR --> DOMAIN
APP --> DOTNET
DOTNET --> DOMAIN
+ COMPOSITION --> OBJDUMP
+ OBJDUMP --> DOMAIN
+ COMPOSITION --> RIZIN
+ RIZIN --> DOMAIN
+ CLI --> GDB
+ SERVER --> GDB
+ GDB --> PROCESS
+ GDB --> DOMAIN
HOPPER --> HOPPER_BRIDGE
GHIDRA --> GHIDRA_BRIDGE
HOPPER --> PROCESS
diff --git a/docs/browser-observation.md b/docs/browser-observation.md
index 72dde900b..879d01520 100644
--- a/docs/browser-observation.md
+++ b/docs/browser-observation.md
@@ -243,6 +243,7 @@ REA retains local observation data and removes only URL userinfo credentials, ex
- Console observations with a stack source retain call type, argument types, timestamp, and source location. When console text capture is selected, already-delivered primitive values are retained verbatim; objects, getters, and remote properties are never expanded.
- WebSocket observations retain direction, opcode, and payload byte length. When shape capture is selected, text frames are classified as text or complete value-free JSON shape; binary bytes, hashes, prefixes, and raw frames are never retained.
- Storage observations always redact values. Key names, IndexedDB names, and cache names require `include_storage_keys`; stable content fingerprints require the additional `include_storage_fingerprints` selection. Cookie fingerprints cover cookies applicable to the current main-frame URL, including its path, rather than every cookie path on that origin. Other storage remains scoped to the origin. Cache bodies above 64 KiB and partial IndexedDB remote objects make fingerprint coverage incomplete, so identical observations remain `unknown`.
+- Unavailable or malformed storage inventories mark `storage_keys` unavailable, even without fingerprints. Comparisons suppress inferred key additions and removals while preserving modifications proven by complete fingerprints on both sides. Previously exported captures are not retroactively corrected; recapture them to obtain these coverage markers.
- Script metadata is included only when CDP supplies a URL on an allowed origin. Stable keys exclude transient CDP script IDs, and exact transient raw URLs are used only during script/resource reconciliation. URL-less scripts are excluded because their origin cannot be established. When CDP supplies an execution-context association, the accepted script retains its authorized frame ID for later attribution. Script metadata admission bounds retained identity and source-map strings to 8 MiB and 50,000 records. When a source-map URL cannot fit, the script identity remains and source-map coverage reports `resource_budget_exhausted`; rejected script observations are counted without retaining their IDs. Source content is omitted unless explicitly requested and is returned inline as a self-verifying artifact with its SHA-256 digest.
Frames, resources, scripts, events, and workers outside the selected origin scope are excluded. An explicit `allowed_origins` list can include additional origins; excluded target details are counted without being exposed.
diff --git a/docs/cli.md b/docs/cli.md
index c43ec2181..c801bf006 100644
--- a/docs/cli.md
+++ b/docs/cli.md
@@ -83,6 +83,18 @@ The shared CLI/MCP workflow reports compiler version, modules, replacements,
build settings, original bytes and SHA-256. See [Go binaries](go-binaries.md)
for supported containers and absent or malformed metadata.
+Inspect an embedded cargo-auditable Rust dependency list without installing
+Rust or configuring a disassembler:
+
+```bash
+rea inspect-rust-binary /absolute/path/to/program --json
+```
+
+The same workflow reports package names, versions, sources, the root package,
+dependency edges, and the section's file range. A missing `.dep-v0` section is
+null metadata, not proof that the binary has no dependencies. See
+[Rust binaries](rust-binaries.md).
+
Configure [Hopper or Ghidra](installation.md#hopper), or the
[IDA adapter](ida-provider.md), before analyzing a native target. Substitute
your target, search text and function name or address in these examples:
diff --git a/docs/client-configuration.md b/docs/client-configuration.md
index 409bc7f19..d03984ec8 100644
--- a/docs/client-configuration.md
+++ b/docs/client-configuration.md
@@ -265,6 +265,21 @@ The user-level MCP paths follow the editor's VS Code layout. Trae's official
documentation describes the raw JSON editor without specifying those paths;
loading this registration in Trae remains unverified.
+## WorkBuddy
+
+For [WorkBuddy](https://www.workbuddy.cn/docs/workbuddy/From-Beginner-to-Expert-Guide/Function-Description/MCP-Guide), setup writes `mcpServers.rea` into the
+global `~/.workbuddy/mcp.json` (a standard `mcpServers` JSON file at the user
+home root on every platform). Entries carry `command`, `args`, and `env`;
+stdio is the default transport. WorkBuddy discovers personal skills from
+`~/.workbuddy/skills` (`%userprofile%\.workbuddy\skills` on Windows), so
+setup installs its skill copy there.
+
+The global MCP path is a plain JSON file; setup preserves every other server
+and top-level key when it upserts `rea`. WorkBuddy also reads a project-level
+`/.workbuddy/mcp.json`, which setup does not touch: it registers the
+user-level file only. Loading this registration and real REA tool calls
+through WorkBuddy are verified.
+
## ZCode
For [ZCode](https://zcode.z.ai/en/docs/mcp-services), setup registers `rea` in the nested `mcp.servers` table of the
diff --git a/docs/frida-instrumentation.md b/docs/frida-instrumentation.md
index c93c76465..718af30c2 100644
--- a/docs/frida-instrumentation.md
+++ b/docs/frida-instrumentation.md
@@ -6,8 +6,8 @@ For remote devices, pass a host with an optional port (`host[:port]`; bracket IP
MCP exposes `list_frida_devices`, `list_frida_processes`, `start_frida_session`, `load_frida_script`, `resume_frida_session`, `unload_frida_script`, `frida_session_status`, and `close_frida_session`. A spawned process starts paused so scripts can load first. Closing unloads session scripts and detaches. If a spawn remains paused, REA resumes it only after script cleanup or session detach confirms the instrumentation is released; if both cleanup paths fail, the target stays paused and the cleanup error is retained. REA never kills the target. MCP sessions end with the server process. The one-shot CLI command `instrument-with-frida` accepts one inline script or script file, observes messages for a bounded duration, and returns Evidence with the source hash and captured messages.
-Failed startup cleanup retains acquired Frida handles, including a paused spawn that has not attached, for later cleanup retries. Its remote connection stays leased until session detach and target resume complete. CLI commands await provider cleanup before returning; MCP retains these resources for cleanup when its server closes. A failed cleanup remains reported rather than treating an unregistered session as fully released.
+Failed startup cleanup retains acquired Frida handles, including a paused spawn that has not attached, for later cleanup retries. Its remote connection stays leased until session detach and target resume complete. CLI commands await provider cleanup before returning; MCP retains these resources for cleanup when its server closes. Server shutdown stops admitting new Frida operations and waits for admitted startup, discovery, and queued work before releasing their resources. A failed cleanup remains reported and can be retried by closing the server again, rather than treating an unregistered session as fully released.
-Frida scripts execute inside the selected target with that process's memory, filesystem, and network authority. Message capture is bounded to 16 MiB and reports truncation. Script source is not stored in Evidence; its selected path and SHA-256 digest are. Evidence retains non-authentication target selection such as device or endpoint, mode, program or PID, and spawn arguments. Authentication values remain excluded. Results report the observed session and messages, while unsupported platform metadata remains unknown. A request cancelled before a Frida operation starts has no provider effects. If cancellation arrives during a native Frida call, REA waits for that call to return, then closes any session it created and returns the observed session as a partial observation. Cancellation Evidence separately reports whether a target resume was observed, whether instrumentation cleanup completed, and that final target liveness remains unknown. REA does not kill the target. Remote discovery retains its observations and reports a separate cleanup error if the temporary connection cannot be removed.
+Frida scripts execute inside the selected target with that process's memory, filesystem, and network authority. Message capture is bounded to 16 MiB and reports truncation. Script source is not stored in Evidence; its selected path and SHA-256 digest are. Evidence retains non-authentication target selection such as device or endpoint, mode, program or PID, and spawn arguments. Authentication values remain excluded. Results report the observed session and messages, while unsupported platform metadata remains unknown. A request cancelled before a Frida operation starts has no provider effects. If cancellation arrives during a native Frida call, REA waits for that call to return, then closes any session it created and returns the observed session as a partial observation. A cancelled script load on an existing session releases only its new script before queued operations can run; the session and previously loaded scripts remain available. Once the provider commits a successful load, later request cancellation does not unload that script; use `unload_frida_script` to release it. If script cleanup fails, the script remains owned and a paused target cannot resume until the pending instrumentation is released. Queued script loads, resumes, and unloads check cancellation before dispatching native effects. Cancellation Evidence separately reports whether a target resume was observed, whether instrumentation cleanup completed, and that final target liveness remains unknown. REA does not kill the target. Remote discovery retains its observations and reports a separate cleanup error if the temporary connection cannot be removed.
The [Frida Core API](https://frida.re/docs/frida-core-api/) documents device management, sessions, and script loading; the [JavaScript API](https://frida.re/docs/javascript-api/) documents target-side instrumentation. [Luma](https://luma.frida.re/) is Frida's standalone GUI; REA does not automate or control it. `npm run verify:frida` requires a Linux host with the optional Frida binding and uses a short-lived local Node.js fixture created by the verifier. Add `-- --address HOST:PORT --pid PID` to run the separate remote attach lane against a caller-selected target. Mock tests do not establish real device/server compatibility.
diff --git a/docs/ghidra-ppc.md b/docs/ghidra-ppc.md
new file mode 100644
index 000000000..034158378
--- /dev/null
+++ b/docs/ghidra-ppc.md
@@ -0,0 +1,90 @@
+# PowerPC ELF analysis through Ghidra
+
+This lane extends native ELF analysis to 32-bit big-endian PowerPC; it does
+not execute PowerPC code. It is the first milestone of
+[#1741](https://github.com/morluto/rea/issues/1741). GameCube/Wii DOL
+executables, PPC64 and other PowerPC variants are separate follow-ups.
+
+## Admission boundary
+
+ELF `EM_PPC` (20) is retained as the provider-neutral `powerpc` family. The
+target also retains class, byte order, `e_type` and raw `e_flags`. Recognition
+does not mean every provider or PowerPC variant is supported. `EM_PPC64` (21)
+is not recognized as this family.
+
+The Ghidra lane admits ELF32 `ET_EXEC`, big endian. ELF64, little-endian,
+relocatable and shared objects are refused with a specific unsupported
+constraint. `EF_PPC_EMB` declares an embedded variant and is refused; the
+remaining `e_flags` bits only declare relocation conventions and do not change
+instruction decoding.
+
+## Bound language
+
+Ghidra's ELF loader selects `PowerPC:BE:32:e500:default` for a plain `EM_PPC`
+header with `e_flags` 0. e500 is the embedded Book-E variant whose SPE unit
+replaces the classic floating-point unit, so it misinterprets ordinary
+750-family code. Nothing in the header distinguishes the two.
+
+The lane therefore imports with `PowerPC:BE:32:default` and compiler spec
+`default`, commits that choice in the analysis profile as
+`language_selection: "provider-pinned-v1"`, and checks the language Ghidra
+reports after import. A different language set through
+`REA_GHIDRA_LANGUAGE_ID` (and optionally `REA_GHIDRA_COMPILER_SPEC_ID`)
+replaces the lane default and is admitted against the installed language
+catalog as usual; for example `PowerPC:BE:32:e500` for genuine e500 code.
+
+PowerPC header facts are committed in the analysis profile so interpretation
+changes invalidate profile-bound snapshots. The support-lane identity is
+versioned independently of file identity. Evidence, lifecycle outputs and saved
+snapshots preserve the `powerpc` family. Hopper and IDA adapters do not
+implicitly gain PowerPC support. Windows Ghidra P0 remains PE x86/x86-64 only.
+
+## Use
+
+With a caller-installed Ghidra and compatible JDK on a supported Linux/macOS host:
+
+```bash
+rea analyze /absolute/path/to/ppc.elf --provider ghidra --json
+rea function /absolute/path/to/ppc.elf function_name --provider ghidra --json
+```
+
+MCP uses the existing tools, including `open_binary` with an absolute path and
+`provider_id: "ghidra"`. No PowerPC-specific tool catalog or emulator is
+introduced.
+
+## Verification
+
+The source-owned freestanding fixture is `tests/conformance/c/ppc.c`. The
+optional cross-target lane requires a caller-supplied Clang with the PowerPC
+target, LLD, GNU `readelf`, Ghidra and its compatible JDK; ordinary host-native
+checks do not acquire these tools. `REA_PPC_CLANG` selects Clang and
+`REA_PPC_READELF` selects GNU readelf (for example
+`/opt/homebrew/opt/binutils/bin/readelf` on macOS). These are test-time
+prerequisites, not REA runtime providers.
+
+```bash
+GHIDRA_INSTALL_DIR=/absolute/path/to/ghidra npm run verify:ghidra:ppc
+```
+
+The lane compiles the fixture for a 750-family core (`-mcpu=750`) and never
+executes it. GNU readelf independently supplies header facts and symbol
+addresses, compared with the production REA target resolver before provider
+startup. Its version, bounded command output and selected tool path are
+retained in the verification report. The parser deliberately recognizes only
+this fixture's GNU output, rejecting missing, ambiguous or unexpected fields
+rather than substituting defaults.
+
+Existing public MCP operations check Ghidra's **observed** language and
+compiler specification and loaded-file hashes; an e500 import fails the lane.
+The source-owned `ppc-probe.S` supplies fixed `addi` and conditional-branch
+encodings: verification checks bytes, length, immediate and direct destination,
+independent of decompiler variable names. Global and string bytes are checked at
+independently reported symbol addresses.
+
+CLI/MCP discovery and decompilation, direct calls, target/profile/Evidence
+identity, snapshot round-trip and final process cleanup remain covered.
+Pseudocode presence is a liveness check, not proof of semantic equivalence.
+Instruction decoding and reader agreement do not establish runtime or complete
+floating-point semantics. Parser/profile tests are narrower checks, not
+substitutes for this real-provider run. Report the actual lanes run and leave
+unexecuted verification pending.
diff --git a/docs/index.md b/docs/index.md
index 732ad9f05..9e0912dd1 100644
--- a/docs/index.md
+++ b/docs/index.md
@@ -49,7 +49,7 @@ For runtime instrumentation, see the [Frida guide](./frida-instrumentation.md).
| Task | Guides |
| --------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
-| Inspect native functions, references, or types | [Native binaries](native-investigation.md), [Go metadata](go-binaries.md), [offline ELF layout](binary-diagnostics.md), [recorded crashes](recorded-crashes.md) |
+| Inspect native functions, references, or types | [Native binaries](native-investigation.md), [Go metadata](go-binaries.md), [Rust dependencies](rust-binaries.md), [offline ELF layout](binary-diagnostics.md), [recorded crashes](recorded-crashes.md) |
| Select a native provider or import format | [Ghidra semantics](ghidra-provider.md), [Windows](windows-ghidra-p0.md), [DOS](ghidra-dos.md), [raw bytes](raw-binary.md), [MIPS](ghidra-mips.md), [PSP](ghidra-psp.md), [NativeAOT](ghidra-nativeaot.md), [IDA](ida-provider.md), [JEB](jeb-analysis.md) |
| Analyze JavaScript or Electron artifacts | [Static analysis](javascript-artifact-reconstruction.md), [tracing and comparisons](javascript-application-workflows.md), [application graph](javascript-application-graph.md), [source recovery](javascript-recovery.md) |
| Compare static JavaScript with runtime observations | [Runtime attribution](javascript-runtime-observation.md), [reconciliation](javascript-runtime-reconciliation.md), [Electron observation](electron-observation.md) |
diff --git a/docs/installation.md b/docs/installation.md
index a39471585..38c4d7966 100644
--- a/docs/installation.md
+++ b/docs/installation.md
@@ -288,11 +288,12 @@ hosted connector; see [its configuration](client-configuration.md#other-client-s
| Qoder | `qoder` |
| Factory Droid | `droid` |
| Trae | `trae` |
+| WorkBuddy | `workbuddy` |
| ZCode | `zcode` |
| DeepSeek Harness | `dsh` |
-`dsh` and `kimi` require a release containing them. The registry check above does
-not cover them.
+`dsh`, `kimi`, and `workbuddy` require a release containing them. The registry
+check above does not cover them.
See [client configuration and troubleshooting](client-configuration.md) for
configuration paths, profiles, trust policies, and schema-limit workarounds.
@@ -493,7 +494,13 @@ copying license secrets into logs, or killing unrelated Hopper processes.
REA connects to an existing Ghidra installation on Linux x64/arm64, macOS x64/arm64,
or experimental Windows x64 P0.
It accepts Ghidra 12.1.x and the 64-bit full JDK declared by that installation's
-`application.java.min` and `application.java.max`. Current 12.1 releases require
+`application.java.min` and `application.java.max`. The JDK's reported platform must also
+map to the same native decompiler directory as the host: a JDK running under
+emulation (for example x86_64 under Rosetta on Apple silicon) or reporting a
+non-host `os.name` fails the doctor
+even when it is an otherwise accepted 64-bit full JDK. Select a matching JDK
+through `JAVA_HOME`; adding another decompiler in the JVM-selected directory
+does not satisfy this host-validated admission rule. Current 12.1 releases require
JDK 21 or newer and set no maximum. The bridge is verified with Ghidra 12.1.4
and JDK 21. Each installation must include the native decompiler for the host
architecture in `Ghidra/Features/Decompiler/os//` or the corresponding
@@ -556,6 +563,11 @@ registrations. See [Windows Ghidra P0](windows-ghidra-p0.md) for provider diagno
Doctor validates the platform, architecture, Ghidra 12.1.x application version,
`support/analyzeHeadless` or `support/analyzeHeadless.bat`, the installation's
Java major range, 64-bit JDK bitness, and the presence of `javac`/`javac.exe`.
+It also checks that the reported JVM `os.arch` and `os.name` map to the same native decompiler
+directory validated for the host; unrecognized reported architectures or a
+non-host operating system are refused. LaunchSupport overrides of either
+property, including through helper-environment JVM option variables, are
+rejected at launch.
When Java is found through `PATH`, setup records its observed JDK home so GUI
MCP clients do not depend on an incidental shell path. Setup shows every exact
environment entry in its plan, writes only after approval, and never downloads,
@@ -764,6 +776,9 @@ errors expose Error names, messages, and available codes under
truncation markers. Darwin ownership-helper compilation failures also retain
the compiler outcome, exit code or signal, and bounded stderr; a timed-out
compiler remains distinguishable from a compiler that exited with an error.
+The helper compile deadline defaults to 120,000 ms; set
+`REA_SWIFTC_COMPILE_TIMEOUT_MS` to an integer between 1 and 2,147,483,647 when
+a busy host needs longer.
Other rejection values retain their primitive value or an explicit type.
Shutdown warnings include the failure
kind, message, and diagnostics, while process and temporary-project cleanup
diff --git a/docs/managed-code-analysis.md b/docs/managed-code-analysis.md
index 1d1a579e6..05782b4a2 100644
--- a/docs/managed-code-analysis.md
+++ b/docs/managed-code-analysis.md
@@ -139,14 +139,40 @@ The result preserves the requested names in `selection.type_names` and reports
unmatched names separately. If metadata names are unreadable, limitations retain
that uncertainty. With no selection, `selection` is null and whole-assembly
inspection keeps its existing resource policy. Selection restricts types,
-fields, methods, call edges and field accesses; member references remain an
-assembly-wide inventory, so output size is not guaranteed to be small.
+fields, methods, call edges, field accesses and user strings; member references
+remain an assembly-wide inventory, so output size is not guaranteed to be small.
Coverage describes the selected members. Comparisons leave members outside the
opposite observation's selection unknown, and application graphs report their
selected scope as partial. Neither makes an absence claim about the whole
assembly. Older member Evidence without `selection` remains readable.
+### User strings
+
+`ldstr` anchors keep their build-local `0x70` tokens. `user_strings.literals`
+lists each distinct token loaded by an inspected method body once, in token
+order, with its `#US` file offset and decoded value. Values keep their stored
+UTF-16 code units, including unpaired surrogates. Token `0x70000000` names the
+heap's empty entry at offset 0 and reads as `""`; any other item there is
+malformed. The terminal flag is checked against ECMA-335 II.24.2.4, and the
+length prefix against the width II.23.2 assigns. A difference is reported with
+its byte offset as a user-string issue while preserving the readable literal.
+Toolchain conventions can differ; this conformance observation does not
+establish runtime rejection. A missing `#US` stream is one stream-level issue;
+a malformed item is reported against its token. User strings carry their own
+coverage: any of these makes `user_strings.state` partial and lists the issue
+in `user_strings.issues`, while the member `coverage` and `metadata.status` are
+unaffected, and readable literals remain. The state is also partial when an
+inspected method's CIL was not fully decoded (a malformed exception section
+alone does not count) or a method that may belong to the inspected types was
+not inspected (its row could not be read, or its type's name could not be read
+under a `type_names` selection), because `ldstr` instructions there were not
+observed. Normalized CIL digests do not include decoded values.
+`compare_managed_members` does not decode or compare literals: an unchanged
+method does not establish unchanged strings, and the comparison says so in its
+limitations. Older member Evidence without `user_strings` reads as `null`: not
+examined.
+
### Edge target declaring types
`member_refs[].parent_name`, `call_edges[].target_declaring_type`, and
@@ -183,21 +209,34 @@ native/runtime questions retain their own authority and coverage.
Names and semantic labels do not establish durable member identity. Comparison
preserves observed differences, structural inferences, and unknowns separately.
-Cross-version matching first prefers exact CIL/signature identity. When that
-does not match, it pairs an exact declared type, method name, and raw signature
-before trying structural body shape. The exact-signature key uses names only
-as part of that full tuple; names alone never select a pair. Duplicate tuples
-remain ambiguous rather than being paired by token order. Fields follow the
-same exact tiers. The raw signature is exact whether or not REA decoded it, so
-undecoded signatures pair by that tuple too, but they never enter structural
-rounds. An unmatched member is therefore reported as `unknown`, not added or
-removed, when its own signature was not decoded or when the other side has an
-unpaired member, one-sided or ambiguous, with the same declared type and name
-whose signature was not decoded.
+Cross-version matching first pairs members of the same declared type and name:
+methods by exact CIL/signature identity, then by raw signature, then by
+structural body shape; fields by raw signature. Exact CIL/signature identity
+requires equal raw CIL digests and decoded fingerprints. Equal decoded
+fingerprints alone cannot establish an exact match: they omit switch targets
+and collapse distinct NaN encodings. Only then may a remap pair a
+renamed or moved member across declared types or names: a method by exact
+CIL/signature identity or structural body shape, a field by exact decoded
+signature. A remap never takes a member whose own declared type and name remain
+unpaired on the other side, so a member is never taken from its own changed
+counterpart. A member whose declaring type is unresolved could belong to any
+type, so it pairs by name only when no other member shares its exact
+CIL/signature key, never by structural shape alone, and any remap group it
+joins stays ambiguous. The exact-signature key uses names only as part of that
+full tuple; names alone never select a pair. Duplicate tuples remain ambiguous
+rather than being paired by token order. The raw signature is exact whether or
+not REA decoded it, so undecoded signatures pair by that tuple too, but they
+never enter structural rounds. An unmatched member is therefore reported as
+`unknown`, not added or removed, when its own signature was not decoded or when
+the other side has an unpaired member, one-sided or ambiguous, with the same
+declared type and name whose signature was not decoded.
For a matched method, an unavailable or partial body makes body-shape facets
-unknown while preserving observed signature differences. Structural identity
-compares normalized signatures, the limited decoded-CIL tuple fingerprint,
+unknown while preserving observed signature differences. For complete bodies,
+a difference in either the raw CIL digest or the decoded fingerprint is reported
+in the `cil` dimension, including reordered switch targets with equal decoded
+fingerprints. Structural identity compares normalized signatures, the limited
+decoded-CIL tuple fingerprint,
constants, and bounded shape context. It reports all candidates at the winning
score and remains ambiguous when the evidence does not distinguish them. The
digest does not itself remap metadata tokens.
@@ -334,8 +373,8 @@ Opcode names retain short/long forms and prefix opcodes. This makes the value a
reproducible decoded-tuple fingerprint, not a canonical semantic CIL identity:
it does not resolve tokens across MVIDs, does not commit full control flow or
exception semantics, and does not replace the raw CIL digest. Cross-build
-comparison uses the value only as one unique-only structural tier and reports
-ambiguity when that evidence is insufficient.
+comparison requires equal raw CIL digests as well for exact CIL/signature
+matches and reports ambiguity when structural evidence is insufficient.
For example, a decoded `nop; ret` body serializes exactly as
`[["nop","none",null],["ret","none",null]]` and has digest
diff --git a/docs/mcp-contracts.md b/docs/mcp-contracts.md
index 7052a0372..05d92972a 100644
--- a/docs/mcp-contracts.md
+++ b/docs/mcp-contracts.md
@@ -21,6 +21,31 @@ directly to `compare_functions`, and `inspect_artifact` Evidence to
`compare_artifacts`. Use `get_evidence_bundle` when the task needs broader
retained session history or an explicit bundle for transfer.
+Artifact inventories declare `coverage.scope` (`root` or `container-entries`)
+and `coverage.status` (`complete` or `unknown`) independently of diagnostic
+wording. Comparison requires matching scopes, complete graph members and complete
+traversal coverage before concluding equality. A fully observed root without
+container children can be `unchanged`; its scope notice stays in `limitations`
+without creating a gap. Content and executable metadata changes still produce
+root deltas. Missing graph members yield `truncated`; unknown traversal coverage
+or unavailable occurrence hashes retain uncertainty. Integrity mismatches remain
+contradictions. Static artifact equality does not establish unchanged runtime
+behavior.
+
+Exhausted traversals with deliberately omitted filesystem entries retain
+`unknown` coverage and identify each omitted path/type in `limitations`; FIFO,
+socket and device entries are not opened. Case-distinct archive members remain
+separate observations. Their case-insensitive materialization diagnostic does
+not reduce traversal coverage when both spellings were read.
+
+Current output also retains `coverage_status` (`complete` or `partial`) for existing clients, derived from the scoped traversal status. The owning parser normalizes retained `coverage_status` records to scoped coverage and rejects conflicting declarations.
+
+The coverage field is optional when reading older retained Evidence. The owning
+inventory parser recognizes historical REA scope notices for compatibility;
+unfamiliar legacy limitations remain conservative. Typed records do not derive
+coverage from limitation text. Pages from one inventory must carry the same
+coverage declaration.
+
Function dossiers can declare per-facet coverage in the optional
`normalized_result.facet_availability` map. Each supplied facet is either
`{ "available": true }` or `{ "available": false, "reason": "..." }`.
@@ -33,7 +58,13 @@ side declares a required facet unavailable. An empty legacy facet without a
coverage declaration is also unknown. A producer that actually observed an
empty facet can declare it available so observed absence remains comparable.
Call comparison additionally retains the Evidence relationship coverage
-checks. IDA declares the facets its selected adapter cannot provide; an empty
+checks. Ghidra and Hopper declare their observed dossier facets and complete
+provider-observed caller/callee sets, including empty sets. Missing decompiler
+output remains unavailable; complete call coverage does not claim every runtime
+target. Hopper's unavailable reference-kind metadata and reported unresolved
+calls still prevent equality conclusions for those dimensions. Analysis profiles
+version these declarations so older snapshots are not reused as current results.
+IDA declares the facets its selected adapter cannot provide; an empty
unsupported array does not prove observed absence.
Evidence and bundle imports reject unsupported fields in their envelopes,
identities, manifests and other fixed metadata before authentication. Imports
@@ -486,6 +517,7 @@ These deadlines have different owners:
| MCP initialize | The client bounds transport/REA connection startup, before any target query. |
| Hopper startup | REA allows 120,000 ms by default; `REA_HOPPER_STARTUP_TIMEOUT_MS` accepts decimal integers from 1–2,147,483,647 ms. Invalid values fail configuration validation. Startup failure is reported by the first provider query. |
| Ghidra startup | REA allows 330,000 ms by default; `REA_GHIDRA_STARTUP_TIMEOUT_MS` accepts decimal integers from 1–2,147,483,647 ms. Invalid supplied values fail configuration validation. Startup failure is reported by the first provider query. |
+| Darwin swiftc helper | REA allows 120,000 ms by default for ownership-helper compilation; `REA_SWIFTC_COMPILE_TIMEOUT_MS` accepts decimal integers from 1–2,147,483,647 ms. Invalid values fail configuration validation. |
| Individual tool call | The client bounds its wait, including cold engine startup. The pinned client SDK 2.3.1 defaults to 60,000 ms and can cancel earlier than REA's startup deadline. |
For an already connected client using the pinned SDK, request options are the
diff --git a/docs/readme/README_ar.md b/docs/readme/README_ar.md
index 8df2522bc..64e963c91 100644
--- a/docs/readme/README_ar.md
+++ b/docs/readme/README_ar.md
@@ -1,6 +1,7 @@
[English](../../README.md) · [简体中文](README_zh.md) · [繁體中文](README_zh-TW.md) · [日本語](README_ja.md) · [한국어](README_ko.md) · [Türkçe](README_tr.md) · [Русский](README_ru.md) · [Tiếng Việt](README_vi.md) · [ไทย](README_th.md) · [Deutsch](README_de.md) · [Español](README_es.md) · [Français](README_fr.md) · [Українська](README_uk.md) · [Polski](README_pl.md) · [Português (Brasil)](README_pt-BR.md) · **العربية** · [فارسی](README_fa.md) · [Bahasa Indonesia](README_id.md) · [Italiano](README_it.md)
+· [Ελληνικά](README_el.md)
# REA: هندسة عكسية لأي شيء
@@ -19,10 +20,14 @@
**[الموقع](https://rea.tools/ar/) · [الأدلة](https://rea.tools/ar/guides/) · [دراسات الحالة](https://rea.tools/ar/showcase/)**
-[البدء السريع](#البدء-السريع) · [كيف يعمل REA](#كيف-يعمل-rea) · [ما الذي يمكنك تحليله](#ما-الذي-يمكنك-تحليله) · [دراسات الحالة](#دراسات-الحالة) · [الأسئلة الشائعة](#الأسئلة-الشائعة) · [التوثيق](#التوثيق)
+[البدء السريع](#البدء-السريع) · [كيف يعمل REA](#كيف-يعمل-rea) · [ما الذي يمكنك تحليله](#ما-الذي-يمكنك-تحليله) · [دراسات الحالة](#دراسات-الحالة) · [الأسئلة الشائعة](#الأسئلة-الشائعة) · [أدوات GNU objdump وRizin](../reverse-engineering-tools.md) · [التوثيق](#التوثيق)
npx rea-agents setup
+> [!IMPORTANT]
+> المصادر الرسمية لـ REA: [morluto/rea](https://github.com/morluto/rea), [rea.tools](https://rea.tools/), [rea-agents](https://www.npmjs.com/package/rea-agents) (npm).
+> استخدم روابط التثبيت والإصدارات المنشورة في هذه المصادر الرسمية. تشابه اسم المستودع أو نسخ ملف README لا يثبت أن التنزيل رسمي.
+
@@ -135,6 +140,7 @@ npx rea-agents@latest setup
| الملفات الثنائية للشيفرة الأصلية | شيفرة شبه برمجية، وتعليمات تجميع، وسلاسل نصية، ورموز، واستدعاءات، ومراجع | Hopper أو Ghidra أو IDA؛ [تحليل الشيفرة الأصلية](https://rea.tools/ar/guides/native/) |
| بنية ELF دون تشغيل | الأقسام والمقاطع والرموز ومعلومات إعادة التموضع الأصلية، وآليات الحماية المحتملة المستدلّ عليها بالفحص الثابت | pwntools يوفّره المستدعي على Linux x64؛ [تشخيص الملفات الثنائية](../binary-diagnostics.md) |
| شيفرة EVM البايتية | محددات التوجيه، وإزاحات البايتات، والمعاملات المستنتجة، وقابلية تغيير الحالة | مدخل محلي يحمل بايتات خامًا أو تمثيلًا سداسيًا عشريًا على Linux x64؛ [دليل EVM دون تشغيل](../evm-bytecode.md) |
+| Rust binaries | Embedded cargo-auditable dependency list | [Rust binaries](../rust-binaries.md) |
| أعطال Linux المسجّلة | سجلات note الخام، ومسجّلات المعالج/الإشارات لكل خيط مسجّل، وترشيحات اختيارية لخرائط الذاكرة | pwntools يوفّره المستدعي على Linux x64؛ GDB/pwndbg اختياريان؛ [الأعطال المسجّلة](../recorded-crashes.md) |
| JavaScript / Electron | الوحدات، وعمليات الاستيراد، وخرائط المصدر، والمسارات، وIPC، وعلاقات إضافات الشيفرة الأصلية | Node.js وnpm؛ [تحليل التطبيقات](https://rea.tools/ar/guides/javascript/) |
| المواقع | بنية الصفحة، والسكربتات، وملاحظات الشبكة، ولقطات الشاشة المطلوبة | متصفح من عائلة Chrome؛ [تحليل المتصفح](https://rea.tools/ar/guides/browser/) |
diff --git a/docs/readme/README_de.md b/docs/readme/README_de.md
index 2dbb1d41a..aee634f06 100644
--- a/docs/readme/README_de.md
+++ b/docs/readme/README_de.md
@@ -1,6 +1,7 @@
[English](../../README.md) · [简体中文](README_zh.md) · [繁體中文](README_zh-TW.md) · [日本語](README_ja.md) · [한국어](README_ko.md) · [Türkçe](README_tr.md) · [Русский](README_ru.md) · [Tiếng Việt](README_vi.md) · [ไทย](README_th.md) · **Deutsch** · [Español](README_es.md) · [Français](README_fr.md) · [Українська](README_uk.md) · [Polski](README_pl.md) · [Português (Brasil)](README_pt-BR.md) · [العربية](README_ar.md) · [فارسی](README_fa.md) · [Bahasa Indonesia](README_id.md) · [Italiano](README_it.md)
+· [Ελληνικά](README_el.md)
# REA: Alles reverse-engineeren
@@ -19,10 +20,14 @@
**[Website](https://rea.tools/de/) · [Anleitungen](https://rea.tools/de/guides/) · [Fallstudien](https://rea.tools/de/showcase/)**
-[Schnellstart](#schnellstart) · [So funktioniert REA](#so-funktioniert-rea) · [Was du analysieren kannst](#was-du-analysieren-kannst) · [Fallstudien](#fallstudien) · [Häufige Fragen](#häufige-fragen) · [Dokumentation](#dokumentation)
+[Schnellstart](#schnellstart) · [So funktioniert REA](#so-funktioniert-rea) · [Was du analysieren kannst](#was-du-analysieren-kannst) · [Fallstudien](#fallstudien) · [Häufige Fragen](#häufige-fragen) · [GNU-objdump- und Rizin-Tools](../reverse-engineering-tools.md) · [Dokumentation](#dokumentation)
npx rea-agents setup
+> [!IMPORTANT]
+> Offizielle Quellen für REA: [morluto/rea](https://github.com/morluto/rea), [rea.tools](https://rea.tools/), [rea-agents](https://www.npmjs.com/package/rea-agents) (npm).
+> Folge den Installations- und Release-Links aus diesen offiziellen Quellen. Ein ähnlicher Repository-Name oder eine kopierte README belegt nicht, dass ein Download offiziell ist.
+
@@ -135,6 +140,7 @@ REA benötigt Node.js 22.x (>=22.19), 24.x (>=24.11) oder 26+ sowie npm. Zusätz
| Native Binärdateien | Pseudocode, Assemblercode, Zeichenketten, Symbole, Aufrufe und Verweise | Hopper, Ghidra oder IDA; [native Analyse](https://rea.tools/de/guides/native/) |
| Offline-ELF-Struktur | Sektionen, Segmente, ursprüngliche Symbole/Relokationen und mögliche Schutzmaßnahmen aus statischer Analyse | Vom Aufrufer bereitgestelltes pwntools unter Linux x64; [Binärdiagnostik](../binary-diagnostics.md) |
| EVM-Bytecode | Dispatch-Selektoren, Byte-Offsets, abgeleitete Argumente und Veränderbarkeit des Zustands | Lokale Eingabe mit Rohbytes oder Hexdarstellung unter Linux x64; [Offline-EVM-Leitfaden](../evm-bytecode.md) |
+| Rust binaries | Embedded cargo-auditable dependency list | [Rust binaries](../rust-binaries.md) |
| Aufgezeichnete Linux-Abstürze | Unverarbeitete Note-Einträge, Register/Signale aller aufgezeichneten Threads und optionale Zuordnungskandidaten | Vom Aufrufer bereitgestelltes pwntools unter Linux x64; optional GDB/pwndbg; [aufgezeichnete Abstürze](../recorded-crashes.md) |
| JavaScript / Electron | Module, Imports, Source Maps, Routen, IPC und Beziehungen zu nativen Add-ons | Node.js und npm; [Anwendungsanalyse](https://rea.tools/de/guides/javascript/) |
| Websites | Seitenstruktur, Skripte, Netzwerkbeobachtungen und angeforderte Screenshots | Ein Browser aus der Chrome-Familie; [Browseranalyse](https://rea.tools/de/guides/browser/) |
diff --git a/docs/readme/README_el.md b/docs/readme/README_el.md
new file mode 100644
index 000000000..699a3ebdc
--- /dev/null
+++ b/docs/readme/README_el.md
@@ -0,0 +1,316 @@
+
+
+[English](../../README.md) · [简体中文](README_zh.md) · [繁體中文](README_zh-TW.md) · [日本語](README_ja.md) · [한국어](README_ko.md) · [Türkçe](README_tr.md) · [Русский](README_ru.md) · [Tiếng Việt](README_vi.md) · [ไทย](README_th.md) · [Deutsch](README_de.md) · [Español](README_es.md) · [Français](README_fr.md) · [Українська](README_uk.md) · [Polski](README_pl.md) · [Português (Brasil)](README_pt-BR.md) · [العربية](README_ar.md) · [فارسی](README_fa.md) · [Bahasa Indonesia](README_id.md) · [Italiano](README_it.md) · **Ελληνικά**
+
+# REA: Reverse Engineer Anything
+
+### Ένα MCP για αντίστροφη μηχανική δυαδικών αρχείων, εφαρμογών και της συμπεριφοράς τους κατά την εκτέλεση.
+
+**Βρήκες μια λειτουργία που σου αρέσει; Μάθε πώς λειτουργεί, μέχρι το επίπεδο του δυαδικού κώδικα.**
+
+[](https://www.npmjs.com/package/rea-agents)
+[](../mcp-contracts.md#generated-catalog)
+[](#τι-μπορείς-να-αναλύσεις)
+[](https://skills.sh/morluto/rea/reverse-engineer-anything)
+[](../../LICENSE)
+[](https://discord.gg/GkcryMnJDM)
+
+
+
+**[Ιστότοπος](https://rea.tools/) · [Οδηγοί](https://rea.tools/guides/) · [Μελέτες περίπτωσης](https://rea.tools/showcase/)**
+
+[Γρήγορη εκκίνηση](#γρήγορη-εκκίνηση) · [Πώς λειτουργεί το REA](#πώς-λειτουργεί-το-rea) · [Τι μπορείς να αναλύσεις](#τι-μπορείς-να-αναλύσεις) · [Μελέτες περίπτωσης](#μελέτες-περίπτωσης) · [Συχνές ερωτήσεις](#συχνές-ερωτήσεις) · [Τεκμηρίωση](#τεκμηρίωση)
+
+npx rea-agents setup
+
+> [!IMPORTANT]
+> Κατέβασε το REA από το [morluto/rea](https://github.com/morluto/rea),
+> το [rea.tools](https://rea.tools/) ή το [πακέτο rea-agents στο npm](https://www.npmjs.com/package/rea-agents).
+> Ακολούθησε τους συνδέσμους εγκατάστασης και εκδόσεων από αυτές τις επίσημες πηγές.
+> Ένα παρόμοιο όνομα αποθετηρίου ή ένα αντιγραμμένο README δεν αποδεικνύει ότι
+> ένα αρχείο προς λήψη προέρχεται από επίσημη πηγή.
+
+
+
+
+
+
+
+---
+
+Είδες σε μια εφαρμογή μια λειτουργία που θα ήθελες στο δικό σου προϊόν; Ζήτησε από τον πράκτορά σου (agent) να τη διερευνήσει με το REA. Μπορεί να εξετάσει την εφαρμογή χωρίς τον πηγαίο κώδικά της, να εξηγήσει πώς λειτουργεί η συγκεκριμένη δυνατότητα, να παρουσιάσει τα τεκμήρια και να υλοποιήσει μια αντίστοιχη έκδοση για το έργο σου.
+
+Το REA συνδέει τον πράκτορά σου με εργαλεία εξέτασης δυαδικών αρχείων με κώδικα μηχανής, εφαρμογών JavaScript και Electron, assemblies .NET και ιστοτόπων. Μπορείς να χρησιμοποιήσεις τα ίδια εργαλεία και από το τερματικό. Η ανάλυση εκτελείται τοπικά και τα αποτελέσματα περιλαμβάνουν τα τεκμήρια και τους περιορισμούς στους οποίους βασίζεται κάθε συμπέρασμα.
+
+Η διαδικασία εγκατάστασης καταχωρίζει το REA στον agent σου και εγκαθιστά τις αντίστοιχες οδηγίες εργασίας. Για ανάλυση κώδικα μηχανής μπορείς να χρησιμοποιήσεις μια υπάρχουσα εγκατάσταση του Hopper, του Ghidra ή του IDA. Η διαδικασία εγκατάστασης μπορεί επίσης να εγκαταστήσει το Hopper, εφόσον το εγκρίνεις. Η στατική ανάλυση JavaScript δεν χρειάζεται μηχανή ανάλυσης κώδικα μηχανής.
+
+> **[Επισκέψου τον ιστότοπο του REA](https://rea.tools/)** για οδηγίες εγκατάστασης, εικονογραφημένους οδηγούς και πραγματικές μελέτες περίπτωσης.
+
+## Γρήγορη εκκίνηση
+
+### Ρύθμιση του agent σου
+
+Αφού εγκαταστήσεις το Node.js και το npm, εκτέλεσε:
+
+```bash
+npx rea-agents setup
+```
+
+Επίλεξε τους agents σου, έλεγξε τις προτεινόμενες αλλαγές και ενέκρινέ τες. Η διαδικασία προσθέτει τον MCP server του REA και τις αντίστοιχες οδηγίες εργασίας, κρατώντας αντίγραφα ασφαλείας των υφιστάμενων ρυθμίσεων. Στη συνέχεια, επανεκκίνησε τον agent σου.
+
+Υποστηρίζονται τα Claude Code, Codex, Cursor, Gemini CLI, Grok Build και [άλλοι agents](../installation.md#supported-agents). Για τη ρύθμιση των παρόχων ανάλυσης και τη μη αυτόματη καταχώριση MCP, δες τον οδηγό [εγκατάστασης και ρύθμισης](../installation.md).
+
+### Ζήτησε από τον agent σου
+
+```text
+Διερεύνησε πώς λειτουργεί η αναζήτηση στην εφαρμογή Notes, δείξε μου τα τεκμήρια και υλοποίησε μια παρόμοια λειτουργία για το έργο μου.
+```
+
+Αντικατάστησε το Notes με την εφαρμογή που θέλεις να εξετάσεις και προσδιόρισε τη λειτουργία που θέλεις να κατανοήσεις.
+
+### Χρήση από το τερματικό
+
+Εξέτασε τον αποσυμπιεσμένο φάκελο μιας εφαρμογής JavaScript/Electron ή ένα αρχείο ASAR:
+
+```bash
+npx -y rea-agents@latest analyze-javascript-application /absolute/path/to/app --json
+```
+
+Το αποτέλεσμα περιλαμβάνει modules, εισαγωγές, τα όρια μεταξύ των μερών της εφαρμογής Electron και τα αντίστοιχα τεκμήρια. Αντικατάστησε τη διαδρομή με τη δική σου, για παράδειγμα `"D:/apps/example"` στα Windows.
+
+Για να εγκαταστήσεις την εντολή `rea` για τακτική χρήση:
+
+```bash
+npm install --global rea-agents
+rea --help
+```
+
+Για ανάλυση κώδικα μηχανής, ρύθμισε πρώτα έναν πάροχο ανάλυσης. Ο [οδηγός CLI και Evidence](../cli.md) περιγράφει τις σχετικές εντολές, την επιλογή παρόχου, τα στιγμιότυπα και τη χρήση σε scripts.
+
+### Ενημέρωση του REA
+
+Το REA εξελίσσεται γρήγορα και οι νέες εκδόσεις περιλαμβάνουν συχνές διορθώσεις σφαλμάτων. Διατήρησε την εγκατάστασή σου ενημερωμένη.
+
+Για CLI εγκατεστημένο με `npm install --global` ή `bun add --global`:
+
+```bash
+rea update
+```
+
+Οι ενημερώσεις καθολικής εγκατάστασης με Bun απαιτούν Bun 1.2.17 ή νεότερο. Αν χρησιμοποιείς παλαιότερη έκδοση, αναβάθμισε το Bun χειροκίνητα πριν εκτελέσεις το `rea update`.
+
+Για να ανανεώσεις τις καταχωρίσεις των agents και το skill, εκτέλεσε την εντολή ρύθμισης που εμφανίζει η ενημέρωση.
+
+Αν χρησιμοποιείς `npx`, ενημέρωσε τη ρύθμιση του agent με:
+
+```bash
+npx rea-agents@latest setup
+```
+
+Έλεγξε τις αλλαγές και επανεκκίνησε τον agent σου. Για μεμονωμένες εντολές CLI, χρησιμοποίησε `npx rea-agents@latest` και στη συνέχεια την εντολή που θέλεις. Με Bun, χρησιμοποίησε `bunx rea-agents@latest` αντί για `npx rea-agents@latest`.
+
+## Πώς λειτουργεί το REA
+
+Ο agent σου καλεί το REA μέσω MCP για να εξετάσει τον στόχο και να ακολουθήσει τον σχετικό κώδικα. Το REA επιστρέφει ευρήματα μαζί με τα τεκμήριά τους. Ο agent τα χρησιμοποιεί για να θέσει πρόσθετες ερωτήσεις, να εξηγήσει τη συμπεριφορά ή να γράψει και να δοκιμάσει μια υλοποίηση. Οι εντολές CLI χρησιμοποιούν τις ίδιες ροές εργασίας.
+
+
+
+[Άνοιγμα του διαγράμματος σε πλήρες μέγεθος](../../website/public/assets/figures/rea-investigation-flow.svg).
+
+
+
+## Τι μπορείς να αναλύσεις
+
+Το REA απαιτεί Node.js 22.x (>=22.19), 24.x (>=24.11) ή 26+, καθώς και npm. Τα πρόσθετα εργαλεία και τα υποστηριζόμενα συστήματα εξαρτώνται από τον στόχο:
+
+| Στόχος | Τι επιστρέφει το REA | Απαιτήσεις και οδηγός |
+| -------------------------------------- | --------------------------------------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------- |
+| Δυαδικά αρχεία με κώδικα μηχανής | Ψευδοκώδικα, κώδικα assembly, συμβολοσειρές, σύμβολα, κλήσεις και αναφορές | Hopper, Ghidra ή IDA· [ανάλυση κώδικα μηχανής](https://rea.tools/guides/native/) |
+| Δομή ELF χωρίς εκτέλεση | Ενότητες, τμήματα, αρχικά σύμβολα/εγγραφές μετεγκατάστασης και ενδείξεις πιθανών μέτρων προστασίας από στατική ανάλυση | pwntools που παρέχει ο χρήστης σε Linux x64· [διαγνωστικά δυαδικών αρχείων](../binary-diagnostics.md) |
+| Δυαδικά αρχεία Go | Ενσωματωμένη έκδοση μεταγλωττιστή, modules, αντικαταστάσεις και ρυθμίσεις μεταγλώττισης | Ενσωματωμένη στατική εξέταση ELF/PE/thin Mach-O· δεν απαιτείται εγκατάσταση του Go |
+| Δυαδικά αρχεία Rust | Ενσωματωμένα ονόματα, εκδόσεις, πηγές και σχέσεις εξάρτησης των crates από το cargo-auditable | Ενσωματωμένη στατική εξέταση ELF/PE/thin Mach-O· [δυαδικά αρχεία Rust](../rust-binaries.md) |
+| Αρχείο WASM | Έλεγχο εγκυρότητας, ενότητες, ακριβείς μορφές εισαγωγών/εξαγωγών σε WAT και αποκωδικοποιημένο WAT | WABT 1.0.42 που παρέχει ο χρήστης· `inspect_wasm_artifact` |
+| Bytecode EVM | Selectors επιλογής συνάρτησης, μετατοπίσεις σε bytes, εκτιμώμενα ορίσματα και δυνατότητα μεταβολής της κατάστασης | Τοπική είσοδος σε ακατέργαστα bytes ή δεκαεξαδική μορφή σε Linux x64· [οδηγός EVM χωρίς εκτέλεση](../evm-bytecode.md) |
+| Βάσεις δεδομένων SQLite | Πίνακες, στήλες, ευρετήρια, όψεις, triggers και επιλεγμένες γραμμές κανονικών πινάκων | Τοπικό στιγμιότυπο βάσης δεδομένων/WAL· Node.js 24.x >=24.15 ή 26+ με εγγενή όρια SQLite |
+| Καταγεγραμμένες καταρρεύσεις στο Linux | Ακατέργαστες εγγραφές notes, καταχωρητές/σήματα κάθε καταγεγραμμένου νήματος και προαιρετικές πιθανές αντιστοιχίσεις μνήμης | pwntools που παρέχει ο χρήστης σε Linux x64· προαιρετικά GDB/pwndbg· [καταγεγραμμένες καταρρεύσεις](../recorded-crashes.md) |
+| JavaScript / Electron | Modules, εισαγωγές, source maps, διαδρομές, IPC και σχέσεις με πρόσθετα κώδικα μηχανής | Node.js και npm· [ανάλυση εφαρμογών](https://rea.tools/guides/javascript/) |
+| Ιστότοποι | Δομή σελίδων, scripts, παρατηρήσεις δικτύου και ζητούμενα στιγμιότυπα οθόνης | Πρόγραμμα περιήγησης της οικογένειας Chrome· [ανάλυση μέσω προγράμματος περιήγησης](https://rea.tools/guides/browser/) |
+| Αποθηκευμένες καταγραφές δικτύου | Αιτήματα, αποκρίσεις, διαθέσιμα δεδομένα φορτίου και θέσεις στην πηγή | HAR ή SAZ του Fiddler Everywhere· mitmdump σε Linux για την εγγενή μορφή mitmproxy· [οδηγός καταγραφών](../web-network-captures.md) |
+| Assemblies .NET | Μεταδεδομένα, εντολές CIL, δηλωμένες εξαρτήσεις από κώδικα μηχανής και συγκρίσεις builds | Καμία πρόσθετη απαίτηση (μόνο στατική ανάλυση)· [οδηγός διαχειριζόμενου κώδικα](../managed-code-analysis.md) |
+| APK Android | Δηλώσεις manifest, κλάσεις, απομεταγλωττισμένες μέθοδοι και αναφορές | JADX χωρίς γραφικό περιβάλλον και πλήρες JDK σε Linux/macOS/Windows x64· [οδηγός Android](../android-analysis.md) |
+| Συσκευές Android | Συσκευές, πακέτα, διεργασίες, logs, dumps, στιγμιότυπα οθόνης και μεταφορές APK/αρχείων | adb που παρέχει ο χρήστης (εξομοιωτής ή φυσική συσκευή)· [οδηγός ADB](../adb-device-analysis.md) |
+| Έργα JEB | Μονάδες έργου από τη μηχανή ανάλυσης, ψηφιακά αποτυπώματα αρχείων και ψευδοκώδικα τύπων/μεθόδων | Client JEB που έχει ξεκινήσει ο χρήστης και παρέχει MCP, επιλεγμένος μέσω REA_JEB_MCP_URL· [οδηγός JEB](../jeb-analysis.md) |
+| Πόροι Android | Αποκωδικοποιημένο manifest, πίνακες συμβολοσειρών, τοπικές ρυθμίσεις και στοιχεία έκδοσης | Launcher του Apktool που παρέχει ο χρήστης· [οδηγός πόρων](../apktool-resource-analysis.md) |
+| Εφαρμογές Flutter | Hash του στιγμιότυπου Dart, ψηφιακά αποτυπώματα βιβλιοθηκών ανά ABI και στοιχεία προέλευσης του build της μηχανής | Καμία πρόσθετη απαίτηση (στατική ανάλυση APK)· [οδηγός Flutter](../flutter-analysis.md) |
+| Firmware | Περιοχές, αποτελέσματα εξαγωγής και παραπομπές για ανάλυση κώδικα μηχανής | Binwalk / Unblob σε Linux· [οδηγός firmware](../firmware-analysis.md) |
+| Καταγεγραμμένα πακέτα | Επιλεγμένα πεδία, επαναλαμβανόμενες τιμές, φίλτρα και κανόνες decode-as | Linux: TShark 4.x + prlimit· [οδηγός καταγραφών](../packet-captures.md) |
+| Σύνοψη πακέτων | Τερματικά σημεία, DNS, TLS SNI, κατανομή πρωτοκόλλων και αιτήματα HTTP | tshark και capinfos που παρέχει ο χρήστης (επαληθευμένα σε Linux)· [οδηγός pcap](../pcap-analysis.md) |
+| Καταγεγραμμένα λογικά σήματα | Σχολιασμούς αποκωδικοποιητή, εύρη δειγμάτων και διαγνωστικά | Linux: sigrok-cli 0.7.2 + prlimit· [καταγραφές σημάτων](../signal-recordings.md) |
+| Πακέτα και πόροι | Καταλόγους αρχείων, ψηφιακά αποτυπώματα, plists, δομή bundles της Apple και εξαγόμενους πόρους | [Οδηγός αρχείων και JavaScript](../javascript-artifact-reconstruction.md), [εφαρμογές Apple](../apple-application-analysis.md) |
+| Συμπεριφορά διεργασιών | Έξοδο τερματικού, αλληλεπιδράσεις, παρατηρήσεις τερματισμού και συστήματος αρχείων, καθώς και συγκρίσεις εκτελέσεων | Linux/macOS με εγγενές PTY· [καταγραφή διεργασιών](../process-capture.md) |
+
+Για εξέταση δυαδικών αρχείων με κώδικα μηχανής χωρίς εκτέλεση, το REA υποστηρίζει
+επίσης GNU objdump και Rizin που παρέχει ο χρήστης. Δες τον
+[οδηγό εργαλείων αντίστροφης μηχανικής](../reverse-engineering-tools.md)
+για τη ρύθμιση των παρόχων, τις εντολές CLI και τους περιορισμούς των Evidence.
+Το REA δεν εγκαθιστά ούτε ενημερώνει κανέναν από τους δύο παρόχους.
+
+Η στατική εξέταση JavaScript και .NET διαβάζει τα αρχεία που παρέχονται χωρίς να εκτελεί την εφαρμογή. Η καταγραφή κατά την εκτέλεση εκτελεί τον επιλεγμένο στόχο ή αλληλεπιδρά μαζί του με τα δικαιώματα του χρήστη σου. Κάθε σχετικός οδηγός περιγράφει τις επιπτώσεις της.
+
+
+
+Οι μορφές κώδικα μηχανής και τα υποστηριζόμενα συστήματα διαφέρουν ανά πάροχο ανάλυσης. Δες τη [ρύθμιση Hopper και Ghidra](../installation.md#hopper), τον [οδηγό IDA](../ida-provider.md) (που περιλαμβάνει προαιρετική γέφυρα MCP για κώδικα μηχανής) και την [πειραματική υποστήριξη Ghidra στα Windows](../windows-ghidra-p0.md). Το Ghidra υποστηρίζει επίσης [ανάλυση DOS 16 bit](../ghidra-dos.md). Για μεγάλα δυαδικά αρχεία, αύξησε το χρονικό όριο εκκίνησης του παρόχου με `REA_GHIDRA_STARTUP_TIMEOUT_MS` ή `REA_HOPPER_STARTUP_TIMEOUT_MS`. Για την επιλογή παρόχου, δες τον [οδηγό CLI](../cli.md#choose-a-provider). Έλεγξε τη [διαθεσιμότητα ανά έκδοση](../installation.md#released-package-and-main) για λειτουργίες που προστέθηκαν μετά την τελευταία έκδοση στο npm.
+
+## Μελέτες περίπτωσης
+
+[](https://rea.tools/showcase/)
+
+### DX-Ball: ανακατασκευή υπολογισμού στερεοφωνικής τοποθέτησης ήχου
+
+Ακολούθησε μια κλήση ήχου μέχρι τη βοηθητική συνάρτηση που μετατρέπει τη θέση σε στερεοφωνική τοποθέτηση, εξέτασε τις εντολές και μετέτρεψε τον ελλιπή ψευδοκώδικα σε C. Η ανακατασκευή περνά 3.205 δοκιμές με τον αρχικό κώδικα x86 και αναπαράγει και τα 63 bytes της μεταγλωττισμένης συνάρτησης.
+
+[Διάβασε τη μελέτη περίπτωσης](https://rea.tools/showcase/dx-ball/) · [Αποθετήριο ανακατασκευής](https://github.com/N0zoM1z0/dx-ball)
+
+### Notion: ιχνηλάτηση της γέφυρας προχείρου στο Electron
+
+Βρες το API προχείρου του renderer, ακολούθησέ το μέσω του preload και του IPC μέχρι την κύρια διεργασία και εξέτασε τη μορφή εμπλουτισμένου περιεχομένου του προχείρου.
+
+[Διάβασε τη μελέτη περίπτωσης](https://rea.tools/showcase/notion/)
+
+### TH04: ανάκτηση υπολογισμού δακτυλίου βλημάτων σε DOS
+
+Εξέτασε τις εντολές 16 bit του αρχικού παιχνιδιού για PC-98, ανέκτησε τους υπολογισμούς σταθερών γωνιών και γωνιών στόχευσης και σύγκρινε τον ανακατασκευασμένο κώδικα C++ με την έξοδο του μεταγλωττιστή της εποχής.
+
+[Διάβασε τη μελέτη περίπτωσης](https://rea.tools/showcase/th04/) · [Αποθετήριο ανακατασκευής](https://github.com/N0zoM1z0/th04)
+
+Αν χρησιμοποίησες το REA για κάτι ενδιαφέρον, θα θέλαμε να το δούμε. Μοιράσου τη μελέτη σου σε ένα [issue](https://github.com/morluto/rea/issues) ή [pull request](https://github.com/morluto/rea/pulls), αναφέροντας τον στόχο, το ερώτημά σου, πώς βοήθησε το REA και τι ανακάλυψες.
+
+## Συχνές ερωτήσεις
+
+
+Ποιοι agents μπορούν να χρησιμοποιήσουν το REA;
+
+Κάθε agent που υποστηρίζει τοπικούς MCP servers. Η διαδικασία εγκατάστασης ρυθμίζει τους [υποστηριζόμενους agents](../installation.md#supported-agents). Οι υπόλοιποι clients μπορούν να χρησιμοποιήσουν τη [μη αυτόματη καταχώριση MCP](../installation.md#mcp-registry).
+
+
+
+
+Χρειάζομαι Hopper, Ghidra ή IDA;
+
+Η εις βάθος ανάλυση κώδικα μηχανής χρησιμοποιεί ένα από αυτά. Η στατική εξέταση JavaScript και .NET λειτουργεί χωρίς μηχανή ανάλυσης κώδικα μηχανής. Η διαδικασία εγκατάστασης μπορεί να εγκαταστήσει το Hopper μετά την έγκρισή σου· το Ghidra και το IDA χρησιμοποιούν τις υπάρχουσες εγκαταστάσεις σου. Δες τη [ρύθμιση παρόχων ανάλυσης](../installation.md#hopper).
+
+
+
+
+Πρέπει να ξεκινήσω πρώτα το Hopper;
+
+Το REA ξεκινά το Hopper όταν το χρειάζεται κάποια λειτουργία. Στο macOS, κατά την πρώτη εκκίνηση μπορεί να εμφανιστεί παράθυρο που ζητά να επιλέξεις τη δοκιμαστική λειτουργία ή να ενεργοποιήσεις την άδειά σου. Ολοκλήρωσε αυτό το βήμα στην ίδια συνεδρία χρήστη πριν από την πρώτη λειτουργία CLI/MCP ή εργασία χωρίς επίβλεψη. Άνοιξε το Hopper χειροκίνητα αν χρειαστεί. Δες την [εκκίνηση και αντιμετώπιση προβλημάτων του Hopper](../installation.md#launcher-paths-and-troubleshooting).
+
+
+
+
+Τι προσφέρει η εγκατάσταση του skill από το skills.sh;
+
+Το skill παρέχει οδηγίες διερεύνησης για τον agent σου. Χρησιμοποίησε `npx rea-agents setup` για να καταχωρίσεις τον MCP server του REA και να εγκαταστήσεις τις αντίστοιχες οδηγίες. Στη συνέχεια, επανεκκίνησε τον agent σου. Δες την [εγκατάσταση μόνο του skill](../installation.md#skill-only-installation).
+
+
+
+
+Τι κώδικα επιστρέφει το REA;
+
+Η ανάλυση κώδικα μηχανής επιστρέφει ψευδοκώδικα και assembly. Η ανάλυση JavaScript/Electron ανακτά modules και τις σχέσεις τους. Ο agent σου χρησιμοποιεί αυτά τα ευρήματα για να γράψει και να δοκιμάσει μια υλοποίηση. Οι [μελέτες περίπτωσης](#μελέτες-περίπτωσης) παρουσιάζουν ολοκληρωμένα παραδείγματα.
+
+
+
+
+Το REA ανεβάζει την εφαρμογή μου στο διαδίκτυο;
+
+Το REA αναλύει τους στόχους τοπικά. Ο agent σου λαμβάνει τα αποτελέσματα των εργαλείων και ο πάροχος του μοντέλου του έχει τη δική του πολιτική δεδομένων.
+
+
+
+
+Τι να κάνω αν συναντήσω κάποιο σφάλμα;
+
+Κάνε πρώτα ενημέρωση· μια πρόσφατη έκδοση μπορεί να το έχει ήδη διορθώσει.
+
+Για CLI εγκατεστημένο με `npm install --global` ή `bun add --global`:
+
+```bash
+rea update
+```
+
+Για ρύθμιση agent μέσω `npx`:
+
+```bash
+npx rea-agents@latest setup
+```
+
+Αν χρησιμοποιείς agent, ολοκλήρωσε την [ανανέωση της ρύθμισης](#ενημέρωση-του-rea) και επανεκκίνησέ τον. Δοκίμασε ξανά την ίδια εργασία. Αν το πρόβλημα παραμένει, [άνοιξε ένα issue](https://github.com/morluto/rea/issues) με την έκδοση του REA, τον τύπο του στόχου, τα βήματα αναπαραγωγής και την έξοδο του σφάλματος.
+
+
+
+## Τεκμηρίωση
+
+Ξεκίνα από τους [οδηγούς με ολοκληρωμένα παραδείγματα](https://rea.tools/guides/) στον ιστότοπο. Για τις ακριβείς επιλογές, τις προϋποθέσεις και τις προδιαγραφές των αποτελεσμάτων:
+
+- [Εγκατάσταση και ρύθμιση](../installation.md): καταχώριση agents, ρύθμιση παρόχων ανάλυσης, ενημερώσεις και απεγκατάσταση.
+- [Έλεγχος ετοιμότητας και αντιμετώπιση προβλημάτων](../installation.md#check-readiness-for-your-task): διάγνωση ενός agent ή μιας μηχανής ανάλυσης.
+- [CLI και Evidence](../cli.md): εντολές, επιλογή παρόχου, στιγμιότυπα, εισαγωγή/εξαγωγή και κωδικοί εξόδου.
+- [Προδιαγραφές MCP](../mcp-contracts.md) και [prompts για agents](../mcp-prompts.md): αποτελέσματα εργαλείων, συνεδρίες και καθοδηγούμενες διερευνήσεις.
+- [Κατάλογος εργαλείων](../mcp-contracts.md#generated-catalog): αυτόματα παραγόμενος κατάλογος εργαλείων, παρόχων και εντολών CLI.
+- [Οδικός χάρτης](../roadmap.md): προγραμματισμένες εργασίες και παρακολούθηση της ανάπτυξης δυνατοτήτων.
+
+Ανάφερε ευπάθειες ακολουθώντας το [SECURITY.md](../../SECURITY.md).
+
+## Ιστορικό αστεριών
+
+🎉 **180.000 αστέρια στο GitHub — σας ευχαριστούμε!**
+
+Ευχαριστούμε όλους όσοι χρησιμοποιούν το REA, αναφέρουν σφάλματα, ζητούν νέες λειτουργίες, δοκιμάζουν builds και συνεισφέρουν διορθώσεις.
+
+
+
+
+
+
+
+
+
+## Αποποίηση ευθύνης
+
+Δες το [DISCLAIMER.md](../../DISCLAIMER.md) για οδηγίες υπεύθυνης χρήσης.
+
+Η θέση του έργου σχετικά με την παράνομη ή μη εξουσιοδοτημένη χρήση ισχύει για όλες τις εκδόσεις του REA, τα κανάλια διανομής και τις πλατφόρμες. Κάθε αντίγραφο διατηρεί τις άδειες που το διέπουν.
+
+Το REA προορίζεται για νόμιμη έρευνα, εκπαίδευση, διαλειτουργικότητα, διατήρηση λογισμικού και ανάλυση λογισμικού που σου ανήκει ή για το οποίο έχεις άδεια εξέτασης.
+
+## Συνεισφορά
+
+Θα χαρούμε να βοηθήσεις στο REA! [Άνοιξε ένα issue](https://github.com/morluto/rea/issues) για να αναφέρεις ένα σφάλμα ή να προτείνεις μια λειτουργία, ή [στείλε ένα pull request](https://github.com/morluto/rea/pulls) για να βελτιώσεις τον κώδικα ή την τεκμηρίωση.
+
+Δες το [CONTRIBUTING.md](../../CONTRIBUTING.md) για τη ρύθμιση του περιβάλλοντος ανάπτυξης και τους ελέγχους, τον [οδηγό δοκιμών](../testing.md) για τις διαδικασίες επαλήθευσης και τον [χάρτη αρχιτεκτονικής](../architecture.mermaid) για τη δομή του έργου.
+
+## Άδεια χρήσης
+
+[MIT](../../LICENSE)
+
+Ο κώδικας που έχει δημιουργηθεί για το REA διατίθεται με την άδεια MIT. Το υλικό τρίτων διατηρεί τις δικές του άδειες και δηλώσεις πνευματικών δικαιωμάτων. Δες τις [δηλώσεις για υλικό τρίτων](../../THIRD_PARTY_NOTICES.md) για τον κατάλογο εξαρτήσεων, τα διατηρημένα κείμενα αδειών και τους όρους των εξωτερικών εργαλείων.
+
+**Πρέπει να αποκτήσεις νόμιμα τα εμπορικά εργαλεία και να έχεις δικαίωμα χρήσης τους**, συμπεριλαμβανομένων των IDA Pro / Hex-Rays decompilers, Hopper και JEB, σύμφωνα με τις αντίστοιχες άδειές τους. Το REA δεν παρέχει άδεια εργαλείου, κλειδί ή δικαίωμα ενεργοποίησης. Οι δοκιμαστικές εκδόσεις και τα demos εξακολουθούν να υπόκεινται στους όρους του προμηθευτή, συμπεριλαμβανομένων των περιορισμών χρήστη, λειτουργιών, αυτοματοποίησης και κοινοποίησης αποτελεσμάτων. Δεν επιτρέπονται σπασμένες εκδόσεις, κλεμμένα κλειδιά, μη εξουσιοδοτημένη κοινοποίηση κλειδιών ή παράκαμψη περιορισμών αδειοδότησης. Αυτές οι οδηγίες δεν τροποποιούν την άδεια MIT ή τις άδειες τρίτων.
diff --git a/docs/readme/README_es.md b/docs/readme/README_es.md
index d09be25e2..07f2517ea 100644
--- a/docs/readme/README_es.md
+++ b/docs/readme/README_es.md
@@ -1,6 +1,7 @@
[English](../../README.md) · [简体中文](README_zh.md) · [繁體中文](README_zh-TW.md) · [日本語](README_ja.md) · [한국어](README_ko.md) · [Türkçe](README_tr.md) · [Русский](README_ru.md) · [Tiếng Việt](README_vi.md) · [ไทย](README_th.md) · [Deutsch](README_de.md) · **Español** · [Français](README_fr.md) · [Українська](README_uk.md) · [Polski](README_pl.md) · [Português (Brasil)](README_pt-BR.md) · [العربية](README_ar.md) · [فارسی](README_fa.md) · [Bahasa Indonesia](README_id.md) · [Italiano](README_it.md)
+· [Ελληνικά](README_el.md)
# REA: Ingeniería inversa de cualquier cosa
@@ -19,10 +20,14 @@
**[Sitio web](https://rea.tools/es/) · [Guías](https://rea.tools/es/guides/) · [Casos prácticos](https://rea.tools/es/showcase/)**
-[Inicio rápido](#inicio-rápido) · [Cómo funciona REA](#cómo-funciona-rea) · [Qué puedes analizar](#qué-puedes-analizar) · [Casos prácticos](#casos-prácticos) · [Preguntas frecuentes](#preguntas-frecuentes) · [Documentación](#documentación)
+[Inicio rápido](#inicio-rápido) · [Cómo funciona REA](#cómo-funciona-rea) · [Qué puedes analizar](#qué-puedes-analizar) · [Casos prácticos](#casos-prácticos) · [Preguntas frecuentes](#preguntas-frecuentes) · [Herramientas GNU objdump y Rizin](../reverse-engineering-tools.md) · [Documentación](#documentación)
npx rea-agents setup
+> [!IMPORTANT]
+> Fuentes oficiales de REA: [morluto/rea](https://github.com/morluto/rea), [rea.tools](https://rea.tools/), [rea-agents](https://www.npmjs.com/package/rea-agents) (npm).
+> Usa los enlaces de instalación y de versiones publicados en estas fuentes oficiales. Un nombre de repositorio parecido o un README copiado no demuestra que una descarga sea oficial.
+
@@ -135,6 +140,7 @@ REA requiere Node.js 22.x (>=22.19), 24.x (>=24.11) o 26+, además de npm. Las h
| Binarios nativos | Pseudocódigo, ensamblador, cadenas, símbolos, llamadas y referencias | Hopper, Ghidra o IDA; [análisis nativo](https://rea.tools/es/guides/native/) |
| Estructura ELF sin ejecutar | Secciones, segmentos, símbolos/reubicaciones originales y posibles medidas de mitigación detectadas mediante análisis estático | pwntools proporcionado por quien llama, en Linux x64; [diagnóstico de binarios](../binary-diagnostics.md) |
| Bytecode EVM | Selectores de despacho, desplazamientos en bytes, argumentos inferidos y mutabilidad | Entrada local con bytes sin procesar o representación hexadecimal, en Linux x64; [guía de EVM sin ejecutar](../evm-bytecode.md) |
+| Rust binaries | Embedded cargo-auditable dependency list | [Rust binaries](../rust-binaries.md) |
| Fallos de Linux registrados | Registros note sin procesar, registros/señales de cada hilo registrado y posibles correspondencias de memoria opcionales | pwntools proporcionado por quien llama, en Linux x64; GDB/pwndbg opcionales; [fallos registrados](../recorded-crashes.md) |
| JavaScript / Electron | Módulos, importaciones, mapas de código fuente, rutas, IPC y relaciones con complementos nativos | Node.js y npm; [análisis de aplicaciones](https://rea.tools/es/guides/javascript/) |
| Sitios web | Estructura de la página, scripts, observaciones de red y capturas de pantalla solicitadas | Un navegador de la familia Chrome; [análisis del navegador](https://rea.tools/es/guides/browser/) |
diff --git a/docs/readme/README_fa.md b/docs/readme/README_fa.md
index b24e4380c..bd129ded1 100644
--- a/docs/readme/README_fa.md
+++ b/docs/readme/README_fa.md
@@ -1,6 +1,7 @@
[English](../../README.md) · [简体中文](README_zh.md) · [繁體中文](README_zh-TW.md) · [日本語](README_ja.md) · [한국어](README_ko.md) · [Türkçe](README_tr.md) · [Русский](README_ru.md) · [Tiếng Việt](README_vi.md) · [ไทย](README_th.md) · [Deutsch](README_de.md) · [Español](README_es.md) · [Français](README_fr.md) · [Українська](README_uk.md) · [Polski](README_pl.md) · [Português (Brasil)](README_pt-BR.md) · [العربية](README_ar.md) · **فارسی** · [Bahasa Indonesia](README_id.md) · [Italiano](README_it.md)
+· [Ελληνικά](README_el.md)
# REA: مهندسی معکوس هر چیزی
@@ -19,10 +20,14 @@
**[وبسایت](https://rea.tools/fa/) · [راهنماها](https://rea.tools/fa/guides/) · [نمونههای عملی](https://rea.tools/fa/showcase/)**
-[شروع سریع](#شروع-سریع) · [REA چگونه کار میکند؟](#rea-چگونه-کار-میکند) · [چه چیزهایی را میتوان تحلیل کرد؟](#چه-چیزهایی-را-میتوان-تحلیل-کرد) · [نمونههای عملی](#نمونههای-عملی) · [پرسشهای متداول](#پرسشهای-متداول) · [مستندات](#مستندات)
+[شروع سریع](#شروع-سریع) · [REA چگونه کار میکند؟](#rea-چگونه-کار-میکند) · [چه چیزهایی را میتوان تحلیل کرد؟](#چه-چیزهایی-را-میتوان-تحلیل-کرد) · [نمونههای عملی](#نمونههای-عملی) · [پرسشهای متداول](#پرسشهای-متداول) · [ابزارهای GNU objdump و Rizin](../reverse-engineering-tools.md) · [مستندات](#مستندات)
npx rea-agents setup
+> [!IMPORTANT]
+> منابع رسمی REA: [morluto/rea](https://github.com/morluto/rea), [rea.tools](https://rea.tools/), [rea-agents](https://www.npmjs.com/package/rea-agents) (npm).
+> از پیوندهای نصب و انتشار در این منابع رسمی استفاده کنید. نام مشابه مخزن یا README کپیشده ثابت نمیکند که فایل دانلودی رسمی است.
+
@@ -127,6 +132,7 @@ REA به Node.js 22.x (>=22.19)، 24.x (>=24.11) یا 26+، بههمراه np
| باینریهای بومی | شبهکد، اسمبلی، رشتهها، نمادها، فراخوانیها و ارجاعات | Hopper، Ghidra یا IDA؛ [تحلیل بومی](https://rea.tools/fa/guides/native/) |
| ساختار ELF آفلاین | بخشها، سگمنتها، نمادها و relocationهای اصلی و گزینههای احتمالی کاهش آسیبپذیری ایستا | pwntools تأمینشده توسط فراخواننده روی Linux x64؛ [تشخیص باینری](../binary-diagnostics.md) |
| بایتکد EVM | selectorهای dispatch، offsetهای بایتی، آرگومانهای استنباطشده و قابلیت تغییر وضعیت | دادهٔ خام/hex محلی روی Linux x64؛ [راهنمای EVM آفلاین](../evm-bytecode.md) |
+| Rust binaries | Embedded cargo-auditable dependency list | [Rust binaries](../rust-binaries.md) |
| کرشهای ثبتشدهٔ لینوکس | یادداشتهای خام، رجیسترها/سیگنالهای تمام threadهای ثبتشده و گزینههای احتمالی نگاشت | pwntools تأمینشده توسط فراخواننده روی Linux x64؛ GDB/pwndbg اختیاری؛ [کرشهای ثبتشده](../recorded-crashes.md) |
| JavaScript / Electron | ماژولها، importها، source mapها، مسیرها، IPC و ارتباط افزونههای بومی | Node.js و npm؛ [تحلیل برنامه](https://rea.tools/fa/guides/javascript/) |
| وبسایتها | ساختار صفحه، اسکریپتها، مشاهدات شبکه و اسکرینشاتهای درخواستی | مرورگر مبتنی بر Chrome؛ [تحلیل مرورگر](https://rea.tools/fa/guides/browser/) |
diff --git a/docs/readme/README_fr.md b/docs/readme/README_fr.md
index e6278ac93..255b15a9a 100644
--- a/docs/readme/README_fr.md
+++ b/docs/readme/README_fr.md
@@ -1,6 +1,7 @@
[English](../../README.md) · [简体中文](README_zh.md) · [繁體中文](README_zh-TW.md) · [日本語](README_ja.md) · [한국어](README_ko.md) · [Türkçe](README_tr.md) · [Русский](README_ru.md) · [Tiếng Việt](README_vi.md) · [ไทย](README_th.md) · [Deutsch](README_de.md) · [Español](README_es.md) · **Français** · [Українська](README_uk.md) · [Polski](README_pl.md) · [Português (Brasil)](README_pt-BR.md) · [العربية](README_ar.md) · [فارسی](README_fa.md) · [Bahasa Indonesia](README_id.md) · [Italiano](README_it.md)
+· [Ελληνικά](README_el.md)
# REA : rétro-ingénierie de tout
@@ -19,10 +20,14 @@
**[Site web](https://rea.tools/fr/) · [Guides](https://rea.tools/fr/guides/) · [Études de cas](https://rea.tools/fr/showcase/)**
-[Démarrage rapide](#démarrage-rapide) · [Comment fonctionne REA](#comment-fonctionne-rea) · [Ce que vous pouvez analyser](#ce-que-vous-pouvez-analyser) · [Études de cas](#études-de-cas) · [Questions fréquentes](#questions-fréquentes) · [Documentation](#documentation)
+[Démarrage rapide](#démarrage-rapide) · [Comment fonctionne REA](#comment-fonctionne-rea) · [Ce que vous pouvez analyser](#ce-que-vous-pouvez-analyser) · [Études de cas](#études-de-cas) · [Questions fréquentes](#questions-fréquentes) · [Outils GNU objdump et Rizin](../reverse-engineering-tools.md) · [Documentation](#documentation)
npx rea-agents setup
+> [!IMPORTANT]
+> Sources officielles de REA : [morluto/rea](https://github.com/morluto/rea), [rea.tools](https://rea.tools/), [rea-agents](https://www.npmjs.com/package/rea-agents) (npm).
+> Utilisez les liens d’installation et de publication de ces sources officielles. Un nom de dépôt similaire ou un README copié ne prouve pas qu’un téléchargement est officiel.
+
@@ -139,6 +144,7 @@ REA nécessite Node.js 22.x (>=22.19), 24.x (>=24.11) ou 26+, ainsi que npm. Les
| Structure ELF hors ligne | Sections, segments, symboles et relocations d'origine, et protections statiques candidates | pwntools fourni par l'utilisateur sous Linux x64 ; [diagnostics binaires](../binary-diagnostics.md) |
| Artefact WASM | Validation, sections, formes WAT exactes d'import/export et WAT décodé | WABT 1.0.42 fourni par l'utilisateur ; `inspect_wasm_artifact` |
| Bytecode EVM | Sélecteurs de dispatch, offsets en octets, arguments inférés et mutabilité | Fichier local brut ou hexadécimal sous Linux x64 ; [guide EVM hors ligne](../evm-bytecode.md) |
+| Rust binaries | Embedded cargo-auditable dependency list | [Rust binaries](../rust-binaries.md) |
| Crashs Linux enregistrés | Notes brutes, registres et signaux de chaque thread enregistré, et correspondances de mappings facultatives | pwntools fourni par l'utilisateur sous Linux x64 ; GDB/pwndbg en option ; [crashs enregistrés](../recorded-crashes.md) |
| JavaScript / Electron | Modules, imports, source maps, routes, IPC et relations avec les modules natifs | Node.js et npm ; [analyse d'applications](https://rea.tools/fr/guides/javascript/) |
| Sites web | Structure des pages, scripts, observations réseau et captures d'écran demandées | Un navigateur de la famille Chrome ; [analyse du navigateur](https://rea.tools/fr/guides/browser/) |
diff --git a/docs/readme/README_id.md b/docs/readme/README_id.md
index fba947fb2..713cfc0c0 100644
--- a/docs/readme/README_id.md
+++ b/docs/readme/README_id.md
@@ -1,6 +1,7 @@
[English](../../README.md) · [简体中文](README_zh.md) · [繁體中文](README_zh-TW.md) · [日本語](README_ja.md) · [한국어](README_ko.md) · [Türkçe](README_tr.md) · [Русский](README_ru.md) · [Tiếng Việt](README_vi.md) · [ไทย](README_th.md) · [Deutsch](README_de.md) · [Español](README_es.md) · [Français](README_fr.md) · [Українська](README_uk.md) · [Polski](README_pl.md) · [Português (Brasil)](README_pt-BR.md) · [العربية](README_ar.md) · [فارسی](README_fa.md) · **Bahasa Indonesia** · [Italiano](README_it.md)
+· [Ελληνικά](README_el.md)
# REA: Rekayasa Balik Apa Pun
@@ -19,10 +20,14 @@
**[Situs Web](https://rea.tools/id/) · [Panduan](https://rea.tools/id/guides/) · [Contoh Penggunaan](https://rea.tools/id/showcase/)**
-[Mulai Cepat](#mulai-cepat) · [Cara Kerja REA](#cara-kerja-rea) · [Hal yang Dapat Anda Analisis](#hal-yang-dapat-anda-analisis) · [Contoh Penggunaan](#contoh-penggunaan) · [FAQ](#faq) · [Dokumentasi](#dokumentasi)
+[Mulai Cepat](#mulai-cepat) · [Cara Kerja REA](#cara-kerja-rea) · [Hal yang Dapat Anda Analisis](#hal-yang-dapat-anda-analisis) · [Contoh Penggunaan](#contoh-penggunaan) · [FAQ](#faq) · [Alat GNU objdump dan Rizin](../reverse-engineering-tools.md) · [Dokumentasi](#dokumentasi)
npx rea-agents setup
+> [!IMPORTANT]
+> Sumber resmi REA: [morluto/rea](https://github.com/morluto/rea), [rea.tools](https://rea.tools/), [rea-agents](https://www.npmjs.com/package/rea-agents) (npm).
+> Gunakan tautan instalasi dan rilis dari sumber resmi ini. Nama repositori yang mirip atau README yang disalin tidak membuktikan bahwa unduhan tersebut resmi.
+
@@ -142,6 +147,7 @@ Alat tambahan dan dukungan sistem host bergantung pada target yang dianalisis:
| Tata letak ELF offline | Bagian, segmen, simbol/relokasi asli, dan kandidat mitigasi statis | pwntools yang disediakan pemanggil pada Linux x64; [diagnostik biner](../binary-diagnostics.md) |
| Artefak WASM | Validasi, bagian, bentuk WAT import/ekspor yang tepat, dan WAT yang didekode | WABT 1.0.42 yang disediakan pemanggil; `inspect_wasm_artifact` |
| Bytecode EVM | Selector dispatch, offset byte, argumen yang disimpulkan, dan sifat mutabilitas | Pembawa data raw/hex lokal; [panduan EVM offline](../evm-bytecode.md) |
+| Rust binaries | Embedded cargo-auditable dependency list | [Rust binaries](../rust-binaries.md) |
| Crash Linux yang direkam | Catatan mentah, register/sinyal setiap thread yang direkam, dan kandidat pemetaan opsional | pwntools yang disediakan pemanggil; GDB/pwndbg opsional; [crash yang direkam](../recorded-crashes.md) |
| JavaScript / Electron | Modul, impor, source map, rute, IPC, dan hubungan add-on native | Node.js dan npm; [analisis aplikasi](https://rea.tools/id/guides/javascript/) |
| Situs web | Struktur halaman, skrip, pengamatan jaringan, dan tangkapan layar yang diminta | Peramban berbasis Chrome; [analisis peramban](https://rea.tools/id/guides/browser/) |
diff --git a/docs/readme/README_it.md b/docs/readme/README_it.md
index b7e7c6100..d9b73e923 100644
--- a/docs/readme/README_it.md
+++ b/docs/readme/README_it.md
@@ -1,6 +1,7 @@
[English](../../README.md) · [简体中文](README_zh.md) · [繁體中文](README_zh-TW.md) · [日本語](README_ja.md) · [한국어](README_ko.md) · [Türkçe](README_tr.md) · [Русский](README_ru.md) · [Tiếng Việt](README_vi.md) · [ไทย](README_th.md) · [Deutsch](README_de.md) · [Español](README_es.md) · [Français](README_fr.md) · [Українська](README_uk.md) · [Polski](README_pl.md) · [Português (Brasil)](README_pt-BR.md) · [العربية](README_ar.md) · [فارسی](README_fa.md) · [Bahasa Indonesia](README_id.md) · **Italiano**
+· [Ελληνικά](README_el.md)
# REA: Reverse Engineer Anything
@@ -19,10 +20,14 @@
**[Sito web](https://rea.tools/it/) · [Guide](https://rea.tools/it/guides/) · [Showcase](https://rea.tools/it/showcase/)**
-[Avvio rapido](#avvio-rapido) · [Come funziona REA](#come-funziona-rea) · [Cosa puoi analizzare](#cosa-puoi-analizzare) · [Showcase](#showcase) · [FAQ](#faq) · [Documentazione](#documentazione)
+[Avvio rapido](#avvio-rapido) · [Come funziona REA](#come-funziona-rea) · [Cosa puoi analizzare](#cosa-puoi-analizzare) · [Showcase](#showcase) · [FAQ](#faq) · [Strumenti GNU objdump e Rizin](../reverse-engineering-tools.md) · [Documentazione](#documentazione)
npx rea-agents setup
+> [!IMPORTANT]
+> Fonti ufficiali di REA: [morluto/rea](https://github.com/morluto/rea), [rea.tools](https://rea.tools/), [rea-agents](https://www.npmjs.com/package/rea-agents) (npm).
+> Segui i link di installazione e delle versioni pubblicate da queste fonti ufficiali. Un nome di repository simile o un README copiato non dimostra che un download sia ufficiale.
+
@@ -139,6 +144,7 @@ REA richiede Node.js 22.x (>=22.19), 24.x (>=24.11) o 26+, oltre a npm. Strument
| Layout ELF offline | Sezioni, segmenti, simboli/relocazioni originali e candidati mitigazioni statiche | pwntools fornito dal chiamante su Linux x64; [diagnostica binaria](../binary-diagnostics.md) |
| Artefatto WASM | Convalida, sezioni, forme WAT di import/export esatte e WAT decodificato | WABT 1.0.42 fornito dal chiamante; `inspect_wasm_artifact` |
| Bytecode EVM | Selettori di dispatch, offset dei byte, argomenti inferiti e mutabilità | Carrier raw/hex locale; [guida EVM offline](../evm-bytecode.md) |
+| Rust binaries | Embedded cargo-auditable dependency list | [Rust binaries](../rust-binaries.md) |
| Crash Linux registrati | Note grezze, registri/segnai di ogni thread registrato e candidati mapping opzionali | pwntools fornito dal chiamante; GDB/pwndbg opzionale; [crash registrati](../recorded-crashes.md) |
| JavaScript / Electron | Moduli, import, source map, route, IPC e relazioni con add-on nativi | Node.js e npm; [analisi delle applicazioni](https://rea.tools/it/guides/javascript/) |
| Siti web | Struttura della pagina, script, osservazioni di rete e screenshot richiesti | Un browser della famiglia Chrome; [analisi browser](https://rea.tools/it/guides/browser/) |
diff --git a/docs/readme/README_ja.md b/docs/readme/README_ja.md
index c9d7981ba..afad1f488 100644
--- a/docs/readme/README_ja.md
+++ b/docs/readme/README_ja.md
@@ -1,6 +1,7 @@
[English](../../README.md) · [简体中文](README_zh.md) · [繁體中文](README_zh-TW.md) · [日本語](README_ja.md) · **한국어** · [Türkçe](README_tr.md) · [Русский](README_ru.md) · [Tiếng Việt](README_vi.md) · [ไทย](README_th.md) · [Deutsch](README_de.md) · [Español](README_es.md) · [Français](README_fr.md) · [Українська](README_uk.md) · [Polski](README_pl.md) · [Português (Brasil)](README_pt-BR.md) · [العربية](README_ar.md) · [فارسی](README_fa.md) · [Bahasa Indonesia](README_id.md) · [Italiano](README_it.md)
+· [Ελληνικά](README_el.md)
# REA: 무엇이든 리버스 엔지니어링
@@ -23,6 +24,10 @@
npx rea-agents setup
+> [!IMPORTANT]
+> REA 공식 배포처: [morluto/rea](https://github.com/morluto/rea), [rea.tools](https://rea.tools/), [rea-agents](https://www.npmjs.com/package/rea-agents) (npm).
+> 이 공식 배포처에서 제공하는 설치 및 릴리스 링크를 이용하세요. 비슷한 저장소 이름이나 복사된 README만으로는 공식 배포 파일인지 확인할 수 없습니다.
+
@@ -51,7 +56,7 @@
REA는 에이전트를 분석 도구에 연결해 네이티브 바이너리, JavaScript와 Electron 앱, .NET 어셈블리, 웹사이트를 검사할 수 있게 합니다. 터미널에서도 같은 도구를 사용할 수 있습니다. 분석은 로컬에서 실행되며, 결과에는 각 결론의 근거와 한계가 포함됩니다.
-설정 과정은 에이전트에 REA를 등록하고 해당 버전에 맞는 워크플로 지침을 설치합니다. 네이티브 분석에는 기존 Hopper 또는 Ghidra 설치를 사용할 수 있습니다. 승인하면 설정 과정에서 Hopper를 설치할 수도 있습니다. 정적 JavaScript 분석에는 두 엔진 모두 필요하지 않습니다.
+설정 과정은 에이전트에 REA를 등록하고 해당 버전에 맞는 워크플로 지침을 설치합니다. 네이티브 분석에는 기존 Hopper, Ghidra 또는 IDA 설치를 사용할 수 있습니다. 승인하면 설정 과정에서 Hopper를 설치할 수도 있습니다. 정적 JavaScript 분석에는 네이티브 분석 엔진이 필요하지 않습니다.
> **[REA 웹사이트](https://rea.tools/ko/)**에서 설정 방법, 그림으로 설명한 가이드, 실제 사례를 확인하세요.
@@ -100,12 +105,14 @@ rea --help
REA는 빠르게 바뀌며 새 릴리스에는 버그 수정이 자주 포함됩니다. 설치된 버전을 최신으로 유지하세요.
-npm으로 설치한 CLI에서는 다음 명령을 사용합니다.
+`npm install --global` 또는 `bun add --global`로 설치한 CLI에서는 다음 명령을 사용합니다.
```bash
rea update
```
+Bun 전역 업데이트에는 Bun 1.2.17 이상이 필요합니다. 이전 버전의 Bun을 사용한다면 `rea update`를 실행하기 전에 Bun을 직접 업그레이드하세요.
+
에이전트 등록과 스킬을 갱신하려면 업데이트 과정에서 출력된 설정 명령을 실행하세요.
`npx`를 사용한다면 다음 명령으로 에이전트 설정을 갱신하세요.
@@ -114,7 +121,7 @@ rea update
npx rea-agents@latest setup
```
-설정 변경 사항을 검토하고 에이전트를 다시 시작하세요. 일회성 CLI 작업에는 `npx rea-agents@latest` 뒤에 필요한 명령을 붙여 실행하세요.
+설정 변경 사항을 검토하고 에이전트를 다시 시작하세요. 일회성 CLI 작업에는 `npx rea-agents@latest` 뒤에 필요한 명령을 붙여 실행하세요. Bun을 사용한다면 위 명령의 `npx rea-agents@latest`를 `bunx rea-agents@latest`로 바꿔 실행하세요.
## REA의 작동 방식
@@ -134,13 +141,16 @@ REA에는 Node.js 22.x(>=22.19), 24.x(>=24.11) 또는 26+와 npm이 필요합니
| --------------------- | ----------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------- |
| 네이티브 바이너리 | 의사 코드, 어셈블리, 문자열, 심볼, 호출, 참조 | Hopper, Ghidra 또는 IDA; [네이티브 분석](https://rea.tools/ko/guides/native/) |
| 오프라인 ELF 레이아웃 | 섹션, 세그먼트, 원본 심볼/재배치 정보, 정적 분석으로 얻은 보안 완화 기법 후보 | Linux x64에서 호출자가 제공하는 pwntools; [바이너리 진단](../binary-diagnostics.md) |
+| Go 바이너리 | 내장된 컴파일러 버전, 모듈, `replace` 지시어, 빌드 설정 | REA 내장 ELF/PE/thin Mach-O 정적 검사; Go 설치 불필요 |
+| Rust 바이너리 | cargo-auditable이 내장한 크레이트 이름, 버전, 소스, 의존성 관계 | REA 내장 ELF/PE/thin Mach-O 정적 검사; [Rust 바이너리](../rust-binaries.md) |
+| WASM 아티팩트 | 유효성 검사, 섹션, import/export의 정확한 WAT 형식, 디코딩된 WAT | 호출자 제공 WABT 1.0.42; `inspect_wasm_artifact` |
| EVM 바이트코드 | 디스패치 선택자, 바이트 오프셋, 추론한 인자와 상태 변경 가능 여부 | Linux x64의 로컬 원시 바이트/16진수 입력 파일; [오프라인 EVM 가이드](../evm-bytecode.md) |
+| SQLite 데이터베이스 | 테이블, 열, 인덱스, 뷰, 트리거, 선택한 일반 테이블의 행 | 로컬 데이터베이스/WAL 스냅샷; Node.js 24.15 이상의 24.x 또는 26 이상(네이티브 SQLite 한도 적용) |
| 기록된 Linux 크래시 | 원시 note 레코드, 기록된 모든 스레드의 레지스터/시그널, 선택적 매핑 후보 | Linux x64에서 호출자가 제공하는 pwntools; GDB/pwndbg는 선택 사항; [기록된 크래시](../recorded-crashes.md) |
| JavaScript / Electron | 모듈, 임포트, 소스 맵, 라우트, IPC, 네이티브 애드온 관계 | Node.js와 npm; [애플리케이션 분석](https://rea.tools/ko/guides/javascript/) |
| 웹사이트 | 페이지 구조, 스크립트, 네트워크 관찰 결과, 요청한 스크린샷 | Chrome 계열 브라우저; [브라우저 분석](https://rea.tools/ko/guides/browser/) |
-| 저장된 네트워크 캡처 | 요청, 응답, 접근 가능한 페이로드, 소스 위치 | HAR; mitmproxy 네이티브 캡처에는 Linux의 mitmdump; [캡처 가이드](../web-network-captures.md) |
-| 패킷 캡처 | 엔드포인트, DNS, TLS SNI, 프로토콜 분포, HTTP 요청 | 호출자 제공 tshark 및 capinfos; [pcap 가이드](../pcap-analysis.md) |
-| .NET 어셈블리 | 메타데이터, CIL 명령, 선언된 네이티브 의존성, 빌드 비교 | 정적 검사; [관리 코드 가이드](../managed-code-analysis.md) |
+| 저장된 네트워크 캡처 | 요청, 응답, 접근 가능한 페이로드, 소스 위치 | HAR 또는 Fiddler Everywhere SAZ; mitmproxy 네이티브 캡처에는 Linux의 mitmdump; [캡처 가이드](../web-network-captures.md) |
+| .NET 어셈블리 | 메타데이터, CIL 명령, 선언된 네이티브 의존성, 빌드 비교 | 별도 도구 불필요(정적 검사만); [관리 코드 가이드](../managed-code-analysis.md) |
| Android APK | 매니페스트 선언, 클래스, 디컴파일된 메서드, 참조 | Linux/macOS/Windows x64의 헤드리스 JADX와 전체 JDK; [Android 가이드](../android-analysis.md) |
| Android 기기 | 기기, 패키지, 프로세스, 로그, 덤프, 화면 캡처, APK/파일 전송 | 호출자 제공 adb(에뮬레이터 또는 실제 기기); [ADB 가이드](../adb-device-analysis.md) |
| JEB 프로젝트 | 엔진 기반 프로젝트 유닛, 아티팩트 해시, 타입/메서드 의사코드 | 호출자가 시작하고 MCP를 제공하는 JEB 클라이언트(REA_JEB_MCP_URL로 선택); [JEB 가이드](../jeb-analysis.md) |
@@ -148,15 +158,18 @@ REA에는 Node.js 22.x(>=22.19), 24.x(>=24.11) 또는 26+와 npm이 필요합니
| Flutter 앱 | Dart 스냅샷 해시, ABI별 라이브러리 해시, 엔진 빌드 출처 | 없음 (정적 APK 파싱); [Flutter 가이드](../flutter-analysis.md) |
| 펌웨어 | 영역, 추출 결과, 네이티브 분석으로 전달할 정보 | Linux의 Binwalk / Unblob; [펌웨어 가이드](../firmware-analysis.md) |
| 기록된 패킷 | 선택 필드, 반복 값, 필터 및 디코딩 규칙 | Linux: TShark 4.x + prlimit; [캡처 가이드](../packet-captures.md) |
+| 패킷 요약 | 엔드포인트, DNS, TLS SNI, 프로토콜 분포, HTTP 요청 | 호출자 제공 tshark 및 capinfos(Linux에서 검증됨); [pcap 가이드](../pcap-analysis.md) |
| 기록된 논리 신호 | 디코더 주석, 샘플 범위, 진단 정보 | Linux: sigrok-cli 0.7.2 + prlimit; [신호 기록 가이드](../signal-recordings.md) |
| 패키지 및 리소스 | 파일 목록, 다이제스트, plist, Apple 번들 구조, 추출한 리소스 | [아티팩트 및 JavaScript 가이드](../javascript-artifact-reconstruction.md), [Apple 애플리케이션](../apple-application-analysis.md) |
| 프로세스 동작 | 터미널 출력, 상호작용, 종료 및 파일 시스템 관찰 결과, 실행 비교 | 네이티브 PTY를 지원하는 Linux/macOS; [프로세스 캡처](../process-capture.md) |
정적 JavaScript 및 .NET 검사는 제공된 파일을 읽으며 애플리케이션을 실행하지 않습니다. 런타임 캡처는 사용자의 권한으로 선택한 대상을 실행하거나 대상과 상호작용합니다. 각 런타임 가이드에서 그 영향을 설명합니다.
+오프라인 네이티브 바이너리 검사에는 호출자가 제공하는 GNU objdump와 Rizin도 사용할 수 있습니다. 제공자 설정, CLI 명령, Evidence의 한계는 [리버스 엔지니어링 도구 가이드](../reverse-engineering-tools.md)를 참고하세요. REA는 objdump와 Rizin을 설치하거나 업데이트하지 않습니다.
+
-지원하는 네이티브 형식과 호스트는 제공자마다 다릅니다. [Hopper 및 Ghidra 설정](../installation.md#hopper), [IDA 가이드](../ida-provider.md), [실험적 Windows Ghidra 지원](../windows-ghidra-p0.md)을 참고하세요. Ghidra는 [16비트 DOS 분석](../ghidra-dos.md)도 지원합니다. 제공자 선택은 [CLI 가이드](../cli.md#choose-a-provider)를 참고하세요. 최신 npm 릴리스 이후 추가된 기능은 [릴리스에서의 사용 가능 여부](../installation.md#released-package-and-main)를 확인하세요.
+지원하는 네이티브 형식과 호스트는 제공자마다 다릅니다. [Hopper 및 Ghidra 설정](../installation.md#hopper), [IDA 가이드](../ida-provider.md)(선택 사항인 네이티브 MCP 브리지 포함), [실험적 Windows Ghidra 지원](../windows-ghidra-p0.md)을 참고하세요. Ghidra는 [16비트 DOS 분석](../ghidra-dos.md)도 지원합니다. 큰 바이너리를 분석할 때는 `REA_GHIDRA_STARTUP_TIMEOUT_MS` 또는 `REA_HOPPER_STARTUP_TIMEOUT_MS`로 제공자의 시작 제한 시간을 늘리세요. 제공자 선택은 [CLI 가이드](../cli.md#choose-a-provider)를 참고하세요. 최신 npm 릴리스 이후 추가된 기능은 [릴리스에서의 사용 가능 여부](../installation.md#released-package-and-main)를 확인하세요.
## 사례
@@ -203,7 +216,7 @@ REA로 흥미로운 대상을 분석했다면 알려 주세요. [issue](https://
Hopper를 먼저 실행해야 하나요?
-REA는 작업에 필요할 때 Hopper를 실행합니다. macOS에서는 처음 실행할 때 데모 모드를 선택하거나 라이선스를 활성화하라는 대화 상자가 나타날 수 있습니다. [Hopper 실행 및 문제 해결](../installation.md#launcher-paths-and-troubleshooting)을 참고하세요.
+REA는 작업에 필요할 때 Hopper를 실행합니다. macOS에서는 처음 실행할 때 데모 모드를 선택하거나 라이선스를 활성화하라는 대화 상자가 나타날 수 있습니다. 첫 CLI/MCP 작업이나 무인 작업을 실행하기 전에 같은 사용자 세션에서 이 대화 상자를 완료하세요. 필요하면 Hopper를 직접 여세요. [Hopper 실행 및 문제 해결](../installation.md#launcher-paths-and-troubleshooting)을 참고하세요.
@@ -233,7 +246,7 @@ REA는 대상을 로컬에서 분석합니다. 에이전트는 도구 결과를
먼저 업데이트하세요. 최근 릴리스에서 이미 수정되었을 수 있습니다.
-npm으로 설치한 CLI에서는 다음 명령을 사용합니다.
+`npm install --global` 또는 `bun add --global`로 설치한 CLI에서는 다음 명령을 사용합니다.
```bash
rea update
@@ -280,11 +293,9 @@ REA를 사용하고, 버그를 보고하고, 기능을 요청하고, 빌드를
책임 있는 사용에 관한 안내는 [DISCLAIMER.md](../../DISCLAIMER.md)를 참고하세요.
-REA는 법을 준수하는 연구, 교육, 상호운용성 확보, 소프트웨어 보존, 그리고 본인이 소유하거나 조사 권한을 가진 소프트웨어의 분석을 위한 도구입니다.
+불법적이거나 허가되지 않은 사용에 대한 프로젝트의 입장은 모든 REA 버전, 배포 채널, 플랫폼에 적용되며, 각 사본에는 해당 라이선스가 그대로 유지됩니다.
-The project's position on illegal or unauthorized use covers all REA
-versions, distribution channels, and platforms; each copy retains its
-applicable licenses.
+REA는 법을 준수하는 연구, 교육, 상호운용성 확보, 소프트웨어 보존, 그리고 본인이 소유하거나 조사 권한을 가진 소프트웨어의 분석을 위한 도구입니다.
## 기여
@@ -296,14 +307,6 @@ REA에 도움을 보태 주세요! [issue를 등록](https://github.com/morluto/
[MIT](../../LICENSE)
-REA-authored code is licensed under MIT. Third-party material retains its own
-licenses and copyright notices; see [Third-party notices](../../THIRD_PARTY_NOTICES.md)
-for the dependency inventory, preserved license texts, and external-tool terms.
+REA가 작성한 코드는 MIT 라이선스를 따릅니다. 제3자 자료에는 각자의 라이선스와 저작권 고지가 그대로 유지됩니다. 의존성 목록, 보존된 라이선스 원문, 외부 도구 약관은 [제3자 고지](../../THIRD_PARTY_NOTICES.md)를 참고하세요.
-**You must legally obtain and be authorized to use commercial tools**, including
-IDA Pro / Hex-Rays decompilers, Hopper, and JEB, under their applicable licenses.
-REA grants no tool license, key, or activation entitlement. Trials and demos
-remain subject to vendor terms, including user, feature, automation, and output
-sharing restrictions. Cracked copies, stolen keys, unauthorized key sharing,
-and bypassing license restrictions are not authorized. This guidance does not
-amend MIT or third-party licenses.
+IDA Pro / Hex-Rays 디컴파일러, Hopper, JEB 등 상용 도구는 해당 라이선스에 따라 **합법적으로 취득하고 사용 권한을 갖춰야 합니다.** REA는 어떤 도구의 라이선스, 키, 활성화 권한도 부여하지 않습니다. 체험판과 데모는 사용자, 기능, 자동화, 출력 공유 제한을 포함한 공급업체 약관을 따릅니다. 크랙 버전, 도난 키, 무단 키 공유, 라이선스 제한 우회는 허용되지 않습니다. 이 안내는 MIT 라이선스나 제3자 라이선스를 변경하지 않습니다.
diff --git a/docs/readme/README_pl.md b/docs/readme/README_pl.md
index 48200087d..c49c1ec05 100644
--- a/docs/readme/README_pl.md
+++ b/docs/readme/README_pl.md
@@ -1,6 +1,7 @@
[English](../../README.md) · [简体中文](README_zh.md) · [繁體中文](README_zh-TW.md) · [日本語](README_ja.md) · [한국어](README_ko.md) · [Türkçe](README_tr.md) · [Русский](README_ru.md) · [Tiếng Việt](README_vi.md) · [ไทย](README_th.md) · [Deutsch](README_de.md) · [Español](README_es.md) · [Français](README_fr.md) · [Українська](README_uk.md) · **Polski** · [Português (Brasil)](README_pt-BR.md) · [العربية](README_ar.md) · [فارسی](README_fa.md) · [Bahasa Indonesia](README_id.md) · [Italiano](README_it.md)
+· [Ελληνικά](README_el.md)
# REA: Reverse Engineer Anything
@@ -19,10 +20,14 @@
**[Strona internetowa](https://rea.tools/pl/) · [Przewodniki](https://rea.tools/pl/guides/) · [Przykłady zastosowań](https://rea.tools/pl/showcase/)**
-[Szybki start](#szybki-start) · [Jak działa REA](#jak-działa-rea) · [Co możesz analizować](#co-możesz-analizować) · [Przykłady zastosowań](#przykłady-zastosowań) · [FAQ](#faq) · [Dokumentacja](#dokumentacja)
+[Szybki start](#szybki-start) · [Jak działa REA](#jak-działa-rea) · [Co możesz analizować](#co-możesz-analizować) · [Przykłady zastosowań](#przykłady-zastosowań) · [FAQ](#faq) · [Narzędzia GNU objdump i Rizin](../reverse-engineering-tools.md) · [Dokumentacja](#dokumentacja)
npx rea-agents setup
+> [!IMPORTANT]
+> Oficjalne źródła REA: [morluto/rea](https://github.com/morluto/rea), [rea.tools](https://rea.tools/), [rea-agents](https://www.npmjs.com/package/rea-agents) (npm).
+> Korzystaj z odnośników do instalacji i wydań w tych oficjalnych źródłach. Podobna nazwa repozytorium lub skopiowany README nie potwierdzają, że pobierany plik jest oficjalny.
+
@@ -135,6 +140,7 @@ REA wymaga Node.js 22.x (>=22.19), 24.x (>=24.11) lub 26+ oraz npm. Dodatkowe na
| Natywne pliki binarne | Pseudokod, kod asemblera, ciągi znaków, symbole, wywołania i odwołania | Hopper, Ghidra lub IDA; [analiza kodu natywnego](https://rea.tools/pl/guides/native/) |
| Układ pliku ELF w trybie offline | Sekcje, segmenty, oryginalne symbole/relokacje i potencjalne mechanizmy zabezpieczeń wskazane przez analizę statyczną | pwntools dostarczone przez użytkownika w systemie Linux x64; [diagnostyka plików binarnych](../binary-diagnostics.md) |
| Kod bajtowy EVM | Selektory wywołań, przesunięcia bajtowe, wywnioskowane argumenty i mutowalność | Lokalny plik z surowymi bajtami lub zapisem szesnastkowym w systemie Linux x64; [przewodnik EVM offline](../evm-bytecode.md) |
+| Rust binaries | Embedded cargo-auditable dependency list | [Rust binaries](../rust-binaries.md) |
| Zarejestrowane awarie systemu Linux | Surowe rekordy note, rejestry/sygnały każdego zarejestrowanego wątku i opcjonalni kandydaci na mapowania | pwntools dostarczone przez użytkownika w systemie Linux x64; opcjonalnie GDB/pwndbg; [zarejestrowane awarie](../recorded-crashes.md) |
| JavaScript / Electron | Moduły, importy, mapy źródeł, trasy, IPC i powiązania z natywnymi dodatkami | Node.js i npm; [analiza aplikacji](https://rea.tools/pl/guides/javascript/) |
| Strony internetowe | Struktura strony, skrypty, obserwacje sieciowe i zamówione zrzuty ekranu | Przeglądarka z rodziny Chrome; [analiza w przeglądarce](https://rea.tools/pl/guides/browser/) |
diff --git a/docs/readme/README_pt-BR.md b/docs/readme/README_pt-BR.md
index 6c3f34e35..ec7ecd29c 100644
--- a/docs/readme/README_pt-BR.md
+++ b/docs/readme/README_pt-BR.md
@@ -1,6 +1,7 @@
[English](../../README.md) · [简体中文](README_zh.md) · [繁體中文](README_zh-TW.md) · [日本語](README_ja.md) · [한국어](README_ko.md) · [Türkçe](README_tr.md) · [Русский](README_ru.md) · [Tiếng Việt](README_vi.md) · [ไทย](README_th.md) · [Deutsch](README_de.md) · [Español](README_es.md) · [Français](README_fr.md) · [Українська](README_uk.md) · [Polski](README_pl.md) · **Português (Brasil)** · [العربية](README_ar.md) · [فارسی](README_fa.md) · [Bahasa Indonesia](README_id.md) · [Italiano](README_it.md)
+· [Ελληνικά](README_el.md)
# REA: Faça engenharia reversa de qualquer coisa
@@ -19,10 +20,14 @@
**[Site](https://rea.tools/pt-BR/) · [Guias](https://rea.tools/pt-BR/guides/) · [Casos de uso](https://rea.tools/pt-BR/showcase/)**
-[Início rápido](#início-rápido) · [Como o REA funciona](#como-o-rea-funciona) · [O que você pode analisar](#o-que-você-pode-analisar) · [Casos de uso](#casos-de-uso) · [Perguntas frequentes](#perguntas-frequentes) · [Documentação](#documentação)
+[Início rápido](#início-rápido) · [Como o REA funciona](#como-o-rea-funciona) · [O que você pode analisar](#o-que-você-pode-analisar) · [Casos de uso](#casos-de-uso) · [Perguntas frequentes](#perguntas-frequentes) · [Ferramentas GNU objdump e Rizin](../reverse-engineering-tools.md) · [Documentação](#documentação)
npx rea-agents setup
+> [!IMPORTANT]
+> Fontes oficiais do REA: [morluto/rea](https://github.com/morluto/rea), [rea.tools](https://rea.tools/), [rea-agents](https://www.npmjs.com/package/rea-agents) (npm).
+> Use os links de instalação e de versões dessas fontes oficiais. Um nome de repositório parecido ou um README copiado não comprova que um download seja oficial.
+
@@ -135,6 +140,7 @@ O REA requer Node.js 22.x (>=22.19), 24.x (>=24.11) ou 26+, além do npm. As fer
| Binários nativos | Pseudocódigo, assembly, strings, símbolos, chamadas e referências | Hopper, Ghidra ou IDA; [análise nativa](https://rea.tools/pt-BR/guides/native/) |
| Estrutura ELF sem execução | Seções, segmentos, símbolos/relocações originais e possíveis mecanismos de mitigação identificados por análise estática | pwntools fornecido pelo chamador no Linux x64; [diagnóstico de binários](../binary-diagnostics.md) |
| Bytecode EVM | Seletores de despacho, offsets de bytes, argumentos inferidos e mutabilidade | Entrada local com bytes brutos ou representação hexadecimal no Linux x64; [guia de EVM sem execução](../evm-bytecode.md) |
+| Rust binaries | Embedded cargo-auditable dependency list | [Rust binaries](../rust-binaries.md) |
| Falhas do Linux registradas | Registros note brutos, registradores/sinais de cada thread registrada e candidatos opcionais a mapeamentos | pwntools fornecido pelo chamador no Linux x64; GDB/pwndbg opcionais; [falhas registradas](../recorded-crashes.md) |
| JavaScript / Electron | Módulos, importações, source maps, rotas, IPC e relações com complementos nativos | Node.js e npm; [análise de aplicativos](https://rea.tools/pt-BR/guides/javascript/) |
| Sites | Estrutura da página, scripts, observações de rede e capturas de tela solicitadas | Um navegador da família Chrome; [análise do navegador](https://rea.tools/pt-BR/guides/browser/) |
diff --git a/docs/readme/README_ru.md b/docs/readme/README_ru.md
index 744e26f6c..136344e9f 100644
--- a/docs/readme/README_ru.md
+++ b/docs/readme/README_ru.md
@@ -1,6 +1,7 @@
[English](../../README.md) · [简体中文](README_zh.md) · [繁體中文](README_zh-TW.md) · [日本語](README_ja.md) · [한국어](README_ko.md) · [Türkçe](README_tr.md) · **Русский** · [Tiếng Việt](README_vi.md) · [ไทย](README_th.md) · [Deutsch](README_de.md) · [Español](README_es.md) · [Français](README_fr.md) · [Українська](README_uk.md) · [Polski](README_pl.md) · [Português (Brasil)](README_pt-BR.md) · [العربية](README_ar.md) · [فارسی](README_fa.md) · [Bahasa Indonesia](README_id.md) · [Italiano](README_it.md)
+· [Ελληνικά](README_el.md)
# REA: Реверс-инжиниринг чего угодно
@@ -19,10 +20,14 @@
**[Сайт](https://rea.tools/ru/) · [Руководства](https://rea.tools/ru/guides/) · [Примеры](https://rea.tools/ru/showcase/)**
-[Быстрый старт](#быстрый-старт) · [Как работает REA](#как-работает-rea) · [Что можно анализировать](#что-можно-анализировать) · [Примеры](#примеры) · [Частые вопросы](#частые-вопросы) · [Документация](#документация)
+[Быстрый старт](#быстрый-старт) · [Как работает REA](#как-работает-rea) · [Что можно анализировать](#что-можно-анализировать) · [Примеры](#примеры) · [Частые вопросы](#частые-вопросы) · [Инструменты GNU objdump и Rizin](../reverse-engineering-tools.md) · [Документация](#документация)
npx rea-agents setup
+> [!IMPORTANT]
+> Официальные источники REA: [morluto/rea](https://github.com/morluto/rea), [rea.tools](https://rea.tools/), [rea-agents](https://www.npmjs.com/package/rea-agents) (npm).
+> Используйте ссылки на установку и выпуски из этих официальных источников. Похожее название репозитория или скопированный README не подтверждают, что скачиваемый файл является официальным.
+
@@ -135,6 +140,7 @@ REA требует Node.js 22.x (>=22.19), 24.x (>=24.11) или 26+, а так
| Нативные бинарные файлы | Псевдокод, ассемблер, строки, символы, вызовы и ссылки | Hopper, Ghidra или IDA; [нативный анализ](https://rea.tools/ru/guides/native/) |
| Структура ELF без запуска | Секции, сегменты, исходные символы/релокации и возможные механизмы защиты по данным статического анализа | pwntools, предоставленный вызывающей стороной, в Linux x64; [диагностика бинарных файлов](../binary-diagnostics.md) |
| Байт-код EVM | Селекторы диспетчеризации, смещения байтов, предполагаемые аргументы и изменяемость состояния | Локальные необработанные байты или шестнадцатеричное представление в Linux x64; [руководство по EVM без запуска](../evm-bytecode.md) |
+| Rust binaries | Embedded cargo-auditable dependency list | [Rust binaries](../rust-binaries.md) |
| Записанные сбои Linux | Необработанные записи note, регистры/сигналы каждого записанного потока и необязательные кандидаты сопоставления с памятью | pwntools, предоставленный вызывающей стороной, в Linux x64; необязательные GDB/pwndbg; [записанные сбои](../recorded-crashes.md) |
| JavaScript / Electron | Модули, импорты, карты исходного кода, маршруты, IPC и связи с нативными дополнениями | Node.js и npm; [анализ приложений](https://rea.tools/ru/guides/javascript/) |
| Сайты | Структура страницы, скрипты, наблюдения за сетью и запрошенные снимки экрана | Браузер семейства Chrome; [анализ браузера](https://rea.tools/ru/guides/browser/) |
diff --git a/docs/readme/README_th.md b/docs/readme/README_th.md
index 734b28c86..f758ca421 100644
--- a/docs/readme/README_th.md
+++ b/docs/readme/README_th.md
@@ -1,6 +1,7 @@
[English](../../README.md) · [简体中文](README_zh.md) · [繁體中文](README_zh-TW.md) · [日本語](README_ja.md) · [한국어](README_ko.md) · **Türkçe** · [Русский](README_ru.md) · [Tiếng Việt](README_vi.md) · [ไทย](README_th.md) · [Deutsch](README_de.md) · [Español](README_es.md) · [Français](README_fr.md) · [Українська](README_uk.md) · [Polski](README_pl.md) · [Português (Brasil)](README_pt-BR.md) · [العربية](README_ar.md) · [فارسی](README_fa.md) · [Bahasa Indonesia](README_id.md) · [Italiano](README_it.md)
+· [Ελληνικά](README_el.md)
# REA: Her şeyi tersine mühendislikle inceleyin
@@ -19,10 +20,14 @@
**[Web sitesi](https://rea.tools/tr/) · [Rehberler](https://rea.tools/tr/guides/) · [Örnek çalışmalar](https://rea.tools/tr/showcase/)**
-[Hızlı başlangıç](#hızlı-başlangıç) · [REA nasıl çalışır](#rea-nasıl-çalışır) · [Neleri analiz edebilirsiniz](#neleri-analiz-edebilirsiniz) · [Örnek çalışmalar](#örnek-çalışmalar) · [Sık sorulan sorular](#sık-sorulan-sorular) · [Belgeler](#belgeler)
+[Hızlı başlangıç](#hızlı-başlangıç) · [REA nasıl çalışır](#rea-nasıl-çalışır) · [Neleri analiz edebilirsiniz](#neleri-analiz-edebilirsiniz) · [Örnek çalışmalar](#örnek-çalışmalar) · [Sık sorulan sorular](#sık-sorulan-sorular) · [GNU objdump ve Rizin araçları](../reverse-engineering-tools.md) · [Belgeler](#belgeler)
npx rea-agents setup
+> [!IMPORTANT]
+> REA’nın resmî kaynakları: [morluto/rea](https://github.com/morluto/rea), [rea.tools](https://rea.tools/), [rea-agents](https://www.npmjs.com/package/rea-agents) (npm).
+> Kurulum ve sürüm bağlantılarını bu resmî kaynaklardan takip edin. Benzer bir depo adı veya kopyalanmış bir README, indirilen dosyanın resmî olduğunu kanıtlamaz.
+
@@ -137,6 +142,7 @@ REA; Node.js 22.x (>=22.19), 24.x (>=24.11) veya 26+ ve npm gerektirir. Ek araç
| Yerel makine kodu içeren ikili dosyalar | Sözde kod, assembly kodu, dizeler, semboller, çağrılar ve referanslar | Hopper, Ghidra veya IDA; [yerel kod analizi](https://rea.tools/tr/guides/native/) |
| Çevrimdışı ELF yapısı | Bölümler, segmentler, özgün semboller/yeniden konumlandırmalar ve statik analizden olası güvenlik önlemleri | Linux x64 üzerinde çağıranın sağladığı pwntools; [ikili dosya tanılama](../binary-diagnostics.md) |
| Go ikili dosyaları | Gömülü compiler sürümü, modüller, replacement'lar ve build ayarları | Yerleşik statik ELF/PE/thin Mach-O incelemesi; Go kurulumu gerekmez |
+| Rust ikili dosyaları | Gömülü cargo-auditable crate adları, sürümleri, kaynakları ve bağımlılık ilişkileri | Yerleşik statik ELF/PE/thin Mach-O incelemesi; [Rust ikili dosyaları](../rust-binaries.md) |
| WASM artefaktı | Doğrulama, bölümler, tam import/export WAT biçimleri ve decode edilmiş WAT | Çağıranın sağladığı WABT 1.0.42; `inspect_wasm_artifact` |
| EVM bayt kodu | Yönlendirme seçicileri, bayt ofsetleri, çıkarımla belirlenen argümanlar ve durum değiştirilebilirliği | Linux x64 üzerinde yerel ham bayt/onaltılık girdi; [çevrimdışı EVM rehberi](../evm-bytecode.md) |
| SQLite veritabanları | Tablolar, sütunlar, index'ler, view'lar, trigger'lar ve seçilen sıradan tablo satırları | Yerel database/WAL snapshot'ı; native SQLite sınırlarıyla Node.js 24.x >=24.15 veya 26+ |
diff --git a/docs/readme/README_uk.md b/docs/readme/README_uk.md
index 5c77d80b0..3eebfeeab 100644
--- a/docs/readme/README_uk.md
+++ b/docs/readme/README_uk.md
@@ -1,6 +1,7 @@
[English](../../README.md) · [简体中文](README_zh.md) · [繁體中文](README_zh-TW.md) · [日本語](README_ja.md) · [한국어](README_ko.md) · [Türkçe](README_tr.md) · [Русский](README_ru.md) · [Tiếng Việt](README_vi.md) · [ไทย](README_th.md) · [Deutsch](README_de.md) · [Español](README_es.md) · [Français](README_fr.md) · **Українська** · [Polski](README_pl.md) · [Português (Brasil)](README_pt-BR.md) · [العربية](README_ar.md) · [فارسی](README_fa.md) · [Bahasa Indonesia](README_id.md) · [Italiano](README_it.md)
+· [Ελληνικά](README_el.md)
# REA: Реверс-інжиніринг будь-чого
@@ -19,10 +20,14 @@
**[Сайт](https://rea.tools/uk/) · [Посібники](https://rea.tools/uk/guides/) · [Приклади](https://rea.tools/uk/showcase/)**
-[Швидкий старт](#швидкий-старт) · [Як працює REA](#як-працює-rea) · [Що можна аналізувати](#що-можна-аналізувати) · [Приклади](#приклади) · [Поширені запитання](#поширені-запитання) · [Документація](#документація)
+[Швидкий старт](#швидкий-старт) · [Як працює REA](#як-працює-rea) · [Що можна аналізувати](#що-можна-аналізувати) · [Приклади](#приклади) · [Поширені запитання](#поширені-запитання) · [Інструменти GNU objdump і Rizin](../reverse-engineering-tools.md) · [Документація](#документація)
npx rea-agents setup
+> [!IMPORTANT]
+> Офіційні джерела REA: [morluto/rea](https://github.com/morluto/rea), [rea.tools](https://rea.tools/), [rea-agents](https://www.npmjs.com/package/rea-agents) (npm).
+> Користуйтеся посиланнями на встановлення та випуски з цих офіційних джерел. Схожа назва репозиторію або скопійований README не підтверджують, що завантажуваний файл є офіційним.
+
@@ -135,6 +140,7 @@ REA потребує Node.js 22.x (>=22.19), 24.x (>=24.11) або 26+, а та
| Нативні бінарні файли | Псевдокод, асемблерний код, рядки, символи, виклики й посилання | Hopper, Ghidra або IDA; [нативний аналіз](https://rea.tools/uk/guides/native/) |
| Структура ELF без запуску | Секції, сегменти, початкові символи/релокації та можливі захисні механізми за даними статичного аналізу | pwntools, наданий стороною, що викликає інструмент, у Linux x64; [діагностика бінарних файлів](../binary-diagnostics.md) |
| Байт-код EVM | Селектори диспетчеризації, зміщення байтів, виведені припущення про аргументи та змінюваність стану | Локальні необроблені байти або шістнадцяткове представлення у Linux x64; [посібник EVM без запуску](../evm-bytecode.md) |
+| Rust binaries | Embedded cargo-auditable dependency list | [Rust binaries](../rust-binaries.md) |
| Записані аварійні завершення в Linux | Необроблені записи note, регістри/сигнали кожного записаного потоку й необов'язкові кандидати зіставлення пам'яті | pwntools, наданий стороною, що викликає інструмент, у Linux x64; необов'язкові GDB/pwndbg; [записані аварійні завершення](../recorded-crashes.md) |
| JavaScript / Electron | Модулі, імпорти, карти вихідного коду, маршрути, IPC і зв'язки з нативними доповненнями | Node.js і npm; [аналіз застосунків](https://rea.tools/uk/guides/javascript/) |
| Сайти | Структура сторінки, скрипти, мережеві спостереження й запитані знімки екрана | Браузер сімейства Chrome; [аналіз браузера](https://rea.tools/uk/guides/browser/) |
diff --git a/docs/readme/README_vi.md b/docs/readme/README_vi.md
index 3d2062da6..e5879c872 100644
--- a/docs/readme/README_vi.md
+++ b/docs/readme/README_vi.md
@@ -1,6 +1,7 @@
[English](../../README.md) · [简体中文](README_zh.md) · [繁體中文](README_zh-TW.md) · [日本語](README_ja.md) · [한국어](README_ko.md) · [Türkçe](README_tr.md) · [Русский](README_ru.md) · **Tiếng Việt** · [ไทย](README_th.md) · [Deutsch](README_de.md) · [Español](README_es.md) · [Français](README_fr.md) · [Українська](README_uk.md) · [Polski](README_pl.md) · [Português (Brasil)](README_pt-BR.md) · [العربية](README_ar.md) · [فارسی](README_fa.md) · [Bahasa Indonesia](README_id.md) · [Italiano](README_it.md)
+· [Ελληνικά](README_el.md)
# REA: Kỹ thuật dịch ngược cho mọi thứ
@@ -19,10 +20,14 @@
**[Trang web](https://rea.tools/vi/) · [Hướng dẫn](https://rea.tools/vi/guides/) · [Các nghiên cứu điển hình](https://rea.tools/vi/showcase/)**
-[Bắt đầu nhanh](#bắt-đầu-nhanh) · [Cách REA hoạt động](#cách-rea-hoạt-động) · [Những gì bạn có thể phân tích](#những-gì-bạn-có-thể-phân-tích) · [Các nghiên cứu điển hình](#các-nghiên-cứu-điển-hình) · [Câu hỏi thường gặp](#câu-hỏi-thường-gặp) · [Tài liệu](#tài-liệu)
+[Bắt đầu nhanh](#bắt-đầu-nhanh) · [Cách REA hoạt động](#cách-rea-hoạt-động) · [Những gì bạn có thể phân tích](#những-gì-bạn-có-thể-phân-tích) · [Các nghiên cứu điển hình](#các-nghiên-cứu-điển-hình) · [Câu hỏi thường gặp](#câu-hỏi-thường-gặp) · [Công cụ GNU objdump và Rizin](../reverse-engineering-tools.md) · [Tài liệu](#tài-liệu)
npx rea-agents setup
+> [!IMPORTANT]
+> Các nguồn chính thức của REA: [morluto/rea](https://github.com/morluto/rea), [rea.tools](https://rea.tools/), [rea-agents](https://www.npmjs.com/package/rea-agents) (npm).
+> Hãy dùng các liên kết cài đặt và bản phát hành từ những nguồn chính thức này. Tên kho mã tương tự hoặc README được sao chép không chứng minh rằng tệp tải xuống là bản chính thức.
+
@@ -135,6 +140,7 @@ REA yêu cầu Node.js 22.x (>=22.19), 24.x (>=24.11) hoặc 26+, cùng với np
| Tệp nhị phân mã máy | Mã giả, hợp ngữ, chuỗi, ký hiệu, lời gọi và tham chiếu | Hopper, Ghidra hoặc IDA; [phân tích mã máy](https://rea.tools/vi/guides/native/) |
| Bố cục ELF ngoại tuyến | Các section, segment, ký hiệu/thông tin tái định vị gốc và các biện pháp giảm thiểu có thể có từ phân tích tĩnh | pwntools do bên gọi cung cấp trên Linux x64; [chẩn đoán tệp nhị phân](../binary-diagnostics.md) |
| Bytecode EVM | Bộ chọn điều phối, độ lệch byte, tham số suy luận và khả năng thay đổi trạng thái | Đầu vào cục bộ chứa byte thô hoặc dạng thập lục phân trên Linux x64; [hướng dẫn EVM ngoại tuyến](../evm-bytecode.md) |
+| Rust binaries | Embedded cargo-auditable dependency list | [Rust binaries](../rust-binaries.md) |
| Sự cố Linux đã ghi lại | Bản ghi note thô, thanh ghi/tín hiệu của mọi luồng được ghi lại và các ứng viên ánh xạ tùy chọn | pwntools do bên gọi cung cấp trên Linux x64; GDB/pwndbg tùy chọn; [sự cố đã ghi lại](../recorded-crashes.md) |
| JavaScript / Electron | Mô-đun, các khai báo import, source map, tuyến, IPC và quan hệ với tiện ích mở rộng mã máy | Node.js và npm; [phân tích ứng dụng](https://rea.tools/vi/guides/javascript/) |
| Trang web | Cấu trúc trang, script, quan sát mạng và ảnh chụp màn hình được yêu cầu | Trình duyệt thuộc họ Chrome; [phân tích trình duyệt](https://rea.tools/vi/guides/browser/) |
diff --git a/docs/readme/README_zh-TW.md b/docs/readme/README_zh-TW.md
index 1ddbbd26e..86ad30c82 100644
--- a/docs/readme/README_zh-TW.md
+++ b/docs/readme/README_zh-TW.md
@@ -1,6 +1,7 @@