Reverse engineer the 360Eyes Pro (IPC365) wireless CCTV camera

5.00 avg. rating (94% score) - 1 vote

Available on AliExpress, the 360Eyes Pro (also known as IPC365, from the default network host name of this camera) is a cheap wireless CCTV camera supporting night vision and RTSP. I bought mine for around 18 USD, shipping included:

360eyes_pro_ipc365

Despite looking very similar to the V380 Pro which has 10 IR LEDs, this camera has only two. Nevertheless, night vision is still satisfactory. Configuration is also done via a mobile app, with the camera serving an open adhoc wireless network during the initial phase. Unlike the V380 Pro which requires copying a file to the SD card, RTSP on the ICP365 can be easily enabled from the mobile app, and the default RTSP URL is

rtsp://user:password@192.168.1.2:554/live/ch0

As with most cheap security cameras, it would be wise to configure the router to prevent the camera from accessing the Internet. For the IPC365 camera, this would also prevent the live footage from being viewed via the mobile app, which attempts to retrieve the footage from their cloud server, instead of from the camera directly. This does not bother me as I always view the live footage via RTSP and only use the mobile app for initial configuration. Allowing any cameras to upload footage to some unknown server is a serious security risk and should be avoided at all costs. Interestingly, there is what looks like a 3V coin cell battery on the camera board, or perhaps a mini speaker for RTSP backchannel audio as suggested by a reader, highlighted in red in the photo below. I did not have the time to verify exactly what this part is, but the location of the connector jack (near the microphone) suggests that it’s more likely to be a speaker:

ipc365_cr2032

There is a serious issue with the RTSP implementation on this camera caused by a typo before the ssrc parameter during the RTP setup phase. The following is the data sent by the camera:

Transport: RTP/AVP;unicast;client_port=8000-8001;server_port=9000-9001,ssrc=1234

Notice the comma before ssrc which should have been a semicolon. The correct data should have been:

Transport: RTP/AVP;unicast;client_port=8000-8001;server_port=9000-9001;ssrc=1234

The above typo prevents the camera from being used in iSpy or FFmpeg but HappyTime RTSP client has no issues. VLC has no issues playing back the video, but does not split the recorded files properly when used on the iPC365’s RTSP stream. This issue prevented me from using my Raspberry Pi with FFmpeg to remotely capture the camera video footage, and I decided to spend some time finding a solution.

My specific unit uses the Ingenic MIPS T10 camera board that runs on a W25Q64FV 8MB flash chipset and has a specific model number of EC79A-S12 printed at the back. The board has unmarked (but easy to identify) RX/TX/GND pins for a 115200bps serial port connection. After a few minutes of soldering, I was quickly able to connect the board to my PC and access the u-boot menu via Putty. Although there is no firmware dump option in the menu, the objective can be achieved by using sf read and md commands that dump the memory contents as hex and by writing codes to parse the hex dump and write the content back to a binary file. The whole process took around three hours and I was able to retrieve the original firmware as a binary file!

The exact firmware size is 8,388,608 bytes (8MB), divided into 6 partitions, listed in the order they appear in the flash memory dump:

  1. Boot partition (524,288 bytes)
  2. Kernel partition (1,638,400 bytes)
  3. Root partition (2,883,584 bytes)
  4. User partition (1,572,864 bytes)
  5. Web partition (851,968 bytes)
  6. MTD partition (917,504 bytes)

The Root, User and Web partitions use SquashFS filesystem and can be extracted with unsquashfs. The embedded web and RTSP server is run from the bin/Alloca file on the User partition. The bug can be fixed by searching for ,ssrc in that file and replacing suitable occurrences with ;ssrc like below:

ssrc_ipc365

On my unit, telnet is disabled but can be enabled by editing /etc/init.d/rcS on the root partition and uncomment the call to telnetd:

telnet_enable

I also took the opportunity to show a login prompt on the serial interface by modifying /etc/inittab and add a call to getty:

getty_call

With this, I can login normally via Putty through the serial connection. The default account is root with empty password, but you might also want to update /etc/passwd and set the value you want.

After all modifications, use mksquashfs with the xz compression parameter to regenerate the firmware file. To be extra careful, study the original firmware dump and try to match the format as closely as possible. If the generated file is smaller than the original firmware file, pad it with zero to match the original file size. You should only be modifying the Root and the User partitions. Once done, transfer the firmware files to the device and use sf update to write the updated firmware files, one at a time. To do so, configure u-boot to receive files in YMODEM mode and use something like ExtraPuTTY to send the binary files. Although u-boot on this board supports Kermit, attempting to send/receive via Kermit would always result the received file being a few kilobytes smaller in size, regardless of which software I used. In the end, YMODEM is the only protocol that worked well.

Take note to only update the required partitions and leave other partitions untouched. In particular, the Boot and the Kernel partitions contain the u-boot code. If these partitions are damaged and the u-boot menu is inaccessible, the only fix is to unsolder the chips and manually flash the firmware. If this happens, you might as well buy a new camera. Remember to use sf update and not sf write. The latter will appear to write data successfully but will only cause the board to end up in an infinite reboot loop – I wasted 2 days on that!

After successfully flashing, the camera should reboot normally and telnet should now be accessible. RTSP will now work with most software including FFmpeg and iSpy. If you prefer, you might also want to find some simple HTTP/FTP server code, compile for MIPS and embed in the firmware to offer HTTP download and FTP functionality. The only other thing I do not like about this camera is the fact that recorded videos are not timestamped, which result in waste of resources on my Raspberry Pi trying to re-encode the video with the correct timestamps. Wireless reception can be weak at times, but this might just be due to my particular environment. Long story short, this camera is good to play with since it offers the possibility of flashing custom firmware via u-boot. To this end, I like the IPC365 even more than the V380 as I just couldn’t find any way to access the V380 firmware because its board does not contain any easy to identify serial connection pins.

The original dump, modified firmware and some C# code I write to help with the memory dump can be downloaded here for those who are interested.

 

See also

Floureon Wifi/Ethernet CCTV camera with ONVIF & PTZ support
V380 Pro wireless CCTV camera with night vision and RTSP support
Using Raspberry Pi Zero Wireless as cheap CCTV camera replacement

5.00 avg. rating (94% score) - 1 vote
ToughDev

ToughDev

A tough developer who likes to work on just about anything, from software development to electronics, and share his knowledge with the rest of the world.

11 thoughts on “Reverse engineer the 360Eyes Pro (IPC365) wireless CCTV camera

  • July 29, 2020 at 4:46 am
    Permalink

    Nice article, i have the same model with you. and btw, i think it is not a CR2032 battery, but a mini speaker for the camera. regarding the RX/TX/GND pins, can you please tell me where the location for the pins. Thank you.

  • ToughDev
    July 29, 2020 at 9:03 am
    Permalink

    Thanks for the feedback. I have updated the article.

    Regarding the RX/TX/GND pins, they are located on the other side of the board on the picture, roughly where the wires from the breadboard (at the bottom of the picture) are connected to. The 3 pins are grouped together but not marked. Using a multimeter you can quickly identify the GND pin by checking for connectivity with the USB socket outer case. The TX pin can be identified by looking for changes in output voltage as soon as the camera is powered on (the u-boot menu sends several text strings when power is applied). This is preferably done on an oscilloscope but can also be done on a multimeter. The RX pin should be the remaining pin.

  • July 29, 2020 at 11:21 pm
    Permalink

    thank you so much for the answer and currently i’m able to connecting the camera using serials pin connection. Btw, about uploading the modified bin, can you tell me more about how to do it properly.
    i’m searching about the command for the U-Boot but still confuse about how to upload the modified binary into the camera using serial connection. i’m using ExtraPutty as you suggested. thanks.

  • ToughDev
    July 30, 2020 at 8:53 am
    Permalink

    Hi,

    Immediately after power is applied to the board, press a key on the terminal to stop auto-boot and you should see a u-boot prompt, something like ‘#’. Type help to get the list of supported commands. Some useful commands are: getenv (to retrieve boot arguments), setenv (to update boot arguments), sf probe (to get flash memory info), sf read (to read flash data into RAM location 0x8000), md.b (to dump the retrieved data byte by byte), loady (to receive data via ymodem) and sf update (to update the received data into flash memory). You should familiarize yourself with the harmless commands such as getenv, sf read and md.b first to understand how they work before attempting to flash the firmware. Of particular note, the sf read command loads data into a specific memory location (on my board it is at 0x8000) and md.b should be called from the same location for the memory dump to retrieve the correct data. Similarly, a memory location should be passed to loady and the same location must be passed to sf update so that the correct data can be written to flash memory. The location should be sufficiently high so as not to overwrite the bootloader while ensuring that the contents uploaded via ymodem (maximum 8MB) will still stay within the addressable memory range (my board has 48MB). The u-boot help prompt contains some reasonable default values for these memory addresses.

    As mentioned in the article, you will only need to update the root and the user partitions (files 2_root_new.bin and 3_user_new.bin in the archive). Study the partition offsets and their sizes carefully before passing these parameters to sf update. Remember to keep the boot and the kernel partitions (where the u-boot logic is located) intact so that you can always retry should there be an error. Obviously, do not interrupt the sf update processs otherwise your board might fail to boot. If you realize you have made a mistake with sf update, do not reboot immediately – try to check if you can still perform another ymodem upload. On the other hand, the ymodem upload process can always be terminated as it does not affect the flash memory contents.

    If you can send me some screenshots of the u-boot help menu for your board, I will see if I can provide some more specific information.

    Hope this helps.

  • July 30, 2020 at 8:08 pm
    Permalink

    thank you very much (again) for the clear explanation, btw i can’t send the screenshot from this comment section. If may, i’ll copied the result here:

    after establishing connection via serial com. in my console i have this result:
    ====
    Hit any key to stop autoboot: 0
    isvp# help
    ? – alias for ‘help’
    base – print or set address offset
    boot – boot default, i.e., run ‘bootcmd’
    boota – boot android system
    bootd – boot default, i.e., run ‘bootcmd’
    bootm – boot application image from memory
    bootp – boot image via network using BOOTP/TFTP protocol
    chpart – change active partition
    cmp – memory compare
    coninfo – print console devices and information
    cp – memory copy
    crc32 – checksum calculation
    echo – echo args to console
    env – environment handling commands
    fatinfo – print information about filesystem
    fatload – load binary file from a dos filesystem
    fatls – list files in a directory (default /)
    gettime – get timer val elapsed,

    go – start application at address ‘addr’
    help – print command description/usage
    loadb – load binary file over serial line (kermit mode)
    loads – load S-Record file over serial line
    loady – load binary file over serial line (ymodem mode)
    loop – infinite loop on address range
    md – memory display
    mm – memory modify (auto-incrementing address)
    mmc – MMC sub system
    mmcinfo – display MMC info
    mtdparts- define flash/nand partitions
    mw – memory write (fill)
    nm – memory modify (constant address)
    ping – send ICMP ECHO_REQUEST to network host
    printenv- print environment variables
    reset – Perform RESET of the CPU
    run – run commands in an environment variable
    saveenv – save environment variables to persistent storage
    setenv – set environment variables
    sf – SPI flash sub-system
    sleep – delay execution for some time
    source – run script from memory
    tftpboot- boot image via network using TFTP protocol
    version – print monitor, compiler and linker version
    isvp#
    ====
    ————
    and i try
    ————
    isvp# sf probe
    CPM_SSICDR(74) = e000000b
    the manufacturer ef
    SF: Detected W25Q64

    —>probe spend 6 ms
    —————————————-
    and also i try this command
    —————————————–
    isvp# printenv
    HWID=0000000000000000000000000000000000000000
    ID=0000000000000000000000000000000000
    IP=192.168.31.112
    MAC=40:6A:8E:21:17:9C
    SENSOR=1245
    SSID_NAME=QC8
    SSID_VALUE=12345678
    TYPE=T10L
    WIFI=8188FTV
    baudrate=115200
    bootargs=console=ttyS1,115200n8 mem=47M@0x0 ispmem=5M@0x2F00000 rmem=12M@0x3400000 init=/linuxrc rootfstype=squashfs root=/dev/mtdblock2 rw mtdparts=jz_sfc:512K(boot),1600k(kernel),2816k(root),1536k(user),832k(web),896k(mtd)
    bootcmd=sf probe;sf read 0x80600000 0x80000 0x280000; bootm 0x80600000
    bootdelay=1
    ethact=Jz4775-9161
    ethaddr=40:6A:8E:21:17:9C
    gatewayip=193.169.4.1
    ipaddr=193.169.4.81
    ipncauto=1
    ipncuart=1
    loads_echo=1
    netmask=255.255.255.0
    serverip=193.169.4.2
    stderr=serial
    stdin=serial
    stdout=serial

    Environment size: 774/131068 bytes
    isvp#
    ====
    sorry for long text.

  • ToughDev
    July 30, 2020 at 8:32 pm
    Permalink

    Hi,

    Your u-boot menu is exactly the same as mine. You can proceed by typing “sf” to get the correct syntax for the SPI sub-system for your board. It will be like sf probe, sf read and sf update like what I suggested. Spend some time to familiarize yourself with the command line syntax by playing with “sf read” and “md.b” first. It should return valid partition data for the stock camera without updating the firmware, which you can compare with the original firmware dump (found in the archive) for a better understanding. Once you are confident, transfer the partition binary files to the board using ymodem from ExtraPutty, one partition at a time and use the md.b command again to dump some sample memory locations and compare it with the content of the partition binary file to make sure that the correct data has been transferred. Also make sure to get the RAM memory offset (where the loady command transfers the data via ymodem to the board) as well as the flash memory offset (where to write the data to) correct before running sf update. For example, the flash offset to write the root partition should be equal to the size of the preceding partitions (boot + kernel). After each partition, use the “boot” command to reboot the board and make sure that the camera still works before updating the next partition.

    As a start, you can try “sf read 0x8000 0x0 0x100″ followed by “md.b 0x8000 0x100″, which should return the first 0x100 bytes of the boot partition. Just be careful and understand how it works and updating the firmware will be straightforward. :)

  • July 30, 2020 at 11:01 pm
    Permalink

    thanks again, i’m still confuse about the sf update command, like if i want to flash the root partition, which i found that the address is start at 0x218000, and looking from the size of the partition, the len should be 2C0000 (2,883,584). so the command should be this:

    “sf update 0x218000 0x0 0x2C0000″

    or

    “sf update 0x218000 0x218000 0x2C0000″

    (erase and write `len’ bytes from memory at `addr’ to flash at `offset’)

    and btw, more question, when using file transfer, what is the first thing to do, send the file transfer using ymodem first or should i enter the sf update command and then using file transfer menu.

    Thank you so much for your help.

  • July 31, 2020 at 1:59 am
    Permalink

    sorry for bothering you (again), well using the command in my previous reply ended in my camera bootlooping, when i’m checking using “sf read 0x8000 0x0 0x100″ followed by “md.b 0x8000 0x100″ after updating (sf_update) the console showing FF FF FF hex value. Since u-boot still accessible, so i try again updating using sf update, but this time using the binary in original_firmware_dump folder (spi_dump.out). Because it was the whole image for SPI flash, i guess it is easier for me (the offset, i’m pretty sure at 0x000000 when using this whole SPI dump). Boot camera successfully, and right now i’m already using it.

    And what actually surprise me is that your firmware dump not only reviving my bootlooping camera, when using the app on android (360eyes), i have a new menu (onvif), this menu previously doesn’t exist when using my camera original firmware. And actually, that was my goal in the first place on trying modded firmware from your website (to gain local access from camera).

    So i really really appreciate this article of yours, and once again thank you very much for many guide and explanation.

    Thanks toughdev.com.

    btw, i think, it will really help (much) if you can create image for SPI Dump, but with the modded firmware, so the other including can update to your modded firmware more easily.
    sorry for asking to much.
    (Btw, sorry for my English, i’m not native english speaker)

  • ToughDev
    July 31, 2020 at 9:15 am
    Permalink

    Hi again,

    Thanks for updating the progress and glad to hear that you’ve managed to get your camera to support ONVIF!

    I guess you’ve already figured it out by now, but let me add some information for the benefits of other readers. You should start loady on the console first, transfer the files via ExtraPutty, and once done, use md.b to verify the transfer before using sf update to update the flash. The syntax of the sf read and sf update commands is described in the console help, shown below:

    sf read addr offset|partition len
    — read `len’ bytes starting at `offset’ to memory at `addr’
    sf update addr offset|partition len
    — erase and write `len’ bytes from memory at `addr’ to flash at `offset’

    Here “offset” is the offset on flash memory where you want the data to be read/updated, “addr” is the address on the RAM where the needed data is to be retrieved and “len” should be the length of the data to be written. For example, in our case, to update the root partition, offset should be the position of that partition in flash memory and length should be the size of the root partition. Because the root partition is after the boot and kernel partition, offset should be 524,288 + 1,638,400 = 2,162,688 (0x210000) and size should be 2,883,584 (0x2C0000 in hex). The “addr” should be the same as the memory address where the “loady” command stores the transferred data (specified in the output of the loady command – exact address will vary depending on camera). md.b should be used on that address to verify that the memory address indeed contains partition data prior to running sf update

    The offsets on both commands in your first post were wrong, which is why the camera ended up in a boot loop. Luckily the boot partition was not damaged, otherwise your u-boot menu would have been inaccessible :)

    You are correct that the flash offset will be zero if you flash the entire SPI memory at once. File spi_dump.out in the archive is the original firmware dump from my camera, which already has ONVIF enabled by default. Your unit did not have ONVIF enabled by default, which is why flashing this firmware enabled ONVIF and showed the option in the mobile app. :)

    For convenience, I have updated the download link with the whole SPI dump for the modded firmware with RTSP fixes and telnet support, located in the new_firmware_dump folder. The dump for the modified root and the user partitions can be found in the new_firmware_modified_partitions folder. You can also generate the the whole SPI dump for the modded firmware by using a binary file merger utility on Windows and merge files 0_boot.bin + 1_kernel.bin + 2_root_new.bin + 3_user_new.bin + 4_web.bin + 5_mtd.bin to generate a new file of exactly 8MB. After that, use a hex viewer utility to verify that the partition files have been merged properly in the above order before updating the flash memory. However, transferring the entire 8MB data at once via ymodem takes a long time and carries the risk of making the u-boot menu inaccessible if wrong parameters are used for sf update. I do not encourage flashing the modded firmware via this manner. Instead, the users should use the provided modified files for the root and user partitions to update these two partitions one at a time so that they can always retry if there is an error (like what you have experienced).

    Once again, I am happy that the article helped you and thanks a lot for sharing your adventure with this camera. :)

  • July 31, 2020 at 5:14 pm
    Permalink

    thank you again for the update on modified firmware dump, i have update using it, and now telnet is open on port 23.

    regarding update.img, when first booting the camera, is there are any details about this? I’m trying to rename spi_dump.bin to update.img and put in the SD Card, it was detected like this as showed in putty terminal:
    —-
    reading update.img
    ID U▒U▒ (HW PW-S5-TP)

    but do nothing after that, just continue starting kernel (without any modification/not updated).
    if this can be used to update (modding) firmware, then it will be the easiest solution for others to mod this camera without even have to opening the camera or using serial pins and programmer. Just put a file in SDCard and into camera, and you have modded firmware in the camera.

    Well, i’m already very very happy to find this website, follow your guide, and update the camera firmware successfully.

    So, thank you so much (again).

  • ToughDev
    July 31, 2020 at 5:37 pm
    Permalink

    I am happy that the updated firmware works well for you to enable telnet. Enjoy the camera!

    Regarding update.img, you are right that the file should be placed on the root of the SD card for automatic firmware upload. However, I do not know the exact format of the file – it is not the raw 8MB SPI flash dump which I provided. The file might need to be compressed, or should at least contain some required headers. When you copied the raw flash dump into the SD card, the format is not recognized and the device just ignored the file without updating. I tried previously many different formats for update.img, but could not get the device to update the firmware via this method.

Leave a Reply

Your email address will not be published. Required fields are marked *

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>