PART 1
This article was uploaded to a self-published website because the engine here doesn't support KAPEX or LATEX. If you want to see a cleaner and more readable article, go to https://xve-e.github.io/2026/03/23/analyzing-akamai-bmp-part-2.html .
App showcase: Iberia 14.81.0
IDA Pro: 9.3
1. Analyzing the post-decompress lib
The decompiler often misidentified the number of arguments and return values for the polymorphic dispatcher, i used a secret technique to get around this using asm.
As we saw earlier, sub_25E0AC is a large polymorphic dispatcher.
sub_25E0AC(string_ptr) → strlen or string copy
sub_25E0AC(string_ptr, 0x8641, 0xFFFFFFFF) → string deobfuscation
sub_25E0AC(plaintext, output, len, key, iv) → AES-128-CBC encrypt
sub_25E0AC(qword_2466A8) → MT19937 extract
JNI Entry Points
| VA | Java Name | Purpose |
|---|---|---|
| 0x9D394 | SensorDataBuilder.buildN | Main entry: serialize + encrypt sensor data |
| 0x9D074 | SensorDataBuilder.encryptKeyN | Generate session ID (20-char base62 → base64) |
| 0xA0144 | addOne | Set MT flag dword_247A38 |
| 0xA0150 | sampleTest | Set MT flag dword_247A3C |
| 0xA015C | presentData | Set MT flag dword_247A40 |
| 0xA0168 | testOne | Set MT flag dword_247A44 |
Internal Functions (Called by buildN)
| VA | Size | Name | Purpose |
|---|---|---|---|
0x9ED74 |
0xAFC | sub_9ED74 |
Core encrypt+format: MT → AES → HMAC → b64 → header assembly |
0x9EAF0 |
0x34 | sub_9EAF0 |
Crypto context singleton getter |
0x9EB24 |
0x250 | sub_9EB24 |
One-time key initialization |
0x9E840 |
0x1D0 | sub_9E840 |
LCG-based string deobfuscation |
0x9E660 |
0xF8 | sub_9E660 |
RSA_public_encrypt wrapper |
0x9E594 |
0x90 | sub_9E594 |
HMAC-SHA256 wrapper |
0x9E620 |
0x24 | sub_9E620 |
RAND_bytes wrapper |
0x9E75C |
0xE0 | sub_9E75C |
Base64 encode (OpenSSL BIO) |
0x9FAD0 |
0x130 | sub_9FAD0 |
MT19937 bounded random |
0x9F91C |
0xFC | sub_9F91C |
C++ stringstream initialization |
0x9FDF8 |
0xD0 | sub_9FDF8 |
Stringstream write (string) |
0x1CFD2C |
0x158 | sub_1CFD2C |
Stringstream write (integer) |
0x9DBD0 |
0x98 | sub_9DBD0 |
JNI CallIntMethodV wrapper |
1.1. Serialization Pipeline
Function: Java_com_cyberfend_cyfsecurity_SensorDataBuilder_buildN @ 0x9D394
| Property | Value |
|---|---|
| Size |
0x83C (2108 bytes) |
| Input |
ArrayList<Pair<String, String>> — 28 entries from Java |
| Output | Encrypted header string "6,a,{rsa1},{rsa2}${b64}${timing}"
|
Step-by-step from Assembly
- Phase 1: JNI Environment Setup (0x9D3CC–0x9D54C)
The function begins by resolving all required JNI references prior to data extraction:
FindClass("android/os/Build$VERSION")
GetStaticFieldID("SDK_INT", "I")
GetStaticIntField() → must be >= 1 (sanity check)
FindClass("java/util/ArrayList")
GetMethodID("size", "()I") → pair_count
GetMethodID("get", "(I)Ljava/lang/Object;")
FindClass("android/util/Pair")
GetFieldID("first", "Ljava/lang/Object;")
GetFieldID("second", "Ljava/lang/Object;")
- Phase 2: Pair Vector Extraction (0x9D554–0x9D6A8)
Each element of the input ArrayList is extracted via a JNI iteration loop over indices $i \in [0,\ \text{pair_count} - 1]$:
pair = ArrayList.get(i)
key = GetStringUTFChars(pair.first)
value = GetStringUTFChars(pair.second)
Pairs are stored in a C++ vector as 48-byte structs with the layout:
$$\text{struct PairEntry} = \underbrace{\text{key}}{\text{std::string, 24 B}} \;|\; \underbrace{\text{value}}{\text{std::string, 24 B}}$$
The 24-byte std::string layout corresponds to the standard libc++ small-string-optimized (SSO) representation on AArch64.
- Phase 3: Output Initialization from First Pair (0x9D6F8–0x9D724)
The value field of the first pair (index 0) is used as the initial content of the serialized output buffer; its key is discarded. In the observed execution context, this value is the SDK version string "4.1.3".
- Phase 4: Separator String Deobfuscation (0x9D728–0x9D768)
The separator literal is stored obfuscated in .rodata and decoded at runtime. The encoded form "WUfOL#f}+" is passed to a .pb dispatcher alongside the constant 0x8641, which drives a substitution-based decode:
9d728 ADRL X9, aWufolF ; load encoded string "WUfOL#f}+"
9d738 STRB W8, [SP, #var_90] ; SSO length field = 9
9d744 STUR X9, [SP, #var_90+1] ; copy encoded bytes onto stack
9d748 ADD X8, SP, #var_78 ; destination buffer = var_78
9d750 MOV W1, #0x8641 ; deobfuscation constant
9d754 MOV W2, #0xFFFFFFFF ; flag
9d758 BL sub_25E0AC ; decode → "-1,2,-94," stored in var_78
The decode mechanism and constant 0x8641 are identical to those observed in buildVerification() , indicating a shared obfuscation scheme across the serialization pipeline.
- Phase 5: Pair Serialization Loop (0x9D76C–0x9D84C)
Pairs at indices $i \in [1,\ \text{pair_count} - 1]$ are serialized in order. Each iteration appends to the output buffer with the pattern:
$$\text{output} \mathrel{+}= \text{separator} \;|\; \text{key}_i \;|\; \texttt{","} \;|\; \text{value}_i$$
The struct stride is 48 bytes (ADD X22, X22, #0x30).
9d7b0 ADD X0, SP, #var_60 ; output string ptr
9d7b4 BL sub_25E0AC ; append(output, separator)
9d7d4 LDRB W9, [X8] ; load key SSO flag (offset +0)
9d7f0 BL sub_1CDC48 ; append(output, key)
9d7f8 MOV X1, X20 ; X20 = "," literal @ 0x51163
9d7fc BL sub_1CE08C ; append(output, ",")
9d81c LDRB W9, [X8, #0x18] ; load value SSO flag (offset +24)
9d83c BL sub_1CDC48 ; append(output, value)
9d840 ADD X22, X22, #0x30 ; advance to next struct (stride = 48)
9d844 ADD X24, X24, #1 ; i++
9d84c B.NE loop_start
-
Phase 6:
SECURITY_PATCHField Appended via JNI (0x9D850–0x9D92C)
After exhausting the input vector, the function retrieves Build.VERSION.SECURITY_PATCH directly from the Android runtime via JNI reflection, bypassing the Java-side pair list entirely:
9d888 ADRL X2, aSecurityPatch ; field name "SECURITY_PATCH"
9d890 ADRL X3, aLjavaLangStrin ; descriptor "Ljava/lang/String;"
9d8a4 BLR X8 ; GetStaticFieldID
9d8bc BLR X8 ; GetStaticObjectField
9d8d4 BLR X8 ; GetStringUTFChars → X20 = e.g. "2025-04-01"
The value is appended with a dedicated tag identifier -164:
$$\text{output} \mathrel{+}= \texttt{"-1,2,-94,"} \;\|\; \texttt{"-164,"} \;\|\; \text{SECURITY\_PATCH}$$
9d900 BL sub_25E0AC ; append(output, "-1,2,-94,")
9d904 ADRL X1, a164 ; literal "-164"
9d910 BL sub_25E0AC ; append(output, "-164")
9d914 ADRL X1, asc_51163 ; ","
9d920 BL sub_25E0AC ; append(output, ",")
9d928 MOV X1, X20 ; SECURITY_PATCH string
9d92c BL sub_1CE08C ; append(output, security_patch)
The native-side injection of SECURITY_PATCH — absent from the Java-supplied pair list — constitutes an integrity signal that cannot be trivially spoofed by intercepting the Java layer alone.
- Phase 7: Encryption and Formatting (0x9D930–0x9D96C)
The assembled plaintext is passed to the shared cryptographic context singleton and encrypted via sub_9ED74, which implements the same AES-128-CBC + HMAC-SHA256 pipeline.
9d930 BL sub_9EAF0 ; acquire crypto context singleton
9d964 ADD X8, SP, #var_90 ; output buffer
9d968 ADD X1, SP, #var_B0 ; plaintext string
9d96c BL sub_9ED74 ; encrypt + assemble → "6,a,{rsa1},{rsa2}${b64}${timing}"
- Phase 8: Return to Java (0x9D99C–0x9D9AC)
The formatted output string is converted to a managed Java string and returned to the caller:
9d99c BLR X8 ; NewStringUTF(output_cstr)
9d9ac ; return jstring
Serialization Format
4.1.3-1,2,-94,-90,{val}-1,2,-94,-91,{val}-1,2,-94,-70,-1,2,-94,-80,...-1,2,-94,-164,{SECURITY_PATCH}
- First: SDK version (pair 0 value only)
- Then:
{separator}{key},{value}for each remaining pair - Last:
{separator}-164,{SECURITY_PATCH}(from JNI)
2. Cryptographic Pipeline — sub_9ED74
Function: sub_9ED74 @ 0x9ED74
| Property | Value |
|---|---|
| Size |
0xAFC (2812 bytes) |
| Calling convention |
__usercall — X8 = output ptr, X0 = crypto context, X1 = plaintext |
| Input | Crypto context (from sub_9EAF0), serialized sensor plaintext |
| Output | "6,a,{rsa1},{rsa2}${base64(IV+cipher+HMAC)}${timing}" |
- Phase 1: Separator Decode (0x9EDC8–0x9EE50)
Employs the same deobfuscation routine as buildN : the encoded string "WUfOL#f}+" is decoded with constant 0x8641, yielding the separator "-1,2,-94,". The result is stored in var_180 for later concatenation with the suffix "-170" during payload assembly.
- Phase 2: MT19937 PRNG Initialization (0x9EE64–0x9EE80)
Two atomic guard flags gate one-time initialization of the Mersenne Twister state:
-
byte_2466A0— controls MT state array initialization -
byte_247A30— controls seeding fromclock_gettime(CLOCK_REALTIME)
The seeding routine at 0x9F698–0x9F6E4 implements the standard MT19937 initialization recurrence:
$$\text{state}[i] = i + 1812433253 \cdot \left(\text{state}[i-1] \oplus \left(\text{state}[i-1] \gg 30\right)\right), \quad i \in [1, 623]$$
; state[0] = seed (from clock_gettime)
9f6ac STR X9, [qword_2466A8]
9f6b0 MOV X10, #1 ; i = 1
loop:
9f6c0 MUL+ADD X9 = i + 1812433253 * (state[i-1] ^ (state[i-1] >> 30))
9f6c4 STR X9, [qword_2466A8 + i*8]
9f6cc ADD X10, X10, #1
9f6d0 CMP X10, #624
9f6d4 B.NE loop
9f6e0 STR XZR, [qword_247A28] ; index = 0
- Phase 3: Verification Value Generation (0x9EE84–0x9F060)
Five sampling ranges are loaded from .rodata:
| Variable | Range | Conditional Flag | Semantic |
|---|---|---|---|
var_188 |
[1, 1000] | — (unconditional) | base values |
var_190 |
[1, 6] |
dword_247A38 (addOne) |
optional |
var_198 |
[1, 7] |
dword_247A3C (sampleTest) |
optional |
var_1A0 |
[1, 8] |
dword_247A40 (presentData) |
optional |
var_1A8 |
[1, 4] |
dword_247A44 (testOne) |
optional |
Sampling sequence (registers mapped from assembly):
v13 = MT_rand(1, 1000)
v14 = MT_rand(1, 6) if dword_247A38 == 1 else 0
v15 = MT_rand(1, 1000)
v16 = MT_rand(1, 7) if dword_247A3C == 1 else 0
v17 = MT_rand(1, 1000)
v18 = MT_rand(1, 8) if dword_247A40 == 1 else 0
v19 = MT_rand(1, 1000)
v20 = MT_rand(1, 4) if dword_247A44 == 1 else 0
Four verification values are derived through a chained XOR construction:
$$\text{val}1 = v{14} + 7 \cdot v_{13}$$
$$\text{val}2 = \left(v{16} + 8 \cdot v_{15}\right) \oplus \text{val}_1$$
$$\text{val}3 = \left(v{18} + 9 \cdot v_{17}\right) \oplus \text{val}_2$$
$$\text{val}4 = \left(v{20} + 5 \cdot v_{19}\right) \oplus \text{val}_3$$
The AArch64 encoding uses shift-and-subtract/add idioms for constant multiplication:
; val1 = v14 + 7*v13
9efe4 LSL W8, W22, #3 ; W8 = v13 * 8
9efe8 SUB W8, W8, W22 ; W8 = v13*8 - v13 = v13*7
9efec ADD W22, W24, W8 ; W22 = v14 + 7*v13
; val2 = (v16 + 8*v15) ^ val1
9f00c LSL W8, W23, #3
9f010 ADD W8, W26, W8
9f014 EOR W22, W8, W22
; val3 = (v18 + 9*v17) ^ val2
9f030 ADD W8, W25, W25,LSL#3 ; W8 = v17 + v17*8 = v17*9
9f034 ADD W8, W28, W8
9f038 EOR W22, W8, W22
; val4 = (v20 + 5*v19) ^ val3
9f054 ADD W8, W27, W27,LSL#2 ; W8 = v19 + v19*4 = v19*5
9f058 ADD W8, W19, W8
9f05c EOR W1, W8, W22
The final verification string is serialized via a std::stringstream-equivalent dispatcher with the form "val1,val2,val3,val4".
- Phase 4: Plaintext Payload Assembly (0x9F070–0x9F0E4)
The plaintext buffer is constructed by sequential concatenation:
$$\text{plaintext} \mathrel{+}= \texttt{"-1,2,-94,"} \;|\; \texttt{"-170,"} \;|\; \text{val}_1\texttt{,val}_2\texttt{,val}_3\texttt{,val}_4$$
9f090 BL sub_1CDC48 ; append(plaintext, "-1,2,-94,")
9f0a0 ADRL X1, a170 ; literal "-170"
9f0ac BL sub_25E0AC ; append(plaintext, "-170")
9f0b0 ADRL X1, asc_51163 ; ","
9f0bc BL sub_1CE08C ; append(plaintext, ",")
9f0e4 BL sub_1CDC48 ; append(plaintext, "val1,val2,val3,val4")
- Phase 5: AES-128-CBC Encryption (0x9F110–0x9F16C)
A fresh 16-byte IV is generated per invocation via RAND_bytes(). The AES key and IV are read from the crypto context at offsets ctx[0] and ctx[8] respectively. PKCS#7 padding is applied implicitly. The output layout is:
$$\text{ciphertext_blob} = \text{IV}{16} \;|\; \text{AES\text{-}128\text{-}CBC}(\text{plaintext},\; k{\text{AES}},\; \text{IV})$$
The IV is prepended via a single 128-bit NEON store (STR Q0), and the output buffer is allocated as encrypted_len + 17 bytes.
9f114 BL sub_9E620 ; RAND_bytes(16) → ctx[8]
9f12c LDP X3, X4, [X20] ; X3 = AES key ptr, X4 = IV ptr
9f160 LDR Q0, [X8] ; load IV (16 bytes, NEON Q-register)
9f168 STR Q0, [X0], #0x10 ; prepend IV; advance pointer
9f16c BL sub_25E0AC ; memcpy(buf+16, ciphertext, encrypted_len)
- Phase 6: HMAC-SHA256 Authentication (0x9F190–0x9F1A4)
The authentication tag is computed over the full IV || ciphertext blob, using a 32-byte HMAC key at ctx[16]:
$$\text{tag} = \text{HMAC\text{-}SHA256}!\left(k_{\text{HMAC}},\; \text{IV} | \text{ciphertext}\right)$$
9f190 ADD W26, W25, #0x10 ; input_len = encrypted_len + 16
9f194 LDR X2, [X20, #0x10] ; HMAC key ptr (32 bytes) from ctx[16]
9f198 MOV X0, X22 ; data = IV || ciphertext
9f19c MOV W1, W26 ; data length
9f1a0 BL sub_9E594 ; HMAC-SHA256 → X23 (32-byte tag)
- Phase 7: Final Binary Assembly and Base64 Encoding (0x9F1B8–0x9F204)
The authenticated ciphertext is assembled into a contiguous binary blob:
$$\text{output_bin} = \text{IV}{16} \;|\; \text{ciphertext}{n} \;|\; \text{HMAC}_{32}$$
Total length: $n + 48$ bytes. The 32-byte HMAC is written atomically via two 128-bit NEON registers (LDP Q0, Q1 / STP Q0, Q1). The blob is then Base64-encoded:
$$\text{b64} = \text{Base64}!\left(\text{output_bin}\right)$$
9f1d8 LDP Q0, Q1, [X23] ; load 32-byte HMAC (two NEON Q-registers)
9f1dc ADD X8, X24, X19 ; offset = base + (IV + ciphertext length)
9f1e0 STP Q0, Q1, [X8] ; store 32-byte HMAC
9f1f8 ADD W1, W25, #0x30 ; total_len = encrypted_len + 48
9f200 BL sub_9E75C ; base64_encode(output_bin, total_len) → X25
- Phase 8: Final Header String Construction (0x9F21C–0x9F4F4)
The complete header value is assembled through three logical segments:
Segment A — Key material header:
$$\texttt{"6,a,{base64(RSA(}k_{\text{AES}}\texttt{))},{base64(RSA(}k_{\text{HMAC}}\texttt{))}"}$$
where ctx[24] and ctx[32] hold the RSA-wrapped, Base64-encoded symmetric keys.
Segment B — Timing telemetry:
$$\texttt{"{}\Delta t_{\text{encrypt}}\texttt{µs},{}\Delta t_{\text{hmac}}\texttt{µs},{}\Delta t_{\text{b64}}\texttt{µs}"}$$
Derived from three consecutive clock_gettime delta measurements converted to microseconds via FCVTMS.
Final concatenation:
$$\text{header} = \text{Segment_A} \;|\; \texttt{"\$"} \;|\; \text{b64} \;|\; \texttt{"\$"} \;|\; \text{Segment_B}$$
9f4a4 BL sub_1CDC48 ; output = Segment_A
9f4ac LDR X1, [off_245020] ; "$"
9f4b4 BL sub_25E0AC ; append "$"
9f4c0 BL sub_1CE08C ; append b64 (X25)
9f4c4 LDR X1, [off_245020] ; "$"
9f4cc BL sub_25E0AC ; append "$"
9f4f4 BL sub_25E0AC ; append Segment_B (timing)
3. Mersenne Twister Verification
3.1 MT19937 Implementation
The native code employs a standard MT19937 PRNG, confirmed by three structural markers: a 624-element state array at qword_2466A8, the initialization multiplier 1812433253, and a twist operation matching the reference implementation.
3.2 sub_9FAD0: Bounded Random Sampling
This function implements rejection sampling for uniform distribution over the range [lo, hi]:
int mt_rand_range(int lo, int hi) {
int range = hi - lo + 1;
int result;
do {
result = mt_extract() % range;
} while (result >= range); // rejection to ensure uniformity
return lo + result;
}
3.3 Verification String Serialization
The four values $\text{val}_{1..4}$ are written to a C++ std::stringstream as comma-separated decimal integers:
-
sub_25E0AC(stream, value)— write integer -
sub_9FDF8(stream, ",", 1)— write separator -
sub_1CFD2C(stream, value)— write final integer
Output format: "val1,val2,val3,val4" (e.g., "427,1052,5765,4661").
3.4 Flag Behavior
The four control flags (dword_247A38, dword_247A3C, dword_247A40, dword_247A44) are set by the JNI entry points addOne, sampleTest, presentData, and testOne respectively. Their static value in the binary is 0xFFFFFFFF (not equal to 1), rendering all conditional terms zero by default. At runtime, the Java layer may activate individual flags by calling the corresponding JNI setter with argument 1. When enabled, a bounded random offset is added to each accumulator term. For the initial sensor data generation these flags are typically inactive, yielding $v_{14} = v_{16} = v_{18} = v_{20} = 0$.
4. Key Initialization — sub_9EB24
Function: sub_9EB24 @ 0x9EB24
Invoked when ctx[40] == 0 (uninitialized). Executes once per process lifetime.
- Phase 1: Key Buffer Allocation
Three heap buffers are allocated from the crypto context:
9eb5c BL sub_25E0AC ; ctx[0] = malloc(17) → AES key buffer (16 B + null)
9ec54 BL sub_20AF7C ; ctx[8] = malloc(17) → IV buffer (16 B + null)
9ec64 BL sub_25E0AC ; ctx[16] = malloc(33) → HMAC key buffer (32 B + null)
- Phase 2: RSA Public Key Deobfuscation
268 bytes of obfuscated key material are loaded from off_245030 and decoded via the LCG substitution cipher with seed 63:
9eb7c LDR X3, [off_245030] ; load 268-byte obfuscated PEM blob
9ebf4 BL sub_9E840 ; deobfuscate(material, seed=63, flag=-1) → PEM
- Phase 3: Session Key Generation and RSA Encryption
; AES key: 16 random bytes, RSA-encrypted, Base64-encoded → ctx[24]
9ec38 BL sub_9E660 ; RSA_public_encrypt(pem_key, rand_16)
9ec48 BL sub_9E75C ; base64(encrypted) → ctx[24]
; HMAC key: 32 random bytes, RSA-encrypted, Base64-encoded → ctx[32]
9ec9c BL sub_9E660 ; RSA_public_encrypt(pem_key, rand_32)
9eca8 BL sub_9E75C ; base64(encrypted) → ctx[32]
; Mark context as initialized
9ecd4 STRB #1, [X2, #40] ; ctx[40] = 1
5. String Deobfuscation
5.1 Native: LCG Substitution Cipher (sub_9E840)
Used for RSA key deobfuscation (off_245030) and separator decoding ("WUfOL#f}+").
The charset is constructed from 92 printable ASCII characters in the range [32, 126], excluding " (0x22), ' (0x27), and \ (0x5C). For each input byte, a linear congruential generator advances the state and produces a reverse-lookup shift:
def build_charset():
return [ch for ch in range(32, 127) if ch not in (34, 39, 92)]
def lcg_decode(data: bytes, seed: int) -> bytes:
charset = build_charset()
n = len(charset)
lcg = seed
out = []
for byte in data:
idx = charset.index(byte)
shift = ((lcg >> 8) & 0xFFFF) % n
out.append(charset[(idx - shift + n) % n])
lcg = (lcg * 65793 + 4282663) & 0x7FFFFF
return bytes(out)
5.2 Java: XOR Table Cipher (C0018K.kpR)
Used for all Java-side string deobfuscation. A pseudo-random table of 32 768 entries is derived from a feedback recurrence seeded at 3, then applied as a positional XOR stream:
def build_table(size: int = 32768) -> list[int]:
table, prev = [0] * size, 3
for i in range(size):
val = prev ^ i
prev = (prev + val + 88) % 63 # constant +88 in C0018K.kpR
table[i] = prev
return table
def decode_kpR(encoded: str) -> str:
table = build_table()
return ''.join(chr(ord(c) ^ table[i]) for i, c in enumerate(encoded))
Note: The method
CYFMonitor.KkIuses an additive constant of+11with a distinct table. The two routines are not interchangeable.
6. Java-Side Architecture
6.1 CYFManager.buildSensorData()
The primary Java entry point orchestrates the full sensor data pipeline:
- Evaluate event count thresholds to select fast path or full path
- Collect data from 12+ sensor subsystems
- Assemble a
LinkedHashMap<String, String>with ~25 entries - Convert to
ArrayList<Pair<String, String>> - Invoke native
buildN(pairs)→ encrypted header - Append
$[3]..$[6]sections: Proof-of-Work, CCA token, signal, metadata
6.2 Fast Path (event count < 16)
When both GA and IIT accumulators hold fewer than 16 events and EG.D.isValid() is true, the function returns cached sensor data from EG.D.get(). Every fifth call triggers key rotation via encryptString(). Cache access is serialized via AtomicBoolean.compareAndSet() with a 5-second reentrance lock.
6.3 Full Path (event count ≥ 16)
- Call
buildN()with suffix,0→ return this header - If
GA ≥ 16ORIIT ≥ 16: callbuildN()again with suffix,1→ cache viaEG.D.put() - If
GA ≥ 128ORIIT ≥ 128: reset both accumulators
6.4 Sensor Collector Classes (java)
| Class | Alias | Data Collected |
|---|---|---|
C0005GA |
EG.GA |
Accelerometer, gyroscope, magnetometer (orientation) |
C0009GN |
EG.GN/McP.IIT |
Motion analysis, jerk derivatives (9 axes) |
C0022KG |
EG.KG |
Touch events (DOWN/MOVE/UP with coordinates) |
C0018K |
EG.K |
Text input events (keystroke timing) |
C0054Z |
EG.Z |
EditText field metadata |
C0008GK |
EG.GK |
Activity lifecycle (resume/pause events) |
C0006GE |
EG.GE |
Device info (40+ fields) |
C0051Vw |
EG.Vw/KWS |
System fingerprint, device ID |
C0001C |
EG.C |
DCI/JavaScript bridge (WebView challenges) |
C0002D |
EG.D |
CPR signal cache |
C0042U |
EG.U |
Proof-of-Work responses |
C0038M |
EG.M |
CCA challenge tokens |
I'm going to rest here, wait for part 3.


Top comments (0)