A Bruce-style security toolkit UI for the CardputerZero — a purple/black LVGL menu that launches Wi-Fi, Bluetooth, RF, network and SMB tooling from the device screen. Built on the M5Stack CardputerZero LVGL template.
👉 Just want to install it? See INSTALL.md for a copy-paste, step-by-step guide. The sections below are for developers.
⚠️ For authorized security testing, CTF and research only. Several menu entries (Responder, port scans, packet capture) are only legal against systems you own or have written permission to test.
Rather than reimplementing each protocol, the app shells out to the real
Linux binaries already on the device (curl, nc, iw, bluetoothctl,
smbclient, responder, rtl_433, …) and streams their output to the screen.
That keeps the port tiny and every tool behaves exactly like its CLI.
- Tool catalog —
src/model/tool_catalog.cppis a plain data list of categories and actions. Add a tool by editing this one file; no screen code changes needed. Each action is anargvrun withexecvp(no shell string built from user input → no injection surface). - Process runner —
src/platform/process_runner.*forks/execs a tool, captures stdout+stderr on a reader thread, and delivers it to the LVGL thread vialv_async_call. - Screens — one reusable
MenuScreendrives both the category menu and each tool list;ToolRunScreenruns a tool and shows its live log;TerminalScreenis the interactive shell behind the "Terminal" category;AboutScreenis static.ScreenManagerswaps screens from the current-page subject. - Theme — purple/black palette in
src/view/ui_const.h; light/dark toggle in the nav bar. - Terminal — type a command on the device keyboard, Enter runs it, output
streams into a tail-following log (Up/Down recall history while typing, ESC
leaves the input). A leading
sudoruns the command as root: passwordless through the installed sudoers rule, otherwise the app asks for the sudo password on-device once and caches it for the terminal session. The same screen serves the user and root rows;Tool catalogstays the only place tools are defined. - Status bar — the title bar shows Wi-Fi strength and battery
(charge state included) read from
/sys, refreshed every 5 s (src/platform/device_status.*, ported from the official M5Stack template). - Screenshot — pressing the PrintScreen key dumps the active screen to a
PNG (
src/platform/screenshot_service.*, LVGL snapshot + libpng) and confirms with a toast (src/view/widgets/toast.*).
- UI layer:
BaseScreenowns the root, title bar, nav bar, and content. Widgets derive fromBaseWidgets. The 5-slotNavBaris generic — each screen configures its own buttons. - Data model:
BaseModelholds current page, theme mode, and menu selection. - View model:
BaseViewModelexposes state as LVGL observer subjects and provides navigation actions (open menu, run tool, back, quit, toggle theme). - Reactive/data binding:
src/reactivewraps LVGL observer subjects. - Platform layer: Linux input + the process runner, plus native subcommands
the app re-execs itself for (
--ble-spam,--wardrive,--evil-portal, …) and the--root-shpassthrough that lets the sudoers rule carry root shell rows (catalog scripts and terminal commands alike) without ever authorizing/bin/shitself. The nav bar maps hardware/keyboard keys4–8, plusESCfor back/quit.
.
├── assets/ # Runtime assets used by the app
│ ├── applications/ # Desktop/app launcher metadata
│ ├── audio/ # Audio resources
│ ├── fonts/ # TTF fonts loaded through FreeType
│ └── images/ # Image resources
├── screenshot/ # Simulator screenshots for supported desktop platforms
├── src/
│ ├── app/ # Application lifecycle, asset loading, screen management
│ ├── config/ # LVGL config headers for desktop and device builds
│ ├── logger/ # Project logging wrapper
│ ├── model/ # Base application data model
│ ├── platform/ # Platform input/hardware integration
│ ├── reactive/ # LVGL observer subjects and binding helpers
│ ├── view/ # Theme, UI constants, screens, and widgets
│ │ ├── screens/ # Page-level LVGL screens
│ │ └── widgets/ # Reusable LVGL widgets such as nav bar and icon button
│ ├── viewmodel/ # BaseViewModel and UI-facing actions/state subjects
│ └── main.cpp # Program entry point
├── CMakeLists.txt # Build graph, dependencies, options, targets
├── CMakePresets.json # Desktop/device configure and build presets
└── README.md
src/app/asset_manager.*: resolves runtime assets from the optional CMake asset root, source treeassets/, and installed/usr/share/<APP_NAME>/path.src/app/screen_namager.cpp: switches LVGL screens when the current page subject changes.src/config/lv_conf_desktop.h: desktop SDL simulator LVGL settings.src/config/lv_conf_cm0.h: embedded Linux/CardputerZero LVGL settings.src/platform/linux_input.*: Linux evdev keypad support and desktop SDL keyboard routing for nav buttons.src/reactive/subjects.*: typed wrappers around LVGL subjects.src/reactive/bindings.*: helper bindings for labels, theme, widget values, flags, states, and events.src/view/widgets/navbar.*: bottom navigation bar and icon-button action mapping.src/view/widgets/icon_button.*: transparent icon button with font/color/theme support.
Common CMake cache options:
| Option | Default | Description |
|---|---|---|
USE_DESKTOP |
ON |
Build SDL desktop simulator when ON; build embedded Linux target when OFF. |
APP_NAME |
bruce_cardputerzero |
Application name used by installed asset lookup. |
APP_ASSETS_ROOT |
empty | Optional runtime asset root. Expected layout includes fonts/, images/, etc. |
APP_KEY_INPUT_DEVICE |
empty | Optional Linux evdev device path, e.g. /dev/input/event0. Empty means auto-scan /dev/input/event*. |
APP_FRAMEBUFFER_DEVICE |
/dev/fb0 |
Linux framebuffer device used by embedded builds when APP_USE_DRM=OFF. |
APP_USE_DRM |
OFF |
Use LVGL's Linux DRM/KMS backend instead of fbdev for embedded builds. |
APP_DRM_DEVICE |
/dev/dri/card0 |
DRM device path used when APP_USE_DRM=ON. |
APP_DRM_CONNECTOR_ID |
-1 |
DRM connector id used when APP_USE_DRM=ON; -1 auto-selects. |
Asset lookup order:
APP_ASSETS_ROOTwhen provided by CMake- source-tree
assets/for development /usr/share/<APP_NAME>/for installed deployments
Desktop builds are intended for fast UI development and use SDL as the LVGL display/input backend.
| macOS simulator | Debian simulator | Windows simulator |
|---|---|---|
![]() |
![]() |
![]() |
Current dependencies info:
| Dependency | Version | Source | Notes |
|---|---|---|---|
| LVGL | v9.5.0 |
CMakeLists.txt FetchContent |
Main GUI framework. |
| fmt | 12.1.0 |
System package manager or vcpkg.json override |
Logging and formatted strings. |
| libpng | 1.6.48 |
System package manager or vcpkg.json override |
PNG image decoding support for LVGL. |
| libjpeg-turbo | 3.1.3 |
System package manager or vcpkg.json override |
JPEG image decoding support for LVGL. |
| zlib | 1.3.1 |
System package manager or vcpkg.json override |
Compression dependency used by image libraries. |
| SDL2 | 2.32.54 |
System package manager or vcpkg.json override |
Desktop simulator display and input backend. |
| FreeType | 2.13.3 |
System package manager or vcpkg.json override |
Runtime TTF font rendering. |
Windows vcpkg installs use the versions above through manifest mode, with the registry baseline pinned in
vcpkg-configuration.json.
Debian/Ubuntu dependencies:
sudo apt update
sudo apt install -y \
build-essential \
cmake \
ninja-build \
libpng-dev \
libjpeg-dev \
libfmt-dev \
libsdl2-dev \
libfreetype-dev \
zlib1g-devNote
Minimal CMake version is 3.31.0 (in coordinate with Debian 13 trixie), for Ubuntu user you can install latest cmake via snap
and run following cmake command with /snap/bin/cmake.
Configure:
cmake --preset linux-x86-64Build:
cmake --build --preset linux-x86-64-dbg
# alternatively, you can run release build
# cmake --build --preset linux-x86-64-relRun:
./build/linux-x86-64/Debug/bruce_cardputerzero
# or launch release build
# ./build/linux-x86-64/Release/bruce_cardputerzeroInstall the Apple command-line tools:
xcode-select --installInstall dependencies with Homebrew:
brew install \
cmake \
ninja \
libpng \
jpeg \
fmt \
sdl2 \
freetype \
dpkg \
zlibConfigure for Apple Silicon:
cmake --preset darwin-arm64Build:
cmake --build --preset darwin-arm64-dbg
# alternatively, you can run release build
# cmake --build --preset darwin-arm64-relRun:
./build/darwin-arm64/Debug/bruce_cardputerzero
# or launch release build
# ./build/darwin-arm64/Release/bruce_cardputerzeroFor Intel macOS, use the darwin-x86-64 configure preset and matching build preset:
cmake --preset darwin-x86-64
cmake --build --preset darwin-x86-64-dbg
# alternatively, you can run release build
# cmake --build --preset darwin-x86-64-rel
./build/darwin-x86-64/Debug/bruce_cardputerzero
# or launch release build
# ./build/darwin-x86-64/Release/bruce_cardputerzeroInstall CMake and Ninja:
winget install Kitware.CMake
winget install Ninja-build.NinjaInstall a C++ toolchain. Choose one of the following.
MSVC Build Tools:
winget install Microsoft.VisualStudio.BuildToolsIn the Visual Studio installer, enable Desktop development with C++ and make sure MSVC and a Windows SDK are selected.
MinGW-w64:
winget install BrechtSanders.WinLibs.POSIX.UCRTAlternatively, you can download from winlibs.
Install vcpkg:
git clone https://github.com/microsoft/vcpkg.git C:\vcpkg
C:\vcpkg\bootstrap-vcpkg.bat -disableMetricsConfigure environmental variables for current terminal:
$env:VCPKG_ROOT="C:\vcpkg"
$env:PATH="$env:VCPKG_ROOT;$env:PATH"
Tip
Change VCPKG_ROOT to your vcpkg installation directory if it is located elsewhere.
These environment variables are session-scoped, meaning they only apply to the current terminal session. Once the terminal is closed, the variables will be lost and need to be set again in a new session. If you want to a persist environment variable
setx VCPKG_ROOT "C:\vcpkg"Configure and build with MSVC:
Important
These steps require launching CMake from a Developer Command Prompt or Developer PowerShell provided by Visual Studio.
This ensures that the MSVC environment (compiler, linker, and Windows SDK paths) is properly initialized.
Without this environment setup, CMake may fail to detect or correctly configure the MSVC toolchain.
cmake --preset win32-msvc
cmake --build --preset win32-msvc-dbg
.\build\msvc\Debug\bruce_cardputerzero.exe
# alternatively for release build
# cmake --build --preset win32-msvc-rel
# .\build\msvc\Release\bruce_cardputerzero.exeConfigure and build with MinGW-w64:
Important
These steps require a properly configured MinGW-w64 toolchain in your system PATH.
Please ensure the following compilers are available in the current terminal session:
- gcc
- g++
- ld
- ar
You can verify the setup by running:
gcc --version
g++ --versionIf these commands are not recognized, you must add MinGW-w64 bin directory to your PATH before configuring CMake.
cmake --preset win32-mingw64
cmake --build --preset win32-mingw64-dbg
.\build\mingw64\Debug\bruce_cardputerzero.exe
# alternatively for release build
# cmake --build --preset win32-mingw64-rel
#.\build\mingw64\Release\bruce_cardputerzero.exeNote
VCPKG will handle the dependencies during CMake configuration process automatically,
this may take several minutes depend on you network connection.
This preset builds an aarch64 Linux target from a host machine with an aarch64-linux-gnu-gcc/g++ toolchain.
The BSP at .cache/sdk_bsp-src is treated as a sysroot: headers, libraries, startup objects, and pkg-config metadata are resolved from that directory first.
If that directory does not exist on the first configure, the toolchain downloads and extracts sdk_bsp.tar.gz automatically before CMake runs compiler checks.
Install a cross compile toolchain:
- Debian
sudo apt install crossbuild-essential-arm64- MacOS
brew install aarch64-unknown-linux-gnuNote
On Windows, it's recommended to use WSL for cross build
or you can download cross compile tools and configure the PATH yourself
Configure:
cmake --preset cp0-crossBuild:
cmake --build --preset cp0-cross-dbg
# or release build
# cmake --build --preset cp0-cross-relDeploy the release package to your device after filling in your device user and IP:
REMOTE_USER=<user> REMOTE_HOST=<device-ip> ./deploy.shBy default, the debian package is copied to '$HOME' folder, normally it's under '/home/<user>'.
On your device, install the copied package with apt and replace the package file name with the one you copied:
sudo apt install --no-install-recommends ./BruceCardputerZero_0.1.0_bruce1_arm64.debThe bottom nav bar maps hardware/keyboard keys from left to right:
| Key | Nav item |
|---|---|
4 |
First icon button |
5 |
Second icon button |
6 |
Third icon button |
7 |
Fourth icon button |
8 |
Fifth icon button |
ESC |
First icon button / quit |
Per-screen slot meanings: on ToolRunScreen the slots are Back / Up / Run-Stop
/ Down / Clear; on TerminalScreen they are Back / Up / Run-Stop / Down / Type
(slot 4 focuses the command input — while typing, every key feeds the input,
Enter runs the command, Up/Down recall history and ESC returns to nav mode;
a leading sudo in a command runs it as root).
- Add a new page enum value in
src/model/base_model.h. - Add the page transition/state logic in
BaseModelandBaseViewModel. - Create a screen under
src/view/screens/deriving fromBaseScreen. - Extend
ScreenManagerto load the new screen when the page subject changes. - Update
NavBaricons and callbacks as needed.
- Derive from
BaseWidgets. - Implement
build()and create LVGL objects underparent_. - Use
reactive::bind_*helpers for text, style, state, or event bindings. - Keep LVGL observer lifetimes object-bound whenever possible with
lv_subject_add_observer_objorreactive::observe_obj.
Fonts are loaded at runtime through FreeType. Place development fonts in:
assets/fonts/
Installed deployments can place fonts in:
/usr/share/<APP_NAME>/fonts/
You can also provide an asset root at configure time:
cmake --preset darwin-arm64 -DAPP_ASSETS_ROOT=/path/to/assetsDebian packages are produced with CPack and written to dist/. The package file name follows:
<AppName>_<Version>_m5stack1_arm64.deb
Default example:
dist/BruceCardputerZero_0.1.0_bruce1_arm64.deb
Package layout:
| Path | Content |
|---|---|
/usr/bin/bruce_cardputerzero |
Application executable. |
/usr/share/bruce_cardputerzero/ |
Runtime assets: fonts, images, audio. |
/usr/share/APPLaunch/applications/bruce_cardputerzero.desktop |
APPLaunch launcher entry. |
/usr/share/APPLaunch/share/images/template*.png |
APPLaunch launcher icons/fallbacks. |
/usr/lib/systemd/system/bruce_cardputerzero.service |
Optional systemd service for embedded autostart. |
/usr/share/doc/bruce_cardputerzero/ |
README and third-party asset license notes. |
Build and package:
cmake --preset cp0-cross
cmake --build --preset cp0-cross-rel
cpack --preset cp0-cross-debOr run the full configure, release build, and package flow with one workflow preset:
cmake --workflow --preset cp0-cross-packageNote
Use Debian based Linux host with CPack generator as other OS may not resolve the dependencies correctly no full dpkg, dpkg-shlibdeps, objdump and readelf support
If you have better framework designs on Embedded Linux or build system practice, feel free to let us know.
Project released under MIT license. More license information can be found in assets folder.


