에이전트에게 질문하기
앱에 표시되는 로그인 코드는 계속 바뀝니다. 무엇 때문에 바뀌는지 찾아 그 동작을 재현하려고 합니다.
REA로 aegis-v3.4.3.apk을 분석해 주세요. 6자리 로그인 코드가 어떻게 생성되고 왜 30초마다 바뀌는지 설명한 뒤, 시간을 바꿔 볼 수 있는 작은 데모를 만들어 주세요.
다운로드한 APK를 분석하는 예시 프롬프트입니다. 먼저 REA의 Android 분석 환경을 설정하세요. APK와 분석 설정 받기 ↓
코드 생성기 사용해보기
앱은 시간을 30초 단위로 나눕니다. 시간 구간 번호와 계정 키를 결합해 코드를 생성합니다. 같은 키와 같은 시간 구간이면 같은 코드가 나옵니다.
60초 구간에서 시간 바꾸기
- 시간 초시간을 조절할 수 있는 예제 시계
-
30초 구간구간
floor(59 / 30)
30~59초 - 생성된 코드계정 키 + 시간 구간 → 여섯 자리 숫자
59초: 287082. 60초: 359152. 같은 구간 안에서는 시간을 바꿔도 코드가 유지됩니다.
REA로 코드 찾기
-
APK에서 클래스 3개로 좁히기
에이전트 → 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로 바뀝니다.
계정 키도 중요합니다. 코드 생성기는 계정 키와 시간 구간을 조합합니다. 키가 다른 계정에서는 같은 시간에도 다른 코드가 나올 수 있습니다.
6자리를 유지합니다. 1,000,000으로 나눈 나머지의 앞자리를 필요에 따라 0으로 채우므로, 코드는 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);
}
해시의 마지막 바이트로 위치를 고릅니다. 그 위치의 4바이트로 양의 31비트 수를 얻고, 요청한 자릿수로 줄여 표시합니다. 생성자의 마지막 인수는 컴파일 과정에서 생긴 코드 서식 분기를 선택합니다.
재구성 결과 검증
REA가 반환한 Java 메서드 중 필요한 것들을 작은 테스트 프로그램으로 묶었습니다. 시간 구간의 경계와 0으로 시작하는 코드를 포함한 테스트 133개에서 출력이 독립적으로 재구현한 프로그램과 일치했습니다.
| 예제 시간 | 구간 | 6자리 코드 |
|---|---|---|
| 30초 | 1 | 287082 |
| 59초 | 1 | 287082 |
| 60초 | 2 | 359152 |
| 1,111,111,109초 | 37,037,036 | 081804 |
코드 생성기는 RFC 6238에 공개된 SHA-1 기준 사례 6개와도 일치합니다. 브라우저 구현도 이 사례로 확인해 보세요:
기준 사례는 8자리를 사용합니다. 검사에서는 이에 대응하는 6자리 결과도 검증합니다.
검증할 때는 추출한 Java 메서드와 새 데모를 실행합니다. Android APK는 정적으로 분석했습니다.
APK 직접 살펴보기
macOS나 Linux에서 REA를 설정한 뒤 아래 두 파일을 같은 폴더에 내려받으세요. Windows에서는 WSL 같은 Linux 환경을 사용하세요.
Android 분석에는 전체 JDK 17 이상이 필요합니다. JAVA_HOME이나 PATH로 사용할 JDK를 지정하세요. 이 사례에서는 JDK 21을 사용했습니다.
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로 분석을 이어갈 수 있습니다. MCP를 사용하려면 REA 서버의 환경에 REA_JADX_MCP_JAR를 추가한 뒤 다시 연결하세요.
다음 질문: 계정이 8자리 코드나 다른 시간 간격을 사용하면 어떻게 될까요?
출처
2026년 10월 9일 REA 6.1.0으로 분석했습니다. 디컴파일 발췌문의 출처는 Aegis이며, 그림과 요약, 브라우저 재구현은 새로 만든 교육 자료입니다.