
Reverse engineering research and custom firmware for Allwinner V3-based IoT cameras, including firmware parsers, an AVIOCTRL client, and a CVE-2026-52656 proof of concept for unauthenticated OTA firmware replacement.

Trocar para Português (Brasil)
Reverse engineering notes, firmware tooling, proof-of-concept code, and a custom firmware interface for the Allwinner V3 version of the SJCAM SJ4000 Air.
Research article
This repository contains the code and artifacts produced during the research. The full article explains the investigation from the first firmware dump to the Lelouch custom firmware.
Read the complete research article in English) or Portuguese.
This work started with a simple question: how much control could I get over a cheap action camera without documentation, source code, or vendor support?
The camera turned out to use an Allwinner V3 SoC and a heavily modified Android 4.4 system. Its main application, sdv, talks to proprietary camera and display services through Binder, renders the interface with MiniGUI, exposes an HTTP server for firmware updates, and communicates with the SJCAM Zone application through a proprietary protocol.
The repository documents the path from firmware extraction to a working replacement for sdv. It includes tools for the firmware formats encountered during the research, IDA databases for the reversed binaries, an AVIOCTRL client, the Lelouch custom firmware source, and the proof of concept associated with CVE-2026-52656.
The code targets the researched Allwinner V3 board with an ST7789V display controller and GC4653 image sensor. Older SJ4000 units based on Novatek NT9665X are included only as part of the earlier research and use a different firmware format. Product names alone are not enough to establish compatibility.
Lelouch is a replacement interface for the camera's original sdv executable. It is built against the proprietary libraries recovered from the firmware and uses the same internal interfaces as the original application.
The current implementation includes:
HerbCamera and CedarDisplay interfaceswpa_supplicantLelouch is not a complete distribution or a generic firmware for every product sold as an SJ4000. It depends on the libraries, kernel, services, partition layout, display controller, image sensor, and board configuration used by the researched Allwinner V3 unit.
| Path | Contents |
|---|---|
Lelouch/ | Custom firmware interface, build environment, recovered headers, proprietary shared libraries, and modified wpa_supplicant source |
Reversing Scripts/ | Parsers and repackers for Allwinner IMAGEWTY, Allwinner eGON, and Novatek BCL1 firmware |
AVIOCTRL Script/ウタ.py | Interactive client for the proprietary TCP protocol used by SJCAM Zone |
Bin Files/ | IDA databases for the main executables and shared libraries analyzed during the research |
Firmwares Files/ | Allwinner and Novatek firmware samples used during development |
POC/ | CVE-2026-52656 proof of concept, crafted eGON image, and the framebuffer/audio demo payload |
Some directories contain third-party or proprietary material recovered from the device. Their presence is for interoperability and security research and does not make those components open source.
The build was tested on x86-64 Linux. You need Python 3, GNU Make, Git, a C/C++ build environment, and network access for the initial toolchain download.
git clone https://github.com/keowu/sjcam.git
cd sjcam/Lelouch
python3 prepare_build.py
prepare_build.py downloads a pinned Android API level 14 sysroot and the ARM arm-linux-androideabi-4.6 cross-compiler into Lelouch/android_compilers/. It then builds the modified static wpa_supplicant and runs make. The downloaded compiler files and generated binaries are ignored by Git.
The resulting files are:
Lelouch/Lelouch
Lelouch/wpa_supplicant
To rebuild after changing the C++ sources:
cd Lelouch
make clean
make
The generated Lelouch binary is intended to replace /bin/sdv in the extracted root filesystem. Wi-Fi station mode also expects the built wpa_supplicant at /mnt/extsd/sta/wpa_supplicant on the camera's SD card.
The experimental face detection code looks for the generated cascade file at:
/mnt/extsd/frontalface.casc
The repository includes a pre-generated cascade and the converter used to create it:
cd Lelouch/gen_cascade
python3 gen_cascade.py haarcascade_frontalface_alt2.xml frontalface.casc
The complete extraction, root filesystem replacement, repacking, and LiveSuit flashing procedure is documented in the research article linked at the top of this page.
All parser entry points are Python 3 scripts with built-in --help output and no third-party Python dependencies.
Reads the outer LiveSuit firmware image, extracts its files, and rebuilds an image from an extracted directory.
python3 "Reversing Scripts/AllWinner IMAGEWTY - ROM Parser/main.py" info firmware.img
python3 "Reversing Scripts/AllWinner IMAGEWTY - ROM Parser/main.py" extract firmware.img -o image_dump
python3 "Reversing Scripts/AllWinner IMAGEWTY - ROM Parser/main.py" repack image_dump rebuilt.img
Inspects, extracts, and repacks the full_img.fex container found inside the IMAGEWTY image.
python3 "Reversing Scripts/AllWinner EGON - Firmware Parser/main.py" info full_img.fex
python3 "Reversing Scripts/AllWinner EGON - Firmware Parser/main.py" extract full_img.fex -o egon_dump
python3 "Reversing Scripts/AllWinner EGON - Firmware Parser/main.py" pack egon_dump \
--original full_img.fex \
-o rebuilt_full_img.fex
The packer uses the original file as a template so that the original layout and metadata can be preserved while partitions are replaced.
Decompresses the BCL1 container used by older Novatek-based SJ4000 firmware and can scan the result for embedded assets and firmware sections.
python3 "Reversing Scripts/Novatek もな BCL1 - Firmware Parser/main.py" \
"Firmwares Files/OLD_SJCAM_Novatek_NT9665X/SJCAM001.bin" \
-o firmCode.bin \
--extract
ウタ.py implements the proprietary TCP protocol used by the mobile application to control the camera on port 6666. It handles the camera's login sequence, keepalive messages, IOCTRL frames, file transfer, and video stream requests using only the Python standard library.
Connect your computer to the camera's Wi-Fi network and run:
python3 "AVIOCTRL Script/ウタ.py"
The default target is 192.168.100.1:6666. A different address can be supplied when required:
python3 "AVIOCTRL Script/ウタ.py" --host 192.168.100.1 --port 6666 --debug