Popular A9 mini Wi-Fi camera & the HA challenge

@Tamadite indeed, I had first set the baud rate to 115000 instead of 115200 by mistake. With the right value I can now indeed see the running logs, which are basically the same as yours.

At some point it briefly shows something resembling a shell prompt msh />, but keeps spamming with logs and I don’t think it cares for any of my input. @combe15, did you do anything specific to have the device listen to input? Is there a magic escape sequence or are there any other serial settings that might play a role here (parity/flow control or the like?)

Update: This was due to a faulty FTDI dongle. With a working dongle I can communicate both-ways nicely.

I played around a bit with the A9 mini WiFi camera using all of the great information here in this thread. So, I thought I would add what I found in case it helps anyone.

Setup:
I have the A9 (v2) mini WiFi camera. I created a WiFi hot spot on my Ubuntu machine and then connected my phone (with Little Stars app) and the camera to the WiFi hot spot. (Camera connection setup given here.) Then I used Wireshark to save off the communication between the app and the camera. Finally, I analyzed the data that was sent with a python script.

Findings:
The packets were encrypted with the weak encryption method mentioned above. The key for my cameras was: [0xB8, 0x48, 0x90, 0x00] (also given above).

Using the pppp decoder ring (from above) I was able to make some sense of the data. I only saw a couple of message formats: [‘MSG_ALIVE’, ‘MSG_PUNCH_PKT’, ‘MSG_P2P_RDY’, ‘MSG_ALIVE_ACK’, ‘MSG_DRW’, ‘MSG_DRW_ACK’, ‘MSG_CLOSE’]. Maybe there are more possible message, I wasn’t exhaustive in my testing.

Of the messages, there really only seemed to be one interesting message: MSG_DRW. This seems to be the message that is used for command and control of the camera, as well as sending back data like the video stream or the name of the video files stored on the SD card.

Looking at the MSG_DRW packet, it has a header on it that consists of a magic_number, a channel, and an index. The magic number was always 0xd1. I’m not sure what that magic number signifies. Maybe identifying that it is an A9 (v2) mini WiFi camera. For command and control type messages the channel seemed to be 0 and for the video stream the channel seemed to be 1.

I was unable to make any sense of the data in the MSG_DRW packet. The video streaming packets didn’t have the first couple of bytes being [0xff, 0xd8] like JFIF header for the unencrypted video stream. The data seemed to be random.

I didn’t know where to start on trying to decode the command and control type packets, so I didn’t do much with them.

Code:
In case this helps anyone here’s some code snippets.

I modified the encode and decode method to make a copy of the input so that it isn’t destroyed on encryption and decryption. (Also passes in the key by default.)

LITTLE_STARS_KEY = [0xB8, 0x48, 0x90, 0x00]


def encrypt(buf, key=None):
    if key is None:
        key = LITTLE_STARS_KEY

    ret = buf.copy()
    prev_byte = 0
    for i in range(0, len(ret)):
        key_idx = (key[prev_byte & 0x03] + prev_byte) & 0xFF
        ret[i] = ret[i] ^ KEY_TABLE[key_idx]
        prev_byte = ret[i]
    return ret


def decrypt(buf, key=None):
    if key is None:
        key = LITTLE_STARS_KEY

    ret = buf.copy()
    prev_byte = 0
    for i in range(0, len(ret)):
        key_idx = (key[prev_byte & 0x03] + prev_byte) & 0xFF
        prev_byte = ret[i]
        ret[i] = ret[i] ^ KEY_TABLE[key_idx]
    return ret

Here’s how I was reading in a Wireshark capture and filtering on the packets of interest:

import struct
from pyshark import FileCapture

def ip_info(packet):
    return f"[src={packet.ip.src}:{packet.udp.srcport} dst={packet.ip.dst}:{packet.udp.dstport}]"


def to_str(val):
    return val.rstrip(b'\x00').decode("utf-8")


def parse_uid(data):
    prefix, serial, check_code = struct.unpack(">8sI8s", data)
    return f"{to_str(prefix)}{serial}{to_str(check_code)}"


ips = ["x.x.x.x", "y.y.y.y"]  # Fill in with your values (or remove ip address filter)

cap = FileCapture("data.pcapng")
for packet in cap:
    if "ip" not in packet:
        continue

    if packet.ip.src not in ips or packet.ip.dst not in ips:
        continue

    if "icmp" in packet:
        continue

    raw_data = bytearray.fromhex(packet.DATA.get_field("data"))
    data = decrypt(raw_data)
    magic_num, msg_type, msg_size = struct.unpack(">BBH", data[:4])

    if magic_num != 0xf1:
        print(f"WARNING: Did not find correct magic number ({hex(magic_num)}) != 0xf1)")
        continue

    if msg_type == 0xd0:
        msg_drw_magic_num, channel, index = struct.unpack(">BBH", data[4:8])
        print(f"MSG_DRW ({msg_size}): {ip_info(packet)} -- {hex(msg_drw_magic_num)} {channel} {index} -> {hex(data[8])} {hex(data[9])} -- {hex(data[-2])} {hex(data[-1])}")

Summary:
Mostly just a restating of everything else on this thread, but hopefully there is some helpful information in there for someone. A big thanks to everyone for the great information in this thread!

1 Like

@shmic how were you able to display the encrypted packets in Wireshark? Do you have a code snippet for saving a decrypted pcap file?

Check this out: https://github.com/HoffmannP/A9
He created a python script that piped the video-stream to ffplay
use it like this: python3 a9_ffplay.py | ffplay -

import socket
import sys
s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
s.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
s.bind(('',0))
s.sendto(b'\x42\x76', ('192.168.4.153', 8080))
while True:
    (buf, rinfo) = s.recvfrom(4096)
    port = rinfo[1]
    if port == 8080:
        sys.stdout.buffer.write(buf[8:])
    else:
        sys.stderr.buffer.write(bytes(port))

Interestingly, this does not work with my camera version.
I’ve got a working solution for playing video and audio by implementing the PPPP protocol in node.js.
The code can be found here: GitHub - datenstau/A9_PPPP: HDWiFiCam Pro (DGOA WiFi Prefix) JS-Api

My camera uses the DGOA prefix and works with the HDWifiCamPro app. The PPPP packets are encrypted using the algorithm in this thread, but the posted key 0x6900cc19 is wrong. The right key is 0x6997cc19

If you want to analyze your Wireshark dumps with the PPPP dissector, you can easily modify @shmic 's script to send the decrypted packets to a different port and recapture them in wireshark. Then the packets are visible as unencrypted PPPP packets.

3 Likes

thank you. it really works (my SSID starts with DGO. marks on PCB: HB6_V0.4D211016 and it is for the HDWifiCamPro). but there are some problems.
when i tried to run it, it can’t find the speaker module and crashed (even if i use the version without sound).
after i installed it using npm install speaker it runs.
i tried the version with sound (on my mac) but it is unusable for me because it makes noise all the time and the delay is very high (out of sync to the video).
the video framerate is low. delay could be lower. picture quality is bad.
i think it is a good start. now someone needs to polish it to be even better :slight_smile:
i removed the line: const speaker = require(’./speaker’) in the run.js file and it runs as well.
I only tested it in AP Mode. I would like to use it in router mode. is this possible?

2 Likes

Hi,
I removed the line const speaker = require(’./speaker’) from run.js. This line shouldn’t have been there :wink:
How would you describe the noise you hear? My camera was creating a very strange rattling sound. This sound was gone when I removed the state between the audio packages in the ADPCM decoder. Maybe different versions behave different here, too.

With my test camera, the frame rate was also very low if the network environment was bad, even in the HDWifiCamPro app. With router mode, you mean using your own router and entering your wifi ssid and pasword into the app/camera? This worked without problems. You can even use pppp.sendCMDsetWifi(ssid, pw) to do this programmatically.

The autodetection worked in router mode, too.

The image quality is as bad as it is, since the sellers are lying in the listings. My cam has definitely no h264 1080p or 720p. I’ve analyzed the network traffic between the app and the camera in AP-Mode. The highest possible image quality is a 640x480 MJPEG stream.

So there is no way to improve the image quality.

yes a rattling sound. can you explain in detail how to fix the sound problem? i do not understand how to remove the state between the audio packages in the ADPCM decoder.
yes, i mean using my own router. I can confirm that i got it to work in this mode as well. that is awesome.
it would be a much better picture quality if it is a real 640x480 pixel resolution, but it looks really really bad. it is very blurry. like it is only 160x120 pixels upscaled to 640x480 pixel. maybe the firmware code of the camera is bad or maybe the sensor used in this cam is garbage?

In the ADPCM implementation I’ve found on GitHub, the values of valpred and index were cached after every decode call, so that the next packet of audio samples can be decoded with continuous values. But there was a strange rattling sound every 80 ms. After trial and error, I’ve moved the initialization of stateIndex and stateValpred into the decode function and the rattling sound was gone.

So you can try to move line 17 and 18 in adpcm.js out of the decode function, so that these values are stored globally.

i tried it as you suggested, but the result was not better. It changed but was even worse than before so i restored it to the original state. the delay in sound output increases more and more as the script is running. it needs to be faster.

I have two cameras of the D9 model that uses 365Cam app on the mobile.
They seem to be a bit different from the once described above. Anyone know if these cams can be used without the mobile app.
Tried to log and it seems like it connects alot to an external site (alot of ipaddresses, this servers a23-62-180-[different numbers here]deply.static.akamaitechnologies.com and last an amazonaws.com server)

image
image

please note that those of you who has ports 8080 and 8070 open, it means your camera is in demo mode. it will use an open wifi network that anyone in range can connect to, and then with simple udp packet it will stream video and voice unencrypted in the network. luckily, there is another open port 8090, which can be used to configure a new ssid that the camera will connect to. i’ve managed to successfully connect my camera this way. after the camera boots to the new network, it will no longer be in demo mode and these ports will not be open anymore. instead, the above mentioned PPPP is used, and as it seems, there are numerous solutions already in this thread to communicate with the camera. might as well pack it into a complete project ready to be made as a plugin or integration for HA.

i will upload the code to communicate with 8090 to configure ssid later today

1 Like

here’s the code to configure the A9 camera to connect to your wifi and make it leave demo mode.
note - demo mode has static ip address for all cameras. see below.


import socket
import struct

CMD_PORT = 8090
VIDEO_PORT = 8080
VOICE_PORT = 8070

DEMO_IP = '192.168.4.153'

def configure_wifi(ssid, pwd):
    assert len(ssid) < 0x20
    assert len(pwd) < 0x20
    data = b"\x66"
    data += ssid.encode()
    data += b"###"
    data += pwd.encode()
    data += b"\x00"
    data += b"\x99"
    sock = send_cmd(data)

    resp1 = sock.recvfrom(1024)
    resp2 = sock.recvfrom(1024)
    resp3 = sock.recvfrom(1024)

    assert resp1 == resp2 == resp3
    return True

def send_cmd(data):
    sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
    sock.sendto(data, (DEMO_IP, CMD_PORT))
    return sock
1 Like

Anyone from China or Chinese knowledge that can download the following package?
https://pan.baidu.com/s/1mgfsoUbUpUKBQdjX8qQMdA
Extraction Code: bbi5

I’ve tried my best using google translate, but I get stuck when I should download some proprietary baidu client software. I don’t want to download shady chinese spyware.
Has anyone succeeded downloading the actual files and can reupload them to a more trustworthy place?

Hi, Hope this can help : shareid-556237598 - Google Drive

Thank you so much!
It seems the package is the same one I could download from somewhere else.
Let’s see if manage to compile and create the bin file and get something useful out of it. Unfortunately I am stuck in some compilation errors.

https://blog.csdn.net/Mculover666/article/details/105615591

lo del archivo es una programadora que utiliza el ci bk7252

1 Like

My cam looks exact the same, I just grab the rtsp link… You just need to Wireshark the token/secret…

They offer indeed an android app, but then the string goes to cloud… They also offer a windows camera software tool, that grabs the link local and exposes the token… It’s static as long you don’t factory reset the device… So I just use the local rtsp

@pergola.fabio in fact those cams where in the market since 2019 but there are many varient with the same body shell and different soc. The old one that i found on line works with HDminicam apk and pctools on windows. But the newer model use another apk named littlestars (i think) and there is no windows app from my research if you have one feel free to share it (because all link for the first windows app are dead or blocked by my isp)