Hey! So i did some digging, and got some results about the new app, and this information is quite valuable to the person making an update to
https://github.com/prsyahmi/v380
So, that old repo would not work anymore as the app and encryption keys and all changed.
Firstly some prior information, I have a 3 lens PTZ camera from maizic, and I also have a 2 lens ptz camera from Dr Vision. Though the 2 lens worked perfectly on Onvif, the 3 lens one did not do so. I did the sd card trick which got no results, tried to mess with the file a lot but that did not do anything.
I knew that the camera is broadcasting the stream on local host ip, and the app is fetching that and showing me the stream, so I decided to find open ports on this. None of the ports worked, except 8800 and 9800.
On some more messing around, tryna fetch the packets which were sent and received by the app, I used PCAPdroid to save the info and sent it to claude which accessed it along with the entire situation. here is a claude generated summary of everything, it would explain better than I can. Here is the entire situation summary and what all I did (Keep in mind its ai generated, but its just a summary of what all I did and told it)
Notes: trying to get RTSP/ONVIF (or any local stream) out of a newer 3-lens V380 (Xiongmai-based) camera
Sharing a full breakdown of what I found trying to pull a local video feed from a newer 3-lens V380 PTZ camera (sold under a rebrand, but it's a generic Xiongmai board running the V380 Pro app). Goal was to add it to an NVR/XVR over ONVIF. Posting everything so the next person doesn't start from zero. TL;DR: the local video is AES-encrypted and the key is inside a Qihoo-360-Jiagu-packed app, so it can't be pulled with network captures or static tools alone. But the protocol framing that causes the -11 error is now understood — details below.
The camera
- App: V380 Pro (
com.macrovideo.v380pro).
- Chip vendor: Xiongmai (the "Equipment Model" string in the app started with
HsXM... — the XM = Xiongmai).
- It is cloud/P2P only. On a clean boot it works perfectly in the app but exposes almost nothing on the LAN.
Step 1 — Port scan (do this first)
Full scan (nmap -p- <cam_ip>) on a normal boot showed only:
8800/tcp open
9800/tcp open
Nothing else. No 554 (RTSP), no 8899 (ONVIF), no 23 (telnet), no 80 (web), no 34567 (Xiongmai Sofia/DVRIP), no 9527/9530 (Xiongmai debug shells).
Warning: if you scan and see a block of email ports "open" (25, 110, 143, 465, 587, 993, 995, etc.), that is NOT the camera — it's your ISP/router/AV intercepting standard mail ports. Prove it by scanning an empty IP with nmap -Pn -p 25,110,143,993,8800,9800 <unused_ip>: the email ports show "open" even there, but 8800/9800 show filtered. Only 8800/9800 are the real camera.
Step 2 — Things that DON'T work on this generation
- ceshi.ini SD card trick (
[CONST_PARAM] rtsp=1 etc.): the camera reads it and announces test mode in Chinese ("ceshi" = test), reports WiFi OK, but opens no new ports. Adding onvif=1, telnet=1, ssh=1, web=1 changed nothing. The firmware accepts the file but doesn't contain the services to switch on. NOTE: test mode makes the camera drop off the network — any scan done while the ceshi card is inserted is invalid. Delete the file, reboot normally, then scan.
- Telnet / Sofia / web / 9527 / 9530: all closed. So
python-dvr, CMS tools, telnet shells — none have a port to connect to.
- OpenIPC / custom firmware over the network: impossible, because there's no open port to push it through. Would require UART/flash-chip hardware access.
Step 3 — Port 8800 with prsyahmi/v380 (this partly works)
The tool github.com/prsyahmi/v380 connects on 8800. Build the C++ version (Makefile is inside the v380/ subfolder). --discover correctly returns the camera's ID, IP, and MAC. So discovery and the initial auth work.
But streaming fails with:
Login response: unsupported -11, continuing
Unknown 0x9c command
Stream stopped, restarting stream
Notes on this:
- There are two login stages in the code: a first auth (
command 1167, returns codes 1001/1011/1012/1018) and a stream login (command 301). The -11 happens at the stream login, not the first auth.
- Someone in an earlier thread found setting the login field
unknown2 from 2 to 31 helped on their firmware. On this newer 3-lens firmware, changing unknown2 to 31 made the FIRST auth fail with code 1011 instead — so leave unknown2 = 2 for this generation. It's not the fix here.
- The tool's stream parser only knows packet types
0x7f, 0x00, 0x01, 0x16, 0x1f, 0x6f. The camera sends 0x9c, which didn't exist in the old firmware, so the tool bails.
I patched the tool's default: case to hex-dump the unknown packet. The 0x9c "packet" turned out to be a rejection carrying the -11 code (9c ff ff ff f5 ff ff ff ... = signed -1 / -11 little-endian) followed by zero padding, then the connection closes. So the camera is refusing to start the stream because the stream-login handshake format is newer than the tool sends.
Step 4 — Capturing the app's real handshake (the useful part)
Captured the V380 Pro app talking to the camera on the LAN using PCAPdroid (Android, no root, app-filter set to V380 Pro), then analysed the .pcap.
The main video connection (phone → camera:8800) received ~700 KB in ~30 s, so video does flow locally over 8800. The key discovery is the framing. Every packet in the new protocol is wrapped in a 30-byte header:
00 00 01 07 20 21 00 00 28 4a "V380 Pro"(ASCII, 8 bytes) 00-padding <4 varying bytes>
i.e. magic 00 00 01 07 20 21 00 00 28 4a, then the literal ASCII string V380 Pro, then zero padding, then a per-packet value. The old prsyahmi tool sends none of this — that mismatch is what produces -11. So to get past -11, the tool would need to be rewritten to speak this V380 Pro-framed protocol (magic + identifier header) for the 301 stream login.
Step 5 — Is the video encrypted? (yes)
Reassembled the camera→phone payload and measured entropy: ~7.92 bits/byte (8.0 = random/encrypted). No consistent H.264 NAL structure (the few 00 00 00 01 start codes are coincidental, not a real SPS/PPS/IDR/P sequence). Conclusion: the media payload is encrypted, not just reframed.
From the app's native lib libaes.so I found the export:
Java_com_macrovideo_sdk_tools_AESUtils_aes128_ecb_encrypt
So the crypto is AES-128-ECB via com.macrovideo.sdk.tools.AESUtils — same class family the 2020 write-ups documented. I tested the old publicly-known keys (macrovideo+*#!^@, 8pV39QG114F230qW) against the payload in AES-128-ECB: entropy went UP to 8.0, meaning wrong keys. The old keys are dead on this firmware.
Step 6 — Why you can't just read the key out of the APK
Decompiled the real V380 Pro APK. jadx only recovered ~6 classes: com.stub.StubApp, com.tianyu.util.DtcLoader, Configuration. The real 35 MB classes.dex is a stub, and the assets contain libjiagu.so + libjiagu_a64.so and two encrypted .dat blobs.
That's Qihoo 360 Jiagu, a commercial packer. The actual app code (including AESUtils and the key/derivation) is encrypted on disk and only decrypted in memory at runtime, with anti-debug / anti-Frida protection. So static analysis cannot reach the key. grep/strings/jadx will all come up empty for the stream key — this is expected, not a mistake.
Where the next person should start
The remaining path to the key is runtime unpacking, not static or network work:
- Rooted Android phone or emulator.
- Use a memory-dex dumper (e.g. BlackDex or FRIDA-DEXDump) to dump the decrypted
classes.dex from the running V380 Pro process, fighting Jiagu's anti-hook defenses.
- jadx the recovered dex, read
com.macrovideo.sdk.tools.AESUtils and its callers to find the AES-128-ECB key and how it's derived (may be per-device, from the device ID/password, or a new static key).
- Once the key + the
V380 Pro frame format are known, you can decrypt the 8800 stream, strip the 30-byte headers, and get H.264 out — then restream it as RTSP for an NVR.
Alternatively, if you just want the camera on an NVR and don't care about this specific unit: use a camera that supports ONVIF out of the box, or one on an OpenIPC-supported chip that you can reflash via UART/SPI.
Summary of what's confirmed
- Newer 3-lens V380 (Xiongmai) = cloud/P2P only, ports 8800/9800 only, encrypted.
- ceshi.ini does nothing on this generation; no telnet/Sofia/web/ONVIF/RTSP.
- Local video does stream on 8800, wrapped in a 30-byte
00 00 01 07 20 21 00 00 28 4a "V380 Pro" header.
-11 / 0x9c error = the old tool doesn't speak the new V380 Pro-framed handshake.
- Media is AES-128-ECB (
AESUtils); old public keys don't work; current key is inside a 360 Jiagu-packed, anti-Frida app → needs runtime dex dumping to recover.
Hope this saves someone a few days. If anyone gets past the Jiagu unpacking and finds the current key/derivation, please post it that's the last missing piece.
I hope this helps, im not a master in this, but ig adding more information here is never bad, I have posted the same thing at https://gist.github.com/SolveSoul/9be5d9599c8b4b59f7cfa4cd0ce79c9c
Hey! So i did some digging, and got some results about the new app, and this information is quite valuable to the person making an update to
https://github.com/prsyahmi/v380
So, that old repo would not work anymore as the app and encryption keys and all changed.
Firstly some prior information, I have a 3 lens PTZ camera from maizic, and I also have a 2 lens ptz camera from Dr Vision. Though the 2 lens worked perfectly on Onvif, the 3 lens one did not do so. I did the sd card trick which got no results, tried to mess with the file a lot but that did not do anything.
I knew that the camera is broadcasting the stream on local host ip, and the app is fetching that and showing me the stream, so I decided to find open ports on this. None of the ports worked, except 8800 and 9800.
On some more messing around, tryna fetch the packets which were sent and received by the app, I used PCAPdroid to save the info and sent it to claude which accessed it along with the entire situation. here is a claude generated summary of everything, it would explain better than I can. Here is the entire situation summary and what all I did (Keep in mind its ai generated, but its just a summary of what all I did and told it)
Notes: trying to get RTSP/ONVIF (or any local stream) out of a newer 3-lens V380 (Xiongmai-based) camera
Sharing a full breakdown of what I found trying to pull a local video feed from a newer 3-lens V380 PTZ camera (sold under a rebrand, but it's a generic Xiongmai board running the V380 Pro app). Goal was to add it to an NVR/XVR over ONVIF. Posting everything so the next person doesn't start from zero. TL;DR: the local video is AES-encrypted and the key is inside a Qihoo-360-Jiagu-packed app, so it can't be pulled with network captures or static tools alone. But the protocol framing that causes the
-11error is now understood — details below.The camera
com.macrovideo.v380pro).HsXM...— the XM = Xiongmai).Step 1 — Port scan (do this first)
Full scan (
nmap -p- <cam_ip>) on a normal boot showed only:Nothing else. No 554 (RTSP), no 8899 (ONVIF), no 23 (telnet), no 80 (web), no 34567 (Xiongmai Sofia/DVRIP), no 9527/9530 (Xiongmai debug shells).
Step 2 — Things that DON'T work on this generation
[CONST_PARAM] rtsp=1etc.): the camera reads it and announces test mode in Chinese ("ceshi" = test), reports WiFi OK, but opens no new ports. Addingonvif=1,telnet=1,ssh=1,web=1changed nothing. The firmware accepts the file but doesn't contain the services to switch on. NOTE: test mode makes the camera drop off the network — any scan done while the ceshi card is inserted is invalid. Delete the file, reboot normally, then scan.python-dvr, CMS tools, telnet shells — none have a port to connect to.Step 3 — Port 8800 with prsyahmi/v380 (this partly works)
The tool
github.com/prsyahmi/v380connects on 8800. Build the C++ version (Makefile is inside thev380/subfolder).--discovercorrectly returns the camera's ID, IP, and MAC. So discovery and the initial auth work.But streaming fails with:
Notes on this:
command 1167, returns codes 1001/1011/1012/1018) and a stream login (command 301). The-11happens at the stream login, not the first auth.unknown2from2to31helped on their firmware. On this newer 3-lens firmware, changingunknown2to31made the FIRST auth fail with code 1011 instead — so leaveunknown2 = 2for this generation. It's not the fix here.0x7f,0x00,0x01,0x16,0x1f,0x6f. The camera sends0x9c, which didn't exist in the old firmware, so the tool bails.I patched the tool's
default:case to hex-dump the unknown packet. The0x9c"packet" turned out to be a rejection carrying the-11code (9c ff ff ff f5 ff ff ff ...= signed -1 / -11 little-endian) followed by zero padding, then the connection closes. So the camera is refusing to start the stream because the stream-login handshake format is newer than the tool sends.Step 4 — Capturing the app's real handshake (the useful part)
Captured the V380 Pro app talking to the camera on the LAN using PCAPdroid (Android, no root, app-filter set to V380 Pro), then analysed the
.pcap.The main video connection (phone → camera:8800) received ~700 KB in ~30 s, so video does flow locally over 8800. The key discovery is the framing. Every packet in the new protocol is wrapped in a 30-byte header:
i.e. magic
00 00 01 07 20 21 00 00 28 4a, then the literal ASCII stringV380 Pro, then zero padding, then a per-packet value. The old prsyahmi tool sends none of this — that mismatch is what produces-11. So to get past-11, the tool would need to be rewritten to speak thisV380 Pro-framed protocol (magic + identifier header) for the301stream login.Step 5 — Is the video encrypted? (yes)
Reassembled the camera→phone payload and measured entropy: ~7.92 bits/byte (8.0 = random/encrypted). No consistent H.264 NAL structure (the few
00 00 00 01start codes are coincidental, not a real SPS/PPS/IDR/P sequence). Conclusion: the media payload is encrypted, not just reframed.From the app's native lib
libaes.soI found the export:So the crypto is AES-128-ECB via
com.macrovideo.sdk.tools.AESUtils— same class family the 2020 write-ups documented. I tested the old publicly-known keys (macrovideo+*#!^@,8pV39QG114F230qW) against the payload in AES-128-ECB: entropy went UP to 8.0, meaning wrong keys. The old keys are dead on this firmware.Step 6 — Why you can't just read the key out of the APK
Decompiled the real V380 Pro APK. jadx only recovered ~6 classes:
com.stub.StubApp,com.tianyu.util.DtcLoader,Configuration. The real 35 MBclasses.dexis a stub, and the assets containlibjiagu.so+libjiagu_a64.soand two encrypted.datblobs.That's Qihoo 360 Jiagu, a commercial packer. The actual app code (including
AESUtilsand the key/derivation) is encrypted on disk and only decrypted in memory at runtime, with anti-debug / anti-Frida protection. So static analysis cannot reach the key. grep/strings/jadx will all come up empty for the stream key — this is expected, not a mistake.Where the next person should start
The remaining path to the key is runtime unpacking, not static or network work:
classes.dexfrom the running V380 Pro process, fighting Jiagu's anti-hook defenses.com.macrovideo.sdk.tools.AESUtilsand its callers to find the AES-128-ECB key and how it's derived (may be per-device, from the device ID/password, or a new static key).V380 Proframe format are known, you can decrypt the 8800 stream, strip the 30-byte headers, and get H.264 out — then restream it as RTSP for an NVR.Alternatively, if you just want the camera on an NVR and don't care about this specific unit: use a camera that supports ONVIF out of the box, or one on an OpenIPC-supported chip that you can reflash via UART/SPI.
Summary of what's confirmed
00 00 01 07 20 21 00 00 28 4a "V380 Pro"header.-11 / 0x9cerror = the old tool doesn't speak the newV380 Pro-framed handshake.AESUtils); old public keys don't work; current key is inside a 360 Jiagu-packed, anti-Frida app → needs runtime dex dumping to recover.Hope this saves someone a few days. If anyone gets past the Jiagu unpacking and finds the current key/derivation, please post it that's the last missing piece.
I hope this helps, im not a master in this, but ig adding more information here is never bad, I have posted the same thing at https://gist.github.com/SolveSoul/9be5d9599c8b4b59f7cfa4cd0ce79c9c