An ESP32 Start to Finish, Serial to Flash
Nearly everything written up about cracking open a cheap gadget points you at embedded Linux- the little routers, the IP cameras, the NAS boxes. Good advice, honestly.. it is the easier place to start. But what is it, exactly?
So What Is Embedded Linux?
It's the same Linux kernel that runs servers and a good chunk of the internet, trimmed down and squeezed onto a small board that does one job. Your router is a tiny computer. It has a processor, some RAM, a flash chip holding the whole operating system, and a kernel booting up underneath the web page you log into. Crack one open and you'll usually find a bootloader (very often U-Boot), a compressed read-only filesystem (squashfs is the usual suspect), BusyBox standing in for all the everyday commands, and a pile of plain-text config files and shell scripts that make it all go.
That last part is why everyone points beginners there. A Linux device is made of files. Pull the firmware, carve out the filesystem with binwalk, and you can read how the thing works- passwords sitting in config files, startup scripts, the web server's own code. Hook up to the serial port and there's a fair chance it drops you at a root shell. You're poking around a computer you already know how to drive.
...and what it isn't
A microcontroller is a different animal. An ESP32 or an STM32 has no Linux kernel, no filesystem and no shell. It runs one compiled program straight out of flash- either "bare metal" or on top of a small real-time OS like FreeRTOS, which is what Espressif's ESP-IDF builds on. Memory is measured in hundreds of kilobytes, not hundreds of megs (an ESP32 has about 520 KB of on-chip SRAM). Pull the firmware off one of these and you don't get a folder of readable files.. you get one big blob of machine code, and reading it means disassembling it.
For the folks in the back who already know all this- yes, the line isn't perfectly clean. The real divide is the MMU. Linux-class chips (Cortex-A, the MIPS parts in routers) have one and microcontrollers mostly don't. uClinux has run on MMU-less parts for decades, and people have booted Linux on an ESP32-S3 as a stunt. Nobody ships a baby sound machine that way though- in the real world, microcontroller means firmware blob.
Half the interesting gadgets on a bench are running a microcontroller- an ESP Espressif chip or other. There just aint much written about pulling those apart. For me, had I come.across a post like this 3- 4 years ago... i'd be well ahead of the learning curve- Sure, plenty is out there- many tear downs on YouTube... but, details are skimmed over- experience assumes the viewer/ reader knows to use A hardware B software C do the chicken dance and Z you got yourself some firmware extracted- the observer is left deer in headlights, only to un derstand a year or 2 later, many dead microcontrollers latter, many monies up in the magic smoke, much time not given back- sure, eventually a lesson is learned- why not mitigate all that non sense and actually teach the studious student appropriatelly? For the next time one of them lands in front of us we know where to start, follow through, and end victorious... Wherre to start?.... The last time I did this dance it was a router (Extracting Firmware from Routers)- this time the target is a little ESP32 baby sound machine that was headed for the trash.
Purpose of this post: get from a sealed device to a firmware.bin sitting on your disk that you can read. On this particular chip that turned out to be wide open.. no encryption, no locked fuses, nothing in the way. Lucky us... Not every chip will be this friendly- but when it is, it is genuinely a handful of commands. No problem, a refresher for the experienced, a repository of commands to come back to- for those new to this world, a must to bookmark, save.
Reading The Silk, Then The Datasheet-
"Silk" being silk screen... - some PCBs are more verbose than others- some go graphics heavy on the creative side- others are a true enigma... First job is knowing what you're even looking at. Flip the board, find the module, read the marking under the RF can. This one said ESP32-WROVER-E. That is one of Espressif's own modules- an ESP32 with onboard flash and some extra PSRAM tucked under the metal shield, and it comes in 4, 8 and 16 megabyte flash flavours. The nice part for us is the package: it's got castellated edges, those little half-circle plated notches down the sides, so every pin is exposed right there on the edge of the board.
Then you go get the datasheet. I know- "elite hacking skills", reading a PDF. But that genuinely is where half the time goes on this kind of work, and it's where the whole thing lives or dies. Pull the ESP32-WROVER-E datasheet straight from Espressif and skip past the electrical characteristics to the pin definitions. Two pins matter first: TX and RX, the serial lines. Just like an embedded Linux box, the UART is the front door and it's the first thing that we want to be looking for. Then note where IO0 and IO2 sit- what we are doing is going to require placing this into boot mode/ download mode so to say...sometimes there's a convenient button which does this for us- again; reading the "silk" we are looking for the word "boot"- some times, it can be a sequence of button presses, a conveniant button to press, or in this case from the data sheet we've identified IO0 and IO2 those are the two we'll drag to ground later to force the chip into its download mode. Carefull poking arround- There's also a "keep out zone" marked up top by the antenna- leave that alone.
Getting On The UART
Hook a USB-to-serial adapter to three pads: GND, TX and RX. The one thing people trip on- cross them. Your adapter's TX goes to the board's RX, and RX to TX. If you're staring at silence, you've almost certainly got those straight instead of crossed.
For the physical contact on those castellated edges I like spring-loaded probes, but you don't need fancy gear- you can solder a wire straight to the notch. And when you run out of probes, there's a scrappy trick that works surprisingly well: an acupuncture pin. Thin, pointed, and it flexes, so you can wiggle the tip into a tight spot and lean a little tension on it for a solid connection. Clip it into a magnetic third hand to hold it, then either solder a wire to the metal or just bite onto it with a test clip. I said a test clip... not your mouth...
Power it on and watch the boot log scroll by. There is a lot in there if you slow down and read it, same as you would with a Linux dmesg dump:
- the ESP-IDF version it was built against- Espressif's IoT dev framework- and here it was a slightly dirty fork of it
- the SPI flash read, with the partition offsets and labels laid out- Wi-Fi data, OTA data, RF calibration, the app segments
- the project name (this one literally read as a "rest" build) and a compile date, July 17th 2025.. so, fresh
- the MAC address being read out of the eFuses
- an MQTT block, so we already know it phones home to some server
- and a pile of errors- a touch controller wanting an update, an SD card mount that failed, a little crash dump
Grab a copy of that log and read it offline later. It's a free map of the device before you've done anything clever.
That Little Command Prompt
Here's the sauce on this one. It's not Linux and there's no shell- but a lot of these microcontroller builds still leave a little command parser sitting on the serial line. Type into it and this one bit back with command of zero length and a parameter error. So something is listening. Try help? Nothing. Try ?? Nothing. So the commands are in there, we just don't know the words yet. Not a win on its own, but a thread worth pulling later. For now it's enough to know the UART is live and it takes input.
Into Boot Mode
The interesting stuff over serial needs the chip in its download/boot mode, and that's a hardware move, not a command. Back to the datasheet for IO0 and IO2. To enter download boot you pull IO0 low at reset- that's the pin a boot button would sit on if the board had one, and this little device didn't. IO2 wants to be low as well. A lot of the time IO2 is already grounded and IO0 is the one you have to force.. so pull both to ground to be safe, hold them there, and power-cycle.
You'll know it worked from the reset banner. Instead of the normal app boot you get a power-on-reset line, a boot mode of (2), and the words that mean you're in:
waiting for download
That's the chip sitting there with its hands up, waiting for a tool to talk to it.
esptool, And What The Fuses Say
The tool for all of this is Espressif's own esptool, a Python package. One quick heads-up- in the video I pulled this from, the spoken install line comes out as "pip install esp32", and that is not the package. The real one is:
pip install esptool
That drops three commands on your path: esptool, espefuse and espsecure. Worth knowing that recent esptool (v5 and up) dropped the old .py suffix and switched the sub-commands to hyphens- so esptool.py read_flash from an older writeup is esptool read-flash now. Both eras still float around online, so don't be thrown when the syntax doesn't match. Confused? Rightfully so- it won't hurt you to try either or- let us just clarrify what the differrences are- so for the older chips it looks like this: esptool.py read_flash that now has been changed to esptool read-flash... no more python extention aka removed the .py also the read_flash is now read-flash... from a "_" to a "-". No harm- no fowl if mixed up, just try the other- a tip to the wise.. when trying out differrent variations, do yourself a huge favor and write down- physically write the exact syntax you'd / you are trying- if anything like me if you don't, thinking you're the wiser and able to remember- how hard is it to remember that you enterred in a - instead of a _... seriously... pen to paper... not even typed but actually written down down. The moment you've inised that keystroke enterring the version of command you thiunk it were is the exact moment you instantly forgot..you end up typing in the wrong versoon times 100- write it down- test iy0- .
Before dumping anything, ask the chip what it will even let you do. That's espefuse. The eFuses are actual one-time fuses burned into the silicon at the factory- a manufacturer uses them to store the MAC, sure, but also to flip protections: turn on flash encryption, disable JTAG, kill the UART download mode you just used to get in, lock in secure boot, stash encryption keys. Read the summary:
espefuse --port /dev/ttyUSB0 summary
(swap /dev/ttyUSB0 for your adapter- it'll be a COM port on Windows). On this device the whole page came back the boring, beautiful way: flash encryption off, JTAG not disabled, UART download mode not disabled, no encryption keys set. The only thing actually burned in was the MAC address. Wide open. That is exactly the picture that makes the next step trivial- and exactly the set of switches a careful vendor would have blown in production to stop you.
Dumping 16 Megs
First, prove you can talk to the flash at all. These two are your handshake:
esptool --port /dev/ttyUSB0 chip-id
esptool --port /dev/ttyUSB0 flash-id
The flash ID comes back with the size- 16 megabytes here. Good, now we know how much to read. Then the actual dump. You get to pick the baud rate, and it's a real trade: lower is slower but safe, higher is fast but starts throwing errors and can hand you a corrupted image. A solid middle is 460800. You can push to 921600 to roughly halve the time, but that's where corruption creeps in, so I wouldn't for a read I actually care about.
esptool --port /dev/ttyUSB0 --baud 460800 read-flash 0x0 0x1000000 firmware.bin
Reading from offset 0x0- the very start- for 0x1000000 bytes, which is 16 MB in hex, out to firmware.bin. If you'd rather not do hex arithmetic, newer esptool takes the keyword ALL in place of the size and autodetects it:
esptool --port /dev/ttyUSB0 --baud 460800 read-flash 0x0 ALL firmware.bin
Then you wait. At that baud a full 16 MB is about six and a half minutes.
What's In It
Dump done, poke at it. Start dumb and simple:
file firmware.bin
strings -n 8 firmware.bin | less
Real, readable strings pouring out of strings is your confirmation the image isn't encrypted- which we already expected from the fuses, but it's nice to see it. Then the reflex is to reach for binwalk:
binwalk firmware.bin
And here's the honest bit that a lot of writeups skip: on an ESP32, binwalk mostly gives you noise. It'll flag a load of "Unix paths" and other signatures that are false positives. That's not binwalk failing- it's that there's no tidy filesystem in here to carve out. An ESP32 build is usually a real-time OS or bare-metal firmware, not the squashfs/jffs2 layout you'd pull off an embedded Linux router. So the "extract the filesystem" move from the router world just doesn't apply. The real work from here is loading the binary into Ghidra and reversing it as raw code- which is a whole thing of its own, for another day.
But the front door is open. UART found, boot mode forced, fuses read, 16 megs of unencrypted firmware sitting on the disk. On a chip this un-locked-down it really is a short list of commands.. the trick is mostly knowing which pins to grab and what the fuse page is telling you.
Ever pulled firmware off a microcontroller instead of an embedded Linux box- and hit a chip that wasn't this wide open, fuses blown and JTAG dead? What did you do next? Leave a comment, I'd like to hear how far you got. And if you want the softer intro, the router version and a pile of cool tools for hacking are already up here.
Glossary
- Embedded Linux — a trimmed-down Linux kernel and filesystem running on a small single-purpose device like a router or camera.
- U-Boot — the common bootloader on embedded Linux boards; it loads the kernel from flash at power-on.
- squashfs — a compressed, read-only filesystem often used to pack an embedded Linux system into flash.
- BusyBox — a single small program that provides the common Unix commands (ls, cp, sh...) on embedded Linux.
- Microcontroller — a small chip with its own processor, memory and flash that runs one compiled program directly, with no full operating system.
- FreeRTOS — a small real-time operating system that ESP-IDF builds on; it schedules tasks but has no filesystem or shell.
- MMU — memory management unit; the hardware Linux normally needs, found on application processors and mostly missing from microcontrollers.
- ESP32-WROVER-E — an Espressif module built around an ESP32 chip, with onboard SPI flash (4/8/16 MB) and extra PSRAM under the shield can.
- UART — universal asynchronous receiver/transmitter, the plain two-wire serial link (TX and RX) that most boards expose for logs and debug.
- Castellated edges — the plated half-circle notches along a module's edge, so every pin is reachable from the side of the board.
- Boot / download mode — a chip state entered by holding certain pins low at reset, in which a host tool can read and write the flash instead of running the app.
- IO0 / IO2 — the ESP32 strapping pins pulled to ground to force download mode.
- eFuse — one-time-programmable fuses inside the chip, burned at the factory to store the MAC and to flip security switches (flash encryption, JTAG disable, secure boot).
- esptool / espefuse — Espressif's Python command-line tools for talking to the chip over serial and reading its fuses.
- ESP-IDF — Espressif's IoT Development Framework, the SDK these firmwares are built with.
- read_flash / read-flash — the esptool command that copies flash contents out to a file; takes a start offset and a size (or
ALL). - Baud rate — serial link speed; higher reads faster but risks a corrupted dump.
- binwalk — a firmware analysis tool that scans a binary for known signatures and embedded filesystems.
- Ghidra — a free reverse-engineering suite (from the NSA) for disassembling and decompiling binaries.
- RTOS — real-time operating system; the kind of lightweight firmware an ESP32 typically runs, with no Linux-style filesystem to carve out.
Sources
- esptool basic commands — Espressif docs — verbatim syntax for read-flash, flash-id and the port/baud flags (checked 2026-09-28).
- esptool on PyPI — the real package name and current version (5.4.0), install with
pip install esptool. - espefuse reference — Espressif docs — the summary command and what each eFuse protection does.
- ESP32-WROVER-E datasheet (PDF) — pin definitions, flash sizes, PSRAM, the keep-out zone.
- Boot mode selection — Espressif docs — which strapping pins force download mode.
- Ghidra — free disassembler/decompiler for the reverse-engineering stage.
- binwalk — firmware signature scanner, useful caveats on RTOS images.
Comments
Post a Comment