Skip to content

bcm43439a0: add 802.11 frame injection support#688

Open
TBX3D wants to merge 1 commit into
seemoo-lab:masterfrom
TBX3D:bcm43439a0-frame-injection
Open

bcm43439a0: add 802.11 frame injection support#688
TBX3D wants to merge 1 commit into
seemoo-lab:masterfrom
TBX3D:bcm43439a0-frame-injection

Conversation

@TBX3D

@TBX3D TBX3D commented Jul 1, 2026

Copy link
Copy Markdown

Summary

Adds 802.11 frame injection (TX) for the Raspberry Pi Pico W (Infineon CYW43439, bcm43439a0), firmware 7.95.49 (7_95_49_2271bb6).

Monitor mode and radiotap for this chip were merged in #579. Frame injection was the remaining gap in the support table (M:X RT:X I: blank) and is the feature requested in #687. NEX_GET_CAPABILITIES now returns 0x7 (monitor | radiotap | injection).

Approach

The Pico W runs the WLAN firmware over gSPI with no Linux netdev and no mon0 interface, so the classic injection trigger (hijacking the netdev transmit path and watching mon0 for radiotap frames) does not exist here. Injection is driven instead by a new NEX_INJECT_FRAME (408) case added to the existing wlc_ioctl_hook (GenericPatch4 @ 0x1EFC4). The host sends a single radiotap-prefixed 802.11 frame per ioctl; the patch parses and strips the radiotap header, then hands the bare frame to the classic sendframe() TX path (wlc_d11hdrs -> wlc_get_txh_info -> wlc_txfifo).

Changes

  • patches/bcm43439a0/7_95_49_2271bb6/nexmon/src/sendframe.c (new): classic TX path, ported from bcm43430a1.
  • .../src/injection.c (new): radiotap parse plus inject_frame(wlc, p). Takes wlc directly because the ioctl hook already holds it, rather than deriving it from a wl_info.
  • .../src/ioctl.c: the NEX_INJECT_FRAME case. It reserves headroom before the copy so wlc_d11hdrs can prepend the d11 TX header without reallocating, and it does not report IOCTL_SUCCESS for a frame that failed radiotap parsing.
  • .../src/patch.c: capabilities = NEX_CAP_MONITOR_MODE | NEX_CAP_MONITOR_MODE_RADIOTAP | NEX_CAP_FRAME_INJECTION.
  • firmwares/bcm43439a0/structs.common.h: the three struct fields sendframe() needs, plus the software wlc_band and the opaque wlc_txh_info scratch type.

All required firmware symbols (wlc_d11hdrs, wlc_get_txh_info, wlc_txfifo, pkt_buf_*, wlc_ioctl) are already resolved for CHIP_VER_BCM43439a0 in patches/common/wrapper.c, and NEX_INJECT_FRAME and NEX_CAP_FRAME_INJECTION already exist in the common headers, so no common-header or Makefile changes were needed.

Struct offsets

sendframe() reads three fields the 43439 header did not describe. Each was resolved against a real bcm43439a0 ROM/RAM dump. The firmware dispatches these wlc_* functions through a ROM patch-table thunk into the loaded RAM blob, so the struct-access idioms were read from the RAM image and cross-checked against the ROM.

Field Offset Evidence
wlc_band.hwrs_scb 0x018 the firmware default-scb routine at ROM 0x82e7c8: wlc+0x20 is band, band+0x18 loads the scb, then scb+16 is dereferenced.
wlc_hw_info.up 0x006 the dominant wlc->hw boolean gate (23 sites, base verified as wlc). The originally seeded 0x0a0 has zero gate sites.
sk_buff.scb 0x028 wlc_txfifo reads the packet scb from [p+0x28], and wlc_d11hdrs does not write it, so the p->scb = assignment is required rather than redundant.

Verification

Built with the nexmon toolchain; the patched firmware links within the reclaim region. Confirmed over the air with an independent monitor-mode witness on channel 6: the injected beacon is received with the expected test SSID, source/BSSID 02:13:37:de:ad:01, and broadcast destination ff:ff:ff:ff:ff:ff.

Notes

  • Injection is ioctl-driven and does not gate on wlc->monitor; the host is responsible for setting the channel first (and monitor mode too if it also wants RX). sendframe() still self-guards on hwrs_scb and hw->up.
  • monitormode.c is unchanged. Its MONITOR_* enum drift from the canonical bitflag scheme is noted in-tree but injection does not depend on it.

Adds frame injection (TX) for the Raspberry Pi Pico W CYW43439, firmware
7.95.49 (tag 7_95_49_2271bb6). Monitor mode and radiotap already landed for
this chip in seemoo-lab#579, so injection was the remaining capability gap; it is the
feature requested in seemoo-lab#687. NEX_GET_CAPABILITIES now returns 0x7.

The Pico W exposes no Linux netdev and no mon0 interface, so the classic
netdev transmit hook does not apply here. Injection is driven instead by a new
NEX_INJECT_FRAME (408) case on the existing wlc_ioctl_hook. The host sends one
radiotap-prefixed 802.11 frame per ioctl; the patch strips the radiotap header
and hands the bare frame to the classic sendframe() TX path.

sendframe.c and injection.c are ported from the bcm43430a1 patch. inject_frame
takes wlc directly, since the ioctl hook already holds it, rather than deriving
it from a wl_info. The ioctl path reserves headroom before the copy so
wlc_d11hdrs can prepend the d11 TX header without reallocating, and it does not
report success for a frame that failed radiotap parsing.

Three struct offsets that sendframe needs were resolved against a real 43439
ROM/RAM dump and are documented inline: wlc_band.hwrs_scb at 0x018,
wlc_hw_info.up at 0x006, and sk_buff.scb at 0x028.

Verified over the air: the injected beacon appears on channel 6 with
source/BSSID 02:13:37:de:ad:01 and a broadcast destination, and the patched
firmware links within the reclaim region.
@jlinktu jlinktu self-assigned this Jul 1, 2026
@jlinktu

jlinktu commented Jul 1, 2026

Copy link
Copy Markdown
Member

Thanks, looks fine on first glance. Will properly revisit in a couple of days.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants