Merge pull request #1266 from morluto/feat/website-apk-showcase

feat(website): add an Android showcase and clearer project prompts
This commit is contained in:
Ysopwnable
2026-10-09 18:46:52 +08:00
committed by GitHub
11 changed files with 1191 additions and 20 deletions
+21 -1
View File
@@ -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 <http://127.0.0.1:4173/>. 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.
+140
View File
@@ -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.
+12
View File
@@ -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
+84
View File
@@ -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();
+36
View File
@@ -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");
}
+87 -1
View File
@@ -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 {
+98 -7
View File
@@ -71,7 +71,8 @@
<li><a href="#dinosaur">Dinosaur game</a></li>
<li><a href="#calculator">Calculator</a></li>
<li><a href="#try-rea">Try REA</a></li>
<li><a href="#analyze">Analysis guides</a></li>
<li><a href="#analyze">Go deeper</a></li>
<li><a href="#questions">Help &amp; feedback</a></li>
</ul>
</div>
</nav>
@@ -93,7 +94,7 @@
</div>
<p class="home-expert-shortcut">
Already know RE?
<a href="#analyze">Skip to analysis guides →</a>
<a href="#analysis-guides">Skip to analysis guides →</a>
</p>
</div>
<div class="home-setup">
@@ -167,7 +168,10 @@
class="manual-investigation step-comparison"
data-step-comparison
>
<p class="comparison-label">Before REA · You investigate</p>
<p class="comparison-label">
Before REA · You investigate it
<strong class="prose-highlight">yourself</strong>
</p>
<div
class="step-selector expert-step-selector"
role="group"
@@ -316,7 +320,10 @@
</p>
</div>
<div class="agent-investigation" id="ask-your-agent">
<p class="comparison-label">With REA · You ask your agent</p>
<p class="comparison-label">
With REA · You ask
<strong class="prose-highlight">your agent</strong>
</p>
<h3 class="agent-one-prompt">One prompt.</h3>
<p class="agent-intro">
Ask your agent <strong>in plain English.</strong>
@@ -385,6 +392,11 @@
<section class="section" id="dinosaur">
<div class="container">
<p class="home-examples-intro">
<strong>Let's try two examples.</strong>
Use REA to change a game's speed, then explain a calculator's
percentage rule.
</p>
<div class="section-heading">
<p class="eyebrow">Example 01 · Chrome’s dinosaur game</p>
<h2>Why does the dinosaur get faster?</h2>
@@ -863,12 +875,77 @@ SPEED: 6</code></pre>
<section class="section" id="analyze">
<div class="container">
<div class="section-heading">
<h2>Analyze your software</h2>
<h2>Want to go deeper?</h2>
<p>
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
<strong>cloning this website</strong> to
<strong>reconstructing a game from its executable.</strong>
</p>
</div>
<div class="workflows">
<div class="code-comparison home-project-prompts">
<div>
<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
>Example prompt · Website
</span>
<button
class="copy-button"
type="button"
data-copy="website-project-prompt"
aria-label="Copy website project prompt"
>
Copy
</button>
</div>
<p class="terminal-prompt">
<span class="prompt-symbol" aria-hidden="true">›</span
><span id="website-project-prompt"
>Use REA to inspect https://rea.tools/. Clone this website
for me.</span
><span class="terminal-cursor" aria-hidden="true"></span>
</p>
</div>
<p class="caption">Yes, this one.</p>
</div>
<div>
<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
>Example prompt · Game reconstruction
</span>
<button
class="copy-button"
type="button"
data-copy="game-project-prompt"
aria-label="Copy game reconstruction prompt"
>
Copy
</button>
</div>
<p class="terminal-prompt">
<span class="prompt-symbol" aria-hidden="true">›</span
><span id="game-project-prompt"
>Use REA to reconstruct this game from its executable.
Recover the gameplay logic in C and test it against the
original.</span
><span class="terminal-cursor" aria-hidden="true"></span>
</p>
</div>
<p class="caption">
<a href="showcase/dx-ball/">See the DX-Ball case study →</a>
</p>
</div>
</div>
<p class="caption">
For setup and analysis steps, choose your target:
</p>
<div class="workflows" id="analysis-guides">
<div>
<h3>Native binaries</h3>
<p>
@@ -896,6 +973,20 @@ SPEED: 6</code></pre>
</div>
</div>
</section>
<section class="section" id="questions">
<div class="container">
<h2>Any questions?</h2>
<p>
<a href="faq/">Read the FAQ</a>,
<a href="https://discord.gg/GkcryMnJDM">chat with us on Discord</a>,
or
<a href="https://github.com/morluto/rea/issues/new/choose"
>open a GitHub issue</a
>
for bugs and feature requests.
</p>
</div>
</section>
</main>
<footer class="site-footer">
<div class="container footer-inner">
+669
View File
@@ -0,0 +1,669 @@
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<title>How an authenticator generates a login code — REA</title>
<meta
name="description"
content="Inspect the Aegis Android APK with REA, recover its six-digit login-code calculation and try a reconstruction with an adjustable clock."
/>
<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>
<script type="module" src="../../assets/aegis-demo.js"></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>
</nav>
<a
class="nav-github"
href="https://github.com/morluto/rea"
aria-label="REA on GitHub"
title="REA on GitHub"
>
<svg
width="22"
height="22"
viewBox="0 0 16 16"
aria-hidden="true"
focusable="false"
>
<path
fill="currentColor"
d="M8 0C3.58 0 0 3.58 0 8c0 3.54 2.29 6.53 5.47 7.59.4.07.55-.17.55-.38 0-.19-.01-.82-.01-1.49-2.01.37-2.53-.49-2.69-.94-.09-.23-.48-.94-.82-1.13-.28-.15-.68-.52-.01-.53.63-.01 1.08.58 1.23.82.72 1.21 1.87.87 2.33.66.07-.52.28-.87.51-1.07-1.78-.2-3.64-.89-3.64-3.95 0-.87.31-1.59.82-2.15-.08-.2-.36-1.02.08-2.12 0 0 .67-.21 2.2.82a7.65 7.65 0 0 1 2-.27c.68 0 1.36.09 2 .27 1.53-1.04 2.2-.82 2.2-.82.44 1.1.16 1.92.08 2.12.51.56.82 1.27.82 2.15 0 3.07-1.87 3.75-3.65 3.95.29.25.54.73.54 1.48 0 1.07-.01 1.93-.01 2.2 0 .21.15.46.55.38A8.01 8.01 0 0 0 16 8c0-4.42-3.58-8-8-8Z"
/>
</svg>
<span class="nav-github-count" hidden aria-hidden="true"></span>
</a>
</div>
</header>
<main id="main" class="container">
<div class="page-heading">
<p class="breadcrumb">
<a href="../../">Home</a> / <a href="../">Showcases</a> / Android
</p>
<p class="eyebrow">Case study · Aegis Authenticator</p>
<h1>How does an authenticator<br />generate a login code?</h1>
<p class="lede">
A six-digit code changes every 30 seconds. Use REA to find the
calculation in an Android APK, then rebuild it with a clock you can
control.
</p>
<p class="page-meta">
Aegis 3.4.3 · Android · Official release APK · 6.9 MB
</p>
<a class="text-link" href="https://github.com/beemdevelopment/Aegis"
>View Aegis on GitHub</a
>
</div>
<figure class="workflow-figure case-figure">
<p class="figure-title">From an APK to a code you can reproduce</p>
<ol class="flow-steps">
<li>
<span class="flow-label">01 · Locate the feature</span
><strong class="flow-node"
>3,077 classes → generating methods</strong
><span class="flow-note"
>REA searches the package and traces the code used by the display
and copy actions.</span
>
</li>
<li>
<span class="flow-label">02 · Read the calculation</span
><strong class="flow-node">Time + account key → six digits</strong
><span class="flow-note"
>REA returns the methods and defaults. The agent explains how they
fit together.</span
>
</li>
<li>
<span class="flow-label">03 · Rebuild and check</span
><strong class="flow-node">Change the clock. Watch the code.</strong
><span class="flow-note"
>A new browser demo reproduces the rule and checks it against
public reference cases.</span
>
</li>
</ol>
</figure>
<div class="article-layout">
<nav class="contents" aria-label="On this page">
<p>In this case study</p>
<ul>
<li><a href="#ask">Ask your agent</a></li>
<li><a href="#demo">Try the code generator</a></li>
<li><a href="#find">Find the code with REA</a></li>
<li><a href="#calculation">Read the calculation</a></li>
<li><a href="#verify">Check the reconstruction</a></li>
<li><a href="#try">Inspect the APK yourself</a></li>
</ul>
</nav>
<article class="article" aria-label="Aegis APK case study">
<section class="article-section" id="ask">
<h2>Ask your agent</h2>
<p>
The app shows a changing login code. We want to find
<strong>what makes it change</strong> and reproduce that behavior.
</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="aegis-prompt"
aria-label="Copy Aegis example prompt"
>
Copy
</button>
</div>
<p class="terminal-prompt">
<span class="prompt-symbol" aria-hidden="true">›</span
><span id="aegis-prompt"
>Use REA to inspect aegis-v3.4.3.apk. Find how it generates
the six-digit login code, explain why it changes every 30
seconds, and rebuild a small demo where I can change the
time.</span
><span class="terminal-cursor" aria-hidden="true"></span>
</p>
</div>
<p class="caption">
An example prompt for the downloaded APK, with REA's Android
analysis configured.
<a href="#try">Get the APK and analysis setup ↓</a>
</p>
</section>
<section class="article-section" id="demo">
<h2>Try the code generator</h2>
<p>
The app groups time into
<mark class="prose-highlight">30-second blocks.</mark> It combines
the block number with the account key to generate a code. The same
key and block produce the same code.
</p>
<figure class="workflow-figure aegis-demo" data-aegis-demo>
<p class="figure-title">Move the clock across 60 seconds</p>
<ol class="flow-steps aegis-calculation">
<li>
<span class="flow-label">Time</span
><strong class="flow-node"
><output data-aegis-seconds>59</output> seconds</strong
><span class="flow-note">Our adjustable example clock</span>
</li>
<li>
<span class="flow-label">30-second block</span
><strong class="flow-node"
>Block <output data-aegis-block>1</output></strong
><span class="flow-note"
><code data-aegis-division>floor(59 / 30)</code><br /><span
data-aegis-window
>30–59 seconds</span
></span
>
</li>
<li>
<span class="flow-label">Generated code</span
><strong class="flow-node aegis-code"
><output
data-aegis-code
aria-live="polite"
aria-atomic="true"
>287082</output
></strong
><span class="flow-note"
>Account key + block → six digits</span
>
</li>
</ol>
<div class="aegis-clock-controls">
<label for="aegis-clock">Example time, in seconds</label>
<input
id="aegis-clock"
type="range"
min="0"
max="120"
step="1"
value="59"
disabled
/>
<div class="aegis-clock-scale" aria-hidden="true">
<span>0</span><span>30</span><span>60</span><span>90</span
><span>120</span>
</div>
<div class="actions">
<button
class="button secondary"
type="button"
data-aegis-time="59"
disabled
>
59 seconds</button
><button
class="button secondary"
type="button"
data-aegis-time="60"
disabled
>
60 seconds
</button>
</div>
<p class="caption">
At 59 seconds: <strong>287082.</strong> At 60 seconds:
<strong>359152.</strong> Moving within one block leaves the
code unchanged.
</p>
<p class="caption" data-aegis-status role="status"></p>
</div>
<figcaption>
The key is fixed, public
<a href="https://www.rfc-editor.org/rfc/rfc6238.html#appendix-B"
>RFC 6238 test data</a
>. The demo implements the calculation recovered from the APK.
</figcaption>
<noscript
><p class="caption">
The diagram shows the result at 59 seconds. Enable JavaScript
to change the clock.
</p></noscript
>
</figure>
</section>
<section class="article-section" id="find">
<h2>Find the code with REA</h2>
<ol class="rea-queries">
<li>
<h4>Narrow the APK to three classes</h4>
<div class="rea-query-pair">
<div class="code-block">
<div class="code-header"><span>Agent → REA</span></div>
<pre><code>inspect_android_package
{"path": "aegis-v3.4.3.apk"}
search_android_classes
{"path": "aegis-v3.4.3.apk",
"query": "Totp"}</code></pre>
</div>
<div class="code-block">
<div class="code-header">
<span>REA → agent · selected results</span>
</div>
<pre><code>Package: com.beemdevelopment.aegis
Version: 3.4.3
Classes: 3,077
Three matches:
otp.TotpInfo
ui.views.TotpProgressBar
importers.TotpAuthenticatorImporter</code></pre>
</div>
</div>
<p>
<code>TotpInfo</code> is the calculation candidate; the other
two concern the progress bar and imported accounts. TOTP means
a time-based one-time password.
</p>
</li>
<li>
<h4>Connect the generator to the app's display</h4>
<div class="rea-query-pair">
<div class="code-block">
<div class="code-header"><span>Agent → REA</span></div>
<pre><code>inspect_android_class
{"path": "aegis-v3.4.3.apk",
"class_name":
"com.beemdevelopment.aegis.otp.TotpInfo"}
trace_android_references
{"path": "aegis-v3.4.3.apk",
"class_name":
"com.beemdevelopment.aegis.otp.OtpInfo",
"method_name": "getOtp"}</code></pre>
</div>
<div class="code-block">
<div class="code-header">
<span>REA → agent · methods and callers</span>
</div>
<pre><code>TotpInfo methods:
getOtp() · overload 0
getOtp(long) · overload 1
OtpInfo.getOtp callers:
ui.views.EntryHolder.getOtp
ui.MainActivity.copyEntryCode</code></pre>
</div>
</div>
<p>
REA identifies the timestamp overload and finds the base
method's callers. Inspecting
<code>EntryHolder.getOtp</code> then shows the display calling
<code>TotpInfo.getOtp(timestamp)</code>.
</p>
</li>
</ol>
<p class="caption">
Requests show a shortened input path. Result class names omit the
common <code>com.beemdevelopment.aegis</code> prefix.
</p>
</section>
<section class="article-section" id="calculation">
<h2>Read the calculation</h2>
<p>
REA returns
<strong
>the clock conversion, default settings and generating
methods.</strong
>
The constructors set a 30-second interval and six digits; the
timestamp method turns seconds into a block number.
</p>
<div class="step-comparison" data-step-comparison>
<div
class="step-selector aegis-step-selector"
role="group"
aria-label="Follow the login-code calculation"
>
<button
type="button"
data-select-step="1"
aria-pressed="true"
aria-controls="aegis-java aegis-summary"
>
<span>01</span> Time block
</button>
<button
type="button"
data-select-step="2"
aria-pressed="false"
aria-controls="aegis-java aegis-summary"
>
<span>02</span> Key + block
</button>
<button
type="button"
data-select-step="3"
aria-pressed="false"
aria-controls="aegis-java aegis-summary"
>
<span>03</span> Six digits
</button>
</div>
<div class="code-comparison">
<div class="code-block">
<div class="code-header">
<span>REA · selected decompiled Java</span>
</div>
<pre><code id="aegis-java"><span class="code-step is-active" data-step="1">// TotpInfo defaults and clock
setPeriod(30);
getOtp(System.currentTimeMillis() / 1000);
// OtpInfo default algorithm and digits
this(bArr, "SHA1", 6);
// Block passed to the generator
(long) Math.floor(j / ((double) this._period))</span>
<span class="code-step" data-step="2">// generateOTP → getHash
byte[] hash = getHash(bArr, str, j);
// getHash: account key + encoded block
Mac mac = Mac.getInstance(str);
mac.init(secretKeySpec);
return mac.doFinal(bArrArray);</span>
<span class="code-step" data-step="3">// OTP.toString, case 0
this._code % ((int) Math.pow(10.0d, i))
// Keep leading zeroes
while (sb.length() &lt; i) {
sb.insert(0, "0");
}</span></code></pre>
</div>
<div class="code-block">
<div class="code-header">
<span>Readable summary · agent's interpretation</span>
</div>
<pre><code id="aegis-summary"><span class="code-step is-active" data-step="1">// Group time into 30-second blocks
seconds = current_time_ms / 1000
block = floor(seconds / 30)</span>
<span class="code-step" data-step="2">// Generate a number from the key and block
hash = HMAC_SHA1(account_key,
big_endian_8_bytes(block))
number = select_31_bits(hash)</span>
<span class="code-step" data-step="3">// Format the default six-digit code
code = number % 1_000_000
code = pad_with_zeroes(code, 6)</span></code></pre>
</div>
</div>
<div class="step-detail" data-step-note="1">
<p>
<strong>Time chooses the block.</strong> Seconds 30 through 59
all give block 1. At 60 seconds, the block becomes 2.
</p>
</div>
<div class="step-detail" data-step-note="2">
<p>
<strong>The key matters too.</strong> The generator combines
the account key with the block. An account with a different
key can show a different code at the same time.
</p>
</div>
<div class="step-detail" data-step-note="3">
<p>
<strong>Keep six digits.</strong> The remainder after division
by 1,000,000 is padded with zeroes when needed, so a code can
begin with 0.
</p>
</div>
</div>
<p class="caption">
Excerpts from several inspected methods, with line wrapping for
display. The right panel is an explanatory summary.
<a
href="https://github.com/beemdevelopment/Aegis/blob/d6f4e5925a97e4e91593f1542085eae03432a759/LICENSE"
>Aegis code is GPL-3.0.</a
>
</p>
<details class="evidence-details">
<summary>Inside the generator</summary>
<p>
The released APK puts <code>generateOTP</code> in
<code>kotlin.ExceptionsKt</code>. REA follows the call from
<code>TotpInfo</code> and returns this body; the original source
calls the helper <code>HOTP</code>.
</p>
<div class="code-block">
<div class="code-header">
<span>REA · generateOTP, complete body</span>
</div>
<pre><code>public static OTP generateOTP(byte[] bArr, String str,
int i, long j)
throws NoSuchAlgorithmException, InvalidKeyException {
byte[] hash = getHash(bArr, str, j);
int i2 = hash[hash.length - 1] &amp; 15;
return new OTP(
(hash[i2 + 3] &amp; 255)
| ((hash[i2] &amp; 127) &lt;&lt; 24)
| ((hash[i2 + 1] &amp; 255) &lt;&lt; 16)
| ((hash[i2 + 2] &amp; 255) &lt;&lt; 8), i, 0);
}</code></pre>
</div>
<p>
The last hash byte selects a position. Four bytes at that
position provide a positive 31-bit number; formatting reduces it
to the requested number of digits. The constructor's last
argument selects the code-formatting branch introduced by
compilation.
</p>
<p>
<a
href="https://github.com/beemdevelopment/Aegis/blob/d6f4e5925a97e4e91593f1542085eae03432a759/app/src/main/java/com/beemdevelopment/aegis/crypto/otp/HOTP.java"
>Compare with the matching release source</a
>.
</p>
</details>
</section>
<section class="article-section" id="verify">
<h2>Check the reconstruction</h2>
<p>
We assembled the selected REA-returned Java methods in a small
test program. Their output matched an independent reconstruction
on <strong>133 test cases</strong>, including time-block
boundaries and codes beginning with zero.
</p>
<div class="evidence-table-wrap">
<table class="evidence-table">
<thead>
<tr>
<th>Example time</th>
<th>Block</th>
<th>Six-digit code</th>
</tr>
</thead>
<tbody>
<tr>
<td>30 seconds</td>
<td>1</td>
<td><code>287082</code></td>
</tr>
<tr>
<td>59 seconds</td>
<td>1</td>
<td><code>287082</code></td>
</tr>
<tr>
<td>60 seconds</td>
<td>2</td>
<td><code>359152</code></td>
</tr>
<tr>
<td>1,111,111,109 seconds</td>
<td>37,037,036</td>
<td><code>081804</code></td>
</tr>
</tbody>
</table>
</div>
<p>
The generator also matches the six published SHA-1 reference cases
in
<a href="https://www.rfc-editor.org/rfc/rfc6238.html#appendix-B"
>RFC 6238</a
>. Check the browser implementation against them:
</p>
<div class="actions">
<button
class="button secondary"
type="button"
data-aegis-check
disabled
>
Run reference checks</button
><a class="text-link" href="../../assets/aegis-otp.js"
>Read the demo's calculation</a
>
</div>
<p
class="caption"
data-aegis-check-result
role="status"
aria-live="polite"
>
The reference cases use eight digits. The check also verifies
their six-digit results.
</p>
<p class="caption">
Verification runs extracted Java methods and the new demo. The
Android APK was inspected statically.
</p>
</section>
<section class="article-section" id="try">
<h2>Inspect the APK yourself</h2>
<p>
<a href="../../get-started/">Set up REA</a> on macOS or Linux,
then download these two files into the same folder. On Windows,
use a Linux environment such as WSL.
</p>
<p>
Android analysis needs a full JDK 17 or later. Select it with
<code>JAVA_HOME</code> or <code>PATH</code>. This case used JDK
21.
</p>
<div class="actions">
<a
class="button"
href="https://github.com/beemdevelopment/Aegis/releases/download/v3.4.3/aegis-v3.4.3.apk"
>Download Aegis APK</a
><a
class="text-link"
href="https://github.com/1013503897/jadx-headless-mcp/releases/download/v0.7.1/jadx-headless-mcp-0.7.1-all.jar"
>Download Android analysis provider</a
>
</div>
<p>Point REA at the provider file and inspect the APK:</p>
<div class="code-block">
<div class="code-header">
<span>macOS / Linux</span
><button
class="copy-button"
type="button"
data-copy="aegis-inspect-command"
aria-label="Copy APK inspection command"
>
Copy
</button>
</div>
<pre><code id="aegis-inspect-command">REA_JADX_MCP_JAR="$PWD/jadx-headless-mcp-0.7.1-all.jar" \
npx rea-agents@latest inspect-android-package \
./aegis-v3.4.3.apk --format json --full-output</code></pre>
</div>
<p>
Your agent can continue through REA's CLI using the prompt above.
For MCP, add <code>REA_JADX_MCP_JAR</code> to the REA server's
environment and reconnect it.
</p>
<p>
A next question:
<strong
>what happens when an account uses eight digits or a different
time interval?</strong
>
</p>
</section>
<section class="article-section" id="sources">
<h2>Sources</h2>
<ul class="compact-list">
<li>
<a
href="https://github.com/beemdevelopment/Aegis/releases/tag/v3.4.3"
>Official Aegis 3.4.3 release</a
>
·
<a
href="https://github.com/beemdevelopment/Aegis/tree/d6f4e5925a97e4e91593f1542085eae03432a759"
>Matching source commit</a
>
·
<a
href="https://github.com/beemdevelopment/Aegis/blob/d6f4e5925a97e4e91593f1542085eae03432a759/LICENSE"
>GPL-3.0 license</a
>
</li>
<li>
<a href="https://www.rfc-editor.org/rfc/rfc6238.html#appendix-B"
>RFC 6238 reference cases</a
>
· <a href="test-vectors.json">Demo test data</a>
</li>
</ul>
<p class="caption">
Inspected with REA 6.1.0 on 9 October 2026. Decompiled excerpts
are credited to Aegis; the diagram, readable summary and browser
reconstruction are new teaching material.
</p>
</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,14 @@
{
"source": "https://www.rfc-editor.org/rfc/rfc6238.html#appendix-B",
"public_test_key_ascii": "12345678901234567890",
"algorithm": "HMAC-SHA1",
"period_seconds": 30,
"vectors": [
{ "seconds": 59, "sha1": "94287082" },
{ "seconds": 1111111109, "sha1": "07081804" },
{ "seconds": 1111111111, "sha1": "14050471" },
{ "seconds": 1234567890, "sha1": "89005924" },
{ "seconds": 2000000000, "sha1": "69279037" },
{ "seconds": 20000000000, "sha1": "65353130" }
]
}
+21 -3
View File
@@ -6,7 +6,7 @@
<title>Case studies — REA</title>
<meta
name="description"
content="Follow REA investigations through code and figures: DX-Ball, Notion, TH04 and a DownUnderCTF flag checker."
content="Follow REA investigations through code, figures and demos: an Android authenticator, 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" />
@@ -54,11 +54,29 @@
<p class="breadcrumb"><a href="../">Home</a> / Showcases</p>
<h1>REA case studies.</h1>
<p class="lede">
Game reconstruction, Electron analysis and a CTF challenge, with code,
figures and checked results.
Android, games, Electron and a CTF challenge, with code, figures and
checked results.
</p>
</div>
<div class="guide-index">
<section class="guide-entry">
<p class="eyebrow">Android · APK analysis</p>
<div>
<h2>How an authenticator generates a login code</h2>
<p>
Find Aegis's six-digit code calculation in its released APK. Trace
the generating methods with REA, then move a demo clock to see why
the code changes every 30 seconds.
</p>
<div class="guide-route">
<span>APK</span> <b aria-hidden="true">→</b>
<span>Generating code</span> <b aria-hidden="true">→</b>
<span>Recovered rule</span> <b aria-hidden="true">→</b>
<span>Adjustable clock</span>
</div>
<a class="text-link" href="aegis/">Read the Android case study</a>
</div>
</section>
<section class="guide-entry">
<p class="eyebrow">CTF · Native analysis</p>
<div>
+9 -8
View File
@@ -142,14 +142,15 @@ purposes, and immutable commit links for checkpoint-dependent facts.
Each figure should answer a question that a reader can name. Choose the visual
form that fits it:
| Relationship | Existing example | Useful form |
| ----------------------------------------- | ---------------------------------------- | ------------------------------------------------------- |
| Analysis and verification steps | DX-Ball sound pan | Flow with separate verification branches |
| Code across process boundaries | Electron CSV export and Notion clipboard | HTML flow with APIs and channel names |
| A mathematical transformation | TH04's fixed and aimed rings | SVG drawn from the angle formula |
| Original instructions and readable source | DX-Ball and TH04 | Selectable HTML code with matching step highlights |
| A request caused by an action | Notes browser export | Flow, observed result table and script excerpt |
| Recovered behavior and a new experiment | Dinosaur speed reconstruction | Inspected code, playable demo and original-result check |
| Relationship | Existing example | Useful form |
| ----------------------------------------- | ---------------------------------------- | ---------------------------------------------------------------- |
| Analysis and verification steps | DX-Ball sound pan | Flow with separate verification branches |
| Code across process boundaries | Electron CSV export and Notion clipboard | HTML flow with APIs and channel names |
| A mathematical transformation | TH04's fixed and aimed rings | SVG drawn from the angle formula |
| Original instructions and readable source | DX-Ball and TH04 | Selectable HTML code with matching step highlights |
| A request caused by an action | Notes browser export | Flow, observed result table and script excerpt |
| Recovered behavior and a new experiment | Dinosaur speed reconstruction | Inspected code, playable demo and original-result check |
| Time blocks and changing output | Aegis login-code reconstruction | Adjustable clock, block number and code beside recovered methods |
Use SVG or semantic HTML for diagrams with exact text, numbers and arrows.
Generated illustrations can help explore a layout, but the published labels,