# TiviMate 5.3.3 — DexProtector Audit & Bypass Reference

**Package**: `ar.tvplayer.tv` | **Version**: 5.3.3 (1000005332) | **APK**: 21MB | **Dev**: Armobsoft FZE (Dubai)

---

## Protection: DexProtector 5.x (Arxan)

Enterprise-grade commercial protection — not standard ProGuard. Four layers:

| Layer | Mechanism | Bypass |
|-------|-----------|--------|
| **1. Cert pinning (Java)** | `ProtectedTvPlayerApplication.b()` — SHA-256 of signing cert vs hardcoded `cafa2c5e0affcc243b4df23d1a69720a78a92412afc210a051b88ca68fca2c5b` | Patch `b()` → `return-void` |
| **2. Native lib** | `libdexprotector.so` (443KB, ARM64/v7a/x86/x86_64) — encrypts DEX string pool, runtime self-decryption, anti-tamper | Hard — requires Frida hook or DexIntercept |
| **3. String encryption** | All app strings replaced with native `s()` calls — no plaintext in DEX | Hook `ProtectedTvPlayerApplication.s()` at runtime to dump |
| **4. Class renaming** | 6,066 classes → Unicode filenames (`ːʤﹶᵷ.smali`) — no readable names | Unavoidable — trace via entry points only |

---

## Critical Build Fix: `extractNativeLibs`

**Root cause of `INSTALL_FAILED_INVALID_APK: Failed to extract native libraries, res=-2`**:

Manifest has `android:extractNativeLibs="false"` (line 73) → Android 7+ expects **page-aligned** native libs in APK. Without `zipalign -p`, misaligned `.so` files fail extraction.

**Fix**: Change to `android:extractNativeLibs="true"` in `AndroidManifest.xml` — Android then extracts `.so` files to `/data/app/.../lib/` normally instead of mmaping from APK.

> **Do NOT skip this** when patching any DexProtected app. The error is misleading — it looks like a zip corruption issue but is actually alignment + manifest flag mismatch.

---

## APK Structure

| File | Size | Content |
|------|------|---------|
| `classes.dex` | 39KB | Entry points: `ProtectedTvPlayerApplication`, `LibTvPlayerApplication`, `R` class |
| `classes2.dex` | 548KB | More entry points / wrappers |
| `classes3.dex` | 5.6MB | **6,066 obfuscated classes** — all app logic |

---

## Premium System (What to Hook)

| Component | Detail |
|-----------|--------|
| **Auth** | Parse SDK (email/password) — `ParseLoginActivity` in manifest |
| **Billing** | Google Play Billing 9.1.0 — `ProxyBillingActivityV2` |
| **Backend** | Parse Server (likely Back4App or self-hosted) |
| **License** | 5-device limit per account |
| **Plans** | Annual subscription + 7-day trial OR one-time payment |
| **Activation** | Via tivimate.com or TiviMate Companion app |
| **Premium flags** | Stored on Parse `_User` or custom `Subscription` class |

---

## Bypass Strategy (Runtime — Recommended)

Since static DEX patching is blocked by encrypted string pool and native integrity checks:

### 1. Frida Script to Dump Strings
```javascript
// Hook the string decryptor
var ProtectedApp = Java.use("ar.tvplayer.tv.ProtectedTvPlayerApplication");
ProtectedApp.s.implementation = function(str) {
    var result = this.s(str);
    console.log("[*] s(" + str + ") = " + result);
    return result;
};
```

### 2. Hook Parse Premium Check
```javascript
var ParseUser = Java.use("com.parse.ParseUser");
ParseUser.getBoolean.overload("java.lang.String").implementation = function(key) {
    if (key.indexOf("premium") >= 0 || key.indexOf("subscription") >= 0) {
        return true;
    }
    return this.getBoolean(key);
};
```

### 3. Hook SharedPreferences
```javascript
var SharedPrefs = Java.use("android.content.SharedPreferences");
SharedPrefs.getBoolean.overload("java.lang.String", "boolean").implementation = function(key, def) {
    if (key.toLowerCase().indexOf("premium") >= 0 || 
        key.toLowerCase().indexOf("pro") >= 0 ||
        key.toLowerCase().indexOf("subscription") >= 0) {
        return true;
    }
    return this.getBoolean(key, def);
};
```

### 4. Bypass Anti-Frida (DexProtector)
DexProtector derives native key from `rtld_db_dlactivity()` linker address — Frida trampolines corrupt it.

**Workaround**: Use Frida 16.x with `--disable-jit` or spawn with `--no-pause` and hook AFTER `JNI_OnLoad`:
```bash
frida -U -f ar.tvplayer.tv --no-pause -l bypass.js --disable-jit
```

Or use **DexIntercept** (from World of IPTV forums) — monitors `/data/dalvik-cache/` for `.dex` writes and dumps decrypted DEX before DexProtector cleans up.

---

## Patches Applied in This Session

| Patch | File | Method |
|-------|------|--------|
| Cert check bypass | `ProtectedTvPlayerApplication.smali` | `b()` → `return-void` |
| `extractNativeLibs` | `AndroidManifest.xml` (binary AXML) | `false` → `true` (byte-patched at offset 20348) |

---

## Files Produced

| File | Purpose |
|------|---------|
| `tivimate-patched-unsigned.apk` | Signed with debug key, cert check bypassed, `extractNativeLibs=true` |
| `TIVIMATE_AUDIT.md` | Full architecture doc |
| `tivimate-jadx/` | 4,881 class decompilation |
| `tivimate-smali/` | 6,659 SMALI files (apktool --no-res) |
| `tivimate-dexworking/classes.dex` | Rebuilt DEX with patched `b()` |

---

## Key Offsets for Binary Manifest Patch

| Item | Offset | Original | Patched |
|------|--------|----------|---------|
| `extractNativeLibs` boolean attr | 20348 (val_data) | `0x00000000` (false) | `0xFFFFFFFF` (true) |
| String pool index for "extractNativeLibs" | 21 | — | — |
| AXML magic | 0 | `0x00080003` | unchanged |
| Chunk size | 8 | `1835009` | unchanged |

---

## Lessons for Future DexProtector Apps

1. **Always check `extractNativeLibs`** in manifest before rebuilding — it's the #1 cause of `res=-2` on modified APKs
2. **Preserve original zip metadata** — `compress_type`, `flag_bits`, `extra`, `date_time` for EVERY entry when rebuilding with zipfile
3. **Classes.dex must stay DEFLATED** if original was — don't force STORED
4. **Native libs must stay STORED** — never compress `.so` files
5. **v2/v3 signing block is lost** when removing META-INF — use `apksigner` if you need it, but `extractNativeLibs=true` allows v1-only installs on API 24+
6. **Static string search won't work** — all strings encrypted; must hook at runtime
7. **Anti-Frida is linker-based** — use `--disable-jit` or DexIntercept