Skip to main content

Kenwood TH-D75 firmware extraction and cipher tools

Project description

thd75-fw

CI codecov Python 3.10+ License: GPL v3 Typed

Firmware extraction and cipher tools for the Kenwood TH-D75 amateur radio transceiver.

What This Does

Extracts the 7 firmware sections from the official Kenwood TH-D75 firmware updater executable. Also provides encrypt/decrypt for the serial transfer cipher used during USB firmware updates.

Two independent ciphers are implemented:

Cipher Purpose Algorithm
File-storage Firmware embedded in updater .exe Rolling-key XOR + alternating inversion (key=39, step=39) → Intel HEX
Serial transfer USB packets during firmware update 256-byte substitution + XOR + 3-bit rotation (key=0x75)

Install

With pip:

pip install thd75-fw

With uv:

uv tool install thd75-fw    # all four CLIs globally
uv add thd75-fw             # or add as a project dependency

Try without installing

uvx --from thd75-fw thd75-extract TH-D75_V103_e.exe ./out/

Usage

Extract firmware from updater

thd75-extract TH-D75_V103_e.exe ./extracted/

Verify against known-good files

thd75-extract TH-D75_V103_e.exe ./out/ --verify ./known-good/

Serial cipher (encrypt/decrypt individual packets)

thd75-serial-cipher decrypt packet.bin -o plain.bin
thd75-serial-cipher encrypt plain.bin -o packet.bin
thd75-serial-cipher selftest

Extract voice prompts as WAV

thd75-extract-voice ./extracted/DATA_0160_0x01600000.bin ./prompts/
thd75-extract-voice ./extracted/DATA_0160_0x01600000.bin ./prompts/ --lang en

Extracts 749 voice prompts (327 English, 356 Japanese, 66 Chinese) as 8 kHz mono WAV files.

Extract display images as PNG

thd75-extract-images ./extracted/IMAGE_DATA_0x00600000.bin ./images/

Extracts 862 PNG images (APRS symbols, status icons, splash screens, UI elements).

Use as a Python library

The same primitives that power the CLIs are exposed as importable functions:

from pathlib import Path
from thd75_fw.serial_cipher import encrypt, decrypt
from thd75_fw.sections import lookup_by_address
from thd75_fw import voice

# Round-trip a serial packet (default key 0x75)
ciphertext = encrypt(b"hello world")
assert decrypt(ciphertext) == b"hello world"

# Look up a section by flash address
info = lookup_by_address(0x01600000)
assert info is not None and info.name == "DATA_0160"

# Parse a voice prompt database
data = Path("./extracted/DATA_0160_0x01600000.bin").read_bytes()
database = voice.load(data)
print(f"{len(database.prompts)} prompts: {len(database.by_language('en'))} en")

Inline single-file scripts work too — paste this into decode.py and run with uv run decode.py:

# /// script
# requires-python = ">=3.10"
# dependencies = ["thd75-fw"]
# ///
import sys
from thd75_fw.serial_cipher import decrypt
sys.stdout.buffer.write(decrypt(sys.stdin.buffer.read()))

Extracted Sections

Section Flash Address Size Content
FIRMWARE 0x00200000 2.5 MB ARM926EJ-S executable (OMAP-L138)
CHECKBYTES 0x00200062 2 B Bootloader integrity checksum (0xB01D in V1.03)
FINAL_ZZZ 0x00200040 32 B Build marker confirming update completion
IMAGE_DATA 0x00600000 384 KB 862 PNG display images
DATA_00E0 0x00E00000 1.0 MB TI C6748 AMBE2+ DSP firmware
FONT_DATA 0x01500000 768 KB Shift-JIS display fonts (16x16 and 24x24, 1-bit mono)
DATA_0160 0x01600000 10.0 MB Voice prompt database (8-bit PCM, 8 kHz)

CHECKBYTES and FINAL_ZZZ are patched into the FIRMWARE region's exception vector padding (0x40-0x7F) after the main firmware write completes.

Reverse-engineering with IDA Pro / Ghidra

Drop-in setup scripts for both tools live under loaders/. They auto-configure the processor, segment permissions, and ARM exception vectors so you don't have to manually figure out why a raw .bin won't decode as ARM. See loaders/README.md for setup.

For format/protocol details (cipher algorithms, section layout, OMAP-L138 memory map, voice/image database structure), see docs/FORMAT.md.

Development

With pip (≥25.1):

pip install -e . --group dev

With uv:

uv sync

Both read the same [dependency-groups] table in pyproject.toml.

Legal Disclaimer

This software is provided for amateur radio interoperability and educational purposes only.

This project reverse-engineers the encryption used by the Kenwood TH-D75 firmware updater to enable firmware analysis, interoperability research, and amateur radio experimentation. It does not contain, distribute, or facilitate unauthorized access to any copyrighted firmware. Users are responsible for obtaining firmware images through legitimate means.

Reverse engineering for interoperability is protected under:

  • United States: DMCA §1201(f) (reverse engineering for interoperability); Sega v. Accolade (9th Cir. 1992); Oracle v. Google (S.Ct. 2021)
  • European Union: Directive 2009/24/EC, Article 6 (decompilation for interoperability)

"Kenwood" and "TH-D75" are trademarks of JVCKENWOOD Corporation. This project is not affiliated with, endorsed by, or sponsored by JVCKENWOOD Corporation.

No warranty. This software is provided "as is" without warranty of any kind. Use at your own risk. Do not use this software to modify radio firmware in ways that violate FCC Part 97 or your local amateur radio regulations.

Acknowledgments

This project builds on the prior reverse engineering work by DD4CR on the Kenwood TH-D74: github.com/cr/thd74. Their documentation of the D74 firmware update protocol, .NET updater structure, and XOR permutation cipher provided the foundation for the D75 analysis. The D75 ciphers are evolutionary variations on the same cryptographic primitives (modular arithmetic, XOR, single-byte keys), though with different compositions and key schedules.

License

GPL-3.0

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

thd75_fw-0.1.0.tar.gz (97.3 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

thd75_fw-0.1.0-py3-none-any.whl (39.6 kB view details)

Uploaded Python 3

File details

Details for the file thd75_fw-0.1.0.tar.gz.

File metadata

  • Download URL: thd75_fw-0.1.0.tar.gz
  • Upload date:
  • Size: 97.3 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.12

File hashes

Hashes for thd75_fw-0.1.0.tar.gz
Algorithm Hash digest
SHA256 33a338ce2646b8e6c8f8f7082be8c8c8548005872973d5818e792fdb14f8b020
MD5 d3a9f53c4b0225c10baf9ce48d9be905
BLAKE2b-256 bffe5b77c3ec78825181deeb642e03e2480e39b973aef8bea8c2f30ab6fba2da

See more details on using hashes here.

Provenance

The following attestation bundles were made for thd75_fw-0.1.0.tar.gz:

Publisher: release.yml on swiftraccoon/thd75-fw

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file thd75_fw-0.1.0-py3-none-any.whl.

File metadata

  • Download URL: thd75_fw-0.1.0-py3-none-any.whl
  • Upload date:
  • Size: 39.6 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.12

File hashes

Hashes for thd75_fw-0.1.0-py3-none-any.whl
Algorithm Hash digest
SHA256 bf9b63a64073a22876a64bcbcb19ff1c8d5e3c7bc39ae5baf6546ab90e84a012
MD5 992ef7a25f8a0a55ab059354301e4189
BLAKE2b-256 3ab6706f8896860d27bd7f8f6b501dd590c1b2f7d52761111f8c96e08428dc5c

See more details on using hashes here.

Provenance

The following attestation bundles were made for thd75_fw-0.1.0-py3-none-any.whl:

Publisher: release.yml on swiftraccoon/thd75-fw

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page