Now Reading
Zeus WPI | Unveiling secrets and techniques of the ESP32: creating an open-source MAC Layer

Zeus WPI | Unveiling secrets and techniques of the ESP32: creating an open-source MAC Layer

2023-12-06 15:50:34

The ESP32 is a well-liked microcontroller identified within the maker group for its low value (~ €5) and helpful options: it has a dual-core CPU, built-in Wi-Fi and Bluetooth connectivity and 520 KB of RAM. It is usually used commercially, in units starting from sensible CO₂-meters to industrial automation controllers. A lot of the software program improvement package that’s used to program for the ESP32 is open-source, besides notably the wi-fi bits (Wi-Fi, Bluetooth, low-level RF capabilities): that performance is distributed as precompiled libraries, which are then compiled into the firmware the developer writes.

A closed-source Wi-Fi implementation has a number of disadvantages in comparison with an open-source implementation although:

  • You might be depending on the seller (Espressif on this case) so as to add options; in case you have a considerably non-standard usecase, you is perhaps out of luck. For instance, standards-compliant mesh networking (IEEE 802.11s) just isn’t supported on the ESP32; there may be a partially closed-source mesh networking implementation made by Espressif, however that is somewhat restricted: the mesh community has a tree topology, and makes use of NAT on the nodes related to the foundation community, making it arduous to attach from outdoors the mesh community to nodes within the mesh community. The protocol can be not documented, so it’s not interoperable with different units.
  • It’s arduous to audit the safety of the implementation: since there is no such thing as a supply code out there, you must resort to black-box fuzzing and reverse engineering to search out safety vulnerabilities.
  • Moreover, an open-source implementation would make analysis into low-power Wi-Fi mesh networking extra reasonably priced; if every node solely prices about €5, analysis involving tons of of nodes could be reasonably priced on a modest price range.

Espressif has an open concern of their esp32-wifi-lib repository, asking to open-source the MAC layer. In that concern, they confirmed in 2016 that open sourcing the higher MAC is on their roadmap, however as of 2023, nothing has been revealed but. Having the supply code would for instance enable us to implement correct 802.11s-compliant mesh networking.


The principle aim of this venture is to construct a minimal substitute for Espressifs proprietary Wi-Fi binary blobs. We don’t intend to be API-compatible with present code that makes use of the Espressif ESP-IDF API, somewhat, we’d wish to have a completely working, open supply networking stack.

The remainder of this part will include details about how the community stack and Wi-Fi (the 802.11 commonplace) works, so for those who’re already acquainted, you’ll be able to skip it.

OSI model of the network stack (the difference between application/presentation/session is a bit murky)
OSI mannequin of the community stack (the distinction between utility/presentation/session is a bit murky)

Above, you’ll be able to see a diagram displaying the community stack. Pc networking is completed with a community stack, the place each layer within the stack has its personal goal; this design makes it simpler to swap out layers and permits for separate improvement of layers. The layer on the backside of the stack interacts with the bodily world (for instance, through the use of radiowaves or electrical indicators); each layer provides their very own options. Wi-Fi (also referred to as the 802.11 commonplace by engineers) is applied within the backside two layers: the PHY layer (what the radio waveforms seem like, …) and the MAC layer (how we connect with an entry level, what packets exist, the way to ship packets to native units, …).

On the ESP32, the PHY layer is applied in {hardware}; a lot of the MAC layer is applied within the proprietary blob. One notable exception to this separation is sending acknowlegement body: if a tool receives a body, it ought to ship a packet again to acknowledge that this packet was acquired appropriately. This ACK packet must be despatched inside ~10 microseconds; it might be arduous to get this timing right in software program.

There are 3 sorts of MAC frames:

  • Administration frames: principally for managing the connection between the entry level and station (consumer)
  • Management frames: assist with supply of different sorts of frames (for instance ACK, but additionally request-to-send and clear-to-send)
  • Information frames: include the info of the layers above the MAC layer

Earlier work

Because it doesn’t seem like Espressif will launch an open supply MAC implementation anytime quickly, we’re on our personal to create this. That is somewhat arduous to do, as a result of the {hardware} with which we ship and obtain 802.11 packets on the ESP32 is solely undocumented. Which means we might want to reverse engineer the {hardware}; first we’ll must doc what the {hardware} does, then we’ll want to put in writing our personal code to appropriately work together with it. In 2021, Uri Shaked did some very gentle reverse engineering of ESP32 Wi-Fi {hardware}, to mock this in his emulator. That means, packages for the ESP32 could be emulated as an alternative of working them on actual {hardware}. Shaked gave a talk about this, however solely mentioned very excessive stage particulars concerning the {hardware}. Espressif has their own fork of QEMU (a well-liked, open-source emulator) that may additionally emulate the ESP32, however this fork doesn’t assist emulating the Wi-Fi {hardware}. In 2022, Martin Johnson added fundamental assist for the Wi-Fi {hardware} to their own fork of Espressif’s QEMU. The emulated ESP32 can connect with a digital entry level, or have a digital consumer connect with it.

esp-idf (the SDK for the ESP32) has a perform to transmit frames (esp_wifi_80211_tx), however this perform solely accepts sure sorts of frames; it doesn’t enable sending most administration frames, severely limiting the usefulness of this API to base an 802.11 MAC stack on. Additionally they have a perform (esp_wifi_set_promiscuous_rx_cb) to obtain a callback on reception of a body.

Earlier than we are able to begin reverse engineering how the 802.11 PHY {hardware} works and the way we work together with it, we first want to search out or construct instruments that may assist. We’ll use 3 primary approaches:

  • Static reverse engineering: now we have the compiled libraries that implement the Wi-Fi stack, so we are able to have a look at the compiled code and attempt to decompile it to human-readable code. From this extra readable code, we then attempt to see what the {hardware} expects the software program to do.
  • Dynamic code evaluation in an emulator: we are able to run the firmware in an emulator and examine the way it interacts with the digital {hardware}. This has the benefit of getting a variety of freedom to how we examine the {hardware}, however the drawback that the emulator won’t behave the identical as actual {hardware}. Since we’ll want to put in writing the emulated peripherals ourselves, this danger is actual: there is no such thing as a public datasheet for the Wi-Fi peripheral, so now we have to guess how the {hardware} will behave from the code that interacts with it.
  • Dynamic code evaluation on actual {hardware}: we are able to run the firmware on an precise ESP32, and debug it utilizing a JTAG debugger. This permits us to position breakpoints, examine the reminiscence and registers, cease and resume the execution, … The drawback is that the debugging capabilities are extra restricted in comparison with working in an emulator: we are able to solely place 2 breakpoints, we can not place watchpoints (breakpoints that set off on reminiscence reads/writes to a sure deal with), … The massive benefit in comparison with utilizing an emulator is that we’ll know for positive that the behaviour of the {hardware} is right.

Static evaluation

For the static evaluation, we use Ghidra, an open-source reverse engineering device made by the NSA. Out of the field, Ghidra doesn’t have assist yet for Xtensa (the CPU structure of the ESP32), however there’s a plugin that adds support. The construct instruments used within the ESP32 SDK generate each an ELF file (a kind of binary file that may include metadata) and a flat binary file: utilizing the ELF file has the advantage of routinely setting most perform names.

Dynamic evaluation in emulator

We began off from Martin Johnsons’s fork of Espressifs model of QEMU (a well-liked open-source emulator), and ported their modifications to the most recent model of Espressif’s QEMU fork. The ESP32 talks to its peripherals by way of reminiscence mapped IO: by studying from and writing to sure reminiscence addresses, the peripherals offers info to the CPU and does issues. To assist in reverse engineering, we added log statements to the QEMU Wi-Fi peripherals that log each entry to their reminiscence ranges.

Moreover, we additionally applied stack unwinding in QEMU; that is carried out for each reminiscence entry to a {hardware} peripheral associated to Wi-Fi. That means, we are able to get a full stack hint for each peripheral entry. Symbols will not be stripped, so this can be a very great tool. Nonetheless, to get stack unwinding correctly working, now we have to run QEMU in single step mode: QEMU has a JIT compiler that compiles sequences of emulated meeting directions into optimized fundamental blocks. This enormously improves the execution velocity, however for the reason that CPU execution state is barely assured to be right initially of a fundamental block, if a peripheral reminiscence entry occurs in the midst of such a fundamental block, the stack unwinding algorithm provides incorrect outcomes.

Operating in single-step mode negates a lot of the advantage of the QEMU JIT compiler, inflicting the code to run a lot slower. This isn’t that huge of a drawback, in comparison with the treasure trove of data the execution hint provides us.

Beneath is an instance of a single reminiscence entry logged by QEMU: it’s a write (W) to handle 3ff46094 with worth 00010005, carried out by the perform ram_pbus_force_test. The remainder of the callstack can be logged, and translated to a logo identify if out there.

W 3ff46094 00010005 ram_pbus_force_test 400044f4 set_rx_gain_cal_dc set_rx_gain_testchip_70 set_rx_gain_table bb_init register_chipv7_phy esp_phy_load_cal_and_init esp_phy_enable wifi_hw_start wifi_start_process ieee80211_ioctl_process ppTask vPortTaskWrapper

Lastly, we additionally corrected the dealing with of MAC addresses (in comparison with Martin Johnsons model), so {that a} packet seize has right MAC addresses in packets as an alternative of hardcoded addresses.

Dynamic evaluation on actual {hardware}

To dynamically analyze the firmware on actual {hardware}, we use the JTAG {hardware} debugging interface. By connecting some jumper wires between the ESP32 and a JTAG debugger, we are able to debug the ESP32. We adopted the steps described in this GitHub repository to get our JTAG debugger (CJMCU-232H) working.

In additon to the JTAG debugger, we additionally related a USB Wi-Fi dongle on to the ESP32: the ESP32-WROOM-32U variant of the ESP32 has an antenna connector. We join that antenna connector to a 60 dB attenuator (this weakens the sign by 60dB), then join that to the antenna connector of the wi-fi dongle. That means we’ll be capable of solely obtain the packets coming from the ESP32, and the ESP32 will solely obtain packets despatched by the wi-fi dongle.

This concept sadly didn’t solely work: sufficient radio waves from outdoors entry factors leaked into the antenna connector that the wi-fi dongle additionally receieved their packets. We tried to construct a low-cost faraday cage from a paint can to forestall this, however this solely attenuated outdoors indicators with an additional 10dB: this eliminated some APs, however not all of them. The present answer is unquestionably not preferrred, so we’ve began work on constructing a greater and bigger faraday cage, from conducting material and with fiber-optic information communication.

Wi-Fi dongle connected to the ESP32, with two 30 dB attenuators in between
Wi-Fi dongle related to the ESP32, with two 30 dB attenuators in between
Faraday cage made from a paint tin, with copper tape to close the hole for the USB connectors, and ferrite chokes to reduce the RF leaking in
Faraday cage comprised of a paint tin, with copper tape to shut the opening for the USB connectors, and ferrite chokes to cut back the RF leaking in


SoftMAC vs HardMAC

SoftMAC (Software program MAC) and HardMAC ({Hardware} MAC) refer to 2 completely different approaches for implementing the MAC layer for Wi-Fi. SoftMAC depends on software program to handle MAC layer capabilities, which gives flexibility and ease of modification however can eat extra energy/CPU cycles. HardMAC, alternatively, offloads MAC layer processing to devoted {hardware}, lowering CPU utilization and energy consumption however limiting the flexibility to adapt to new options with out {hardware} modifications.

The ESP32 appears to make use of a SoftMAC strategy: you’ll be able to straight ship and obtain 802.11 frames (as an alternative of with HardMAC, the place you inform the {hardware} you wish to connect with a sure AP, and it might then routinely craft the nescessary frames and ship them). That is excellent news for our open supply implementation, since there exist already open-source 802.11 MAC stacks for SoftMAC (for instance, mac80211 within the Linux kernel).


The Wi-Fi performance is applied by way of a number of {hardware} peripherals, every chargeable for a separate a part of the performance. By way of reverse engineering, the next peripherals had been recognized as ‘used for Wi-Fi functionaliy’ (these are reminiscence addresses, by way of which the peripherals could be accessed):

  • MAC peripherals, at 0x3ff73000 to 0x3ff73fff and at 0x3ff74000 to 0x3ff74fff
  • RX management registers, at 0x3ff5c000 to 0x3ff5cfff
  • baseband, at 0x3ff5d000 to 0x3ff5dfff
  • chipv7_phy (?) at 3ff71000 to 3ff71fff
  • chipv7_wdev (?) at 3ff75000 to 3ff75fff
  • RF frontend, at 3ff45000 to 3ff45fff and 3ff46000 to 3ff46fff
  • analog at 3ff4e000 to 3ff4efff (that is additionally utilized by the DAC related to GPIO pins)

It needs to be famous that these peripherals are mirrored to a different place within the deal with area:

Peripherals accessed by the CPU by way of 0x3FF40000 ~ 0x3FF7FFFF deal with area (DPORT deal with) will also be accessed by way of 0x60000000 ~ 0x6003FFFF (AHB deal with). (0x3FF40000 + n) deal with and (0x60000000 + n) deal with entry the identical content material, the place n = 0 ~ 0x3FFFF.


By writing some minimal firmware that simply sends packets in a loop and utilizing the three reverse engineer methods described earlier, a excessive stage overview of the Wi-Fi {hardware} lifecycle for sending a packet was decided:

See Also

  1. Calling esp_wifi_start(), this not directly calls esp_phy_enable()
  2. esp_phy_enable() is chargeable for initializing the wifi {hardware}:
    1. Calibrate the PHY {hardware}: this tries to compensate imperfections of the {hardware}. In line with the info sheet, this does, at the very least: I/Q part matching; antenna matching; compensating provider leakage, baseband nonlinearities, energy amplifier nonlinearities and RF nonlinearities (I’m extra of a software program individual than an digital engineer, so I don’t precisely know what these phrases imply). This calibration could be saved to the non-volatile storage and to reminiscence. That is used so we don’t need to do a full calibration each time the ESP32 wakes up from modem sleep.
    2. Initialize the MAC peripherals: set RX MAC deal with filters, set the buffers the place the packets will likely be acquired into, set the auto-ACKing coverage, set the chips personal MAC deal with.
    3. Set varied bodily radio properties (TX fee, frequency, TX energy, …)
    4. Arrange the ability administration timer: if packets will not be despatched usually sufficient, the modem energy save timer kicks in and de-initializes a part of the Wi-Fi {hardware} to save lots of energy.
  3. Now, we’re able to ship a packet:
    1. Get up some Wi-Fi peripherals from deep sleep and restore their calibration, if we’d like to
    2. Set some metadata, associated to the packet (probably the speed and different PHY settings)
    3. Create a DMA entry, consisting of the size of the packet and the deal with of the buffer containing the MAC information. The MAC Body Checksum is routinely calculated by the {hardware}. DMA stands for Direct Reminiscence Entry: that signifies that we simply inform the {hardware} the deal with and size of the place our packet is, and the {hardware} will then learn that reminiscence and transmit the packet, all on its personal.
    4. Write the bottom bits of the DMA entry right into a {hardware} register, then allow it for transmission by setting a bit within the bitmask of that register.
    5. As soon as the packet is shipped, interrupt 0 will fireplace to inform us how succesful the transmission was. We will react to collisions and timeouts (and possibly additionally to ACKs acquired?). We additionally need to clear the interrupt bit that signifies a packet was despatched.

Implementing transmitting packets

As a (very restricted) proof-of-concept, we needed to ship arbitrary 802.11 frames by straight utilizing the reminiscence mapped peripherals, so with out utilizing the SDK capabilities. As you’ll be able to see within the lifecycle diagram above, earlier than transmitting, we first must initialize the wifi {hardware}. Sadly, this initialization is much more complicated than sending packets: to intialize the {hardware}, about 50000 peripheral reminiscence accesses are wanted, in comparison with about 50 for transmitting a packet (together with dealing with the interrupt). These will not be precise numbers in any respect, however they offer an concept concerning the complexity concerned.

For the fundamental ‘transmitting packets’ proof-of-concept, we’re at the moment nonetheless utilizing the proprietary capabilities to initialize the wifi {hardware}. We encountered the difficulty that after initializing, the modem energy save timer would kick in and de-initialize the wifi peripherals, stopping us from sending packets. To work round this, we ship a single packet utilizing the SDK after which instantly name the undocumented pm_disconnected_stop() perform, which disables the modem energy save mode timer. After this, we are able to ship arbitrary packets by straight writing to the MAC peripheral addresses. For this PoC, we don’t want to interchange the interrupt handler for wifi occasions: the prevailing, proprietary handler will deal with the ‘packet was despatched’ interrupt simply high quality.

The basic proof of concept works, we are able to transmit arbitrary packets by straight writing and studying from reminiscence addresses!

Present roadmap

Now we are able to transmit packets, however we nonetheless have a variety of work forward of us: that is the to-do listing, in tough order of priorities

  • ☑ Ship packets
  • ☐ Obtain packets: to do that, we might want to do the following:
    • Set the RX coverage (this filters packets primarily based on MAC deal with) / allow promiscous mode to obtain all packets
    • Set the reminiscence deal with wherein we wish to obtain the packet by way of DMA
    • Substitute the wifi interrupt with our personal interrupt; the code signifies that there is perhaps some sort of wifi watchdog, we’ll want to determine the way to pet it.
  • ☐ Ship ACK (acknowledgment) packets again if we obtain a packet that’s destined for us
  • ☐ Implement altering the wifi channel, fee, transmit energy, …
  • ☐ Mix our implementation with an present open supply 802.11 MAC stack, so the ESP32 can affiliate with entry factors
  • ☐ Implement the {hardware} initialization (now carried out by esp_phy_enable()). This will likely be a tough enterprise, since all calibration routines will should be applied, but additionally has a excessive payoff: we’ll then have a very blob-free firmware for the ESP32.

And a listing of doable future extensions that aren’t but on the roadmap, however are helpful to do in any case:

  • ☐ Implement modem energy saving: turning off the modem when not in use
  • ☐ AMSDU, AMPDU, HT40, QoS
  • ☐ Do the cryptography wanted for WPA2 and so on in {hardware} as an alternative of in software program
  • ☐ Bluetooth
  • ☐ Write SVD documentation for all reverse engineered registers. An SVD file is an XML file that describes the {hardware} options of a microcontroller, this makes it doable to routinely generate an API from the {hardware} description. Espressif already has an SVD file containing the documented {hardware} registers; we are able to doc the undocumented registers and (routinely) merge them in.


All code and documentation is on the market within the esp32-open-mac GitHub organisation. I believe particularly the QEMU fork could be helpful for different reverse engineers due to the reminiscence tracing function.


Because the starting of penning this weblog publish, receiving packets was additionally applied. To perform this, we wanted to implement the Wi-Fi MAC interrupt handler and handle the RX DMA buffers. Which means we now can ship and obtain packets utilizing solely open supply code: the {hardware} initialization continues to be carried out with proprietary code, however after this setup is completed, solely open supply code is used to ship and obtain packets, no extra proprietary code is executed. The second half is here

Questions? Wish to collaborate?

This can be a sizeable venture that would positively use a number of contributors; I’d actually wish to collaborate with different folks to create a completely practical, open-source Wi-Fi stack for the ESP32. If this seems like one thing you’d wish to work on, contact me by way of , possibly we are able to have a weekly hacking session?

So far as I do know, that is the primary enterprise to construct an open supply 802.11 MAC for an reasonably priced microcontroller. If you wish to financially assist this venture, you’ll be able to wire cash by way of, please put “ESP32” within the transaction description, so our treasurer is aware of what the cash is for. Please don’t donate for those who’re a scholar or for those who’re not financially impartial. In the event you’re an organization and want to donate {hardware} (for instance, a faraday cage or measuring gear that is perhaps helpful), please contact me.

This project was funded by way of the NGI0 Core Fund, a fund established by NLnet with monetary assist from the European Fee’s Subsequent Era Web programme, below the aegis of DG Communications Networks, Content material and Know-how below grant settlement No 101092990.

Be happy to ship me an e mail in case you could have questions, you assume one thing on this weblog publish could possibly be worded higher otherwise you noticed a mistake.

Source Link

What's Your Reaction?
In Love
Not Sure
View Comments (0)

Leave a Reply

Your email address will not be published.

2022 Blinking Robots.
WordPress by Doejo

Scroll To Top