PostHeaderIcon STM32G030K6: A Byte-Identical Copy Is Not a Copy

Copying a program sounds like the simplest task in this work: read the source, write the destination, verify. That procedure produces two devices with identical flash contents, and it is a complete solution about seventy percent of the time.

The other thirty percent is where the interesting engineering lives, and it almost always comes down to the same handful of addresses that are different on every part ever manufactured.

Three things that are never the same

A device is not just its flash. Three other regions carry values that differ between units, and any of them can be the reason a copy misbehaves.

The unique identifier. Every part carries a factory-programmed identifier in system memory — a multi-byte value guaranteed to be unlike any other. It is not writable, it is not erased by a mass erase, and it is the same value the vendor’s own tools read to distinguish units.

The option bytes. Read protection level, write protection, boot configuration. Two devices can be programmed with identical firmware and have different protection states, which changes behaviour on the first attempt to update or debug.

Factory calibration values. Analog and timing trims measured at test, stored where the firmware can read them. They differ per unit by small amounts, and a copy that omits them either uses defaults or reads the destination’s own values — which is usually correct, but not always, because some firmware copies the trim into RAM once and never rereads it.

Region Per-unit Survives erase Copied by a flash dump
Flash array No No Yes
Unique ID Yes, fixed Yes No
Option bytes Sometimes Yes No
Calibration data Yes Yes No

Why the identifier breaks a copy

Firmware reads the unique identifier for three reasons, and each produces a different symptom when it is not handled.

Licensing and feature enabling. A key derived from the identifier unlocks paid features. The copy has a different identifier, so the derived key is wrong, so the feature is off — or the whole application refuses to start.

Network identity. The identifier seeds a device address, a node ID, or a serial number reported to a host. Two devices on the same bus derived from the same flash but different identifiers will not collide, which is good, but a host that expected a specific serial will reject the new one.

Anti-cloning checks. The firmware compares the identifier against a value stored elsewhere — in an EEPROM, in an external memory, in a server. A mismatch is treated as tampering, and the response ranges from a warning to a deliberate brick.

The third case is the one that produces the support ticket “your copy does not work”, because the device may start, run for a while, and then stop.

Finding the read in the disassembly

The identifier lives at a fixed address in system memory, so reading it produces a very specific code pattern: a load from an address outside both flash and SRAM, into a register, followed by arithmetic that combines several words.

Look for loads whose target address is in the system memory range rather than in the flash or RAM ranges. On a Cortex-M0+ that means a literal pool entry holding an address near the top of the address space, followed by a load instruction. Two or three such loads in close succession, with shifts and exclusive-ors afterwards, is a key derivation.

The derived value is usually compared against something, stored somewhere, or written into a packet. Following it forward for twenty instructions tells you which of the three purposes applies, and that determines the fix.

The three fixes, in order of preference

Make the firmware read the destination’s own identifier. This is the correct answer when the check is for identity rather than licensing. It requires no patch, because the firmware already does this — the problem is only that something else was copied, such as a cached copy in non-volatile memory that now disagrees.

Patch the comparison. When the derived value is compared against a stored constant, changing the comparison to always accept is a two-byte modification. It works, it is fast, and it removes a security property the customer may still want. That trade-off needs to be stated, not decided silently.

Recompute the stored value. When the identifier is checked against a value in EEPROM or an external memory, the correct fix is to compute the value for the new part and write it to the same place. This preserves the check and satisfies it, which is almost always what the customer actually wants.

The order matters: the third option keeps the design intact, the second breaks it, and the first is only available when the design already supports it.

What a valid copy actually consists of

A delivery that works on the target, rather than only in a hex editor, contains:

  • The flash image, with the read and the verification described.
  • The option byte values, so the destination is configured identically.
  • A statement about which per-unit values the firmware reads, and how each was handled.
  • The calibration strategy, if the firmware caches trims.

The third item is the one that turns a copy into a working device, and it is also the one that is skipped when the job is treated as a file transfer.

Verification that means something

Programming the destination and reading it back proves the write succeeded. It proves nothing about whether the device works.

The verification is behavioural: put the copy into the product, power it, and watch the first thirty seconds. Most identifier-related failures appear in that window, either as a refusal to start or as a startup that completes and then stops when the check runs for the first time.

Related reading

FAQ

Can the unique identifier be changed? No. It is programmed at manufacture and is not writable or erasable.

Is the identifier readable when read protection is active? Its accessibility follows the protection rules, and the value itself is a factory constant rather than a secret.

Does a mass erase clear it? No. The identifier survives every erase, which is why it is useful as an identity.

Is copying a program legitimate? It is a normal engineering operation for repair, replacement of an obsolete part, and second sourcing. The restrictions are legal and contractual, not technical.

Conclusion

The array is the part that copies. The identifier is the part that does not, and it is what separates a device that boots from a device that works. Find the read in the code, decide which of the three fixes the design allows, and verify in the product rather than in a programmer’s window.

Comments are closed.