diff --git a/website/README.md b/website/README.md index 6f9af6887e..33c6e1fadd 100644 --- a/website/README.md +++ b/website/README.md @@ -4,7 +4,7 @@ > > — N0zoM1z0 -An English static website with explanatory figures, worked guides and DX-Ball, Notion, TH04 and CTF investigations. +An English static website with explanatory figures, worked guides and Aegis, DX-Ball, Notion, TH04 and CTF investigations. The public files are in `website/public/`. The site uses HTML, CSS and small scripts for copying code, following code comparisons and playing the dinosaur speed reconstruction. Python @@ -30,6 +30,7 @@ Open . Refresh the browser after editing a file. - `public/examples/dino-lab/index.html`: adjustable-speed mini-game, recovered rule, original-game check and browser analysis steps. - `public/first-investigation/index.html`: a guided Notes export investigation, from setup to a checked CSV prediction. - `public/showcase/index.html`: the case-study index. +- `public/showcase/aegis/index.html`: Aegis's Android login-code calculation, with an adjustable clock and reference checks. - `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. @@ -102,6 +103,10 @@ command, then explains what reverse engineering is and why someone would use it. The manual/agent comparison introduces REA’s role before the examples. Its opening links directly to Showcases and lets experienced readers skip to the analysis guides. Case-study previews live on the Showcases page. +The closing section offers copyable project prompts, from cloning `rea.tools` +to reconstructing a game from its executable. The experienced-reader shortcut +lands directly on the guide links below these prompts. +The page ends with FAQ, Discord and issue-report links under “Any questions?”. A right-side table of contents stays visible at widths of 1440px and above. On narrower screens it becomes a sticky, native disclosure; selecting a link closes the menu and focuses the destination. The homepage script follows @@ -158,6 +163,21 @@ the website. [evidence/notion-clipboard.md](evidence/notion-clipboard.md) records the REA package version, selected findings, source anchors and module-probe scope. +## Aegis Android case study + +The Aegis case inspects the official 3.4.3 APK through REA 6.1.0. It follows +the default login-code calculation from class search and display references to +the time block, keyed hash and six-digit output. Selected decompiled Java is +paired with a readable summary; the original app and excerpts are credited to +Aegis under its GPL-3.0 license. + +`public/assets/aegis-otp.js` is a new teaching reconstruction using the fixed, +public RFC 6238 test key. `aegis-demo.js` connects it to the clock slider and +reference-check button. The diagram remains readable and controls stay disabled +without JavaScript. Keep the demo, selected code and +[evidence/aegis-login-code.md](evidence/aegis-login-code.md) aligned. The APK and +analysis-provider JAR remain official external downloads. + ## TH04 case study The TH04 case inspects the original PC-98 DOS angle helper through REA 4.1.0. diff --git a/website/evidence/aegis-login-code.md b/website/evidence/aegis-login-code.md new file mode 100644 index 0000000000..6ee862139b --- /dev/null +++ b/website/evidence/aegis-login-code.md @@ -0,0 +1,140 @@ +# Aegis Android login-code evidence + +Inspection on 9 October 2026, using the published REA 6.1.0 CLI with the +caller-supplied `jadx-headless-mcp` 0.7.1 provider and OpenJDK 21.0.12.1 on +Linux x64. The case follows the default time-based code in +[Aegis Authenticator](https://github.com/beemdevelopment/Aegis). + +## Target + +- [Official release](https://github.com/beemdevelopment/Aegis/releases/tag/v3.4.3): v3.4.3, published 6 September 2026. +- Artifact: `aegis-v3.4.3.apk`, 6,881,897 bytes. +- SHA-256: `fa091c91912c0d56ec3553394fac4158f9a9d178e93bcbb35419601d788cb528`. +- Package: `com.beemdevelopment.aegis`, version code 82. +- Reported inventory: 3,077 classes and 1,106 resources. +- Matching source commit: `d6f4e5925a97e4e91593f1542085eae03432a759`. +- Android provider JAR SHA-256: `6e5eacf500b64292bfb73c49797c1958f6ee44646e43e868039ae7feb573ff75`. +- Provider source revision: `5844800a486d2248fa949b74c9896285f5b54de2`. + +The artifact's SHA-256 matches the official GitHub release asset digest and every +saved REA record. The APK and provider JAR were downloaded into temporary storage. +They are linked from their official releases; neither file is a website asset. + +The CLI used `REA_JADX_MCP_JAR` for its own process. The connected MCP server's +Android provider was initially unavailable; it was not reconfigured. The page +expresses the inspected selections as REA tool requests, using the same operation +parameters as the successful CLI calls. Input paths and result package prefixes +are shortened for display, as noted beside those blocks. + +## REA findings + +Twenty REA operations completed successfully against this artifact. These are the +records used for the page's main findings: + +| Operation | Finding | Evidence ID | +| -------------------------- | -------------------------------------------------------- | --------------------------------------------------------------------- | +| `inspect_android_method` | Display calls the timestamp overload for TotpInfo. | `ev_c4b51611c987a398359f526dc64866d97a99b146d0ec9177bc1d583a55d64bdc` | +| `trace_android_references` | TotpInfo is one of the three incoming generator callers. | `ev_6b5457705a67bd4952f7e17630bcf948538424fae5bc2951c08e921af7448d58` | +| `inspect_android_method` | Eight-byte big-endian counter, account key and HMAC. | `ev_cad4c03faf5b07347cba699d4f8f643c69e5a8f0c85f622fc8cbebe3f834f228` | +| `inspect_android_method` | Hash-byte selection produces a 31-bit integer. | `ev_5797488db16e72a4e9ad559a8f07e355adc1f73b9eb50566cfb46d47d1b6a883` | +| `inspect_android_method` | Algorithm name is prefixed with Hmac. | `ev_bffbfa3924c79823168df7daa01e8a3f62c4914d3fb4b54e5d4e60d21e75110f` | +| `trace_android_references` | Display and copy callers of the base method. | `ev_d90e3ed81b0bb6815826e4edf312eadec91b9d20ff06aaff61d38a191403676b` | +| `inspect_android_method` | Default SHA1 algorithm and six digits. | `ev_64294487704fa2a9877938017c95163e41c399ba958ea43c5b365118403ae0fe` | +| `inspect_android_method` | Decimal remainder and leading-zero padding. | `ev_5d0906d19ea9281fed36309794fd5697b54cac3abfbd17e2f78665e758e0eb60` | +| `inspect_android_method` | Class discriminator 0 selects the OTP formatting branch. | `ev_f0a82295bf6124055b7a12a21533950644267e8e8340038bca9c32db57d8d71f` | +| `inspect_android_package` | Package identity; 3,077 classes. | `ev_de07111c281cd76c4f2304f42448b06ef3199268815515d59caa02ea2480eb67` | +| `inspect_android_class` | Both getOtp overloads and their selection indices. | `ev_870577d702741f3023494c1bcb6567381a672a7132a60fd35ea2dd21ab9804c8` | +| `search_android_classes` | Three matches for the case-sensitive query Totp. | `ev_deeb75a09990e253881d0dc1932097abe00f94447d29a64c782fb48c42e9f492` | +| `inspect_android_method` | Current clock in milliseconds is converted to seconds. | `ev_ff4d8be5b4797e6f118d37ed55a2f65c9ceb8df39e8058d2cf36cd5561dc4bfd` | +| `inspect_android_method` | Default period: 30 seconds. | `ev_67af69cd39faceec616a9b1b48b03a5ea71b9c94ff2d1263a0a0063029cc1104` | +| `inspect_android_method` | Time is floored into a block before generation. | `ev_f282f536e5690e3e2253903bd979687687cfa5a9a4480758b4cd261da145caaf` | + +All selected methods returned complete Java representations without a fallback. +Signatures are provider display signatures; exact DEX descriptors, source-line +locations and instruction offsets are not available from these results. +Reference lists are static incoming references, with complete coverage of the +engine's reported set; reflection and dynamic loading remain unverified. + +## Interpretation + +`TotpInfo.getOtp()` obtains Unix time in seconds. Its timestamp overload uses +`floor(seconds / period)` as the generator's counter. The default constructor +sets the period to 30; the base constructor sets SHA1 and six digits. +These are defaults, not settings imposed on every imported account. + +`getAlgorithm()` constructs `HmacSHA1` for that default. The generator encodes the +counter as eight big-endian bytes, calculates the keyed hash, uses the low four +bits of the final hash byte as an offset, and selects a positive 31-bit integer. +The `OTP.toString()` branch for discriminator 0 reduces it modulo the requested +power of ten and prepends zeroes. The inspected generator passes discriminator 0 +into the constructor, connecting that branch to the generated code. + +The release exposes `generateOTP` and `getHash` under `kotlin.ExceptionsKt`. +Original-source naming alone would not identify that class in this APK. +The call returned from `TotpInfo.getOtp(long)` supplies the helper name; REA's +method inspection and reference tracing establish its calculation and callers. + +The base `OtpInfo.getOtp` reference list includes `EntryHolder.getOtp` and +`MainActivity.copyEntryCode`. Inspecting `EntryHolder.getOtp(int)` then shows that +the display calls the timestamp overload for a `TotpInfo` account, with an +optional period offset. The page uses the unshifted code calculation. + +## Reconstruction checks + +A temporary JVM harness assembled six method bodies directly from saved REA +results: the timestamp method, algorithm getter, hash method, generator, output +constructor and output formatter. Imports, enclosing classes, fields, exception +type and test driver were supplied for the harness. The original APK was not run. + +An independently authored Python calculation agreed with those Java methods on +133 inputs. The website's separately authored WebCrypto implementation then +agreed with all 133 recorded Java outputs. + +The cases include: + +- All six [RFC 6238 SHA-1 reference vectors](https://www.rfc-editor.org/rfc/rfc6238.html#appendix-B) at eight digits, plus the corresponding six-digit remainders. +- Every integer second from 0 through 120, checking four complete 30-second blocks and four boundary samples. +- A leading-zero result at Unix time 1,111,111,109: `081804`. + +The fixed key is the public ASCII test string `12345678901234567890`. +At 30 and 59 seconds the result is `287082`; at 60 seconds it is `359152`. +The diagram labels these as example clock inputs. A code is recomputed for each +block; the checked examples change at their boundaries. Adjacent codes need not +be universally distinct. + +The page's “Run reference checks” button tests the browser calculation against +all six reference inputs at both digit lengths. Slider updates use a revision +number so an earlier asynchronous calculation cannot overwrite a later input. +The reference JSON contains public test data, not a captured account key. + +## Source comparison and attribution + +After obtaining the calculation through REA, selected files from the matching +release source were read for corroboration: + +- [HOTP.java](https://github.com/beemdevelopment/Aegis/blob/d6f4e5925a97e4e91593f1542085eae03432a759/app/src/main/java/com/beemdevelopment/aegis/crypto/otp/HOTP.java): counter encoding, HMAC and byte selection. +- [TOTP.java](https://github.com/beemdevelopment/Aegis/blob/d6f4e5925a97e4e91593f1542085eae03432a759/app/src/main/java/com/beemdevelopment/aegis/crypto/otp/TOTP.java): time-to-counter transformation. +- [OTP.java](https://github.com/beemdevelopment/Aegis/blob/d6f4e5925a97e4e91593f1542085eae03432a759/app/src/main/java/com/beemdevelopment/aegis/crypto/otp/OTP.java): decimal output and zero padding. + +These files confirm the interpretation. The public page's selected decompiled +Java comes from the APK evidence. It includes fragments from several methods and +line wrapping for display; the paired readable summary is an explanation. +The expanded generator panel retains its complete returned body, with formatting +changes. Aegis excerpts are credited under the upstream +[GPL-3.0 license](https://github.com/beemdevelopment/Aegis/blob/d6f4e5925a97e4e91593f1542085eae03432a759/LICENSE). +The diagram, explanatory summary, demo and its WebCrypto implementation are new +teaching material; the demo is not recovered original application source. + +## Scope and maintenance + +The claim is a static APK investigation and a checked reconstruction of its +default code calculation. It covers neither Android UI execution nor APK +signature verification, split APK handling, native libraries or vault storage. +The Android provider requires a Linux or macOS host and a full JDK 17 or later, +including `jdk.compiler`. The shell walkthrough was executed on Linux with +JDK 21. + +Raw REA results, provider scratch paths, Java harness classes and the downloaded +binaries remain outside the public website. The public page uses generic filenames +and the public test key. Update the release identity, selected excerpts, demo, +reference data and these notes together when moving to another Aegis version. diff --git a/website/figures.md b/website/figures.md index c0ab93d609..5cf50ce29f 100644 --- a/website/figures.md +++ b/website/figures.md @@ -73,6 +73,18 @@ are new teaching code. Check that the fallback is visible with JavaScript disabled and hidden when the playable canvas loads. Keep the speed settings, source excerpt, reconstruction and original-result table consistent. +## Aegis login-code figure + +The Aegis overview and adjustable-clock figure are semantic HTML. The clock +diagram shows seconds, the 30-second block and the resulting six-digit code, +using the public RFC 6238 test key. It retains a static 59-second example when +JavaScript is disabled. The three code selections connect each operation to the +APK's selected Java and an explanatory summary. + +[evidence/aegis-login-code.md](evidence/aegis-login-code.md) records the inspected +APK, methods, source attribution and reconstruction checks. Keep the block +formula, default settings, slider outputs and reference cases consistent. + ## Original image generation prompts ## Prompt 1 diff --git a/website/public/assets/aegis-demo.js b/website/public/assets/aegis-demo.js new file mode 100644 index 0000000000..74afa79b30 --- /dev/null +++ b/website/public/assets/aegis-demo.js @@ -0,0 +1,84 @@ +import { generateExampleCode } from "./aegis-otp.js"; + +async function initializeAegisDemo() { + const demo = document.querySelector("[data-aegis-demo]"); + if (demo === null) return; + const clock = demo.querySelector("input[type=range]"); + const controls = demo.querySelectorAll("input, button"); + const status = demo.querySelector("[data-aegis-status]"); + let revision = 0; + + const updateTime = async (seconds) => { + const currentRevision = ++revision; + try { + const code = await generateExampleCode(seconds); + if (currentRevision !== revision) return false; + const block = Math.floor(seconds / 30); + demo.querySelector("[data-aegis-seconds]").textContent = String(seconds); + demo.querySelector("[data-aegis-block]").textContent = String(block); + demo.querySelector("[data-aegis-division]").textContent = + `floor(${seconds} / 30)`; + demo.querySelector("[data-aegis-window]").textContent = + `${block * 30}–${block * 30 + 29} seconds`; + demo.querySelector("[data-aegis-code]").textContent = code; + clock.setAttribute( + "aria-valuetext", + `${seconds} seconds, time block ${block}`, + ); + status.textContent = ""; + return true; + } catch { + if (currentRevision === revision) { + status.textContent = + "Interactive calculation is unavailable in this browser. The example shows the code at 59 seconds."; + } + return false; + } + }; + + if (!(await updateTime(Number(clock.value)))) return; + controls.forEach((control) => { + control.disabled = false; + }); + clock.addEventListener("input", async () => { + await updateTime(Number(clock.value)); + }); + demo.querySelectorAll("[data-aegis-time]").forEach((button) => { + button.addEventListener("click", async () => { + clock.value = button.getAttribute("data-aegis-time"); + await updateTime(Number(clock.value)); + }); + }); + + const checkButton = document.querySelector("[data-aegis-check]"); + const checkResult = document.querySelector("[data-aegis-check-result]"); + checkButton.disabled = false; + checkButton.addEventListener("click", async () => { + checkButton.disabled = true; + checkResult.textContent = "Checking…"; + try { + const response = await fetch( + new URL("../showcase/aegis/test-vectors.json", import.meta.url), + ); + if (!response.ok) throw new Error("Reference data unavailable"); + const { vectors } = await response.json(); + let passed = 0; + for (const vector of vectors) { + for (const digits of [6, 8]) { + const code = await generateExampleCode(vector.seconds, digits); + if (code === vector.sha1.slice(-digits)) passed += 1; + } + } + checkResult.textContent = + `${passed} / ${vectors.length * 2} checks passed ` + + "(six reference cases, at six and eight digits)."; + } catch { + checkResult.textContent = + "Could not run the reference checks. Try again."; + } finally { + checkButton.disabled = false; + } + }); +} + +await initializeAegisDemo(); diff --git a/website/public/assets/aegis-otp.js b/website/public/assets/aegis-otp.js new file mode 100644 index 0000000000..0952a6070c --- /dev/null +++ b/website/public/assets/aegis-otp.js @@ -0,0 +1,36 @@ +// Teaching reconstruction of the calculation inspected in Aegis v3.4.3. +// The key is public RFC 6238 test data, not an account credential. +const testKey = new TextEncoder().encode("12345678901234567890"); +let importedKey; + +/** Generate a code with the public test key and a 30-second time step. */ +export async function generateExampleCode(seconds, digits = 6) { + if ( + !Number.isSafeInteger(seconds) || + seconds < 0 || + ![6, 8].includes(digits) + ) { + throw new RangeError( + "Use a nonnegative integer time and six or eight digits.", + ); + } + importedKey ??= crypto.subtle.importKey( + "raw", + testKey, + { name: "HMAC", hash: "SHA-1" }, + false, + ["sign"], + ); + const key = await importedKey; + const block = Math.floor(seconds / 30); + const counter = new ArrayBuffer(8); + new DataView(counter).setBigUint64(0, BigInt(block), false); + const hash = new Uint8Array(await crypto.subtle.sign("HMAC", key, counter)); + const offset = hash[hash.length - 1] & 15; + const number = + ((hash[offset] & 127) << 24) | + ((hash[offset + 1] & 255) << 16) | + ((hash[offset + 2] & 255) << 8) | + (hash[offset + 3] & 255); + return String(number % 10 ** digits).padStart(digits, "0"); +} diff --git a/website/public/assets/styles.css b/website/public/assets/styles.css index fc9850f696..d205dab1d2 100644 --- a/website/public/assets/styles.css +++ b/website/public/assets/styles.css @@ -261,6 +261,14 @@ section[id] { background: #38424e; text-decoration: none; } +.button.secondary { + border-color: var(--line); + background: var(--page); + color: var(--ink); +} +.button.secondary:hover { + background: var(--soft); +} .text-link { font-size: 0.95rem; font-weight: 500; @@ -689,6 +697,59 @@ pre code { .case-figure { margin-bottom: 3.2rem; } +.aegis-step-selector { + grid-template-columns: repeat(3, minmax(0, 1fr)); +} +.aegis-step-selector button { + min-height: 44px; +} +.aegis-calculation .flow-node { + display: flex; + align-items: center; + justify-content: center; + gap: 0.35rem; + min-height: 3.5rem; + font-variant-numeric: tabular-nums; +} +.aegis-calculation .aegis-code { + font-family: ui-monospace, SFMono-Regular, Consolas, monospace; + font-size: 1.5rem; + letter-spacing: 0.06em; +} +.aegis-clock-controls { + margin-top: 1.7rem; +} +.aegis-clock-controls label { + display: block; + font-size: 0.875rem; + font-weight: 500; +} +.aegis-clock-controls input { + display: block; + width: 100%; + min-height: 44px; + margin: 0; + accent-color: var(--accent); +} +.aegis-clock-scale { + display: flex; + justify-content: space-between; + color: var(--muted); + font-size: 0.75rem; + font-variant-numeric: tabular-nums; +} +.aegis-clock-controls .actions { + gap: 0.7rem; + margin-top: 1.2rem; +} +.aegis-clock-controls .caption { + margin-bottom: 0; +} +.aegis-demo .button:disabled, +[data-aegis-check]:disabled { + opacity: 0.6; + cursor: default; +} .step-selector { display: grid; grid-template-columns: repeat(5, minmax(0, 1fr)); @@ -1389,6 +1450,31 @@ pre code { box-decoration-break: clone; -webkit-box-decoration-break: clone; } +.home-examples-intro { + max-width: 760px; + margin: 0 0 2.5rem; + color: var(--muted); + font-size: 1.05rem; +} +.home-examples-intro strong { + display: block; + margin-bottom: 0.5rem; + color: var(--ink); + font-size: 1.5rem; + font-weight: 600; +} +.home-project-prompts { + margin-block: 2rem 2.5rem; +} +.home-project-prompts .agent-terminal { + margin: 0; +} +.home-project-prompts .caption { + margin-block: 0.8rem 0; +} +.home-page #analysis-guides { + scroll-margin-top: 5rem; +} .case-brief { display: grid; grid-template-columns: minmax(0, 1fr) minmax(0, 1.3fr); @@ -1443,7 +1529,7 @@ pre code { .comparison-label { padding-bottom: 0.8rem; border-bottom: 1px solid var(--line); - font-size: 0.875rem; + font-size: 1.125rem; font-weight: 600; } .agent-investigation > .comparison-label { diff --git a/website/public/index.html b/website/public/index.html index e3c9a7b0e5..bb77c7819e 100644 --- a/website/public/index.html +++ b/website/public/index.html @@ -71,7 +71,8 @@
  • Dinosaur game
  • Calculator
  • Try REA
  • -
  • Analysis guides
  • +
  • Go deeper
  • +
  • Help & feedback
  • @@ -93,7 +94,7 @@

    Already know RE? - Skip to analysis guides → + Skip to analysis guides →

    @@ -167,7 +168,10 @@ class="manual-investigation step-comparison" data-step-comparison > -

    Before REA · You investigate

    +

    + Before REA · You investigate it + yourself +

    -

    With REA · You ask your agent

    +

    + With REA · You ask + your agent +

    One prompt.

    Ask your agent in plain English. @@ -385,6 +392,11 @@

    +

    + Let's try two examples. + Use REA to change a game's speed, then explain a calculator's + percentage rule. +

    Example 01 · Chrome’s dinosaur game

    Why does the dinosaur get faster?

    @@ -863,12 +875,77 @@ SPEED: 6
    -

    Analyze your software

    +

    Want to go deeper?

    - Choose a guide for your binary, JavaScript app or browser session. + Tell your agent what you want to understand or build. With REA, + you can work together on anything from + cloning this website to + reconstructing a game from its executable.

    -
    +
    +
    +
    +
    + + Example prompt · Website + + +
    +

    + Use REA to inspect https://rea.tools/. Clone this website + for me. +

    +
    +

    Yes, this one.

    +
    +
    +
    +
    + + Example prompt · Game reconstruction + + +
    +

    + Use REA to reconstruct this game from its executable. + Recover the gameplay logic in C and test it against the + original. +

    +
    +

    + See the DX-Ball case study → +

    +
    +
    +

    + For setup and analysis steps, choose your target: +

    +

    Native binaries

    @@ -896,6 +973,20 @@ SPEED: 6

    +
    +
    +

    Any questions?

    +

    + Read the FAQ, + chat with us on Discord, + or + open a GitHub issue + for bugs and feature requests. +

    +
    +