Inside Yaesu’s Firmware Cipher: A Hands-On Reverse Engineering Journey into FT-70D Updates

Article illustration 1

When a manufacturer ships you a Windows-only firmware updater and a sealed black box of RF silicon, they’re not just distributing code; they’re defining how much control you have over hardware you supposedly own. In the case of Yaesu’s FT-70D handheld, that control is mediated by a bespoke encryption scheme, some stubborn resources buried in a PE file, and a Renesas H8SX microcontroller that’s more interesting than its service manual suggests.

This reverse engineering story tracks how a curious operator turned an opaque .exe into a reproducible decryption pipeline, a Rust-based tool, and a disassemblable firmware image—without resorting to vendor tooling or privileged documentation.

Source attribution: This article is based on and expands upon the original research and write-up by Landon "landaire" (https://landaire.net/reversing-yaesu-firmware-encryption/).


From Hobby Radio to Binary Forensics

Ham radio has always been a gateway drug into systems engineering: RF chains, modulation, obscure microcontrollers, and vendor-specific tooling all layered into a single object you can hold in your hand. For the Yaesu FT-70D, prior art on custom firmware was thin—mostly a lone Reddit thread on an older FT1DR, hinting that the device exposed its Renesas H8SX over USB during firmware updates.

That approach relied on Renesas’ own SDK, with all the usual friction: awkward config, opaque tools, uncertain capabilities for clean firmware dumping. Instead of committing to a closed toolchain, the investigation shifted to the one artifact Yaesu does give you willingly: the official Windows firmware updater.

Yaesu’s distribution for the FT-70D includes:

  • A model-specific updater binary: FT-70D_ver111(USA).exe
  • Documentation PDFs
  • USB drivers and support DLLs

The updater is the obvious suspect: if there’s an encrypted firmware blob, it’s going to be in there.


Digging in the Updater: Finding the Payload

Article illustration 2

The updater is a PE file, which means it can hide arbitrary binary data in its .rsrc section. Using tools like XPEViewer, the resources are enumerated and one custom resource type—23—stands out.

Article illustration 3

Within that type:

  • RES_START_DIALOG contains UX strings shown before the update.
  • RES_UPDATE_INFO is a binary blob: large, non-textual, and immediately suspicious.

A quick pass with strings shows nothing useful. This is almost certainly the encrypted firmware image.

The task becomes clear: don’t guess the format; reverse engineer the updater until the transformation from resource blob to device-ready bytes is fully understood.


Attaching the Microscope: Static Meets Time-Travel Debugging

Static analysis can only get you so far before inlined calls, compiler games, and convoluted pointer arithmetic erase the narrative. To keep the investigation grounded in behavior, WinDbg’s Time Travel Debugging (TTD) is used to record an execution trace while the updater performs a real firmware update.

The workflow:

  1. Identify where RES_UPDATE_INFO is loaded via FindResourceA.
  2. Trace where its contents are copied to a dynamic buffer.
  3. Set breakpoints on the memory region holding the resource data.
  4. Observe reads/writes to that region during the update.

This leads directly to a function that reads from the resource buffer, transforms it, and writes into another large buffer that now suddenly contains readable ASCII strings from the radio’s runtime.

The mystery blob has become structured firmware. Now the question is: how?


Unrolling the Cipher: Bit Inflations and Custom Permutations

At the center of the updater’s logic is a data transformation routine we’ll call decrypt_data. Its core pattern is unusual but systematic:

  1. Inflate bytes into bits: Input data is processed in 8-byte chunks; each byte is expanded into 8 bytes of 0/1, yielding a 64-byte "bitplane" buffer.
  2. Apply a nonlinear transformation: This bitplane is passed to another function (sub_407980) that uses lookup tables, XORs, and key-dependent mixing.
  3. Deflate bits back into bytes: The transformed bit buffer is recomposed into standard bytes for the output.

In pseudocode terms, the flow inside decrypt_data looks like:

// High-level sketch
for each chunk of up to 8 input bytes {
    uint8_t inflated[64] = {0};

    // Inflate: one input byte -> 8 bytes of {0,1}
    for (int i = 0; i < 64; i += 8) {
        uint8_t b = *in++;
        inflated[i + 0] = (b >> 7) & 1;
        inflated[i + 1] = (b >> 6) & 1;
        inflated[i + 2] = (b >> 5) & 1;
        inflated[i + 3] = (b >> 4) & 1;
        inflated[i + 4] = (b >> 3) & 1;
        inflated[i + 5] = (b >> 2) & 1;
        inflated[i + 6] = (b >> 1) & 1;
        inflated[i + 7] = (b >> 0) & 1;
    }

    // Nonlinear/keyed transform
    transform_with_key_state(this, inflated);

    // Deflate back to bytes
    for (int i = 0; i < chunk_size; i++) {
        out[out_idx++] = pack_8_bits(&inflated[1 + 8*i]);
    }
}

The key material is not passed in directly. Instead, this points at an internal key schedule that is set up before decryption begins. That key schedule is where the real story is.


The Inner Core: A Vendor-Grade DES-Style Construction

Article illustration 4
Article illustration 5

The heavy lifter, sub_407980 (renamed conceptually as transform_with_key_state), is dense: nested loops, lookup tables, strange indexing, and awkward decompiler output. With careful annotation and TTD-backed tracing, its structure becomes recognizable:

  1. Build a 48-byte buffer from the key schedule, XOR’d with bits of the inflated input using several static index tables.
  2. Use that mixed buffer to derive indices into a large static table (~0x800 bytes), effectively acting like S-boxes.
  3. Use another static table to drive how those derived values XOR and permute the bitplane/output.

This is not off-the-shelf AES, nor is it trivial obfuscation. It strongly resembles a custom Feistel-like construction or home-rolled block cipher, with:

  • Bit-level expansion and compression
  • Multiple static tables acting as S-boxes and permutations
  • A multi-round key schedule where round configuration is partially table-driven

For reverse engineers, it’s a reminder: many vendors aren’t using standard, auditable cryptography in their update chains. They’re rolling their own, often inside opaque Windows tools, betting that obscurity and ecosystem friction will delay understanding.

But the design has a fatal property for security-by-obscurity strategies: it’s fully client-side, and therefore fully recoverable.


Timestamp as Key Material: Deriving the Session Key

Before any firmware bytes are decrypted, the updater peels off the first four bytes of RES_UPDATE_INFO. Those bytes form a Unix timestamp. That timestamp is:

  1. Formatted as %Y%m%d%H%M%S.
  2. Parsed byte-by-byte and inflated into its bit representation.
  3. Mixed with several static key blocks in a chained sequence of transformations.
  4. Fed into set_key, which constructs the final key schedule used by decrypt_data.

Conceptually:

// Pseudocode summary
set_key_to_timestamp(timestamp_string):
    uint8_t buf[64] = {0};

    // Four rounds, each using a different built-in static key
    ptr = inflate_and_mix(timestamp_string, buf, static_key_1);
    ptr = inflate_and_mix(ptr,              buf, static_key_2);
    ptr = inflate_and_mix(ptr,              buf, static_key_3);
    inflate_and_mix(ptr,                    buf, static_key_4);

    // Use the final 56 bits/bytes (post-mix) to seed the key schedule
    set_key_schedule(this, buf);

Where set_key_schedule (extracted from set_key) performs:

  • Bit rotations over a 56-byte buffer.
  • Conditional extra rotations based on a global configuration table.
  • A 16-round expansion into a persistent key schedule array, using lookup/permutation tables to select which bits populate each round’s 48-byte slice.

The result is a reproducible, timestamp-bound key schedule driven by:

  • 4 bytes of dynamic input (the timestamp in the update image)
  • Several kilobytes of static, embedded lookup tables

Once this schedule is set, the rest of RES_UPDATE_INFO is passed through decrypt_data, yielding the plaintext firmware.


Why This Matters (Especially If You Build Devices)

For developers, security engineers, and anyone shipping embedded systems, this Yaesu case study is more than a clever hack:

  1. Opaque crypto is still common. Instead of standard algorithms (AES/GCM, Ed25519 signatures), we see a bespoke cipher hidden in an updater binary. That doesn’t buy real security; it slows down scrutiny while increasing the risk of subtle flaws.

  2. Client-side trust anchors are fragile. If your firmware confidentiality and integrity checks live entirely inside a distributable Windows executable, a motivated analyst can—and will—replicate them. Any attacker who can run your updater can run your crypto.

  3. Reverse engineering is table stakes for defenders now. Techniques used here—PE resource inspection, IDA/Ghidra analysis, TTD-based time-travel debugging, dynamic breakpoints on key state—are increasingly standard practice for both offensive and defensive work.

  4. Owner control vs. vendor control. Once the decryption path is understood, owners can:

    • Audit what actually runs on their radios.
    • Study undocumented behavior and RF constraints.
    • Explore custom firmware—responsibly or otherwise.

    Vendors who rely only on obscurity for control will lose that control over time.

If you design update mechanisms or secure boot chains, this should nudge you toward:

  • Public, documented cryptography
  • Signed firmware with verifiable keys on-device
  • Keeping secret material out of client-distributed binaries

Not because it’s academic best practice, but because reverse engineering like this piece demonstrates how weak the alternatives are in practice.


From Ciphertext to Code: Opening the H8SX

With the decryption algorithm fully modeled, the firmware image can be produced deterministically. Landon’s Rust utility, porkchop, reimplements the entire chain:

  • Parse updater resources
  • Extract timestamp + encrypted blob
  • Derive the key schedule
  • Decrypt into a raw firmware binary

Once decrypted, the FT-70D image can be loaded into IDA Pro using the Hitachi/Renesas H8SX processor module (e.g., H8S/2215R). After a bit of manual massaging—recognizing the interrupt vector table, defining entry points—the binary begins to resolve into meaningful code.

At that point, the radio is no longer a monolith. It’s just software.

And software can be read.


When the Black Box Cracks Open

What starts as a curiosity about “how hackable is my handheld?” ends with:

  • A complete reconstruction of Yaesu’s firmware decryption flow.
  • A working open-source implementation.
  • A loadable firmware image for real static analysis.

For the broader ecosystem of device makers and security architects, the lesson is blunt: if your trust story depends on no one doing the work you’ve just seen done here, you don’t have a trust story.

Reverse engineering isn’t the edge case anymore. It’s the expected path for anyone serious about understanding the systems they depend on—whether they’re radios, cars, routers, or baseband modems.

And once you can see inside, you can ask much better questions.