Reverse-engineering and hardware exploration notes for the Smiirl Counter — a wifi-connected physical number display sold for showing metrics like Facebook likes, Stripe revenue, etc. Smiirl does not provide repair services, spare parts, or repair documentation, so keeping these devices alive is left to owners.
This document is a working reference, not a finished guide.
- SoC: Atheros AR9331 (or close AR933x/AR934x relative). MIPS 24K core, integrated 2.4GHz wifi. Extremely well-documented in the OpenWrt community.
- OS: OpenWrt Chaos Calmer 1.0.4 (r43781). Released late 2015, EOL in 2017. Frozen base — Smiirl patches their own Lua endpoints on top, does not update the OpenWrt base.
- BusyBox: v1.22.1, build dated 2017-11-14. Stripped of
non-essential utilities (e.g. no
whoami, noncin some builds). - Hostname:
SmiirlCounter - Web stack: uhttpd serving
/www, CGI under/cgi-bin, LuCI Lua dispatcher for the API. - Boot: standard OpenWrt — U-Boot → kernel → procd → services.
askfirstis configured on the serial console, meaning the UART is wired up in software even on boards with unpopulated headers.
Notable entries from ps:
/usr/sbin/uhttpd -f -h /www -r SmiirlCounter -x /cgi-bin .../sbin/rpcd— OpenWrt RPC daemon, exposes ubus over HTTP/sbin/ubusd— ubus broker/usr/sbin/dnsmasq,/usr/sbin/ntpd,/usr/sbin/crond/sbin/askfirst /bin/ash --loginon the console/usr/bin/lua /usr/bin/smiirl-count...— Lua daemonsmiirlcounter— compiled binary, presumably handles the display protocol (SPI or serial to chained digit modules)- No dropbear running by default, but the binary is almost certainly still in the firmware image — it just isn't started.
All under /cgi-bin/luci/smiirl/. All GET, all return JSON with
at least a result field.
-
api/calibrate/is/readyPolled while the device boots its hardware enumeration. Returns{result: "OK", digits: N}once the digit modules are detected. The digit count is stored in a browser cookie for the next page. -
api/calibrate/calibratedCalled when stripe-test calibration passes with no defective digits. No params. OnOK, the wizard navigates to/calibrate/finished.html.
-
api/factory/number?number=<string>Sets the persisted displayed number. The string contains one character per digit cell:0–9,a(blank), orb(special glyph / dash, rendered with a tiny base64 PNG marker in the UI). Used during step 1 of calibration to sync software to the physical display. -
api/factory/pushit?mask=<string>Called when at least one digit is flagged as showing stripes during the QA test. The mask string hasxfor good digits andofor bad. Presumably re-runs the test pattern on flagged modules. Does not navigate; the wizard loops until clean.
api/test/number?number=<string>Sets the displayed number temporarily (does not persist). Returns{result: "OK"}. Same string format asfactory/number. Implies atest/namespace with likely siblings; not yet enumerated.
api/activate/ssh(REMOVED in current firmware) Old developer/factory backdoor. Returned{"ssh": "OK", "password": "<random>"}and enabled dropbear with that root password. Removed by Smiirl in a firmware update, but the underlying capability (dropbear binary, password setting, service start) is still present in the firmware — only the dispatcher route was removed.
-
wifi/scan?r=<random>Returns scanned BSSIDs as JSON. Polled until results arrive. Used by the wifi setup wizard between calibrate steps. May return{has_error: true}to trigger redirect toerror.html. -
Other
wifi/*endpoints (connect, status, list, etc.) not yet enumerated. The wifi-connect endpoint specifically is the most promising injection target on the device, since by design it takes user input (SSID, PSK) and turns it into UCI config writes and a service restart.
Three observed generations of the Smiirl counter mainboard, in chronological order:
- Earliest: secret USB socket soldered inside the unit.
- Middle: unpopulated 4-pin UART header on the PCB.
- Current: 4-pin header pads still present on the board, no header soldered. USB footprint also present but unpopulated. Some friend/dev units exist with USB still soldered in.
The trajectory is clear: Smiirl has progressively removed visible debug access without removing the underlying support in software.
- Atheros SoC — center of the board, identifiable by Atheros logo.
- 8-pin SOIC SPI flash — to the right of the SoC. Almost certainly a Winbond W25Q64 (8MB) or W25Q128 (16MB) or Macronix equivalent. Holds U-Boot, kernel, rootfs, and ART (wifi calibration) data.
- 4-pin header pads — top of the board, near the USB-C area.
Unpopulated through-holes. Confirmed UART based on AR9331
conventions and the active
askfirstconsole process. - USB-C connector — power input on current units. Replaces earlier barrel-jack designs. Note: applying the wrong PSU to a barrel-jack unit is a common cause of bricking.
The 4 pins are GND, VCC (3.3V), TX, and RX in some order. To identify without guessing:
- Power off, multimeter in continuity mode. Find which pin connects to a known ground (USB shield, electrolytic cap negative leg). That's GND.
- Power on, multimeter in DC voltage mode, GND probe on the
identified GND pin. Measure each remaining pin:
- Steady 3.3V → VCC. Leave disconnected from your serial adapter.
- 3.3V but flickering/noisy → TX (SoC transmitting). Wire to your USB-serial adapter's RX.
- Steady 3.3V with no activity → RX (SoC listening). Wire to your USB-serial adapter's TX.
- Settings: 115200 baud, 8N1, no flow control.
- Use a 3.3V USB-to-TTL adapter, not 5V. 5V on the AR9331's UART pins will damage the SoC.
Best case: askfirst drops you into an unauthenticated root shell
on the console. Hit enter at the prompt and you're in.
Fallback: interrupt U-Boot during the boot countdown (mash a key when you see "Hit any key to stop autoboot"). At the U-Boot prompt:
printenv
setenv bootargs '<original bootargs> init=/bin/sh'
boot
The kernel will boot directly into a shell as PID 1 with no auth. Then:
mount -o remount,rw /
passwd root
uci set dropbear.@dropbear[0].enable=1
uci commit dropbear
sync
reboot -f
After reboot, dropbear is running on port 22 with your password.
For a bricked unit or as a firmware backup before any modification:
- Programmer: CH341A USB stick (~$10 kit including SOIC-8 clip, ZIF socket, and 1.8V level-shifter adapter board).
- Caveat: the bare CH341A outputs 5V on the clip's VCC line
even with the "3.3V" jumper set, which can damage 3.3V flash
chips over time. Mitigations:
- Route through the included 1.8V adapter board (it's a level shifter and works for 3.3V too).
- Power the chip from a separate 3.3V source and leave the clip's VCC pin disconnected.
- Mod the CH341A board to be properly 3.3V (well-documented online).
- Use a Raspberry Pi with
flashrominstead (native 3.3V SPI).
- Software (Windows 11): AsProgrammer (GUI, easy) or flashrom (CLI, more capable). Driver via Zadig (install WinUSB driver) or the official CH341PAR driver from wch-ic.com.
- Procedure:
- Power the Smiirl off.
- Clip onto the SOIC-8 flash chip, aligning pin 1 carefully.
- Read with AsProgrammer's "Read IC" or
flashrom -p ch341a_spi -r dump.bin. - Save as
dump.bin.original, never modify the original. - Verify by reading a second time and comparing hashes.
- Partition layout (typical AR9331 OpenWrt): u-boot → u-boot-env → kernel → rootfs (squashfs) → rootfs_data (jffs2) → ART. Never overwrite ART (per-device wifi calibration, factory-unique).
binwalk -Me dump.bin— auto-extract embedded filesystems.- Locate the squashfs (binwalk identifies it). Note offset and
compression options with
unsquashfs -s rootfs.squashfs. unsquashfs rootfs.squashfs→squashfs-root/directory.- Modify files:
etc/shadow,etc/config/dropbear,usr/lib/lua/luci/controller/smiirl/*, etc. - Repack with
mksquashfs— must use OpenWrt's patched squashfs-tools that supports squashfs4 + LZMA. Stock Debian/Ubuntu squashfs-tools will produce a subtly broken image that won't boot. - Splice the new squashfs into a copy of the dump with
dd conv=notrunc seek=<offset>, leaving U-Boot, kernel, and ART regions untouched. - Write back to the chip via CH341A. Verify after write.
- Reinstall chip if removed, power on with UART connected, watch boot.
Once you have any shell access (UART or otherwise), changes made
to /etc/shadow, /etc/config/dropbear, etc., persist across
reboots via the JFFS2 overlay (/overlay). No reflash needed for
the SSH-enable goal. The full dump-modify-reflash workflow is only
necessary if you need to change something in the squashfs base
itself, or if you want a clean modified "stock" image.
For a unit you want to gain access to, in order of effort and risk:
- Walk the wifi setup wizard with browser devtools open. Capture every endpoint, especially whatever submits SSID/PSK. Free, immediate, may reveal injection targets.
- Enumerate the
test/,wifi/, andactivate/namespaces by trying obvious sibling endpoint names with curl. Compare 404 vs 403 vs 500 responses to infer route existence. - Try ubus over HTTP via rpcd. rpcd is running. Hit
/cgi-bin/luci/rpc/*and/ubus. Anonymous sessions sometimes have surprising ACLs and may permitservice dropbear start. - Open the bricked unit, find the UART pads. Cheapest reliable path to a root shell. ~$5 for a 3.3V USB-to-TTL adapter.
- Dump the SPI flash from the bricked unit if UART doesn't yield results. Read offline, find the new SSH-enable mechanism in the Lua source, apply the lesson to the working unit.
- Apply learnings to the working unit. Either soft path (endpoint) or hardware path (UART), whichever proved viable on the brick.
- What replaced the
activate/sshendpoint? Renamed, gated, or removed entirely? Firmware diff vs an older image would answer this in minutes. - Is the current U-Boot locked against key-interrupt? Some vendors patch this. UART access on the bricked unit will reveal it.
- Does rpcd accept anonymous ubus calls, and if so what ACLs apply?
- What protocol does
smiirlcounteruse to talk to the digit modules? (Relevant for any "drive the device with my own data" follow-up project.) - Where does the unit phone home, and with what credentials?
(
/etc/config/after shell access will answer this.)
- OpenWrt Chaos Calmer documentation (archived): the base system is standard OpenWrt 15.05.
- AR9331 datasheet and the OpenWrt device pages for any AR9331 board (Carambola, MR3020, Black Swift, etc.) — UART pinouts, U-Boot quirks, and flashing procedures all transfer.
binwalk,flashrom,squashfs-tools-ng, AsProgrammer — all standard tools, all open source, all documented.