Kenwood TH-D75 firmware extraction and cipher tools
Project description
thd75-fw
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
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
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
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
33a338ce2646b8e6c8f8f7082be8c8c8548005872973d5818e792fdb14f8b020
|
|
| MD5 |
d3a9f53c4b0225c10baf9ce48d9be905
|
|
| BLAKE2b-256 |
bffe5b77c3ec78825181deeb642e03e2480e39b973aef8bea8c2f30ab6fba2da
|
Provenance
The following attestation bundles were made for thd75_fw-0.1.0.tar.gz:
Publisher:
release.yml on swiftraccoon/thd75-fw
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
thd75_fw-0.1.0.tar.gz -
Subject digest:
33a338ce2646b8e6c8f8f7082be8c8c8548005872973d5818e792fdb14f8b020 - Sigstore transparency entry: 1456876194
- Sigstore integration time:
-
Permalink:
swiftraccoon/thd75-fw@74a3c655632007a085ef7c8d199b1c7fa1dfa9e2 -
Branch / Tag:
refs/tags/v0.1.0 - Owner: https://github.com/swiftraccoon
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
release.yml@74a3c655632007a085ef7c8d199b1c7fa1dfa9e2 -
Trigger Event:
push
-
Statement type:
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
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
bf9b63a64073a22876a64bcbcb19ff1c8d5e3c7bc39ae5baf6546ab90e84a012
|
|
| MD5 |
992ef7a25f8a0a55ab059354301e4189
|
|
| BLAKE2b-256 |
3ab6706f8896860d27bd7f8f6b501dd590c1b2f7d52761111f8c96e08428dc5c
|
Provenance
The following attestation bundles were made for thd75_fw-0.1.0-py3-none-any.whl:
Publisher:
release.yml on swiftraccoon/thd75-fw
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
thd75_fw-0.1.0-py3-none-any.whl -
Subject digest:
bf9b63a64073a22876a64bcbcb19ff1c8d5e3c7bc39ae5baf6546ab90e84a012 - Sigstore transparency entry: 1456876378
- Sigstore integration time:
-
Permalink:
swiftraccoon/thd75-fw@74a3c655632007a085ef7c8d199b1c7fa1dfa9e2 -
Branch / Tag:
refs/tags/v0.1.0 - Owner: https://github.com/swiftraccoon
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
release.yml@74a3c655632007a085ef7c8d199b1c7fa1dfa9e2 -
Trigger Event:
push
-
Statement type: