I want to add my own functionality to a robot vacuum, and also provision it on my own network.
Reverse engineering the Shark RV1001AED series Robot Vacuum
To make the experience fit your profile, pick a username and tell us what interests you.
We found and based on your interests.
I want to add my own functionality to a robot vacuum, and also provision it on my own network.
proto - 2.48 kB - 06/30/2022 at 16:55 |
|
|
proto - 2.65 kB - 06/30/2022 at 16:55 |
|
|
proto - 695.00 bytes - 06/30/2022 at 16:55 |
|
|
proto - 866.00 bytes - 06/30/2022 at 16:55 |
|
|
proto - 3.03 kB - 06/30/2022 at 16:55 |
|
In playing around with the Shark Clean App version 2.5.35 for android, I found that they left the *.proto files in the resources directory. I have already verified that some of the base64 MQTT messages I captured in the system log translate accurately.
I do have a large number of messages that are only partial, and I have yet to figure out the method to identify the message type.
Using Ghidra to look over the files of the OEM partition, it seems the Roboeye_server application is central to the Visual and Odometry navigation. Data from the RVC_Base is piped in to Roboeye through /dev/ttyS0. Trying to use a USB to serial adapter connected to the traces on the RVC_Base that I anticipate are the same as ttyS0, I was met with unformatted junk that seemed to have string contained within that also would escape the terminal. Oddly enough, there was occasionally legible ascii string.
Trying to figure out how I can make sense out of that mess, I stumbled across strace, and how to use it to parse serial data within an application.
The information that I collected was inspiring! The problem I have is that I have only the mainboard for this robot vacuum, and the robot vacuum I do have has fewer sensors than what the new board uses.
This seems to be causing the board to enter an error state soon after boot that just ends up being a loop.
So I have on the way an appropriate fully functional robot of the correct model to experiment further.
Here is a sample of the output from the strace
read(4,"\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\20\0\0\0 \0\0\0\0\0\0\0\0\0\2\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0 \0\0\0\0\0\0\0\0\0\0 \0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0 \0\0\0\0\0\0\0\0\0\0\0OFSIniErr\0\0\24\0~\320\0\0\252U\r\362\0\0\0\0\0\0\0\0 \0\0\0\0\0\0\0\0\0\0 \0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0 \0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0 \0\0\0\0\0\0\0\0\0\0 \0\0\0\0\0\0\0\0\0\0", 265) = 265 read(4, "###########################\346\255\244\346\226\207\344\273\266\345 \217\252\350\257\273\344\270\215\345\217\257\346\224\271 ######################################\n \n ###############################################################################\n # Debug Configurations\n ###############################################################################\n \n ## LOG \350\256\260\345\275\225\344\275\215\347\275\256\357\274\214\351\273\230\350 \256\244 /tmp/ /mnt/udisk/ /mnt/sdcard/\nlog_file_prefix = /tmp/\n \n ## LOG level # DEBUG = 0, INFO = 1, WARN = 2, ERROR = 3, FATAL = 4\n log_level = 0\n \n ## LOG line constrain , <= 0 - no constrain\n log_size_constrain = 50000\n \n ## \350\260\203\350\257\225\345\264\251\346\272\203\346\214\202\350\265\267\n enable_backtrace = true\n \n ## \345\274\272\345\210\266\351\207\215\345\206\231\347\211\210\346\234\254\345 \217\267\357\274\214\346\255\243\345\274\217\345\205\263\351\227\255,\350\260 \203\350\257\225\345\274\200\345\220\257\n #version = 3.6.0\n \n ## webSocket 9003\345\217\221\351\200\201\346\227\245\345\277\227 \351\227\264\351\232\2241\347\247\222\n enableLogWebsocekt = true\n ## \n \n ###############################################################################\n # DataSender Configurations\n # data_sender_trans_img_scheme: 'ALL':trans all key frame, 'EVERY2PIC', 'EVERY4PIC', 8 16 also ok \n # data_sender_buffer_size_mb: can be 1 2 4 8 16 32 64\n ###########################################################", 1024) = 1024 read(4, "####################\n \n ## \345\205\250\345\261\200\344\275\277\350\203\275 DataSender\n use_data_sender = true\n ## \344\275\277\347\224\250DataSender\346\234\254\345\234\260\350\256\260\345\275\225\345\212\237\350\203\275 true: \346\234\254\345\234\260\350\256\260\345\275\225\357\274\214false\357\274\232\345\261\200\345\237\237 \347\275\221\344\274\240\350\276\223\n data_sender_local_mode = false\n ## \346\234\254\345\234\260\345\255\230\345\202\250\350\267\257\345\276\204\n data_sender_local_path = /tmp/oem/data/data_all.pak\n \n data_sender_host_ip = 192.168.1.100\n data_sender_host_port = 7000\n data_sender_buffer_size_mb = 8\n data_sender_trans_img_scheme = EVERY16PIC\n data_sender_trans_log = true\n data_sender_trans_rvc = true\n data_sender_trans_img = true\n data_sender_trans_imu...Read more »
After wasting too much time with ADB, I decided to move on. Next on the list is to use GDB and Ghidra to RE some of the applications. The main application I am interested in is the RoboEyeServer. Initial analysis shows this is where all of the VSLAM calculations are done, and the RVC_Base inputs are polled.
This will be a fun weekend!
So a problem I have had for some time is getting ADB to work.
I have it setup in init.d for ADB to work, and I can run ADB on my computer to see the device SN, but it says UNAUTHORIZED.
I have tried all I can think of as for the common issues with ADB giving an Unauthorized.
The main culprit I think it there is no mechanism on the Robot to accept the ADB connection request like you would with a phone or tablet.
I can't find anything in the ADC keys or GPIO keys in relation to ADB, but then again I can't find anything about the ADC key I found to get into recovery mode...
Any suggestions?
Wow, this was a quick one! Other than a simple check, there are no major blocks to changing the default sounds.
As of right now, a custom sound has to replace an existing sound, and take its name. In my example I replaced s01_ power_on.mp3 with my own mono mp3. I hade to mount the userdata partition in the tmp directory, and replace the desired mp3 in the /tmp/userdata/music directory.
It also seemed that I needed to replace the same file in /mnt/udisk/music directory, or else I had issues where it would grab the original, or not play at all.
Here is a video example:
Finally got SSH working, and it was fun!!!
Once the sshd config, and ssh starting script in init.d were modified I tried starting sshd. I was presented with a notification that /var/empty must be owned by root, and cannot be group or world writable.
checking permissions with ls -lah, it seems that nearly every directory is owned by sshd..... I certainly did not do that.
With the unsquashed fs I did chown root:root on the /var/empty directory, then squashed and flashed.
Made some progress, but despite the config files, sshd does not want to point to the host keys.... I broke that I bet. Simply grab host keys and put them where sshd thinks they should be... okay, oh wait, the pub keys are in an invalid format?
Using ssh-keygen I created a new keypair, and saved them to the /tmp directory. Manually copied them from the terminal, and added them to the squashfs image, and flashed. Looking at the pub key I generated, it is much longer than the key that was already in the robot.
Ok, SSH to the robot.... YAY! Now I can easily move files around in the R/W directories, and no more serial cables... the robot is now free of its umbilical!
Next will be playing with the mp3 sound bank... I think it should be pretty easy.
Then I will look at some of the applications in the /res directory... it looks interesting!
Yeah, sorry, I know I don't have any screen shots... I was in the zone.
When looking around the OEM partition, I found an interesting file. I completely forgot where the file was located, and what it was called, but it contained a link to an Amazon AWS bucket for shark. The link was for a zipped compressed upgrade firmware. I posted the FW here on my github as a multi-part RAR.
This makes things much easier as this is the firmware as a proper update for all parts.
The update.img can be unpacked with the RKDevtool or the imgRepackerRK from RedScorpion on XDA Dev forums. The imgRepackerRK is a great option, but must use the option to not checkchip id. When the image in unpacked with this tool, it creates some config files for repacking the image.
Do not unsquash the rootfs.img on a Windows machine as it breaks the symlinks.
Make the changes in a unix compatible text editor, run a mksquashfs, repack with imgRepackerRK.
Use RKDevtool to load the update.img with the upgrade tab, and upgrade.
It seems all checks are only done when doing an OTA update, and nothing I have seen is checked at boot time.
Well I decided to play some risky biscuits, and reading up on rockchip documentation I found I can do an upgrade without over writing the entire firmware... Grabbed all the rootfs files, modified passwd and shadow to add my own root user... used the Rkdevtool to upgrade, and all was good!
Did a wget spider to see what it would do...
[root@rk3326_robot64:/]# wget --spider -S http://www.hackaday.io
Connecting to www.hackaday.io (198.54.96.98:80)
HTTP/1.1 301 Moved Permanently
Date: Mon, 30 May 2022 09:41:54 GMT
Content-Type: text/html
Content-Length: 178
Connection: close
Location: http://hackaday.io/
Connecting to hackaday.io (198.54.96.130:80)
HTTP/1.1 301 Moved Permanently
Content-length: 0
Location: https://hackaday.io/
Connecting to hackaday.io (198.54.96.130:443)
wget: TLS error from peer (alert code 40): handshake failure
wget: error getting response: Connection reset by peer
next job is to get ssh working, and then I can update the lame voice packs.
So I got in 2 more sample for a total of 3.
I have all 3 with the emmc dump up to the 32Mb mark due to the loader limitation.
I can also mount the oem and rootfs partitions to play around.
I copied the shadow file from /etc , and two of them have the same hashed password.
ImageType: 1 root:$1$8BiNnKgf$.pPHAXF9DfcGAIHku3PTT.:10933:0:99999:7::: #1 SMP PREEMPT Tue Jul 7 17:42:53 CST 2020 ImageType: 2 root:$1$xXxyYETk$/HJDhqXC6k/MtR0ayrM6K1:10933:0:99999:7::: #1 SMP PREEMPT Mon Sep 7 14:58:09 CST 2020
The ImageType 2 is where I have the shared password hash. Comparing the two, they are identical until the root file system. Since I can't dump the rootfs easily yet, I had to manually compare. It was quickly evident that the rootfs saw some differences.
Trying to crack the hashes has not worked out.... I am guessing that the password is derived from a hash of the kernel, in the area of the dump past the 32Mb limit, or some other method.
STEP1: Communication, sprechen sie beep boop?
So a great place to start with almost any reverse engineering project is finding the UARTS.
One difference with the L02 PCB vs the L01 is the lack of silk screen information besides some model info text.
Lucky for us, the UART test pads stick out like poorly hidden UART test pads. Three test pads in a row with no other pads around, and one of the pads is tied to ground.
Solder on some test leads, connect to USB/UART adapter. Set serial port to 1500000,8,N,1
Pull the lever Kronk!
DDR V1.14 20190925 D3,512MB,333MHz bw col bk row cs dbw 32 10 8 14 1 16 OUT Boot1 Release Time: Sep 24 2019 13:27:46, version: 1.20 chip_id:524b3326_0,0 ChipType = 0x12, 460 mmc2:cmd19,100 SdmmcInit=2 0 BootCapSize=1000 UserCapSize=3776MB FwPartOffset=2000 , 1000 SdmmcInit=0 NOT PRESENT StorageInit ok = 27271 SecureMode = 0 Secure read PBA: 0x4 Secure read PBA: 0x404 Secure read PBA: 0x804 Secure read PBA: 0xc04 Secure read PBA: 0x1004 SecureInit ret = 0, SecureMode = 0 atags_set_bootdev: ret:(0) GPT part: 0, name: uboot, start:0x4000, size:0x2000 GPT part: 1, name: trust, start:0x6000, size:0x2000 GPT part: 2, name: misc, start:0x8000, size:0x2000 GPT part: 3, name: boot, start:0xa000, size:0x10000 GPT part: 4, name: recovery, start:0x1a000, size:0x10000 GPT part: 5, name: backup, start:0x2a000, size:0x10000 GPT part: 6, name: oem, start:0x3a000, size:0x3c000 GPT part: 7, name: rootfs, start:0x76000, size:0x200000 GPT part: 8, name: sys, start:0x276000, size:0x5000 GPT part: 9, name: userdata, start:0x27b000, size:0x4e4fdf find part:uboot OK. first_lba:0x4000. find part:trust OK. first_lba:0x6000. LoadTrust Addr:0x6000 No find bl30.bin No find bl32.bin Load uboot, ReadLba = 4000 Load OK, addr=0x200000, size=0xcafd4 RunBL31 0x40000 @ 74562 us INFO: Preloader serial: 2 NOTICE: BL31: v1.3(debug):ca3dd02 NOTICE: BL31: Built : 10:15:38, Sep 23 2019 NOTICE: BL31:Rockchip release version: v1.0 INFO: ARM GICv2 driver initialized INFO: Using opteed sec cpu_context! INFO: boot cpu mask: 1 INFO: plat_rockchip_pmu_init: pd status f00e INFO: BL31: Initializing runtime services WARNING: No OPTEE provided by BL2 boot loader, Booting device without OPTEE initialization. SMC`s destined for OPTEE will return SMC_UNK ERROR: Error initializing runtime service opteed_fast INFO: BL31: Preparing for EL3 exit to normal world INFO: Entry point address = 0x200000 INFO: SPSR = 0x3c9 U-Boot 2017.09 (Jul 07 2020 - 18:40:52 +0800) Model: Rockchip RK3326 EVB PreSerial: 2 DRAM: 510 MiB Sysmem: init Relocation Offset is: 1dbef000 Using default environment dwmmc@ff370000: 1, dwmmc@ff390000: 0 Bootdev(atags): mmc 0 MMC0: High Speed, 52Mhz PartType: EFI boot mode: None Load FDT from boot part DTB: rk-kernel.dtb I2c speed: 400000Hz PMIC: RK8090 (on=0x40, off=0x00) vdd_logic 1100000 uV vdd_arm 1100000 uV Model: Rockchip rk3326 evb lpddr3 v10 board for robot linux CLK: (sync kernel. arm: enter 600000 KHz, init 600000 KHz, kernel 600000 KHz) apll 600000 KHz dpll 664000 KHz cpll 24000 KHz npll 1188000 KHz gpll 1200000 KHz aclk_bus 200000 KHz hclk_bus 150000 KHz pclk_bus 100000 KHz aclk_peri 200000 KHz hclk_peri 150000 KHz pclk_pmu 100000 KHz Net: Net Initialization Skipped No ethernet found. Hit key to stop autoboot('CTRL+C'): 0 ANDROID: reboot reason: "(none)" Fdt Ramdisk skip relocation Booting LZ4 kernel at 0x03e80000(Uncompress to 0x00280000) with fdt at 0x8300000... ## Booting Android Image at 0x03e7f800 ... Kernel load addr 0x00280000 size 4179 KiB ## Flattened Device Tree blob at 08300000 Booting using the fdt blob at 0x8300000 Uncompressing Kernel Image ... OK 'reserved-memory' ramoops@00000000: addr=8000000 size=a0000 Using Device Tree in place at 0000000008300000, end 0000000008317ef7 Adding bank: 0x00200000 - 0x20000000 (size: 0x1fe00000) Total: 391.595 ms Starting kernel ... [ 0.000000] Booting Linux on physical CPU 0x0 [ 0.000000] Initializing cgroup subsys cpuset [ 0.000000] Initializing cgroup subsys...Read more »
Create an account to leave a comment. Already have an account? Log In.
Those vacuum cleaners are about to takeover the planet. They got ssh, they got cameras, actuators and more. Maybe it is possible to mess with charging control to let battery overheat and explode.
Become a member to follow this project and never miss any updates
Well, that's why I am a proponent for open, self provisioned IOT/Smart Devices.