問問你的程式設計助手
該應用程式顯示更改的登入程式碼。 我們想找到 是什麼讓它改變 再現這種行為。
用 REA 分析 aegis-v3.4.3.apk,找出六位登入驗證碼是怎麼產生的,解釋為什麼它每 30 秒變化一次,再做一個可以調整時間的小示範。
這是用於下載的 APK 的示例 prompt,需要先配置 REA 的 Android 分析後端。獲取 APK 和分析配置 ↓
試試程式碼生成器
應用把時間劃分為每塊 30 秒,再結合時間塊編號和賬戶金鑰生成驗證碼。同樣的金鑰和時間塊,會得到同樣的驗證碼。
將時鐘移動60秒
- 時間 秒數我們的可調節示例時鐘
-
30秒區塊時間塊
floor(59 / 30)
30-59秒 - 生成的驗證碼帳戶金鑰+區塊→六位數字
在59秒: 287082. 在60秒: 359152. 在一個塊內移動會使程式碼保持不變。
用REA查詢程式碼
-
將APK縮小到三個類
程式設計助手→ REAinspect_android_package {"path": "aegis-v3.4.3.apk"} search_android_classes {"path": "aegis-v3.4.3.apk", "query": "Totp"}REA →程式設計助手 · 選定結果Package: com.beemdevelopment.aegis Version: 3.4.3 Classes: 3,077 Three matches: otp.TotpInfo ui.views.TotpProgressBar importers.TotpAuthenticatorImporterTotpInfo是計算邏輯的候選方法;另外兩個涉及進度條和匯入的賬戶。TOTP 指基於時間的一次性密碼。 -
將生成器連線到應用程式的顯示器
程式設計助手→ REAinspect_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"}REA →程式設計助手·方法和呼叫者TotpInfo methods: getOtp() · overload 0 getOtp(long) · overload 1 OtpInfo.getOtp callers: ui.views.EntryHolder.getOtp ui.MainActivity.copyEntryCodeREA 找到接受時間戳的方法過載,並定位基礎方法的呼叫方。繼續檢查
EntryHolder.getOtp,就能看到顯示邏輯呼叫了TotpInfo.getOtp(timestamp)。
請求顯示縮短的輸入路徑。 結果類名省略常用 com.beemdevelopment.aegis 字首。
閱讀計算
REA 返回時間轉換、預設設定和生成方法。建構函式把間隔設為 30 秒、位數設為 6;接受時間戳的方法將秒數轉換為時間塊編號。
// 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))
// 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);
// OTP.toString, case 0
this._code % ((int) Math.pow(10.0d, i))
// Keep leading zeroes
while (sb.length() < i) {
sb.insert(0, "0");
}
// Group time into 30-second blocks
seconds = current_time_ms / 1000
block = floor(seconds / 30)
// Generate a number from the key and block
hash = HMAC_SHA1(account_key,
big_endian_8_bytes(block))
number = select_31_bits(hash)
// Format the default six-digit code
code = number % 1_000_000
code = pad_with_zeroes(code, 6)
時間選擇塊。 秒30到59都給塊1。 在60秒時,塊變為2。
金鑰也會影響結果。 生成函式把賬戶金鑰和時間塊組合起來。金鑰不同的賬戶,在同一時間也可能顯示不同驗證碼。
保留六位數字。 取除以 1,000,000 的餘數,位數不足時在前面補零,因此驗證碼也可以以 0 開頭。
這些節選來自多個檢查過的方法,為便於顯示增加了換行。右側是解釋性摘要。Aegis 原始碼採用 GPL-3.0 許可。
驗證碼生成函式內部
釋出的 APK 將 generateOTP 放在 kotlin.ExceptionsKt 中。REA 從 TotpInfo 追蹤呼叫,並返回這個函式體;原始原始碼把輔助函式命名為 HOTP。
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] & 15;
return new OTP(
(hash[i2 + 3] & 255)
| ((hash[i2] & 127) << 24)
| ((hash[i2 + 1] & 255) << 16)
| ((hash[i2 + 2] & 255) << 8), i, 0);
}
雜湊的最後一個位元組決定讀取位置。從這個位置讀取四個位元組,得到正的 31 位數,再格式化為指定的位數。建構函式的最後一個引數選擇了編譯過程引入的驗證碼格式化分支。
檢查重建
我們將選定的 REA 返回的 Java 方法放進小型測試程式。它們的輸出與獨立重建的實現一致,共驗證 133 個測試用例,包括時間塊邊界和以零開頭的驗證碼。
| 示例時間 | 時間塊 | 六位驗證碼 |
|---|---|---|
| 30秒 | 1 | 287082 |
| 59秒 | 1 | 287082 |
| 60秒 | 2 | 359152 |
| 1,111,111,109秒 | 37,037,036 | 081804 |
生成函式還符合 RFC 6238 中釋出的六個 SHA-1 對照用例。可以用這些用例檢查瀏覽器實現:
參考案例使用八位數字。 檢查也驗證他們的六位數的結果。
驗證執行提取的Java方法和新的演示。 對Android APK進行了靜態檢查。
親自檢查APK
先在 macOS 或 Linux 上設定 REA,再將這兩個檔案下載到同一資料夾。Windows 上可使用 WSL 等 Linux 環境。
Android分析需要完整的JDK17或更高版本。 選擇它與 JAVA_HOME 或 PATH. 本案例使用了JDK21。
讓 REA 使用分析後端配置檔案,再檢查 APK:
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
助手可以透過 REA CLI 繼續使用上方的 prompt。如果使用 MCP,將 REA_JADX_MCP_JAR 加入 REA 服務的環境配置,再重新連線。
下一個問題: 當帳戶使用八位數字或不同的時間間隔時會發生什麼?
資料來源
使用 REA 6.1.0 於 2026 年 10 月 9 日檢查。反編譯節選歸屬 Aegis;圖示、可讀摘要和瀏覽器重建為新增教學材料。