Skip to content

Latest commit

 

History

2 Commits

Folders and files

NameName
Last commit message
Last commit date
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Dog3Decryption

A reverse-engineered decryptor for OPPO / OnePlus ColorOS DOG3 log files.

DOG3 files are encrypted on-disk log archives produced by the clogan / libopluslog.so telemetry stack shipped with ColorOS. They persist app-level diagnostic logs (e.g. HLog_File_*.txt, comheytapspeechassist_*.txt) in a binary, AES-encrypted container that is undocumented and unsupported by any official tool. This project provides a working Python decryptor for those files.


Table of Contents


Background

ColorOS (the Android variant shipped on OPPO, OnePlus and Realme devices for the Chinese market) writes application logs to the user-accessible storage as *.txt files with names such as:

HLog_File_metis_main_2026_08_13_10.txt
HLog_File_main_process_2026_08_13_10.txt
comheytapspeechassist_2026_08_09_06.txt
ipspace_main_process_2026_08_13_10.txt

Despite the .txt extension, these files are not text: they are binary containers in the Logan format (originally open-sourced by Meituan-Dianping as Logan) with OPPO-specific modifications. Each record is AES-encrypted and gzip-compressed, making the logs unreadable without the matching key material.

Because there is no documented way to read these files back, this project was created to fill that gap for users who legitimately want to inspect the diagnostic data their own device has been writing.

How It Works

The decryptor performs three stages:

  1. Container parsing — the DOG3 file is split into individual records. Each record follows the layout:

    +--------+-------------------+-----------------------------+--------+
    | type   | content_len (BE)  | encrypted content           | tail   |
    | 1 byte |     4 bytes       |     content_len bytes       | 0x00   |
    +--------+-------------------+-----------------------------+--------+
    

    type is 0x01 (or occasionally 0x02), content_len is a big-endian uint32, and the record ends with a single 0x00 byte.

  2. AES decryption — each record is decrypted independently using AES-128-CTR with an empty nonce and the IV used as the initial 128-bit counter. The key and IV are hard-coded constants extracted from libopluslog.so inside the device's OTA firmware (see Reverse-Engineering Notes):

    Parameter Value Source offset in libopluslog.so
    Key w5rr6tiSDL6Usl8Q 0x02E0E40B
    IV nNEFaeOInosl3hwF 0x02E0E239

    The cipher is re-initialised for every record (each record is encrypted with the same key+IV — they are not chained).

  3. gzip decompression — the decrypted bytes form a valid gzip stream (1f 8b 08 ...). Inflating it yields UTF-8 JSON: one log event per record.

Repository Structure

Dog3Decryption/
├── decrypt_dog3.py     # Main decryptor (CLI + library)
├── requirements.txt    # Python dependencies
├── LICENSE             # Apache License 2.0
└── README.md           # This file

The examples/ directory (sample DOG3 files from a real device) and the output/ directory (decryption results) are intentionally not tracked. See .gitignore.

Requirements

  • Python 3.8+
  • pycryptodome — AES primitives
  • Python standard library: struct, zlib, os, sys

Tested on CPython 3.11 / 3.12 on Windows and Linux. No native compilation required.

Installation

git clone https://github.com/ChidcGithub/Dog3Decryption.git
cd Dog3Decryption
pip install -r requirements.txt

Usage

Decrypt all files in the bundled examples/ directory

python decrypt_dog3.py

Decrypt specific files

python decrypt_dog3.py /path/to/HLog_File_metis_main_2026_08_13_10.txt
python decrypt_dog3.py file1.txt file2.txt file3.txt

Decrypted output is written to ./output/<filename>.dec as UTF-8 text.

Use as a library

from decrypt_dog3 import parse_dog3, decrypt_record

with open("HLog_File_metis_main_2026_08_13_10.txt", "rb") as f:
    records = parse_dog3(f.read())

for rec_type, length, ciphertext, tail in records:
    plaintext = decrypt_record(ciphertext)   # bytes, UTF-8 JSON
    print(plaintext.decode("utf-8", errors="replace"))

Output Format

Each decrypted record is one JSON object representing a single log event:

{
  "c": "{\"m\":\"onCellInfoChanged: cell size\u003d1\",\"t\":\"metis_v2_MetroAwarenessCollector\",\"l\":3,\"p\":\"\",\"pid\":4631}",
  "f": 101,
  "l": 1786586405155,
  "n": "pool-8-thread-329",
  "i": 26642
}
Field Meaning
c Log content (often a nested JSON string)
f File / module id
l Event timestamp (Unix milliseconds)
n Thread name
i Sequence / thread id

A typical run produces ~120 decrypted records and several megabytes of plain JSON per DOG3 file.

Limitations

  • Static keys. The AES key and IV are baked into libopluslog.so and are shared across all ColorOS devices running the same firmware branch. They are not device-specific on the tested firmware (ColorOS 16.1.0, Android 16, OnePlus RMX5200), but this is not guaranteed for future releases. If a future build introduces a device-specific KDF (e.g. seeding the key from the TEE, IMEI or serial number), this decryptor will stop working without additional reverse-engineering.
  • No partial-record recovery. The final record in each file is frequently a truncated gzip tail written at session shutdown. It cannot be recovered and is reported as a failed record in the script output. All prior records decode cleanly.
  • Format only. This tool decrypts the container; it does not interpret the inner JSON schemas emitted by individual OPPO subsystems (metis_v2_*, ContextAware_*, etc.).

Reverse-Engineering Notes

The key material was located by statically analysing libopluslog.so, extracted from system_ext.img inside a stock OTA payload.bin (EROFS image, payload_dumper-go).

Relevant strings found in the binary include:

  • AES-CFB128-%3d (%s): at 0x02E0E005 — a misleading mode hint; the actual cipher used on-disk is AES-CTR, not CFB128. Counter mode was identified empirically: CTR is the only standard AES mode whose first keystream block equals AES-ECB(K, IV) and whose per-block feedback is independent of the previous block — which is exactly the behaviour exhibited by the file: each record starts with a valid gzip header (1f 8b 08 …) under AES_CTR(K, nonce=b"", initial_value=IV), and the full stream decompresses correctly.
  • w5rr6tiSDL6Usl8Q at 0x02E0E40B — the 16-byte AES key.
  • nNEFaeOInosl3hwF{ at 0x02E0E239 — 17 bytes on disk; the first 16 form the IV, the trailing { is an adjacent unrelated constant.
  • just_byteord, init_key_iv, set_key_iv, aes_init, .dog3, logancrypt — runtime symbol fragments confirming Logan lineage.
  • Verification: AES-ECB(key, IV) produces a keystream block whose XOR with the first 16 ciphertext bytes recovers the canonical gzip header 1f 8b 08 00 00 00 00 00 02 03 …, confirming the key/IV pair.

Disclaimer

This project is provided for legitimate diagnostic and interoperability purposes only — for example, inspecting telemetry that your own device has written about your own usage. The author is not affiliated with OPPO, OnePlus, Realme or Meituan. Do not use this tool to access logs that you do not have permission to read. You are solely responsible for complying with all applicable laws and the terms of service of your device.

License

Released under the Apache License, Version 2.0. This license explicitly disclaims any grant of trade-secret, trademark or patent rights; only copyright is conveyed, and only for uses that independently comply with applicable law (see the Disclaimer above).

About

Decrypt OPPO/OnePlus ColorOS DOG3 (Logan variant) encrypted log files using reverse-engineered AES-128-CTR key material from libopluslog.so

Topics

Resources

Stars

1 star

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages