This has been tested on the Xiaomi Smart Button.
Start sniffing session from terminal:
./Sniff.py /dev/ttyUSB0
Running ./Sniff.py -h
will give you a list of detected serial ports.
Start sniffing session from terminal:
./Sniff.py /dev/cu.usbserial
Running ./Sniff.py -h
will give you a list of detected serial ports.
Open command line, cd to the folder containing the scripts and launch:
python Sniff.py COM3
Running python Sniff.py -h
will give you a list of detected serial ports.
- Compiling the sniffer
- Interfacing to Wireshark
- Hardware
- Using the GUI to set the preferences
- Troubleshooting
- To do list
- [Gory details](# Gory-details)
You will need the NXP JN-SW-4163 SDK in order to compile the source code.
There is a precompiled binary in the bin folder. TODO: Check that it is up to date...
Follow the instructions found here: https://github.com/alephsecurity/BA2-toolchain
You can use the latest version of JennicModuleProgrammer in order to flash you JN5169 device.
You can download NXP's Beyond Studio in order to compile the source code. In eclipse, use the import from C/C++ Makefile menu. You can then use the programmer integrated in the IDE. Do not use any other programmer provided by NXP, because they do not support JN5169 (at least none that I could try out).
On Linux, you will need to be in the dialout group, in order to have enough access rights to access the serial port, as well as in the wireshark group for executing pcap. The script will prompt you to do it if it detects the issue.
sudo usermod -a -G dialout $USER
sudo usermod -a -G wireshark $USER
If you are using Windows, you will have to run the Sniffer.py script (you'll need the win32api and PySerial modules). You must pass the serial port name as the first parameter and optionally the wireshark.exe path as the second argument. Sadly, lua script parameter forwarding does not seem to work. So you might have to use the GUI in order to set your preferences. You can use an installed version of Wireshark or WiresharkPortable. You will need to run the python script as follows (from the directory where the .py and .lua scripts are and change the COM port and path as needed):
python Sniff.py COM3 C:\Users\snif\Downloads\WiresharkPortable\WiresharkPortable.exe
You must send a start command in order to initialize Wireshark and the sniffer device and subsequently get packets. Use the ZB menu. You will not see anything in Wireshark until you send the start command using the Tools/ZB/ZB Start menu command. Please make sure to select the correct channel, or you won't capture any frames. The Sniffer will send you a dummy frame to indicate the current channel every time you change it.
Note that changing the parameters in this dialog will try and send an update to the MCU.
Wiring colors are :
- Purple: Tx (OUT from MCU)
- Green : Rx (IN to MCU)
- Bleue : nBootloader
- Red : 3V3
- White : nReset
- Black : GND You can optionally connect the nReset signal to your serial port nRTS signal and your nBootloader to the nDTR output.
- Purple: Tx (OUT from MCU)
- Green : Rx (IN to MCU)
- Bleue : nBootloader
- Red : 3V3
- White : nReset
- Black : GND You can optionally connect the nReset signal to your serial port nRTS signal and your nBootloader to the nDTR output.
- For UART1 Tx, on this side of the resistor, we have the CPU pin directly.
- You can find some GND on this tantalum capacitor.
This project is based on work from @KiwiHC16 . He also helped me debug this documentation and a few version incompatibilities.
Wireshark might get confused if your MCU sends data before Wireshark has been initialised properly. To avoid this, please reset your MCU and ask it to send data with the ZB/start menu.
You can debug the communication with the following command:
stty -f /dev/cu.usbserial 38400 raw & cat /dev/cu.usbserial | tee /tmp/sf.bin > /tmp/sharkfifo
You can view the file content like this:
hexdump -C /tmp/sf.bin
The lua script creates a file named lua.log in the folder wireshark is started.
You can also pass the dissector parameters through environment varaibles:
env ZBL_CHANNEL=12 ZBL_COMPORT=/dev/ttyUSB1 wireshark -X lua_script:zb.lua -k -i /tmp/sharkfifo &
If you get stuck with remaining data in the FIFO that repeatedly crashes wireshark, you can destroy the pipe and re-create it:
rm -f /tmp/sharkfifo && mkfifo /tmp/sharkfifo
This should not be necessary any more, as the FIFOs are destroyed when leaving the Sniff.py script.
The Sniff.py script has been developped using Python3.6, so this, or a more recent, version of Python3 is recommended. It has not been extensively tested using Python2.
- Add LQI information to the packet
- Add frame duration computation as well as inter frame gap (wireshark)
Original serial queuing functions took 43125us for 26 bytes (166us/byte) and 4923 us for 31 bytes (159us/byte) with a 115200 baud/s UART and 10272us for 26 bytes (395us/byte) and 12860 for 31 bytes (415us/byte) with a 1MBaud/s UART!!! What the hell? memcpy algo takes 636 us for 26 bytes (24us/byte) and 848 for 43 bytes (20us/byte). The longer runtime/byte might come from the fact that the likeliness of having time stolen by an ISR is higher if you take more time. I might do some additional tests with disabled IT to try and get more consistent results...
At 2.4GHz, Zigbee uses a 62500 Hz symbol clock. These symbols encode 4 bits each, providing an on the air bitrate of 250Kbits/s.
Packets are timed with this clock, and have therefore a 16µs resolution.
Send ACK between macSIFSPeriod = 12 symbols >> 192µs and macAckWaitDuration = macSIFSPeriod + phySHRDuration + ceiling(7 × phySymbolsPerOctet) = 12 symbols +
bits are 250kHz (4µs), symbols are 62.5kHz (16-ary, so 4 bits/symbol) 192µs < Tack < 512µs
Inter Frame Spacing If lengthMPDU ≤ aMaxSIFSFrameSize (18 octets) then, symbolsIFS ≥ aMinSIFSPeriod = 12 symbols (192µs) else, symbolsIFS ≥ aMinLIFSPeriod = 40 symbols (640µs)
- Check IT priority, so UART does not prevent packet management at the radio level.
- Fill Tx FIFO before starting Tx ISR to limit the number of ISR.