· 8 min read ·

What Lenovo's WWAN Unlock Binary Was Actually Doing

Source: lobsters

A developer posted a straightforward account this week of replacing Lenovo’s proprietary WWAN unlock binary with about 100 lines of bash. The replacement works. That alone is worth paying attention to, but the more interesting question is what the binary was doing in the first place, why Lenovo ships it the way they do, and what the right mental model is for the category of problem it represents.

Two Generations of the Same Problem

Lenovo’s WWAN locking has a bifurcated history, and understanding both generations clarifies what changed and what didn’t.

The first generation, covering most ThinkPads up to roughly 2016, enforced a hardware whitelist during POST. If you plugged in a wireless card whose USB VID:PID wasn’t in a hardcoded table embedded in the UEFI image, the machine halted at boot with error 1802: “Unauthorized network card is plugged in.” The community response was binary patching. Tools like 1vyrain automated the process of locating the whitelist table in firmware and zeroing it out, using a chain of exploits to write the modified image from a running OS without an external flasher. Alternatively, coreboot replaced the entire firmware with an implementation that had no such concept.

The second generation, which covers newer ThinkPad and ThinkBook hardware, moved the lock out of firmware and into a software gate managed through the combination of a kernel driver, rfkill, and modem firmware. The BIOS whitelist narrowed to basic USB VID:PID approval, which stops arbitrary hardware swaps, but POST no longer halts. Instead, the modem enumerates, appears in lsusb, responds to capability queries, and then simply refuses to register on any cellular network. The gate has moved from firmware initialization to userspace activation.

This architectural shift is what makes a bash script a plausible replacement.

What the Binary Actually Does

The exact behavior of Lenovo’s unlock binary varies across modem generations, which reflects that several different modem chipsets are involved: the Fibocom L850-GL (Intel XMM7560), the Fibocom FM350-GL (for 5G machines), Sierra Wireless EM7455, and Quectel EM05 among others. Community reverse engineering has identified four distinct mechanisms, sometimes applied individually, sometimes in combination.

The MBIM vendor extension path is the most sophisticated. MBIM (Mobile Broadband Interface Model) is the USB-IF specification for 4G/5G modem communication. It structures messages with a 128-bit service UUID, a command ID (CID), a command type (SET/GET/NOTIFY), and a payload. The spec explicitly allows vendor-defined service UUIDs for OEM extensions. Lenovo uses a proprietary UUID to send an unlock payload derived from the modem’s IMEI, typically via a simple XOR with a static key. The modem, on receiving this payload over its /dev/cdc-wdm0 or /dev/wwan0mbim0 interface, transitions from locked to operable. The libmbim library exposes everything needed to replicate this from userspace.

The AT command path is older and applies to modems with accessible serial interfaces at /dev/ttyUSB2, /dev/ttyACM0, or /dev/wwan*ctrl. The Hayes AT command set from 1981 is still the base protocol; 3GPP extended it for cellular (AT+CFUN for radio function state, AT+CIMI for IMSI). Vendor namespaces layer on top: Qualcomm uses AT! commands for NV storage access, Fibocom uses AT+XLOCK and related commands. The unlock sequence is a few commands terminated with \r\n, fully auditable once extracted.

The rfkill path is the simplest and most common on recent hardware. The thinkpad_acpi kernel driver reads ACPI DSDT methods at init time to determine the WWAN radio’s hardware and software kill states, then registers the device with the kernel’s rfkill subsystem. The modem enters soft-blocked state. The blob’s contribution is to write 1 to /sys/class/rfkill/rfkill*/state, equivalent to running rfkill unblock wwan. In these cases the binary exists entirely to do what two words accomplish.

The EFI variable path applies to firmware architectures where the BIOS reads an NVRAM variable before releasing the M.2 slot’s power rail or granting the modem operational state. The blob writes to efivarfs at /sys/firmware/efi/efivars/, populating a Lenovo-specific variable that the next firmware sequence reads. This is observable before and after execution with a simple ls -la /sys/firmware/efi/efivars/ diff.

How the Reverse Engineering Works

The toolchain for this class of problem is standard, documented, and mature.

strace is the starting point. Running strace -e trace=open,read,write,ioctl -f ./unlock_binary captures every file the binary opens and every byte it writes. For AT command sequences this is often sufficient: the commands appear in plain text in the write syscall trace.

For MBIM traffic, usbmon with Wireshark provides the full picture. After modprobe usbmon, capturing on usbmon0 in Wireshark shows every USB control transfer. Wireshark’s built-in MBIM dissector parses message framing, decodes service UUIDs (showing standard ones by name and flagging unknown ones), and displays CIDs and payloads in a readable tree.

A relay via socat provides a transparent proxy for serial-based communication:

socat -v PTY,link=/tmp/modem_fake,rawer /dev/ttyUSB2,rawer

Redirecting the binary to /tmp/modem_fake while monitoring the relay produces a complete, timestamped transcript of the modem conversation with both directions labeled.

EFI variable changes are visible through before/after snapshots of efivarfs, or by using efivar --list from the efivar package.

Once the sequence is known, the replacement is mechanical. mbimcli --device=/dev/cdc-wdm0 --no-open sends individual MBIM messages, including vendor extension messages with arbitrary UUIDs and payloads. AT commands go through direct file descriptor writes or mmcli --command=. rfkill state changes are a sysfs write. A bash script that assembles these in sequence, with appropriate retry logic and device detection, is the complete replacement.

Why Vendors Ship Blobs

The reasons are overlapping and mutually reinforcing, which is part of why the pattern persists.

FCC equipment authorization rules (47 CFR Part 2 Subpart J) require that radio transmitters cannot be reconfigured to operate outside authorized frequency and power parameters. For cellular modems, this is typically satisfied by shipping the modem in a locked state that requires a host-side activation sequence. Opaque binaries offer vendors documentation they can present to the FCC showing that users cannot trivially override RF parameters, even without cryptographic enforcement. That the bash script achieves the same result, and that the unlock sends standard initialization commands rather than unlocking arbitrary RF parameter changes, undermines the regulatory rationale, but vendors filed their documentation years ago and have little incentive to revisit it.

NDA arrangements with modem manufacturers are the more credible technical constraint. Fibocom, Sierra Wireless, and Quectel share initialization sequences, calibration data, and firmware update procedures under NDAs that forbid distribution as readable text. A compiled binary satisfies the letter of the agreement. The NDA may specifically permit compiled distribution while prohibiting source. This creates genuine legal pressure toward shipping blobs regardless of engineering preference.

Hardware upsell protection is a quieter motivation. WWAN connectivity is sold as a paid configuration option on most ThinkPad lines. Keeping the activation mechanism opaque creates friction for users who want to swap in a gray-market modem that is electrically compatible but not the one Lenovo sold. The lock serves a commercial function independent of regulatory cover.

Enterprise IT control is the fourth factor. Large deployments use WWAN lock state to enforce cellular network policy across a fleet. The unlock mechanism can, in principle, be tied to managed deployment workflows.

None of these individually explains every detail of the implementation. Together, they explain why the blob continues to be the default.

ModemManager’s FCC Unlock Framework

ModemManager solved this problem cleanly starting in version 1.18 with a drop-in FCC unlock script architecture. Place an executable script named after the modem’s USB VID:PID, such as 2cb7:0111, in /usr/share/ModemManager/fcc-unlock.d/. ModemManager detects when a modem signals that it requires FCC authorization, then invokes the script with CONTROL_PORT and MODEM_PHYSDEV_UID set in the environment. Several modem vendors already have scripts in the upstream directory.

This is the architecturally correct place for the Lenovo unlock logic. ModemManager manages the modem lifecycle, holds the control port open at the right time, and provides the device identity the script needs. A bash script in this directory would be auditable, upstream-submittable, distribution-packagable, and properly integrated with power management and suspend/resume cycles.

Lenovo bypassed this entirely and shipped their own binary outside the ModemManager lifecycle. The result is a systemd unit or udev rule that fires at device attach, runs as root, and has no coordination with ModemManager’s state machine. This creates races, duplicate state management, and brittleness across OS upgrades because the binary is linked against specific glibc versions and kernel ABIs.

The Security Argument

The blob runs as root. It fires on every qualifying USB device attach event. It has no source code available for audit. It accesses modem control interfaces that, on Qualcomm hardware, include the AT! extended namespace covering NV storage writes and firmware image slot selection. A compromise of Lenovo’s distribution channel, or a supply chain substitution, delivers arbitrary root-level code to every machine that matches the USB hardware ID and has the package installed.

This is not a theoretical concern unique to Lenovo. The “binary that runs as root at hardware events” class is exactly the surface targeted in supply chain attacks. The linux-firmware project maintains a WHENCE file tracking the provenance and redistribution rights for every kernel firmware blob, implicitly encoding the principle that blobs require ongoing justification and should be replaced by open implementations when one exists.

The bash replacement eliminates the liability entirely. It fits into TPM-backed measured boot and dm-verity protected root filesystem security models. It receives updates through normal distribution package management based on readable, auditable diffs. When Lenovo drops hardware support, the script continues working without depending on a binary linked against an EOL glibc version.

There is a broader pattern across the Linux ecosystem of community implementations replacing vendor blobs once the actual protocol is understood. The xmm7360-pci project replaced the firmware loader for the Fibocom L850-GL’s Intel XMM7360 chipset, which required loading firmware directly into device RAM, a genuinely more complex case. libqmi replaced Qualcomm’s proprietary QMI connection managers. libmbim replaced MBIM connection tools for multiple vendors. Each replacement cycle follows the same arc: strace reveals the protocol, the protocol turns out to be a standard or near-standard sequence, and 100 to 1,000 lines of auditable code replaces an opaque binary.

The Lenovo case lands at the shallow end of that complexity range. The fact that the replacement fits in 100 lines of bash is not evidence of exceptional cleverness; it is evidence that the underlying operation was never complex, only obscured. The NDA constraints and regulatory positioning that motivated the blob’s existence don’t change the protocol complexity of what it does. Understanding that distinction is the practical takeaway for anyone dealing with similar hardware on Linux, and for vendors deciding how to package initialization logic in the first place.

Was this interesting?