docs(website): add a CTF showcase and FAQ (#1077)
* docs(website): add a worked CTF reverse-engineering showcase * docs(website): add a dedicated FAQ page
This commit is contained in:
parent
82a1a525d7
commit
a4d77f0ff0
|
|
@ -4,7 +4,7 @@
|
|||
>
|
||||
> — N0zoM1z0
|
||||
|
||||
An English static website with explanatory figures, worked guides and DX-Ball, Notion and TH04 investigations.
|
||||
An English static website with explanatory figures, worked guides and DX-Ball, Notion, TH04 and CTF investigations.
|
||||
The public files are in `website/public/`. The site uses HTML, CSS and a small
|
||||
script for copying code and following the assembly-to-C comparison. Python
|
||||
packages the downloadable example; there is no frontend bundler or npm dependency.
|
||||
|
|
@ -30,7 +30,9 @@ Open <http://127.0.0.1:4173/>. Refresh the browser after editing a file.
|
|||
- `public/showcase/dx-ball/index.html`: sound-pan investigation and project status.
|
||||
- `public/showcase/notion/index.html`: Notion's Electron clipboard bridge and rich clipboard format.
|
||||
- `public/showcase/th04/index.html`: TH04's 16-bit DOS bullet-angle calculation and compiler checks.
|
||||
- `public/showcase/ctf/index.html`: DownUnderCTF's masked-squares flag checker, extracted equations and real process captures.
|
||||
- `public/get-started/index.html`: agent setup, first CLI result and provider guides.
|
||||
- `public/faq/index.html`: concise answers about setup, updates, analysis and troubleshooting.
|
||||
- `public/guides/`: a guide hub and native, JavaScript/Electron and browser examples.
|
||||
- `public/examples/`: downloadable Electron source and an interactive Notes browser app.
|
||||
- `public/assets/`: shared styles, interactions, favicon and explanatory figures.
|
||||
|
|
@ -69,6 +71,13 @@ scrolling and returns to the page header, with smooth scrolling when reduced
|
|||
motion is disabled. Keyboard activation returns focus to the first navigation
|
||||
link. Without JavaScript, the link stays visible and uses its `#top` anchor.
|
||||
|
||||
The FAQ uses native `<details>` for its answers. Keep replies short and link to
|
||||
on-site guides for the next step. Individual answers have stable fragment IDs;
|
||||
the shared script opens an answer when its fragment is visited. The questions
|
||||
and disclosure controls remain usable without JavaScript. Check FAQ commands
|
||||
and support statements against the English README and relevant guides when
|
||||
updating them.
|
||||
|
||||
DX-Ball figures and findings refer to the linked 7 October 2026 checkpoint,
|
||||
commit `a55dca27ec0a07018c1b2c95ae2be027f7d8c3c4`. Update those links and figures
|
||||
together when moving to another checkpoint. Case-study source excerpts come
|
||||
|
|
@ -107,6 +116,20 @@ records the fresh load-image/function evidence and separately credited TH04
|
|||
source and historical compiler replay. The figure illustrates the calculation;
|
||||
original game assets and executable bytes are not website downloads.
|
||||
|
||||
## CTF case study
|
||||
|
||||
The DownUnderCTF 2023 case follows the official `ms_flag_checker` handout from
|
||||
prompt references to checking functions, compressed masks, equations and an
|
||||
accepted flag. [evidence/ctf-masked-squares.md](evidence/ctf-masked-squares.md)
|
||||
records the REA analysis, extracted data and positive/negative process captures.
|
||||
The downloadable `public/showcase/ctf/solve.py` uses Python and `z3-solver`;
|
||||
its constants come from REA's byte reads. The original executable stays in the
|
||||
organizers' repository and is linked from the page.
|
||||
|
||||
The mask SVG is drawn from the seventh decoded mask. It selects zero-based
|
||||
position 21 and compares its character code with 55 (`7`). Keep the figure,
|
||||
solver constants and evidence notes aligned when changing this case.
|
||||
|
||||
## GitHub Pages
|
||||
|
||||
`.github/workflows/website-pages.yml` prepares and deploys only `website/public`.
|
||||
|
|
|
|||
|
|
@ -0,0 +1,110 @@
|
|||
# DownUnderCTF masked-squares evidence
|
||||
|
||||
Fresh inspection and process captures on 8 October 2026, using the published
|
||||
REA 4.1.0 package with Ghidra 12.1.4 on Linux x64. The challenge is
|
||||
**masked squares flag checker**, by joseph, tagged easy in the
|
||||
[official DownUnderCTF 2023 repository](https://github.com/DownUnderCTF/Challenges_2023_Public/tree/b48f83db2984bc7fa2ec7879a32d7efb1be8d333/rev/masked-squares-flag-checker).
|
||||
|
||||
## Target
|
||||
|
||||
- Original handout: `publish/ms_flag_checker`, stripped Linux x86-64 ELF.
|
||||
- Size: 15,248 bytes.
|
||||
- SHA-256: `dcd3bec4f608e11f3a12ce461100aaf5f621851ee63a1f870d7c90d4e3dd51ae`.
|
||||
- Upstream revision: `b48f83db2984bc7fa2ec7879a32d7efb1be8d333`.
|
||||
- Git blob: `af95d9e109b5b700b3285e7e9f3b341dc716d34a`.
|
||||
- REA analysis-profile digest:
|
||||
`7cb87eeb3886b20e336087d762d35caac496279cf9839dcc5e741b973627c1ba`.
|
||||
|
||||
The original challenge was downloaded from GitHub's contents API and decoded
|
||||
from its base64 representation. The decoded length matches the API's size;
|
||||
REA's target digest matches the downloaded bytes. The byte digest was checked
|
||||
again after analysis and execution. The page links to the original handout;
|
||||
the executable is not copied into the website.
|
||||
|
||||
The supplied AGENTS.md and website writing/design guidelines were followed in
|
||||
an isolated worktree. Raw results and local target paths stay in private scratch
|
||||
storage; the public page uses a shortened target name in its example request.
|
||||
|
||||
## Discovery through REA
|
||||
|
||||
The following requests preceded reading the organizers' source, solution or
|
||||
flag metadata. Searching the challenge README established its name/difficulty,
|
||||
and the handout was analyzed directly.
|
||||
|
||||
| Operation | Observed result | Evidence ID |
|
||||
| ----------------------------------------------------- | -------------------------------------------------------------------------------- | --------------------------------------------------------------------- |
|
||||
| `search_strings`, regex `flag\|correct\|wrong\|input` | Prompt at `0x102004`; failure at `0x10201c`; success at `0x102027` | `ev_375cf2c47d9cd666eeb5527e13863ce8d01ca86d11a6957fde79227aab1266c8` |
|
||||
| `xrefs`, address `0x102004` | Reference at `0x10126d` | `ev_31fb7e370e0ce8bccdf981167f49ad9196f67ac06c88eb2c1a19e3cb367e22f3` |
|
||||
| `resolve_containing_procedure`, address `0x10126d` | Function at `0x10124d`, inclusive body through `0x10134f`, 259 bytes | `ev_0627cfd459c1d9255ae1b03355987a93a0827e658a7ff40abcbf2cb1cfd8e613` |
|
||||
| `analyze_function`, procedure `0x10124d` | Input conversion, two helper calls, mask/target pointers, success/failure branch | `ev_4ba6d97358da28a23dc69cb6fde01cc929f28fc14f3a3661f4c65bbb6d86ec29` |
|
||||
| `analyze_function`, procedure `0x101189` | Signed-byte run-length mask expansion | `ev_9e4f339895223a21cbf5bcd008ba8b5890e2e804675a11cb286f7af19246f766` |
|
||||
| `analyze_function`, procedure `0x101217` | Add input integers at selected mask positions | `ev_d89261458e3faddd85110185bcd97f0f3c87e0992edc64cb3b2a1ed44846c3e9` |
|
||||
| `read_bytes`, address `0x104060`, length 144 | Target numbers followed by padding and the start of the mask table | `ev_f8a3529dd5d583b06177be5ce76ca6de36f3242ac90ab30f9a3a96159ff84e53` |
|
||||
| `read_bytes`, address `0x1040e0`, length 624 | All 26 mask records, 24 bytes each | `ev_944d9664d6167bf46c72b9fce662e573ea4e017c5a8ebbc305b1d943a1623301` |
|
||||
|
||||
The first 104 bytes of the target read hold the 26 little-endian integers.
|
||||
The displayed `read_bytes` requests preserve the actual lengths; the page
|
||||
selects the seventh entries from those complete results.
|
||||
|
||||
## Interpretation
|
||||
|
||||
`CMP EDI,0x24` terminates the input conversion at 36 characters. Its inner
|
||||
loop groups six integer cells per row. The checking loop walks from `0x1040e0`
|
||||
to `0x104350` in 24-byte steps: `0x270 / 0x18 = 26` masks.
|
||||
|
||||
In the mask helper, `MOVSX` loads each byte with its sign. Positive runs write
|
||||
ones, negative runs write zeros, and a zero terminates a record. All extracted
|
||||
records decode to exactly 36 cells. In the summing helper, a zero mask cell
|
||||
skips the addition; a nonzero cell adds the corresponding input integer.
|
||||
The caller compares the returned sum against a stored target.
|
||||
|
||||
The seventh record starts at `0x104170` and begins `eb 01 f2 00`, meaning
|
||||
`-21, +1, -14, stop`. It selects only zero-based input position 21.
|
||||
Its target at `0x104078` is `37 00 00 00`, decimal 55, the code for `7`.
|
||||
The SVG is calculated from this record, with only cell 21 highlighted.
|
||||
|
||||
The first mask selects 16 cells and its target is 1441. In the recovered flag,
|
||||
changing position 8 from `z` (122) to `y` (121) changes that sum to 1440.
|
||||
|
||||
The website's C is an analyst-written summary of the sum helper and caller,
|
||||
with a flattened 36-cell loop and descriptive names. Its selected assembly
|
||||
is transcribed from the returned dossiers. The mask/target addresses are
|
||||
analysis-image addresses; the executable's PIE runtime load address can differ.
|
||||
|
||||
## Solver and source cross-check
|
||||
|
||||
The downloadable Python script embeds the REA-extracted masks and targets,
|
||||
expands their run lengths and gives the 26 sums to Z3. It asks for 36 character
|
||||
codes using a conventional `DUCTF{...}` prefix/suffix and an alphabet of
|
||||
letters, digits, underscores and braces. Those format restrictions are analyst
|
||||
assumptions, distinguished from the observed binary checks in the script and
|
||||
page caption.
|
||||
|
||||
The solver returned `DUCTF{ezzzpzzz_07bcda7bfe81faf43caa}` and all 26 extracted
|
||||
equations match that value. A separate second-model search reached its timeout;
|
||||
the completed verification concerns the returned candidate.
|
||||
|
||||
After obtaining and executing the candidate, the
|
||||
[organizers' source](https://github.com/DownUnderCTF/Challenges_2023_Public/blob/b48f83db2984bc7fa2ec7879a32d7efb1be8d333/rev/masked-squares-flag-checker/src/ms_flag_checker.c)
|
||||
confirmed the decoding/summing interpretation, all masks and all targets.
|
||||
The
|
||||
[challenge metadata](https://github.com/DownUnderCTF/Challenges_2023_Public/blob/b48f83db2984bc7fa2ec7879a32d7efb1be8d333/rev/masked-squares-flag-checker/ctfcli.yaml)
|
||||
records the same flag.
|
||||
|
||||
## Original-program verification
|
||||
|
||||
Two `capture_process_scenario` requests launched the original handout in its
|
||||
scratch directory, supplied one line of input and retained terminal output.
|
||||
|
||||
| Input | Output | Exit | Evidence ID |
|
||||
| -------------------------------------- | ------------ | ---- | --------------------------------------------------------------------- |
|
||||
| `DUCTF{ezzzpzzz_07bcda7bfe81faf43caa}` | `Correct!` | 0 | `ev_99f724502d14c798959f510d274f3de0f554a27b5df5dd6cb322acfa3e90a452` |
|
||||
| `DUCTF{ezyzpzzz_07bcda7bfe81faf43caa}` | `Incorrect!` | 255 | `ev_2d164f7f4c7eb139e5df8be21c1e52dc34f0ad238aea384e407a2bd6277024cb` |
|
||||
|
||||
Both captures completed without truncation. REA verified its owned process-group
|
||||
cleanup and removed the capture temporary roots. Filesystem observation paths
|
||||
were not requested. The native analysis session was closed afterward.
|
||||
|
||||
The challenge's original code and metadata are credited to DownUnderCTF/joseph.
|
||||
The page, readable C summary, equation figure and Python solver are new work
|
||||
based on this inspection.
|
||||
|
|
@ -114,3 +114,17 @@ Second check labels verbatim:
|
|||
Use enough horizontal space for clear check names and generous typography. A two-line label is fine where needed.
|
||||
|
||||
Text constraints: render every supplied English label and number verbatim with correct spelling. No extra claims, no additional labels, no footnotes, no source code or assembly, no logos or watermarks. Keep all connectors complete and directed correctly. Large legible labels matter more than decorative density. Never say full reconstruction, exact game clone, all bytes, or automatic source recovery.
|
||||
|
||||
## CTF masked-squares figure
|
||||
|
||||
`public/assets/figures/ctf-mask-equation.svg` explains the seventh check in
|
||||
DownUnderCTF 2023's masked-squares flag checker. The original encoded mask is
|
||||
`-21, +1, -14, 0`: expand it to 36 cells, highlight zero-based cell 21 and
|
||||
compare its code with the recorded target 55 (`7`). Labels and selected cells
|
||||
come from the fresh REA byte reads documented in
|
||||
[evidence/ctf-masked-squares.md](evidence/ctf-masked-squares.md).
|
||||
|
||||
The diagram is maintained as SVG source using the shared blue/gray palette.
|
||||
It illustrates one equation; the downloadable Python solver uses all 26.
|
||||
Keep code selectable in HTML and use the existing scrollable figure viewport
|
||||
on smaller screens.
|
||||
|
|
|
|||
File diff suppressed because one or more lines are too long
|
After Width: | Height: | Size: 7.3 KiB |
|
|
@ -61,6 +61,28 @@ function initializeStepComparisons() {
|
|||
|
||||
initializeStepComparisons();
|
||||
|
||||
function initializeFaqAnswers() {
|
||||
const answers = document.querySelectorAll("[data-faq] details[id]");
|
||||
if (answers.length === 0) return;
|
||||
|
||||
const revealAnswer = (fragment) => {
|
||||
answers.forEach((answer) => {
|
||||
if (`#${answer.id}` === fragment) answer.open = true;
|
||||
});
|
||||
};
|
||||
window.addEventListener("hashchange", () =>
|
||||
revealAnswer(window.location.hash),
|
||||
);
|
||||
document.querySelectorAll('[data-faq] a[href^="#"]').forEach((link) => {
|
||||
link.addEventListener("click", () =>
|
||||
revealAnswer(link.getAttribute("href")),
|
||||
);
|
||||
});
|
||||
revealAnswer(window.location.hash);
|
||||
}
|
||||
|
||||
initializeFaqAnswers();
|
||||
|
||||
function initializeBackToTop() {
|
||||
const link = document.querySelector(".back-to-top");
|
||||
if (link === null) return;
|
||||
|
|
|
|||
|
|
@ -171,6 +171,26 @@ section[id] {
|
|||
.navigation .nav-start {
|
||||
color: var(--ink);
|
||||
}
|
||||
.faq-list {
|
||||
border-top: 1px solid var(--line);
|
||||
}
|
||||
.faq-item {
|
||||
padding-block: 1rem;
|
||||
border-bottom: 1px solid var(--line);
|
||||
scroll-margin-top: 2rem;
|
||||
}
|
||||
.faq-item summary {
|
||||
cursor: pointer;
|
||||
color: var(--ink);
|
||||
font-weight: 600;
|
||||
line-height: 1.55;
|
||||
}
|
||||
.faq-item[open] summary {
|
||||
margin-bottom: 0.9rem;
|
||||
}
|
||||
.faq-item > :last-child {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
.eyebrow {
|
||||
margin-bottom: 1.2rem;
|
||||
font-size: 0.875rem;
|
||||
|
|
@ -872,7 +892,8 @@ pre code {
|
|||
.th04-overview {
|
||||
margin-bottom: 3.2rem;
|
||||
}
|
||||
.th04-ring-figure img {
|
||||
.th04-ring-figure img,
|
||||
.ctf-mask-figure img {
|
||||
min-width: 680px;
|
||||
}
|
||||
.th04-preview img {
|
||||
|
|
@ -1057,10 +1078,16 @@ pre code {
|
|||
}
|
||||
.header-inner {
|
||||
min-height: 78px;
|
||||
gap: 1rem;
|
||||
flex-wrap: wrap;
|
||||
justify-content: flex-start;
|
||||
gap: 0.55rem;
|
||||
padding-block: 0.8rem;
|
||||
}
|
||||
.navigation {
|
||||
gap: 1rem;
|
||||
width: 100%;
|
||||
justify-content: space-between;
|
||||
flex-wrap: wrap;
|
||||
gap: 0.5rem 0.25rem;
|
||||
font-size: 0.875rem;
|
||||
}
|
||||
.hero {
|
||||
|
|
@ -1179,16 +1206,6 @@ pre code {
|
|||
}
|
||||
|
||||
@media (max-width: 360px) {
|
||||
.header-inner {
|
||||
flex-wrap: wrap;
|
||||
justify-content: flex-start;
|
||||
gap: 0.3rem;
|
||||
padding-block: 0.65rem;
|
||||
}
|
||||
.navigation {
|
||||
width: 100%;
|
||||
justify-content: space-between;
|
||||
}
|
||||
.stats,
|
||||
.case-stats {
|
||||
grid-template-columns: 1fr;
|
||||
|
|
|
|||
|
|
@ -0,0 +1,413 @@
|
|||
<!doctype html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||
<title>FAQ — REA</title>
|
||||
<meta
|
||||
name="description"
|
||||
content="Answers to common REA questions: installation, updates, analysis tools, coding agents, troubleshooting and sharing examples."
|
||||
/>
|
||||
<meta name="theme-color" content="#ffffff" />
|
||||
<link rel="icon" href="../assets/favicon.svg" type="image/svg+xml" />
|
||||
<link rel="stylesheet" href="../assets/styles.css" />
|
||||
<script src="../assets/main.js" defer></script>
|
||||
</head>
|
||||
<body id="top">
|
||||
<a class="skip-link" href="#main">Skip to content</a>
|
||||
<header class="site-header">
|
||||
<div class="container header-inner">
|
||||
<a class="brand" href="../" aria-label="REA home"
|
||||
><span class="brand-name">REA</span
|
||||
><span class="brand-caption">Reverse Engineer Anything</span></a
|
||||
>
|
||||
<nav class="navigation" aria-label="Main navigation">
|
||||
<a href="../showcase/">Showcases</a>
|
||||
<a class="nav-start" href="../get-started/">Get started</a>
|
||||
<a href="../guides/">Guides</a>
|
||||
<a href="./" aria-current="page">FAQ</a>
|
||||
<a href="https://github.com/morluto/rea">GitHub</a>
|
||||
</nav>
|
||||
</div>
|
||||
</header>
|
||||
<main id="main" class="container">
|
||||
<div class="page-heading">
|
||||
<p class="breadcrumb"><a href="../">Home</a> / FAQ</p>
|
||||
<h1>REA FAQ.</h1>
|
||||
<p class="lede">Setup, updates, analysis and troubleshooting.</p>
|
||||
</div>
|
||||
<div class="article-layout">
|
||||
<nav class="contents" aria-label="On this page">
|
||||
<p>Topics</p>
|
||||
<ul>
|
||||
<li><a href="#setup">Setup and updates</a></li>
|
||||
<li><a href="#analysis">Analysis</a></li>
|
||||
<li><a href="#troubleshooting">Troubleshooting</a></li>
|
||||
<li><a href="#sharing">Data and sharing</a></li>
|
||||
</ul>
|
||||
</nav>
|
||||
<article
|
||||
class="article"
|
||||
data-faq
|
||||
aria-label="Frequently asked questions about REA"
|
||||
>
|
||||
<section class="article-section" id="setup">
|
||||
<h2>Setup and updates</h2>
|
||||
<div class="faq-list">
|
||||
<details class="faq-item" id="install">
|
||||
<summary>How do I install REA?</summary>
|
||||
<p>
|
||||
With Node.js and npm available, run setup, choose your coding
|
||||
agent and review the proposed changes.
|
||||
</p>
|
||||
<div class="code-block">
|
||||
<div class="code-header">
|
||||
<span>Terminal · agent setup</span
|
||||
><button
|
||||
class="copy-button"
|
||||
type="button"
|
||||
data-copy="faq-setup"
|
||||
aria-label="Copy REA setup command"
|
||||
>
|
||||
Copy
|
||||
</button>
|
||||
</div>
|
||||
<pre><code id="faq-setup">npx rea-agents@latest setup</code></pre>
|
||||
</div>
|
||||
<p>
|
||||
Setup registers REA's MCP connection and installs the matching
|
||||
investigation skill. Restart your agent when it finishes, then
|
||||
give it a target path and a question.
|
||||
<a href="../get-started/">Follow the setup guide</a> for
|
||||
prerequisites and your first analysis.
|
||||
</p>
|
||||
</details>
|
||||
<details class="faq-item" id="agents">
|
||||
<summary>Which coding agents can use REA?</summary>
|
||||
<p>
|
||||
Any agent that supports local MCP servers. Setup includes
|
||||
Claude Code, Claude Desktop, Codex, Cursor, Gemini CLI,
|
||||
Windsurf, Devin, OpenCode, Antigravity, GitHub Copilot CLI,
|
||||
Command Code and VS Code.
|
||||
</p>
|
||||
<p>
|
||||
<a href="../get-started/#agent">Connect your coding agent</a>.
|
||||
</p>
|
||||
</details>
|
||||
<details class="faq-item" id="update">
|
||||
<summary>How do I update REA?</summary>
|
||||
<p>
|
||||
REA releases frequently, including bug fixes. If you installed
|
||||
the CLI with npm:
|
||||
</p>
|
||||
<div class="code-block">
|
||||
<div class="code-header">
|
||||
<span>Terminal · installed CLI</span
|
||||
><button
|
||||
class="copy-button"
|
||||
type="button"
|
||||
data-copy="faq-update-cli"
|
||||
aria-label="Copy REA update command"
|
||||
>
|
||||
Copy
|
||||
</button>
|
||||
</div>
|
||||
<pre><code id="faq-update-cli">rea update</code></pre>
|
||||
</div>
|
||||
<p>
|
||||
Follow the setup refresh command printed by the update. If you
|
||||
set up your agent through <code>npx</code>, run:
|
||||
</p>
|
||||
<div class="code-block">
|
||||
<div class="code-header">
|
||||
<span>Terminal · refresh agent setup</span
|
||||
><button
|
||||
class="copy-button"
|
||||
type="button"
|
||||
data-copy="faq-update-agent"
|
||||
aria-label="Copy agent setup refresh command"
|
||||
>
|
||||
Copy
|
||||
</button>
|
||||
</div>
|
||||
<pre><code id="faq-update-agent">npx rea-agents@latest setup</code></pre>
|
||||
</div>
|
||||
<p>
|
||||
Review the changes and restart your agent. Its REA connection
|
||||
pins a package version, so refreshing setup makes it use the
|
||||
new release. For one-off CLI commands, use
|
||||
<code>npx -y rea-agents@latest</code> followed by the command.
|
||||
</p>
|
||||
</details>
|
||||
<details class="faq-item" id="skill">
|
||||
<summary>
|
||||
Is installing the skill from skills.sh enough?
|
||||
</summary>
|
||||
<p>
|
||||
The skill gives your agent investigation instructions. Run
|
||||
<a href="#install">REA setup</a> to connect the MCP server and
|
||||
install the matching skill, then restart your agent so it can
|
||||
call the tools.
|
||||
</p>
|
||||
</details>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section class="article-section" id="analysis">
|
||||
<h2>Analysis</h2>
|
||||
<div class="faq-list">
|
||||
<details class="faq-item" id="targets">
|
||||
<summary>What can I analyze with REA?</summary>
|
||||
<p>Start with the kind of software you have:</p>
|
||||
<ul>
|
||||
<li>
|
||||
<a href="../guides/native/"
|
||||
>Native executables and libraries</a
|
||||
>: functions, assembly, pseudocode and references.
|
||||
</li>
|
||||
<li>
|
||||
<a href="../guides/javascript/"
|
||||
>JavaScript and Electron apps</a
|
||||
>: modules, imports, IPC and source locations.
|
||||
</li>
|
||||
<li>
|
||||
<a href="../guides/browser/">Browser pages</a>: scripts,
|
||||
requests and runtime observations.
|
||||
</li>
|
||||
</ul>
|
||||
<p>
|
||||
REA also has workflows for managed assemblies, Android APKs,
|
||||
firmware and packaged resources.
|
||||
<a href="../showcase/">See the case studies</a> for worked
|
||||
investigations.
|
||||
</p>
|
||||
</details>
|
||||
<details class="faq-item" id="providers">
|
||||
<summary>Do I need Hopper, Ghidra or IDA?</summary>
|
||||
<p>
|
||||
Deep native analysis uses one of them. Static JavaScript and
|
||||
.NET inspection work with the supplied files and do not need a
|
||||
native analysis engine.
|
||||
</p>
|
||||
<p>
|
||||
Connect your existing Ghidra or IDA installation, or let setup
|
||||
detect Hopper and propose an installation. Your host and
|
||||
target determine which provider can be used.
|
||||
<a href="../get-started/#native">Set up native analysis</a>.
|
||||
</p>
|
||||
</details>
|
||||
<details class="faq-item" id="cli">
|
||||
<summary>Can I use REA without a coding agent?</summary>
|
||||
<p>
|
||||
Yes. The CLI uses the same analysis workflows. For a local
|
||||
JavaScript or Electron app:
|
||||
</p>
|
||||
<div class="code-block">
|
||||
<div class="code-header">
|
||||
<span>Terminal · static application analysis</span
|
||||
><button
|
||||
class="copy-button"
|
||||
type="button"
|
||||
data-copy="faq-cli-analysis"
|
||||
aria-label="Copy REA CLI analysis command"
|
||||
>
|
||||
Copy
|
||||
</button>
|
||||
</div>
|
||||
<pre><code id="faq-cli-analysis">npx -y rea-agents@latest analyze-javascript-application \
|
||||
/absolute/path/to/app --json</code></pre>
|
||||
</div>
|
||||
<p>
|
||||
Replace the path with an extracted application folder or ASAR
|
||||
file. <a href="../get-started/#terminal">Use the CLI</a> for
|
||||
installation options.
|
||||
</p>
|
||||
</details>
|
||||
<details class="faq-item" id="code">
|
||||
<summary>What code does REA return?</summary>
|
||||
<p>
|
||||
Native analysis returns assembly and pseudocode, with calls,
|
||||
references and relevant constants. JavaScript analysis returns
|
||||
modules, their relationships and source locations.
|
||||
</p>
|
||||
<p>
|
||||
Your agent uses these findings to explain the behavior or
|
||||
write and test an implementation. The
|
||||
<a href="../showcase/dx-ball/#sound-pan"
|
||||
>DX-Ball calculation</a
|
||||
>
|
||||
and <a href="../showcase/ctf/">CTF flag checker</a> show how
|
||||
that process works.
|
||||
</p>
|
||||
</details>
|
||||
<details class="faq-item" id="prompt">
|
||||
<summary>What should I ask my agent?</summary>
|
||||
<p>
|
||||
Give it a local target and one behavior to investigate. For a
|
||||
JavaScript or Electron app:
|
||||
</p>
|
||||
<div class="agent-terminal">
|
||||
<div class="terminal-header">
|
||||
<span class="terminal-title"
|
||||
><span class="terminal-dots" aria-hidden="true"
|
||||
><i></i><i></i><i></i></span
|
||||
>Your coding agent</span
|
||||
><button
|
||||
class="copy-button"
|
||||
type="button"
|
||||
data-copy="faq-analysis-prompt"
|
||||
aria-label="Copy example analysis prompt"
|
||||
>
|
||||
Copy
|
||||
</button>
|
||||
</div>
|
||||
<p class="terminal-prompt">
|
||||
<span class="prompt-symbol" aria-hidden="true">›</span
|
||||
><span id="faq-analysis-prompt"
|
||||
>Use REA to analyze /absolute/path/to/app and explain how
|
||||
it exports data. Show the relevant code and source
|
||||
locations.</span
|
||||
><span class="terminal-cursor" aria-hidden="true"></span>
|
||||
</p>
|
||||
</div>
|
||||
<p class="caption">
|
||||
Replace the path with your target. The
|
||||
<a href="../guides/">guides</a> provide prompts for native,
|
||||
Electron and browser examples.
|
||||
</p>
|
||||
</details>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section class="article-section" id="troubleshooting">
|
||||
<h2>Troubleshooting</h2>
|
||||
<div class="faq-list">
|
||||
<details class="faq-item" id="connection">
|
||||
<summary>Why can't my agent see the REA tools?</summary>
|
||||
<p>
|
||||
Restart or reconnect your agent after setup. Check its REA
|
||||
registration with <code>doctor</code>; for Codex:
|
||||
</p>
|
||||
<div class="code-block">
|
||||
<div class="code-header">
|
||||
<span>Terminal · check one client</span
|
||||
><button
|
||||
class="copy-button"
|
||||
type="button"
|
||||
data-copy="faq-doctor"
|
||||
aria-label="Copy client diagnosis command"
|
||||
>
|
||||
Copy
|
||||
</button>
|
||||
</div>
|
||||
<pre><code id="faq-doctor">npx -y rea-agents@latest doctor --client codex --json</code></pre>
|
||||
</div>
|
||||
<p>
|
||||
Replace <code>codex</code> with your client ID, such as
|
||||
<code>claude_code</code> or <code>cursor</code>. If the report
|
||||
shows a missing or stale registration, run setup for that
|
||||
client:
|
||||
</p>
|
||||
<div class="code-block">
|
||||
<div class="code-header">
|
||||
<span>Terminal · repair one client's setup</span
|
||||
><button
|
||||
class="copy-button"
|
||||
type="button"
|
||||
data-copy="faq-repair"
|
||||
aria-label="Copy scoped setup command"
|
||||
>
|
||||
Copy
|
||||
</button>
|
||||
</div>
|
||||
<pre><code id="faq-repair">npx rea-agents@latest setup --client codex</code></pre>
|
||||
</div>
|
||||
<p>
|
||||
Review its plan and restart the client. If registration is
|
||||
healthy but tools remain absent, check the client's MCP
|
||||
connection error.
|
||||
<a href="../get-started/#help">More setup diagnostics</a>.
|
||||
</p>
|
||||
</details>
|
||||
<details class="faq-item" id="hopper">
|
||||
<summary>Do I need to start Hopper first?</summary>
|
||||
<p>
|
||||
REA starts Hopper when an operation needs it. On macOS,
|
||||
complete the first-run choice of demo mode or license
|
||||
activation before unattended analysis.
|
||||
<a href="../get-started/#native">See native provider setup</a
|
||||
>.
|
||||
</p>
|
||||
</details>
|
||||
<details class="faq-item" id="bugs">
|
||||
<summary>What should I do if I hit a bug?</summary>
|
||||
<p>
|
||||
<a href="#update">Update REA</a>, restart your agent and retry
|
||||
the same task. A recent release may already fix the problem.
|
||||
</p>
|
||||
<p>
|
||||
If it persists,
|
||||
<a href="https://github.com/morluto/rea/issues/new/choose"
|
||||
>open an issue</a
|
||||
>
|
||||
with your REA version, operating system, target type, steps to
|
||||
reproduce and error output. Include the provider name when the
|
||||
problem involves native analysis.
|
||||
</p>
|
||||
</details>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section class="article-section" id="sharing">
|
||||
<h2>Data and sharing</h2>
|
||||
<div class="faq-list">
|
||||
<details class="faq-item" id="privacy">
|
||||
<summary>Does REA upload my app?</summary>
|
||||
<p>
|
||||
REA analyzes targets on your machine. Your agent receives the
|
||||
results, and its model provider's data policy applies to those
|
||||
results.
|
||||
</p>
|
||||
</details>
|
||||
<details class="faq-item" id="contribute">
|
||||
<summary>How can I share an example or contribute?</summary>
|
||||
<p>
|
||||
Have a useful REA example? We'd love to see it. Share the
|
||||
target, your question, the findings and how you checked them
|
||||
in an
|
||||
<a href="https://github.com/morluto/rea/issues/new/choose"
|
||||
>issue</a
|
||||
>
|
||||
or
|
||||
<a href="https://github.com/morluto/rea/pulls">pull request</a
|
||||
>. Short code excerpts and figures help others follow the
|
||||
investigation.
|
||||
</p>
|
||||
<p>
|
||||
Bug reports, feature requests, documentation and code
|
||||
improvements are welcome too. You can also discuss an example
|
||||
in the <a href="https://discord.gg/GkcryMnJDM">community</a>.
|
||||
</p>
|
||||
</details>
|
||||
</div>
|
||||
</section>
|
||||
</article>
|
||||
</div>
|
||||
</main>
|
||||
<footer class="site-footer">
|
||||
<div class="container footer-inner">
|
||||
<p>REA · Reverse Engineer Anything</p>
|
||||
<div class="footer-links">
|
||||
<a href="https://github.com/morluto/rea">Source</a
|
||||
><a href="https://github.com/morluto/rea/blob/main/LICENSE"
|
||||
>MIT license</a
|
||||
><a href="https://discord.gg/GkcryMnJDM">Community</a>
|
||||
</div>
|
||||
</div>
|
||||
</footer>
|
||||
<p class="sr-only" id="copy-status" role="status" aria-live="polite"></p>
|
||||
<a class="back-to-top" href="#top" aria-label="Back to top"
|
||||
><span aria-hidden="true">↑</span> Top</a
|
||||
>
|
||||
</body>
|
||||
</html>
|
||||
|
|
@ -24,8 +24,8 @@
|
|||
<nav class="navigation" aria-label="Main navigation">
|
||||
<a href="../showcase/">Showcases</a
|
||||
><a class="nav-start" href="./" aria-current="page">Get started</a
|
||||
><a href="../guides/">Guides</a
|
||||
><a href="https://github.com/morluto/rea">GitHub</a>
|
||||
><a href="../guides/">Guides</a><a href="../faq/">FAQ</a>
|
||||
<a href="https://github.com/morluto/rea">GitHub</a>
|
||||
</nav>
|
||||
</div>
|
||||
</header>
|
||||
|
|
@ -367,6 +367,10 @@ npx rea-agents@latest setup</code></pre>
|
|||
</div>
|
||||
<p>The report gives the failed check and a suggested next step.</p>
|
||||
<ul class="reading-list">
|
||||
<li>
|
||||
<a href="../faq/">REA FAQ</a
|
||||
><span>Quick answers on updates, agents and analysis.</span>
|
||||
</li>
|
||||
<li>
|
||||
<a href="../guides/">Choose an investigation guide</a
|
||||
><span
|
||||
|
|
|
|||
|
|
@ -24,8 +24,8 @@
|
|||
<nav class="navigation" aria-label="Main navigation">
|
||||
<a href="../../showcase/">Showcases</a
|
||||
><a class="nav-start" href="../../get-started/">Get started</a
|
||||
><a href="../">Guides</a
|
||||
><a href="https://github.com/morluto/rea">GitHub</a>
|
||||
><a href="../">Guides</a><a href="../../faq/">FAQ</a>
|
||||
<a href="https://github.com/morluto/rea">GitHub</a>
|
||||
</nav>
|
||||
</div>
|
||||
</header>
|
||||
|
|
|
|||
|
|
@ -24,8 +24,8 @@
|
|||
<nav class="navigation" aria-label="Main navigation">
|
||||
<a href="../showcase/">Showcases</a
|
||||
><a class="nav-start" href="../get-started/">Get started</a
|
||||
><a href="./" aria-current="page">Guides</a
|
||||
><a href="https://github.com/morluto/rea">GitHub</a>
|
||||
><a href="./" aria-current="page">Guides</a><a href="../faq/">FAQ</a>
|
||||
<a href="https://github.com/morluto/rea">GitHub</a>
|
||||
</nav>
|
||||
</div>
|
||||
</header>
|
||||
|
|
|
|||
|
|
@ -24,8 +24,8 @@
|
|||
<nav class="navigation" aria-label="Main navigation">
|
||||
<a href="../../showcase/">Showcases</a
|
||||
><a class="nav-start" href="../../get-started/">Get started</a
|
||||
><a href="../">Guides</a
|
||||
><a href="https://github.com/morluto/rea">GitHub</a>
|
||||
><a href="../">Guides</a><a href="../../faq/">FAQ</a>
|
||||
<a href="https://github.com/morluto/rea">GitHub</a>
|
||||
</nav>
|
||||
</div>
|
||||
</header>
|
||||
|
|
|
|||
|
|
@ -24,8 +24,8 @@
|
|||
<nav class="navigation" aria-label="Main navigation">
|
||||
<a href="../../showcase/">Showcases</a
|
||||
><a class="nav-start" href="../../get-started/">Get started</a
|
||||
><a href="../">Guides</a
|
||||
><a href="https://github.com/morluto/rea">GitHub</a>
|
||||
><a href="../">Guides</a><a href="../../faq/">FAQ</a>
|
||||
<a href="https://github.com/morluto/rea">GitHub</a>
|
||||
</nav>
|
||||
</div>
|
||||
</header>
|
||||
|
|
|
|||
|
|
@ -25,6 +25,7 @@
|
|||
<a href="showcase/">Showcases</a>
|
||||
<a class="nav-start" href="get-started/">Get started</a>
|
||||
<a href="guides/">Guides</a>
|
||||
<a href="faq/">FAQ</a>
|
||||
<a href="https://github.com/morluto/rea">GitHub</a>
|
||||
</nav>
|
||||
</div>
|
||||
|
|
@ -264,6 +265,37 @@
|
|||
</div>
|
||||
</section>
|
||||
|
||||
<section class="section">
|
||||
<div class="container feature-case">
|
||||
<div>
|
||||
<p class="eyebrow">Case study · CTF</p>
|
||||
<h2>Solve a CTF flag checker</h2>
|
||||
<p>
|
||||
Use REA to inspect an easy DownUnderCTF binary. Extract its
|
||||
checking rules, solve for the flag and confirm that the original
|
||||
program accepts it.
|
||||
</p>
|
||||
<a class="text-link" href="showcase/ctf/">See the CTF solution</a>
|
||||
</div>
|
||||
<figure class="workflow-figure case-preview">
|
||||
<p class="figure-title">One check reveals one character</p>
|
||||
<div class="code-block">
|
||||
<div class="code-header">
|
||||
<span>From REA's code and data</span>
|
||||
</div>
|
||||
<pre><code>Mask: skip 21, select 1, skip 14
|
||||
Target: 55
|
||||
|
||||
code[21] = 55 → '7'</code></pre>
|
||||
</div>
|
||||
<figcaption>
|
||||
Each mask selects characters whose codes must add up to a stored
|
||||
number. This one selects a single character.
|
||||
</figcaption>
|
||||
</figure>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section class="section">
|
||||
<div class="container start-row">
|
||||
<div>
|
||||
|
|
|
|||
|
|
@ -0,0 +1,620 @@
|
|||
<!doctype html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||
<title>Solve a CTF flag checker — REA</title>
|
||||
<meta
|
||||
name="description"
|
||||
content="Use REA to solve DownUnderCTF's masked squares flag checker. Find the checking functions, extract the masks and compare a recovered flag with the original program."
|
||||
/>
|
||||
<meta name="theme-color" content="#ffffff" />
|
||||
<link rel="icon" href="../../assets/favicon.svg" type="image/svg+xml" />
|
||||
<link rel="stylesheet" href="../../assets/styles.css" />
|
||||
<script src="../../assets/main.js" defer></script>
|
||||
</head>
|
||||
<body id="top">
|
||||
<a class="skip-link" href="#main">Skip to content</a>
|
||||
<header class="site-header">
|
||||
<div class="container header-inner">
|
||||
<a class="brand" href="../../" aria-label="REA home"
|
||||
><span class="brand-name">REA</span
|
||||
><span class="brand-caption">Reverse Engineer Anything</span></a
|
||||
>
|
||||
<nav class="navigation" aria-label="Main navigation">
|
||||
<a href="../">Showcases</a>
|
||||
<a class="nav-start" href="../../get-started/">Get started</a>
|
||||
<a href="../../guides/">Guides</a>
|
||||
<a href="../../faq/">FAQ</a>
|
||||
<a href="https://github.com/morluto/rea">GitHub</a>
|
||||
</nav>
|
||||
</div>
|
||||
</header>
|
||||
<main id="main" class="container">
|
||||
<div class="page-heading">
|
||||
<p class="breadcrumb">
|
||||
<a href="../../">Home</a> / <a href="../">Showcases</a> / CTF
|
||||
</p>
|
||||
<p class="eyebrow">Case study · DownUnderCTF 2023</p>
|
||||
<h1>Find the flag in<br />a CTF binary.</h1>
|
||||
<p class="lede">
|
||||
Use REA to inspect a small flag checker, turn its rules into equations
|
||||
and find an input that makes the original program print
|
||||
<code>Correct!</code>.
|
||||
</p>
|
||||
<p class="page-meta">
|
||||
masked squares flag checker · Easy · Linux x86-64
|
||||
</p>
|
||||
<a
|
||||
class="text-link"
|
||||
href="https://github.com/DownUnderCTF/Challenges_2023_Public/tree/b48f83db2984bc7fa2ec7879a32d7efb1be8d333/rev/masked-squares-flag-checker"
|
||||
>View the original challenge</a
|
||||
>
|
||||
</div>
|
||||
|
||||
<figure class="workflow-figure case-figure">
|
||||
<p class="figure-title">From a binary to a checked answer</p>
|
||||
<ol class="flow-steps">
|
||||
<li>
|
||||
<span class="flow-label">01 · Find the check</span
|
||||
><strong class="flow-node">Strings → checking functions</strong
|
||||
><span class="flow-note"
|
||||
>REA finds the prompt, its references and the code that accepts or
|
||||
rejects an input.</span
|
||||
>
|
||||
</li>
|
||||
<li>
|
||||
<span class="flow-label">02 · Extract the rules</span
|
||||
><strong class="flow-node">36 characters · 26 sums</strong
|
||||
><span class="flow-note"
|
||||
>REA returns the instructions and data behind each check. The
|
||||
agent turns them into equations.</span
|
||||
>
|
||||
</li>
|
||||
<li>
|
||||
<span class="flow-label">03 · Solve and run</span
|
||||
><strong class="flow-node">Recovered flag → Correct!</strong
|
||||
><span class="flow-note"
|
||||
>A Python script solves the equations. REA captures the original
|
||||
program accepting the result.</span
|
||||
>
|
||||
</li>
|
||||
</ol>
|
||||
<figcaption>
|
||||
The challenge is by joseph, published in DownUnderCTF's official
|
||||
repository.
|
||||
</figcaption>
|
||||
</figure>
|
||||
|
||||
<div class="article-layout">
|
||||
<nav class="contents" aria-label="On this page">
|
||||
<p>In this case study</p>
|
||||
<ul>
|
||||
<li><a href="#find">Find the checking code</a></li>
|
||||
<li><a href="#mask">Read one mask</a></li>
|
||||
<li><a href="#code">Assembly to readable code</a></li>
|
||||
<li><a href="#solve">Solve the equations</a></li>
|
||||
<li><a href="#verify">Check the answer</a></li>
|
||||
<li><a href="#try">Try it yourself</a></li>
|
||||
</ul>
|
||||
</nav>
|
||||
<article class="article" aria-label="CTF flag-checker case study">
|
||||
<section class="article-section" id="find">
|
||||
<h2>Find the checking code</h2>
|
||||
<p>
|
||||
The handout is a 15 KB executable. It asks for a flag, then prints
|
||||
<code>Correct!</code> or <code>Incorrect!</code>. Start with a
|
||||
simple request to your agent.
|
||||
</p>
|
||||
<div class="agent-terminal">
|
||||
<div class="terminal-header">
|
||||
<span class="terminal-title"
|
||||
><span class="terminal-dots" aria-hidden="true"
|
||||
><i></i><i></i><i></i></span
|
||||
>Your coding agent</span
|
||||
>
|
||||
<button
|
||||
class="copy-button"
|
||||
type="button"
|
||||
data-copy="ctf-prompt"
|
||||
aria-label="Copy CTF example prompt"
|
||||
>
|
||||
Copy
|
||||
</button>
|
||||
</div>
|
||||
<p class="terminal-prompt">
|
||||
<span class="prompt-symbol" aria-hidden="true">›</span
|
||||
><span id="ctf-prompt"
|
||||
>Use REA to analyze ms_flag_checker and find the flag. Explain
|
||||
how the checks work, then verify the answer with the original
|
||||
program.</span
|
||||
><span class="terminal-cursor" aria-hidden="true"></span>
|
||||
</p>
|
||||
</div>
|
||||
<p class="caption">
|
||||
An example prompt, with the downloaded challenge available locally
|
||||
and a native analysis provider connected.
|
||||
</p>
|
||||
<ol class="rea-queries">
|
||||
<li>
|
||||
<h4>Follow the input prompt to its function</h4>
|
||||
<div class="rea-query-pair">
|
||||
<div class="code-block">
|
||||
<div class="code-header"><span>Agent → REA</span></div>
|
||||
<pre><code>open_binary
|
||||
{"path": "ms_flag_checker"}
|
||||
|
||||
search_strings
|
||||
{"pattern": "flag|correct|wrong|input",
|
||||
"mode": "regex", "case_sensitive": false}
|
||||
|
||||
xrefs
|
||||
{"address": "0x102004"}
|
||||
|
||||
resolve_containing_procedure
|
||||
{"address": "0x10126d"}</code></pre>
|
||||
</div>
|
||||
<div class="code-block">
|
||||
<div class="code-header">
|
||||
<span>REA → agent · selected results</span>
|
||||
</div>
|
||||
<pre><code>0x102004 "What is the flag? "
|
||||
0x10201c "Incorrect!"
|
||||
0x102027 "Correct!"
|
||||
|
||||
Prompt referenced at: 0x10126d
|
||||
Containing function: 0x10124d</code></pre>
|
||||
</div>
|
||||
</div>
|
||||
<p>
|
||||
The reference gives the agent a place to investigate, even
|
||||
though the executable's original function names have been
|
||||
removed.
|
||||
</p>
|
||||
</li>
|
||||
<li>
|
||||
<h4>Read the checker and its two helpers</h4>
|
||||
<div class="rea-query-pair">
|
||||
<div class="code-block">
|
||||
<div class="code-header"><span>Agent → REA</span></div>
|
||||
<pre><code>analyze_function
|
||||
{"procedure": "0x10124d"}
|
||||
|
||||
analyze_function
|
||||
{"procedure": "0x101189"}
|
||||
|
||||
analyze_function
|
||||
{"procedure": "0x101217"}</code></pre>
|
||||
</div>
|
||||
<div class="code-block">
|
||||
<div class="code-header">
|
||||
<span>REA → agent · selected instructions</span>
|
||||
</div>
|
||||
<pre><code>0x1012c1: CMP EDI, 0x24
|
||||
0x1012c6: LEA RBX, [0x1040e0]
|
||||
0x1012cd: LEA RBP, [0x104060]
|
||||
0x1012d4: LEA R13, [RBX + 0x270]
|
||||
0x1012e1: CALL 0x00101189
|
||||
0x1012ec: CALL 0x00101217
|
||||
0x1012f1: CMP dword ptr [RBP], EAX
|
||||
0x1012f4: JNZ 0x00101335
|
||||
0x1012f6: ADD RBX, 0x18</code></pre>
|
||||
</div>
|
||||
</div>
|
||||
<p>
|
||||
The first loop copies 36 character codes. The checking loop
|
||||
decodes a mask, sums the selected codes and compares the
|
||||
result with a stored number. Its <code>0x18</code>-byte steps
|
||||
across <code>0x270</code> bytes give 26 masks.
|
||||
</p>
|
||||
</li>
|
||||
</ol>
|
||||
<p class="caption">
|
||||
These are selected results from a fresh REA analysis. The local
|
||||
target path is shortened for display; addresses refer to REA's
|
||||
imported image.
|
||||
</p>
|
||||
</section>
|
||||
|
||||
<section class="article-section" id="mask">
|
||||
<h2>One check reveals one character</h2>
|
||||
<p>
|
||||
The checker arranges the input in a 6×6 grid. A mask chooses which
|
||||
cells contribute to a sum. Its compact encoding uses negative
|
||||
numbers to skip cells and positive numbers to select them.
|
||||
</p>
|
||||
<figure class="explanatory-figure ctf-mask-figure">
|
||||
<div
|
||||
class="figure-viewport"
|
||||
tabindex="0"
|
||||
role="region"
|
||||
aria-label="CTF mask equation; scroll horizontally on smaller screens"
|
||||
>
|
||||
<img
|
||||
src="../../assets/figures/ctf-mask-equation.svg"
|
||||
width="760"
|
||||
height="475"
|
||||
alt="The seventh mask skips 21 cells, selects position 21, then skips 14. Only that cell contributes to the sum. Its required character code is 55, which means the character is 7."
|
||||
decoding="async"
|
||||
/>
|
||||
</div>
|
||||
<figcaption>
|
||||
The seventh check selects just one position.
|
||||
<a href="../../assets/figures/ctf-mask-equation.svg"
|
||||
>Open figure</a
|
||||
>.
|
||||
</figcaption>
|
||||
</figure>
|
||||
<h3 class="small-heading">
|
||||
Read the mask and its target from the binary
|
||||
</h3>
|
||||
<div class="rea-query-pair">
|
||||
<div class="code-block">
|
||||
<div class="code-header">
|
||||
<span>Agent → REA · read_bytes</span>
|
||||
</div>
|
||||
<pre><code>{"address": "0x1040e0", "length": 624}
|
||||
{"address": "0x104060", "length": 144}</code></pre>
|
||||
</div>
|
||||
<div class="code-block">
|
||||
<div class="code-header">
|
||||
<span>REA → agent · seventh entries</span>
|
||||
</div>
|
||||
<pre><code>Mask at 0x104170:
|
||||
eb 01 f2 00 → -21, +1, -14, stop
|
||||
|
||||
Target at 0x104078:
|
||||
37 00 00 00 → 55</code></pre>
|
||||
</div>
|
||||
</div>
|
||||
<p>
|
||||
The instruction <code>MOVSX</code> reads mask bytes as signed
|
||||
values. This is why <code>eb</code> means −21. The target is a
|
||||
little-endian integer. Together, these bytes tell the agent that
|
||||
<code>code[21] = 55</code>, so that character is <code>7</code>.
|
||||
</p>
|
||||
</section>
|
||||
|
||||
<section class="article-section" id="code">
|
||||
<h2>From instructions to a readable check</h2>
|
||||
<p>
|
||||
Select a step to connect the summing helper and its caller to the
|
||||
same rule in C.
|
||||
</p>
|
||||
<div class="step-comparison" data-step-comparison>
|
||||
<div
|
||||
class="step-selector"
|
||||
role="group"
|
||||
aria-label="Follow the CTF check"
|
||||
>
|
||||
<button
|
||||
type="button"
|
||||
data-select-step="1"
|
||||
aria-pressed="true"
|
||||
aria-controls="ctf-assembly ctf-check ctf-step-detail"
|
||||
>
|
||||
<span>01</span> Start
|
||||
</button>
|
||||
<button
|
||||
type="button"
|
||||
data-select-step="2"
|
||||
aria-pressed="false"
|
||||
aria-controls="ctf-assembly ctf-check ctf-step-detail"
|
||||
>
|
||||
<span>02</span> Select
|
||||
</button>
|
||||
<button
|
||||
type="button"
|
||||
data-select-step="3"
|
||||
aria-pressed="false"
|
||||
aria-controls="ctf-assembly ctf-check ctf-step-detail"
|
||||
>
|
||||
<span>03</span> Add
|
||||
</button>
|
||||
<button
|
||||
type="button"
|
||||
data-select-step="4"
|
||||
aria-pressed="false"
|
||||
aria-controls="ctf-assembly ctf-check ctf-step-detail"
|
||||
>
|
||||
<span>04</span> Advance
|
||||
</button>
|
||||
<button
|
||||
type="button"
|
||||
data-select-step="5"
|
||||
aria-pressed="false"
|
||||
aria-controls="ctf-assembly ctf-check ctf-step-detail"
|
||||
>
|
||||
<span>05</span> Compare
|
||||
</button>
|
||||
</div>
|
||||
<div class="code-comparison">
|
||||
<div class="code-block">
|
||||
<div class="code-header">
|
||||
<span>REA · selected original instructions</span
|
||||
><button
|
||||
class="copy-button"
|
||||
type="button"
|
||||
data-copy="ctf-assembly"
|
||||
aria-label="Copy CTF assembly"
|
||||
>
|
||||
Copy
|
||||
</button>
|
||||
</div>
|
||||
<pre><code id="ctf-assembly"><span class="code-step is-active" data-step="1">0x10121c: MOV ECX, 0x0</span>
|
||||
|
||||
<span class="code-step" data-step="2">0x10122c: CMP dword ptr [RSI + RAX*0x1], 0x0
|
||||
0x101230: JZ 0x00101223</span>
|
||||
|
||||
<span class="code-step" data-step="3">0x101232: ADD ECX, dword ptr [RDI + RAX*0x1]</span>
|
||||
|
||||
<span class="code-step" data-step="4">0x101223: ADD RAX, 0x4</span>
|
||||
|
||||
<span class="code-step" data-step="5">0x1012f1: CMP dword ptr [RBP], EAX
|
||||
0x1012f4: JNZ 0x00101335</span></code></pre>
|
||||
</div>
|
||||
<div class="code-block">
|
||||
<div class="code-header">
|
||||
<span>Readable C · one-check summary</span
|
||||
><button
|
||||
class="copy-button"
|
||||
type="button"
|
||||
data-copy="ctf-check"
|
||||
aria-label="Copy readable CTF check"
|
||||
>
|
||||
Copy
|
||||
</button>
|
||||
</div>
|
||||
<pre><code id="ctf-check">int check_one_mask(const int codes[36],
|
||||
const int mask[36],
|
||||
int target) {
|
||||
<span class="code-step is-active" data-step="1"> int total = 0;</span>
|
||||
<span class="code-step" data-step="4"> for (int i = 0; i < 36; ++i) {</span>
|
||||
<span class="code-step" data-step="2"> if (mask[i]) {</span>
|
||||
<span class="code-step" data-step="3"> total += codes[i];</span>
|
||||
}
|
||||
}
|
||||
<span class="code-step" data-step="5"> return total == target;</span>
|
||||
}</code></pre>
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
class="step-detail"
|
||||
id="ctf-step-detail"
|
||||
aria-live="polite"
|
||||
aria-atomic="true"
|
||||
>
|
||||
<p data-step-note="1">
|
||||
<strong>01 · Start at zero.</strong> <code>ECX</code> holds
|
||||
the running sum.
|
||||
</p>
|
||||
<p data-step-note="2" hidden>
|
||||
<strong>02 · Check the mask cell.</strong> A zero value skips
|
||||
the character; a nonzero value includes it.
|
||||
</p>
|
||||
<p data-step-note="3" hidden>
|
||||
<strong>03 · Add a character code.</strong> The input's
|
||||
integer at the same offset is added to the sum.
|
||||
</p>
|
||||
<p data-step-note="4" hidden>
|
||||
<strong>04 · Move to the next cell.</strong> The original loop
|
||||
advances four bytes per integer and visits six rows of six
|
||||
cells.
|
||||
</p>
|
||||
<p data-step-note="5" hidden>
|
||||
<strong>05 · Compare with the stored target.</strong> The
|
||||
caller receives the sum in <code>EAX</code>. A mismatch jumps
|
||||
to <code>Incorrect!</code>.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
<p class="caption">
|
||||
The instructions come from the summing helper and its caller. The
|
||||
C is an explanatory summary with descriptive names and a flattened
|
||||
36-cell loop.
|
||||
</p>
|
||||
</section>
|
||||
|
||||
<section class="article-section" id="solve">
|
||||
<h2>Solve the 26 equations</h2>
|
||||
<p>
|
||||
Most masks select several cells. For example, the first check adds
|
||||
16 character codes and requires a total of 1441. The overlapping
|
||||
selections give equations that a small Python script can solve
|
||||
together.
|
||||
</p>
|
||||
<div class="code-block">
|
||||
<div class="code-header">
|
||||
<span>Python · solver excerpt</span
|
||||
><button
|
||||
class="copy-button"
|
||||
type="button"
|
||||
data-copy="ctf-solver-excerpt"
|
||||
aria-label="Copy CTF solver excerpt"
|
||||
>
|
||||
Copy
|
||||
</button>
|
||||
</div>
|
||||
<pre><code id="ctf-solver-excerpt">from z3 import Int, Or, Solver, Sum, sat
|
||||
|
||||
codes = [Int(f"code_{i}") for i in range(36)]
|
||||
solver = Solver()
|
||||
alphabet = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789_{}"
|
||||
for code in codes:
|
||||
solver.add(Or(*[code == ord(c) for c in alphabet]))
|
||||
for code, char in zip(codes, "DUCTF{"):
|
||||
solver.add(code == ord(char))
|
||||
solver.add(codes[-1] == ord("}"))
|
||||
|
||||
for selected, target in checks:
|
||||
solver.add(Sum([codes[i] for i in selected]) == target)
|
||||
|
||||
assert solver.check() == sat
|
||||
model = solver.model()
|
||||
print("".join(chr(model.eval(code).as_long()) for code in codes))</code></pre>
|
||||
</div>
|
||||
<p class="caption">
|
||||
Z3 is a solver that finds values satisfying these equations. This
|
||||
excerpt uses the extracted checks and assumes a
|
||||
<code>DUCTF{…}</code> flag with letters, digits, underscores and
|
||||
braces. The complete download includes all 26 masks and targets.
|
||||
</p>
|
||||
<a class="text-link" href="solve.py" download="solve.py"
|
||||
>Download the complete solver</a
|
||||
>
|
||||
</section>
|
||||
|
||||
<section class="article-section" id="verify">
|
||||
<h2>Check the answer with the original program</h2>
|
||||
<p>
|
||||
REA's process capture records the original executable accepting
|
||||
the solved flag. Changing one selected character from
|
||||
<code>z</code> to <code>y</code> makes the first sum 1440 instead
|
||||
of 1441, and the program rejects it.
|
||||
</p>
|
||||
<div class="evidence-table-wrap">
|
||||
<table class="evidence-table">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Input</th>
|
||||
<th>Observed output</th>
|
||||
<th>Exit code</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr>
|
||||
<td>Solver's flag</td>
|
||||
<td><code>Correct!</code></td>
|
||||
<td>0</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>One character changed</td>
|
||||
<td><code>Incorrect!</code></td>
|
||||
<td>255</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
<details class="evidence-details">
|
||||
<summary>Show the recovered flag and captured output</summary>
|
||||
<div class="code-block">
|
||||
<div class="code-header">
|
||||
<span>REA · original program output</span
|
||||
><button
|
||||
class="copy-button"
|
||||
type="button"
|
||||
data-copy="ctf-flag"
|
||||
aria-label="Copy recovered flag"
|
||||
>
|
||||
Copy flag
|
||||
</button>
|
||||
</div>
|
||||
<pre><code>What is the flag? <span id="ctf-flag">DUCTF{ezzzpzzz_07bcda7bfe81faf43caa}</span>
|
||||
Correct!</code></pre>
|
||||
</div>
|
||||
<p>
|
||||
The recovered value also matches the flag in the organizers'
|
||||
published challenge metadata, checked after solving the binary.
|
||||
</p>
|
||||
</details>
|
||||
<details class="evidence-details">
|
||||
<summary>Source, target identity and analysis details</summary>
|
||||
<p>
|
||||
Fresh analysis and two process captures on 8 October 2026, using
|
||||
REA 4.1.0 with Ghidra 12.1.4 on Linux x64. The binary is a
|
||||
stripped x86-64 ELF, 15,248 bytes.
|
||||
</p>
|
||||
<div class="code-block">
|
||||
<div class="code-header">
|
||||
<span>Original executable · SHA-256</span>
|
||||
</div>
|
||||
<pre><code>dcd3bec4f608e11f3a12ce461100aaf5f621851ee63a1f870d7c90d4e3dd51ae</code></pre>
|
||||
</div>
|
||||
<p>
|
||||
The string references, functions and constants were inspected
|
||||
before reading the organizers' source or solution. The Python
|
||||
solver and the diagram were built from those REA results.
|
||||
</p>
|
||||
<p>
|
||||
<a
|
||||
href="https://github.com/DownUnderCTF/Challenges_2023_Public/blob/b48f83db2984bc7fa2ec7879a32d7efb1be8d333/rev/masked-squares-flag-checker/src/ms_flag_checker.c"
|
||||
>Compare with the organizers' source</a
|
||||
>
|
||||
·
|
||||
<a
|
||||
href="https://github.com/DownUnderCTF/Challenges_2023_Public/blob/b48f83db2984bc7fa2ec7879a32d7efb1be8d333/rev/masked-squares-flag-checker/ctfcli.yaml"
|
||||
>Challenge metadata and flag</a
|
||||
>
|
||||
·
|
||||
<a
|
||||
href="https://github.com/morluto/rea/blob/main/website/evidence/ctf-masked-squares.md"
|
||||
>REA evidence notes</a
|
||||
>
|
||||
</p>
|
||||
</details>
|
||||
</section>
|
||||
|
||||
<section class="article-section" id="try">
|
||||
<h2>Try it yourself</h2>
|
||||
<p>
|
||||
Download the original challenge and the solver into one folder.
|
||||
Use the prompt above to investigate with your agent, or run the
|
||||
included script.
|
||||
</p>
|
||||
<div class="actions">
|
||||
<a
|
||||
class="button"
|
||||
href="https://raw.githubusercontent.com/DownUnderCTF/Challenges_2023_Public/b48f83db2984bc7fa2ec7879a32d7efb1be8d333/rev/masked-squares-flag-checker/publish/ms_flag_checker"
|
||||
>Download the original challenge</a
|
||||
><a class="text-link" href="solve.py" download="solve.py"
|
||||
>Download solver</a
|
||||
>
|
||||
</div>
|
||||
<div class="code-block">
|
||||
<div class="code-header">
|
||||
<span>Terminal · solve and run</span
|
||||
><button
|
||||
class="copy-button"
|
||||
type="button"
|
||||
data-copy="ctf-run"
|
||||
aria-label="Copy CTF solver commands"
|
||||
>
|
||||
Copy
|
||||
</button>
|
||||
</div>
|
||||
<pre><code id="ctf-run">python3 -m venv .venv
|
||||
.venv/bin/python -m pip install z3-solver
|
||||
.venv/bin/python solve.py
|
||||
|
||||
chmod +x ms_flag_checker
|
||||
./ms_flag_checker</code></pre>
|
||||
</div>
|
||||
<p class="caption">
|
||||
The script prints a candidate flag; paste it at the program's
|
||||
prompt. Solving needs Python and <code>z3-solver</code>. Running
|
||||
the original executable needs Linux x86-64.
|
||||
</p>
|
||||
<a class="text-link" href="../../get-started/"
|
||||
>Set up REA with your coding agent</a
|
||||
>
|
||||
</section>
|
||||
</article>
|
||||
</div>
|
||||
</main>
|
||||
<footer class="site-footer">
|
||||
<div class="container footer-inner">
|
||||
<p>REA · Reverse Engineer Anything</p>
|
||||
<div class="footer-links">
|
||||
<a href="https://github.com/morluto/rea">Source</a
|
||||
><a href="https://github.com/morluto/rea/blob/main/LICENSE"
|
||||
>MIT license</a
|
||||
><a href="https://discord.gg/GkcryMnJDM">Community</a>
|
||||
</div>
|
||||
</div>
|
||||
</footer>
|
||||
<p class="sr-only" id="copy-status" role="status" aria-live="polite"></p>
|
||||
<a class="back-to-top" href="#top" aria-label="Back to top"
|
||||
><span aria-hidden="true">↑</span> Top</a
|
||||
>
|
||||
</body>
|
||||
</html>
|
||||
|
|
@ -0,0 +1,72 @@
|
|||
#!/usr/bin/env python3
|
||||
"""Solve the DownUnderCTF 2023 masked-squares handout from REA-extracted checks.
|
||||
|
||||
Requires Python 3 and z3-solver. The constants below were read from the original
|
||||
ELF with SHA-256 dcd3bec4f608e11f3a12ce461100aaf5f621851ee63a1f870d7c90d4e3dd51ae.
|
||||
Challenge by joseph / DownUnderCTF:
|
||||
https://github.com/DownUnderCTF/Challenges_2023_Public/tree/b48f83db2984bc7fa2ec7879a32d7efb1be8d333/rev/masked-squares-flag-checker
|
||||
"""
|
||||
|
||||
from z3 import Int, Or, Solver, Sum, sat
|
||||
|
||||
TARGETS = [1441, 2043, 1259, 2031, 1799, 746, 55, 1450, 1485, 1362, 611, 1314, 358, 1834, 1500, 1355, 2011, 1990, 1939, 1990, 278, 1859, 2111, 1510, 888, 1224]
|
||||
RUNS = [
|
||||
(-5, 1, -2, 1, -1, 3, -3, 2, -1, 1, -1, 1, -1, 1, -1, 2, -3, 2, -2, 2),
|
||||
(-1, 1, -3, 2, -3, 2, -1, 1, -2, 3, -1, 1, -1, 7, -1, 3, -1, 2),
|
||||
(-1, 1, -1, 1, -1, 2, -1, 1, -6, 1, -1, 1, -2, 1, -1, 1, -1, 1, -4, 3, -1, 1, -2),
|
||||
(2, -1, 5, -3, 3, -1, 4, -1, 2, -1, 5, -3, 2, -3),
|
||||
(-1, 1, -3, 1, -1, 1, -2, 1, -1, 3, -1, 1, -1, 1, -2, 5, -1, 4, -1, 1, -3),
|
||||
(1, -2, 2, -8, 1, -7, 2, -1, 1, -3, 1, -2, 1, -4),
|
||||
(-21, 1, -14),
|
||||
(-2, 3, -4, 4, -5, 1, -2, 1, -1, 1, -2, 1, -2, 1, -1, 4, -1),
|
||||
(-4, 5, -3, 1, -5, 1, -3, 1, -2, 5, -4, 2),
|
||||
(-1, 2, -4, 1, -1, 4, -3, 1, -4, 1, -1, 2, -1, 1, -3, 1, -1, 2, -2),
|
||||
(-2, 1, -8, 1, -1, 1, -3, 1, -1, 1, -7, 1, -8),
|
||||
(1, -2, 5, -1, 1, -8, 3, -2, 1, -2, 1, -3, 1, -4, 1),
|
||||
(-3, 1, -9, 1, -2, 1, -17, 1, -1),
|
||||
(3, -5, 4, -1, 1, -2, 1, -1, 2, -3, 3, -1, 1, -1, 3, -1, 2, -1),
|
||||
(-2, 2, -3, 1, -1, 1, -1, 1, -1, 1, -7, 1, -1, 6, -1, 3, -1, 1, -1),
|
||||
(1, -5, 1, -2, 2, -1, 2, -1, 1, -1, 1, -5, 1, -1, 1, -1, 5, -4),
|
||||
(2, -3, 1, -5, 4, -2, 3, -2, 5, -1, 2, -1, 2, -1, 2),
|
||||
(-1, 1, -3, 1, -1, 2, -2, 1, -1, 7, -2, 3, -1, 4, -1, 1, -2, 1, -1),
|
||||
(2, -1, 1, -2, 2, -1, 2, -2, 5, -1, 1, -1, 2, -3, 2, -1, 1, -2, 2, -1, 1),
|
||||
(1, -3, 3, -1, 1, -2, 1, -1, 6, -1, 1, -1, 2, -2, 1, -1, 1, -1, 3, -1, 2),
|
||||
(-9, 1, -9, 1, -5, 1, -10),
|
||||
(1, -1, 2, -2, 1, -1, 9, -1, 1, -1, 1, -7, 2, -2, 1, -2, 1),
|
||||
(-1, 9, -1, 3, -1, 6, -1, 1, -1, 1, -2, 1, -3, 1, -4),
|
||||
(-2, 1, -3, 1, -1, 2, -5, 6, -1, 2, -2, 4, -1, 1, -4),
|
||||
(-2, 1, -4, 1, -1, 1, -2, 1, -3, 1, -3, 1, -3, 3, -6, 1, -2),
|
||||
(-8, 4, -2, 2, -1, 3, -5, 1, -2, 2, -1, 1, -4),
|
||||
]
|
||||
|
||||
|
||||
def decode_mask(runs):
|
||||
"""Expand signed run lengths into 36 selected/unselected cells."""
|
||||
cells = [selected for run in runs for selected in [run > 0] * abs(run)]
|
||||
if len(cells) != 36:
|
||||
raise ValueError('A mask must cover all 36 input positions.')
|
||||
return [i for i, selected in enumerate(cells) if selected]
|
||||
|
||||
|
||||
codes = [Int(f'code_{i}') for i in range(36)]
|
||||
solver = Solver()
|
||||
solver.set(timeout=60000)
|
||||
# Analyst assumptions about a conventional flag's format and alphabet.
|
||||
alphabet = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789_{}'
|
||||
for code in codes:
|
||||
solver.add(Or(*[code == ord(char) for char in alphabet]))
|
||||
for code, char in zip(codes, 'DUCTF{'):
|
||||
solver.add(code == ord(char))
|
||||
solver.add(codes[-1] == ord('}'))
|
||||
for runs, target in zip(RUNS, TARGETS):
|
||||
solver.add(Sum([codes[i] for i in decode_mask(runs)]) == target)
|
||||
|
||||
status = solver.check()
|
||||
if status != sat:
|
||||
raise SystemExit(f'No candidate returned: {status}. {solver.reason_unknown()}')
|
||||
model = solver.model()
|
||||
values = [model.eval(code).as_long() for code in codes]
|
||||
if not all(sum(values[i] for i in decode_mask(runs)) == target
|
||||
for runs, target in zip(RUNS, TARGETS)):
|
||||
raise SystemExit('Candidate did not pass the extracted checks.')
|
||||
print(''.join(map(chr, values)))
|
||||
|
|
@ -25,6 +25,7 @@
|
|||
<a href="../">Showcases</a>
|
||||
<a class="nav-start" href="../../get-started/">Get started</a>
|
||||
<a href="../../guides/">Guides</a>
|
||||
<a href="../../faq/">FAQ</a>
|
||||
<a href="https://github.com/morluto/rea">GitHub</a>
|
||||
</nav>
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -6,7 +6,7 @@
|
|||
<title>Case studies — REA</title>
|
||||
<meta
|
||||
name="description"
|
||||
content="Follow REA investigations through code and figures: DX-Ball's sound pan, Notion's Electron clipboard and TH04's aimed bullet rings."
|
||||
content="Follow REA investigations through code and figures: DX-Ball, Notion, TH04 and a DownUnderCTF flag checker."
|
||||
/>
|
||||
<meta name="theme-color" content="#ffffff" />
|
||||
<link rel="icon" href="../assets/favicon.svg" type="image/svg+xml" />
|
||||
|
|
@ -25,6 +25,7 @@
|
|||
<a href="./" aria-current="page">Showcases</a>
|
||||
<a class="nav-start" href="../get-started/">Get started</a>
|
||||
<a href="../guides/">Guides</a>
|
||||
<a href="../faq/">FAQ</a>
|
||||
<a href="https://github.com/morluto/rea">GitHub</a>
|
||||
</nav>
|
||||
</div>
|
||||
|
|
@ -34,11 +35,29 @@
|
|||
<p class="breadcrumb"><a href="../">Home</a> / Showcases</p>
|
||||
<h1>REA case studies.</h1>
|
||||
<p class="lede">
|
||||
Game reconstruction and Electron analysis, with examples from DX-Ball,
|
||||
Notion and TH04.
|
||||
Game reconstruction, Electron analysis and a CTF challenge, with code,
|
||||
figures and checked results.
|
||||
</p>
|
||||
</div>
|
||||
<div class="guide-index">
|
||||
<section class="guide-entry">
|
||||
<p class="eyebrow">CTF · Native analysis</p>
|
||||
<div>
|
||||
<h2>Solve a CTF flag checker</h2>
|
||||
<p>
|
||||
Inspect an easy DownUnderCTF challenge with REA. Find its checking
|
||||
functions, turn masks and sums into equations, then verify the
|
||||
solved flag with the original program.
|
||||
</p>
|
||||
<div class="guide-route">
|
||||
<span>Binary</span> <b aria-hidden="true">→</b>
|
||||
<span>Checking code</span> <b aria-hidden="true">→</b>
|
||||
<span>Equations</span> <b aria-hidden="true">→</b>
|
||||
<span>Correct!</span>
|
||||
</div>
|
||||
<a class="text-link" href="ctf/">Read the CTF case study</a>
|
||||
</div>
|
||||
</section>
|
||||
<section class="guide-entry">
|
||||
<p class="eyebrow">Native · Reconstruction</p>
|
||||
<div>
|
||||
|
|
|
|||
|
|
@ -25,6 +25,7 @@
|
|||
<a href="../">Showcases</a>
|
||||
<a class="nav-start" href="../../get-started/">Get started</a>
|
||||
<a href="../../guides/">Guides</a>
|
||||
<a href="../../faq/">FAQ</a>
|
||||
<a href="https://github.com/morluto/rea">GitHub</a>
|
||||
</nav>
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -25,6 +25,7 @@
|
|||
<a href="../">Showcases</a>
|
||||
<a class="nav-start" href="../../get-started/">Get started</a>
|
||||
<a href="../../guides/">Guides</a>
|
||||
<a href="../../faq/">FAQ</a>
|
||||
<a href="https://github.com/morluto/rea">GitHub</a>
|
||||
</nav>
|
||||
</div>
|
||||
|
|
|
|||
Loading…
Reference in New Issue