Home Binary

How to Read and Edit a HEX File for Microcontroller Firmware

Binary

September 30, 2026

How to Read and Edit a HEX File for Microcontroller Firmware

Somewhere in a drawer there is a board with the wrong model string burned into its flash, no source code in sight, and one .hex file as the only survivor. Editing that file is entirely possible. The format is decades old, deliberately simple, and it forgives almost anything except bad arithmetic. Once you can read four fields and compute one checksum, firmware patching stops being mysterious.

Intel HEX is a memory map written in lines

An Intel HEX file is a text representation of the bytes a programmer will place into non-volatile memory. Each line is called a record, begins with a colon, and carries both data and its own checksum. Because the file is ASCII, it survives email attachments, Git, and copy-paste without corruption — unlike a raw binary, which turns into garbage the moment someone opens it in a text editor and saves.

Motorola S-records do the same job with a different syntax and are common on older Freescale and NXP parts. Everything below applies conceptually; only the punctuation changes.

Reading a record byte by byte

Here is a real data record, the kind you will see hundreds of times in a single file:

:10010000214601360121470136007EFE09D2190140

The colon is a start marker. Everything after it is pairs of hexadecimal digits, and each pair means something specific:

  • Byte count — the first pair (10) says how many data bytes follow: sixteen. It does not count the address, type, or checksum.
  • Address — the next two pairs (0100) give the 16-bit address where the first data byte belongs. The remaining data bytes fill consecutive addresses after it.
  • Record type — the next pair (00) identifies what kind of record this is. Zero means plain data.
  • Data — the payload. Here, sixteen bytes of actual firmware.
  • Checksum — the final pair (40) validates the whole line.

Count the pairs after the colon: one count, two address, one type, sixteen data, one checksum. Twenty-one pairs, forty-two digits. That arithmetic is the fastest way to sanity-check a line you have just edited by hand.

The record types you will actually meet

Six record types exist. Only a few show up in modern firmware.

  • 00 — Data. The payload. The overwhelming majority of lines in any file.
  • 01 — End of file. A bare :00000001FF. Almost always the last line. Do not delete it; some programmers refuse to flash a file without it.
  • 02 — Extended segment address. Sets an upper address base using the old x86 segment model, where the value is shifted left by four bits. Rare today.
  • 03 — Start segment address. The x86 CS:IP entry point. Also rare.
  • 04 — Extended linear address. Sets the upper sixteen bits of a 32-bit address. Very common on ARM and other 32-bit parts.
  • 05 — Start linear address. The 32-bit entry point, which on a Cortex-M image is usually the reset handler address.

Types 02 and 04 do not contain firmware; they change where the next data records land. If you delete one while tidying up a file, several kilobytes of code quietly move to the wrong address.

Checksums: the part that punishes sloppiness

The checksum exists so a corrupted line can be detected. The rule is compact: add every byte from the byte count through the last data byte, keep only the low eight bits of the sum, then subtract that value from 256. Equivalently, the checksum is the two's complement of the sum. A correctly formed record always sums to zero modulo 256 when you include the checksum byte itself.

In practice you rarely do this by hand more than a few times. Script it, or use a tool. The mistakes are predictable:

  • Including the colon in the sum. It is punctuation, not data.
  • Changing a data byte and forgetting the checksum. Most tools reject the record outright; a few older programmers are less careful.
  • Editing the byte count without adding or removing data. The line no longer parses.
  • Typing letters carelessly. Case does not matter in the hex digits, but a stray character does.

Trailing whitespace at the end of a line is tolerated by most parsers and rejected by a few. Strip it. Intel HEX has no comment syntax, so do not invent one — keep your notes in a separate text file next to the image.

Addresses behave differently than you expect

The 16-bit address field only reaches 64 KB, which is why extended linear address records exist. A type 04 record saying 0800 means subsequent data records start 8 × 65536 bytes into the address space: 0x08000000, the usual flash base on an STM32. Those upper bits persist until another type 04 record changes them, so track the current base as you read downward through the file.

Also remember that a HEX file is sparse. It describes the bytes that matter, not a full flash image. Regions with no records are undefined: they end up as whatever the programmer leaves there, usually erased 0xFF. If you convert to binary with objcopy or srec_cat, those gaps become real bytes in the output, and the file size tells you where the last record landed rather than how much flash the chip has.

Patching without breaking things

This is the workflow that keeps you out of trouble.

  1. Work on a copy. Keep the original untouched, ideally in version control.
  2. Convert to binary with a tool such as objcopy -I ihex -O binary or srec_cat. Search the binary for the byte sequence you want to change and note its offset.
  3. Map the offset back by adding the base address your file uses. Find the data record whose address range covers that offset.
  4. Replace bytes in place. Type ASCII characters as their hex values — a capital A is 0x41, a zero is 0x30. Keep the byte count identical unless you are deliberately restructuring the record.
  5. Recompute the checksum for every record you touched. This is the step that catches almost all human error.
  6. Convert back to binary and diff it against the original binary. You should see exactly the bytes you intended, and nothing else.
  7. Flash, then read back. A verify pass is not optional when the only copy of the firmware lives on your laptop.

Two things bite people after an otherwise perfect patch. First, many bootloaders check a CRC or signature over the application image, so a one-byte change can make the firmware refuse to boot. Second, string literals often share a pool with other constants; if you change a string's length, neighbouring data moves and pointers turn into nonsense. Whenever possible, keep the replacement exactly the same length and pad with zeros or 0xFF.

The habit that makes patching boring, which is the goal

Keep the original file, keep a short text note describing every byte you changed and why, and verify after flashing. On a one-off patch, that note is a courtesy. On the third revision of the same product, it is the only thing standing between you and a confused afternoon.

If you find yourself editing HEX files more than occasionally, write a small script. A hundred lines of Python with an Intel HEX library will locate a byte pattern, compute new checksums, and print a diff — and it will do the arithmetic the same way every time, which is more than can be said for anyone at two in the morning.

Photo: Al Nahian / Pexels

Related Posts

Static vs Dynamic Binary Analysis: Which Should You Use First?
Reverse Engineering

October 12, 2026

Static vs Dynamic Binary Analysis: Which Should You Use First?

Static analysis maps what a binary could do; dynamic analysis shows what it actually does. Here is how to choose your starting workflow...

read more
Embedded Debugging Checklist: Logic Analyzer, SWD, and UART Before You Rewrite Code
Embedded Systems

October 08, 2026

Embedded Debugging Checklist: Logic Analyzer, SWD, and UART Before You Rewrite Code

A hardware-first checklist for embedded targets: verify power, clock, UART, and SWD before assuming your firmware is at fault.

read more
From Source to Silicon: A Beginner's Guide to the Compiler, Assembler, and Linker Pipeline
Architecture

October 03, 2026

From Source to Silicon: A Beginner's Guide to the Compiler, Assembler, and Linker Pipeline

Follow a C file through the preprocessor, compiler, assembler, linker, and loader, and learn the commands that leave every intermediate...

read more