====== Insta360: WiFi protocol reverse engineering ====== Get the open source software from the **[[https://github.com/RigacciOrg/insta360-wifi-api/|RigacciOrg/insta360-wifi-api]]** GitHub repository. I purchased an **[[insta360_one_rs|Insta360 ONE RS]]** action camera in June 2023, I'm rather satisfied by its performances, but I'm really disappointed by the accompanying Android app. The app (version 1.40.1) is a monster download of **676 Mb**, once installed it requires more than 1 Gb of storage. It is rather invasive about permissions request because it wants access to the camera (the phone's camera!), contacts, microphone and phone. Once started it is totally **social oriented**, presenting me **blatantly useless info** about what other people does with their cameras. **What I need is a simple remote control** for my action camera, not another invasive and useless social network. Beside that, the app doesn't work on my phone; maybe because the hardware specs are not at the cutting edge of techonolgy (but neverthless my phone has 4 Gb of RAM and a 4 cores MediaTek Helio A22 SoC), whenever I tap the icon to start the live view the app crashes. So the basic function of remote controlo does not work. The app is required not only to "activate" the camera for the first use, it is also necessary to do very basic and daily operations, like setting the date and time into the camera. For all that reasonos **I definitely need some custom software** to control my camera and get rid of that crappy "official" app. ===== Connecting to the WiFi ===== Fortunately enough it is possibile to connect a GNU/Linux PC to the Insta360 through the WiFi, the default passowrd of the camera internal access point is **88888888**. Said incidentally, this is an **huge security hole** of the camera: as far I know it is not possibile to disable the WiFi interface or change the password (at least from the camera touch screen interface), so any host in the nearby can connect to your camera as soon it is turned on; once estabilished the connection you can also do a **telnet** into the Insta360's GNU/Linux operating system as **root** (the IP address of the camera is **192.168.42.1**) and do whaterver you want, even to damage permanently (brick) the camera. ===== Capturing the WiFi traffic ===== I was hoping the control channel was the 7878/TCP port, like into the [[sjcam-8pro-ambarella-wifi-api|SJCAM SJ8 Pro]] camera which is equipped with the same Ambarella H22 chip, but I was not able to estabilish a connection (cannot succeed at the AMBA_START_SESSION command). So I installed the Insta360 app into a **rooted** Android smartphone. On the same phone I'm running the **Termux** app where I installed the **tcpdump** package. So I discovered that the app talks to the Insta360 camera through the port **6666/TCP**, where a server is expecting and returning messages. The messages are not in clear text: it turned out that they are serialized using the **[[https://protobuf.dev/|Protocol Buffers]]** mechanism (thanks to this **[[https://hackaday.io/project/188975-insta360-x3-ble-remote-control-with-esp32|Hackaday.io post]]** which gave me the enlightening hint). ===== Running tcpdump into the Insta360 ===== If your Android device is not rooted, you can still capture the WiFi traffic between the Android app and the Insta360 camera by just executing **tpcudmp** directly on the GNU/Linux operating system of the camera. You have to download and install the required binaries, which are fortunately provided by the **[[http://entware.net/|Entware Projec]]**. Download from **[[http://bin.entware.net/|entware.net]]** the following packages (choose **aarch64** architecture, kernel **3.10**): * libc_2.27-11_aarch64-3.10.ipk * libgcc_8.4.0-11_aarch64-3.10.ipk * libpcap_1.10.4-1_aarch64-3.10.ipk * librt_2.27-11_aarch64-3.10.ipk * libssp_8.4.0-11_aarch64-3.10.ipk * libthread-db_2.27-11_aarch64-3.10.ipk * tcpdump_4.99.4-1_aarch64-3.10.ipk Create a directory into the SD card **/tmp/SD0/opt/** and unpack the above archives, keeping the directory structure and dereferencing links into plain files (the SD filesystem does not support symbolic links). Suppose that the Android device running the Insta360 app has IP address 192.168.42.2, run the following script at the command line of the camera: #!/bin/sh test -f /opt/bin/tcpdump || mount -o bind /tmp/SD0/opt /opt /opt/bin/tcpdump -w /tmp/SD0/tcpdump-$(date +%m%d%H%M%S).log -s0 -n 'host 192.168.42.2' ===== Packets anatomy ===== ==== Sent Packets ==== === Sync Packet === ^ Offset ^ Content ^ Bytes ^ Note ^ | 0 | Packet Length | 4 | Overall length of the packet, including this 4 bytes. | | 4 | 0x06 0x00 0x00 | 3 | Message Type: Sync Packet. | | 7 | syNceNdinS | 10 | Magic String. | === Keep Alive Packet === ^ Offset ^ Content ^ Bytes ^ Note ^ | 0 | Packet Length | 4 | Overall length of the packet, including this 4 bytes. | | 4 | 0x05 0x00 0x00 | 3 | Message Type: Keep Alive. | === Phone Commands === ^ Offset ^ Content ^ Bytes ^ Note ^ | 0 | Packet Length | 4 | Overall length of the packet, including this 4 bytes. | | 4 | 0x04 0x00 0x00 | 3 | Message Type: Phone Command. | | 7 | Message Code | 2 | Examples: PHONE_COMMAND_GET_OPTIONS, PHONE_COMMAND_SET_OPTIONS, PHONE_COMMAND_TAKE_PICTURE, PHONE_COMMAND_START_CAPTURE, etc. | | 9 | 0x02 | 1 | | 10 | Sequence Number | 3 | Each command sent to the camera have its increasing sequence number, the relative response contains the same sequence number. | | 13 | 0x80 0x00 0x00 | 3 | | 16 | Protobuf Message | Variable | The message serialized using Protocol Buffers. | ==== Received Packets ==== === Notifications or Response to Phone Commands === ^ Offset ^ Content ^ Bytes ^ Note ^ | 0 | Packet Length | 4 | Overall length of the packet, including this 4 bytes. | | 4 | 0x04 0x00 0x00 | 3 | Response Type: Phone Command. | | 7 | Response Code | 2 | Examples: 200: OK, 500: ERROR, CAMERA_NOTIFICATION_CURRENT_CAPTURE_STATUS, etc. | | 9 | 0x02 | 1 | | 10 | Sequence Number | 3 | Matches to the requesting message. | | 13 | 0x80 | 1 | | 14 | Unknown | 2 | | | 16 | Protobuf Message | Variable | The response message serialized using Protocol Buffers. | ===== Inspecting the raw Protobuf messages ===== Unfortunately the [[wp>Protocol Buffers]] is not self-describing; that is, there is no way to tell the names, meaning, or full datatypes of exchanged messages without an external specification. You can however use some freely available programs to inspect the **binary data** of a protobuf message, e.g. the **[[https://pypi.org/project/bbpb/|BlackBox Protobuf Library]]** for the Python language can decode and re-encode protobuf messages without access to the source protobuf descriptor file. Firstly install the bbpb Python library (using the ''pip'' system will take care of dependencies, e.g. installing also the Google protobuf Python library): pip3 install bbpb Then **extract some protobuf binary messages from the tcpdump output**; in the following example we try to decode a binary message received from the camera by the Android app. Each message is prefixed by a 12 bytes header (see packets anatomy, above), so in the Python code strip that header away and keep only the message body before calling the ''protobuf_to_json()'' function: #!/usr/bin/env python3 import blackboxprotobuf packet_hex = '040000c80002000000800000080f0824083012467a0e495242454e323230364d5441424ea202220a114f4e4520525320364d5441424e2e4f534312083838383838383838189501200082030e496e737461333630204f6e655253' packet = bytearray.fromhex(packet_hex) message, typedef = blackboxprotobuf.protobuf_to_json(packet[12:]) print(message) The output will reveal the structure of the message: { "1": [ 15, 36, 48 ], "2": { "15": "IRBEN2206MTABN", "36": { "1": "ONE RS 6MTABN.OSC", "2": { "7": [ 56, 56, 56, 56 ] }, "3": 149, "4": 0 }, "48": "Insta360 OneRS" } } As you can see **dictionary indices** and the **elements** themselves are integers whose meaning is unknown. In some cases the strings are correctly decoded, (for example element **48** is the model of the camera), in other cases they are not (element **7** would be a string with the WiFi password, but it has been interpreted by mistake as a list of integers). If you have access to **the .proto files which define the messages syntax**, you can compile some ad-hoc Python modules and write a code that will produce a much more understandable result like this: option_types: SERIAL_NUMBER option_types: WIFI_INFO option_types: CAMERA_TYPE value { serial_number: "IRBEN2206MTABN" wifi_info { ssid: "ONE RS 6MTABN.OSC" password: "88888888" channel: 149 } camera_type: "Insta360 OneRS" } Let's see in the following paragraphs how to obtain the .proto files and compile them for the Python language. ===== Getting the .proto definitions ===== :!: **WARNING**: In this paragraph you can read about my first attempt in getting the *.proto files from a demo executable. It turned out that **the files were not complete and not fully up-to-date**. It is advised to use the current Android app to get a more recent version of the *.proto files. See the **[[#getting_the_proto_files_from_the_android_apk|next paragraph for the full recipe]]**. To understand the messages structure of the messages exchanged betwwen the software and the camera **it is necessary to have the .proto files** that define the syntax, but how can you do it without having access to the non-free source codes of Insta360? Browsing the **[[https://github.com/Insta360Develop|Insta360Develop]]** GitHub repository you can find some documentation about the **SDK** for the Android, iOS and C++ environment, but the SDK itself is not available. It seems that you can ask to access the [[https://www.insta360.com/sdk/home|Insta360 SDK]], but the End User License agreement forbid to create software that mix the SKD with Open Source software, so I cannot apply. On the GitHub repository there is an example demo of the [[https://github.com/Insta360Develop/CameraSDK-Cpp|CameraSDK-Cpp]], where you can download a compiled executable **[[https://github.com/Insta360Develop/CameraSDK-Cpp/raw/master/demo/build/CameraSDKTest.exe|CameraSDKTest.exe]]**; that executable almost certainly includes the protobuf definitions that we need. Thankfully there are several programs that can extract .proto definitions from an executable, the one that worked for me is **[[https://github.com/marin-m/pbtk|pbtk]]**, a compilation of reverse engineering Protobuf apps. Once you have installed the Google protobuf Python library (due the dependencies seen above or e.g. executing ''pip3 install protobuf''), you can simply copy into your working directory the **utils** subdirectory of the **[[https://github.com/marin-m/pbtk|pbtk]]** repository and the script **from_binary.py** (found into the ''extractor'' subdirectory of the same repository) and then execute: ./from_binary.py CameraSDKTest.exe The program analyzes the executable and extracts **21 files with the .proto extension**: 10 are into the current directory and the others 11 into a subdirectory called ''commands''. It is necessary to use the **protoc** program (the protobuf compiler, installable e.g. with the **protobuf-compiler** Debian package) to create the Python source code files which will handle that specific protobuf messages. Place the **%%*.proto%%** files into a folder e.g. called **proto**, create the output directory e.g. called **pb2** and then compile them all: protoc --proto_path='proto/' --python_out='pb2/' proto/*.proto protoc --proto_path='proto/' --python_out='pb2/' proto/commands/*.proto Here are the files created into the destination directory: pb2/ ├── commands │   ├── delete_files_pb2.py │   ├── get_current_capture_status_pb2.py │   ├── get_file_list_pb2.py │   ├── get_options_pb2.py │   ├── get_photography_options_pb2.py │   ├── set_photography_options_pb2.py │   ├── start_capture_pb2.py │   ├── start_live_stream_pb2.py │   ├── stop_capture_pb2.py │   ├── stop_live_stream_pb2.py │   └── take_picture_pb2.py ├── battery_pb2.py ├── button_press_pb2.py ├── capture_state_pb2.py ├── extra_info_pb2.py ├── media_pb2.py ├── options_pb2.py ├── photography_options_pb2.py ├── photo_pb2.py ├── storage_pb2.py └── video_pb2.py When you move the **pb2** directory into the folder of your Python program, you can finally use the generated Python modules to parse the protobuf messages: #!/usr/bin/env python3 import sys sys.path.append('pb2') sys.path.append('pb2/commands') import get_options_pb2 packet_hex = '040000c80002000000800000080f0824083012467a0e495242454e323230364d5441424ea202220a114f4e4520525320364d5441424e2e4f534312083838383838383838189501200082030e496e737461333630204f6e655253' packet = bytearray.fromhex(packet_hex) message = get_options_pb2.GetOptionsResp() message.ParseFromString(packet[12:]) print(message) ===== Getting the .proto files from the Android APK ===== Download the .proto file extractor tool from **[[https://github.com/RigacciOrg/insta360-wifi-api/tree/main/utils|this repository]]**. Download the Android app and extract the **libOne.so** file from it. Then run the following recipe: #!/bin/sh -e # Extract the Insta360 Proto Buffer files (*.proto) from the library # libOne.so contained into the Android app. # # The protobuf definition files are required to talk to Insta360 # cameras over the WiFi API. # # Requirements: google.protobuf Python library and protoc compiler. # # The from_binary.py script and the utils/* modules were downloaded # from https://github.com/marin-m/pbtk if [ ! -f "libOne.so" ]; then echo "ERROR: File libOne.so not found." echo "Download the Android app from https://www.insta360.com/it/download" echo "The file is inside the apk (zip) file: lib/arm64-v8a/libOne.so" exit 1 fi # Extract the .proto source files from the compiled executables. # The from_binary.py Python script requires the google.protobuf Python library. test -d proto || mkdir proto cd proto ../from_binary.py ../libOne.so cd .. echo "Proto files were extracted into the proto directory." # Compile the *.proto source files into Python classes. # Requires the protoc compiler from the protobuf-compiler Debian package. test -d pb2 || mkdir pb2 protoc --proto_path='proto/' --python_out='pb2/' proto/*.proto echo "Python files were compiled into the pb2 directory." ===== The Insta360 Python remote program ===== On the **[[https://github.com/RigacciOrg/insta360-wifi-api/|insta360-wifi-api GitHub repository]]** you can find a Python library which implements basic communication with the Insta360 camera over WiFi connection. There is also an example Python program with the basic functionality of **remote control**. You can easily run it from a PC with GNU/Linux, but you can also install the required Python language and libraries in MS-Windows and even on Android. I run that program on my smartphone, it just required to install the **[[https://f-droid.org/en/packages/com.termux/|Termux]]** app. ===== Unsolved Problems ===== It seems **impossibile to change some settings via the WiFi API**; e.g. I was not able to change: * Sharpness * Prompt Sound * Indicator Light (LEDs) When some settings are changed via the WiFi API, **the preview on the camera screen does not reflect that change**; nor in the live stream, nor into the on-screen-display labels. E.g. white balance, capture resolution, fielf of view. Fortunately if you start the video capture, the settings are effective. ===== White Balance Settings ===== It is possible to change the white balance setting by changing the value of **white_balance** choosing from some enumerated presets or directly by changing the temperature value of **white_balance_value**. There seems to be some inconsistency between the labels assigned to the presets in the .proto files and the actual temperature values. I think that the best choice is to assign the white_balance_value, ignoring the enumerated presets. ^ white_balance_value ^ white_balance ^ Protobuf Enum Label ^ | AUTO | 0 | WB_AUTO | | 2000 | 6 | | | 2200 | 7 | | | 2400 | 8 | | | 2600 | 9 | | | 2800 | 1 | WB_2700K | | 3000 | 10 | | | 3200 | 11 | | | 3400 | 12 | | | 3600 | 13 | | | 3800 | 14 | | | 4000 | 2 | WB_4000K | | 4500 | 15 | | | 5000 | 5 | WB_7500K | | 5500 | 16 | | | 6000 | 17 | | | 6500 | 3 | WB_5000K | | 7000 | 18 | | | 7500 | 4 | WB_6500K | | 8000 | 19 | | | 8500 | 20 | | | 9000 | 21 | | | 9500 | 22 | | | 10000 | 23 | | ===== Web References ===== * **[[https://www.reddit.com/r/Insta360/comments/wwra18/psa_reminder_wifi_password_still_cant_be_changed/|PSA Reminder: WiFi password still can't be changed.]]** * **[[https://www.reddit.com/r/Insta360/comments/scsue6/really_cool_insta360_one_x2_hidden_feature/|Really cool Insta360 One X2 hidden feature!]]** * **[[https://hackaday.io/project/188975-insta360-x3-ble-remote-control-with-esp32|Insta360 X3 BLE remote control with ESP32]]** Control the Insta360 X3 camera with a Bluetooth Low Energy DIY remote. * **[[https://github.com/Insta360Develop|Insta360Develop]]** Insta360 GitHub for Developers. * **[[https://github.com/nccgroup/blackboxprotobuf|Blackbox Protobuf]]** Tools for working with encoded Protocol Buffers (protobuf) without the matching protobuf definition. * **[[https://protobuf.dev/|Protocol Buffers Documentation]]** * **[[https://pypi.org/project/protobuf/|protobuf]]** Python library repository. * **[[https://github.com/marin-m/pbtk|pbtk - Reverse engineering Protobuf apps]]** Extract .proto files from binaries.