Post

Poking inside the Atomos Ninja V

In a recent post, I got myself an Atomos Ninja V and brought it on a Sony film set to shoot some BTS. For context, the Atomos Ninja V (now archived) is a powerful monitor and recorder for videographers that is capable of recording ProRes 4k RAW video and more.

Obviously, the hardware here is pretty powerful, but it’s marketed for videographers. Let’s hack it instead.

Extracting the Firmware

First thing we need is an image of whatever’s running on the device. Now some might dump the firmware from a EEPROM chip or NAND. Some might just look to see if the manufacturer supplies firmware update images. Since that last one looks easier, let’s do that. At the time of this post, yes, Ninja V firmware updates are available to download.

Obtaining a firmware image online

AtomosFirmware

Atomos Product Support provides firmware packages for their products.


EZ. Time to unzip. After the unzip, we have a supposed firmware file, release notes, and oddly a __MACOSX Mac OS resource fork.

1
2
ventus@Ventus-PC:~/atomos$ ls
ATOMNJV.FW  AtomOS_10.94.01_NINJAV_Release_Notes.html  __MACOSX

Taking a peek inside

The firmware file is the most appealing to me right now so let’s figure out how to peek inside it.

1
2
ventus@Ventus-PC:~/atomos$ file ATOMNJV.FW
ATOMNJV.FW: TIM image, Pixel at (7,0) Size=1052x0

file output looks useless (maybe). Did some research on the TIM image file format and apparently it’s used for PlayStation development. That doesn’t sound relevant. How about binwalk? Might get some more valuable info on the signatures found in the file.

1
2
3
4
5
6
7
8
9
10
11
12
ventus@Ventus-PC:~/atomos$ binwalk ATOMNJV.FW

DECIMAL       HEXADECIMAL     DESCRIPTION
--------------------------------------------------------------------------------
2144          0x860           gzip compressed data, from Unix, last modified: 1970-01-01 00:00:00 (null date)
126286871     0x786FC17       COBALT boot rom data (Flat boot rom or file system)
177635818     0xA9681EA       MySQL ISAM index file Version 7
234434301     0xDF92EFD       Nagra Constant_KEY IDEA_Key: 10192431 38B6EFF1 E25FCAC3
242241596     0xE70503C       Flattened device tree, size: 46572 bytes, version: 17
243242660     0xE7F96A4       CRC32 polynomial table, little endian
243257864     0xE7FD208       Android bootimg, kernel size: 1920091392 bytes, kernel addr: 0x203A726F, ramdisk size: 1635151433 bytes, ramdisk addr: 0x2064696C, product name: ""
243317793     0xE80BC21       LZO compressed data

That’s a lot to process. I would imagine most of these are false positives, but the first entry does look interesting. It’s gzip data and occupies a large portion of the file supposedly from 0x860 to 0x786FC17. We can verify that by hexdumping the head of a file. Take a look at the 0x860 address.

1
2
3
4
ventus@Ventus-PC:~/atomos$ head ATOMNJV.FW | hexdump
0000000 0010 0000 6677 6173 0000 0100 0007 0000
...
+ 0000860 8b1f 0008 0000 0000 0300 d7ec b055 3790

On page 5 of the gzip RFC, we see the specifications for member headers and trailers. ID1 and 2 are fixed values 0x1f and 0x8b, and we can see that in the first two bytes. The next two bytes indicate the compression method (0x08 equating to deflate) then the three flag bytes (0x0 which should be FTEXT but is probably not important here). Let’s put these fixed bytes in another binwalk but using the raw sequence of bytes flag.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
ventus@Ventus-PC:~/atomos$ binwalk -R "\x1f\x8b\x08" ATOMNJV.FW

DECIMAL       HEXADECIMAL     DESCRIPTION
--------------------------------------------------------------------------------
2144          0x860           Raw signature (\x1f\x8b\x08)
10462627      0x9FA5A3        Raw signature (\x1f\x8b\x08)
10474717      0x9FD4DD        Raw signature (\x1f\x8b\x08)
43784774      0x29C1A46       Raw signature (\x1f\x8b\x08)
48312778      0x2E131CA       Raw signature (\x1f\x8b\x08)
105409576     0x6486C28       Raw signature (\x1f\x8b\x08)
114979265     0x6DA71C1       Raw signature (\x1f\x8b\x08)
116786438     0x6F60506       Raw signature (\x1f\x8b\x08)
143345707     0x88B482B       Raw signature (\x1f\x8b\x08)
149922241     0x8EFA1C1       Raw signature (\x1f\x8b\x08)
151433372     0x906B09C       Raw signature (\x1f\x8b\x08)
159904359     0x987F267       Raw signature (\x1f\x8b\x08)
161603434     0x9A1DF6A       Raw signature (\x1f\x8b\x08)
174798694     0xA6B3766       Raw signature (\x1f\x8b\x08)
177702294     0xA978596       Raw signature (\x1f\x8b\x08)
185128728     0xB08D718       Raw signature (\x1f\x8b\x08)
189434130     0xB4A8912       Raw signature (\x1f\x8b\x08)
195538682     0xBA7AEFA       Raw signature (\x1f\x8b\x08)
206417564     0xC4DAE9C       Raw signature (\x1f\x8b\x08)
230582094     0xDBE674E       Raw signature (\x1f\x8b\x08)
241333466     0xE6274DA       Raw signature (\x1f\x8b\x08)
254533971     0xF2BE153       Raw signature (\x1f\x8b\x08)

Ah, now that’s a lot more results. The last found signature starts at address 0xF2BE153 where the original scan stopped at 0x786FC17. This is likely due to the original signature scan finding false positives while seeking within the gzip data. Since gzip members are not delimited, we cannot easily find the EOF which means binwalk didn’t know exactly where the gzip ended and just searched every address. Gzip trailers do consist of a CRC, but it might just be easier to strip garbage data instead of trying to determine and calculate the CRC on the fly seeking through the binary (we might be able to see if there are trailing zeroes after the last member, which might make it easier). Anyways back to extracting. stat gives us a file size of 267652476 or 0xFF40D7C. We’ll assume (and most likely overshoot) that the last gzip member runs to the end of the file and still extract enough. Although in the future, we’ll probably need to ensure that when we repack this firmware, we’re not overwriting trailing data past the last gzip member that we shouldn’t be overwriting. Instead of using binwalk to extract, we’ll just use dd to copy the bytes with an offset.

1
2
3
4
5
ventus@Ventus-PC:~/atomos$ dd if=ATOMNJV.FW bs=8 skip=268 of=fw.gz status=progress
261849384 bytes (262 MB, 250 MiB) copied, 43 s, 6.1 MB/s
33456291+1 records in
33456291+1 records out
267650332 bytes (268 MB, 255 MiB) copied, 43.9279 s, 6.1 MB/s
1
2
3
ventus@Ventus-PC:~/atomos$ gunzip fw.gz

gzip: fw.gz: decompression OK, trailing garbage ignored
1
2
ventus@Ventus-PC:~/atomos$ file fw
fw: DOS/MBR boot sector, code offset 0x3c+2, OEM-ID "mkfs.fat", sectors/cluster 128, reserved sectors 128, root entries 2048, Media descriptor 0xf8, sectors/FAT 128, sectors/track 63, heads 16, sectors 539091 (volumes > 32 MB), serial number 0xca18f21f, unlabeled, FAT (16 bit)

Sweet! As you can see, gunzip did find some garbage data. Probably wouldn’t be a bad idea to figure out how it determines that garbage data. Let’s binwalk the result first and get an overview.

Walking the binary

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
ventus@Ventus-PC:~/atomos$ binwalk fw

DECIMAL       HEXADECIMAL     DESCRIPTION
--------------------------------------------------------------------------------
7911788       0x78B96C        CRC32 polynomial table, little endian
7991728       0x79F1B0        Copyright string: "Copyright (c) 1996-2008 Express Logic Inc. * ThreadX ARM9/RVDS Version G5.1.5.1 SN: 2923-115-1301 *"
113705113     0x6C70099       Xilinx Virtex/Spartan FPGA bitstream dummy + sync word
114499764     0x6D320B4       Intel x86 or x64 microcode, sig 0x020000c0, pf_mask 0x12000000, 2000-02-08, rev 0x2000000, size 2048
115001340     0x6DAC7FC       LZMA compressed data, properties: 0x88, dictionary size: 1073741824 bytes, uncompressed size: 256 bytes
115164534     0x6DD4576       Intel x86 or x64 microcode, sig 0x21890000, pf_mask 0x90200001, 2000-04-10, rev 0x-7fac0000, size 2048
115228793     0x6DE4079       LZMA compressed data, properties: 0x88, dictionary size: 0 bytes, uncompressed size: 136 bytes
115263529     0x6DEC829       Intel x86 or x64 microcode, sig 0x20000000, pf_mask 0x00, 2000-04-04, size 2048
115397674     0x6E0D42A       LZMA compressed data, properties: 0xC0, dictionary size: 0 bytes, uncompressed size: 1024 bytes
115933353     0x6E900A9       Xilinx Virtex/Spartan FPGA bitstream dummy + sync word
116302255     0x6EEA1AF       LZMA compressed data, properties: 0x90, dictionary size: 536870912 bytes, uncompressed size: 32 bytes
116385130     0x6EFE56A       Intel x86 or x64 microcode, sig 0x00048080, pf_mask 0x18080, 2008-01-31, rev 0x21200400, size 16777216
135790593     0x8180001       Copyright string: "Copyright (C) 2018  Intel Corporation. All rights reserved."
135791457     0x8180361       Unix path: /home/luke/projects/phoenix_scratch/fpga/syn/expansion/exp_common/output_files/exp_common.pof Sat Apr 25 10:32:03 2020
140640257     0x8620001       Copyright string: "Copyright (C) 2018  Intel Corporation. All rights reserved."
140641121     0x8620361       Unix path: /home/luke/projects/phoenix_scratch/fpga/syn/expansion/exp_common/output_files/exp_common.pof Tue Apr 28 18:50:25 2020
144375808     0x89B0000       Flattened device tree, size: 7708440 bytes, version: 17
144376028     0x89B00DC       gzip compressed data, maximum compression, from Unix, last modified: 1970-01-01 00:00:00 (null date)
152035648     0x90FE140       Flattened device tree, size: 47248 bytes, version: 17
152174592     0x9120000       Flattened device tree, size: 43507384 bytes, version: 17
152174812     0x91200DC       gzip compressed data, maximum compression, from Unix, last modified: 1970-01-01 00:00:00 (null date)
159834432     0x986E140       Flattened device tree, size: 47248 bytes, version: 17
159881880     0x9879A98       gzip compressed data, maximum compression, from Unix, last modified: 1970-01-01 00:00:00 (null date)
188629687     0xB3E42B7       SHA256 hash constants, little endian
195690496     0xBAA0000       gzip compressed data, maximum compression, from Unix, last modified: 1970-01-01 00:00:00 (null date)
200262789     0xBEFC485       Zlib compressed data, compressed

That’s quite a bit, but the data is much more promissing. FPGA bit streams, Intel microcode, more gzip data, LZMA data, Zlib data… mmm delicious.

[!NOTE]
Atomos has confirmed that the Ninja series utilizes FPGA technology for accelerated hardware processing. This backs the results binwalk gave us stating the firmware contains FPGA bitstreams. This’ll be interesting to disect later as I’ll probably disassemble the device to identify the FPGA model.

At this point, I found the radare2 framework and figured it would be a good time to learn. So I took a little break from this and went off to read the Radare book.

I did run strings on the extracted fw file and found interesting entries in the MBR section of the file. The last line in this snippet hints at a UBOOT environment. If we find UART or JTAG, we could interrupt the bootloader and get a pre-boot environment shell.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
ventus@Ventus-PC:~/atomos$ strings -10 fw | head -n 100
NO NAME    FAT16
This is not a bootable disk.  Please insert a bootable floppy and
press any key to try again ...
                !       "       #       $       %       &       '       (       )       *       +       ,       -       .       /       0       1       2       3       4       5       6       7       8       ~
                !       "       #       $       %       &       '       (       )       *       +       ,       -       .       /       0       1       2       3       4       5       6       7       8       ~
BOOTE~1JBE
APP0000 BIN
DAPP0000BIN
DATA0000BIN
DOCKUSB BIN
DPGA0000BIN
DPGA0001BIN
DPGA0002BIN
DPGA0003BIN
DPGA0005BIN
DPGA0006BIN
DPGA0009BIN
DPGA0012BIN
DPGA0013BIN
DPGA0014BIN
DPGA0015BIN
DPGA0017BIN
DPGA0018BIN
FMODDOCKBIN
FMODSTRMBIN
FWFAILEDBIN
MANIFESTXMD
PLS_WAITBIN
RES0001 BIN
SYNC0001BIN
SYNC0002BIN
SYNC0003BIN
SYNC0004BIN
SYNC0005BIN
XMODDOCKBIN
XMODSDI BIN
XMODSTRMBIN
XMODSYN BIN
FLASH   UB
GECKO_FWBIN
RAMDISK UB
ROOTFS  BIN
ROOTFS  TXT
UBOOT   ENV
...

Looking at the hexdump output of this part of the file, I tried to pick out any patterns such as addresses or metadata. I didn’t really look too far so I skipped this for now. Could possibly be related to the U-Boot environment storage.

1
2
3
4
5
6
7
8
9
10
11
12
00030000  e5 65 00 76 00 6a 00 4e  00 00 00 0f 00 54 ff ff  |.e.v.j.N.....T..|
00030010  ff ff ff ff ff ff ff ff  ff ff 00 00 ff ff ff ff  |................|
00030020  e5 2e 00 75 00 62 00 6f  00 6f 00 0f 00 54 74 00  |...u.b.o.o...Tt.|
00030030  2e 00 65 00 6e 00 76 00  2e 00 00 00 4a 00 42 00  |..e.n.v.....J.B.|
00030040  e5 42 4f 4f 54 45 7e 31  4a 42 45 20 00 c2 76 17  |.BOOTE~1JBE ..v.|
00030050  8c 57 8c 57 00 00 76 17  8c 57 00 00 00 00 00 00  |.W.W..v..W......|
00030060  41 41 00 50 00 50 00 30  00 30 00 0f 00 b5 30 00  |AA.P.P.0.0....0.|
00030070  30 00 2e 00 62 00 69 00  6e 00 00 00 00 00 ff ff  |0...b.i.n.......|
00030080  41 50 50 30 30 30 30 20  42 49 4e 20 00 ab 76 17  |APP0000 BIN ..v.|
00030090  8c 57 8c 57 00 00 76 17  8c 57 03 00 98 a1 38 00  |.W.W..v..W....8.|
000300a0  41 44 00 41 00 50 00 50  00 30 00 0f 00 ef 30 00  |AD.A.P.P.0....0.|
000300b0  30 00 30 00 2e 00 62 00  69 00 00 00 6e 00 00 00  |0.0...b.i...n...|

Extracting the corpse of an embedded Linux

Using the manual binwalk method I did earlier, I went through the fw file and scanned for gzip members once again, but this time, adding a null byte to lessen false positives.

1
2
3
4
5
6
7
8
ventus@Ventus-PC:~/atomos$ binwalk -R "\x1f\x8b\x08\x00" fw

DECIMAL       HEXADECIMAL     DESCRIPTION
--------------------------------------------------------------------------------
144376028     0x89B00DC       Raw signature (\x1f\x8b\x08\x00)
152174812     0x91200DC       Raw signature (\x1f\x8b\x08\x00)
159881880     0x9879A98       Raw signature (\x1f\x8b\x08\x00)
195690496     0xBAA0000       Raw signature (\x1f\x8b\x08\x00)

Only four entries. Still, I’ll extract a part of the file starting from the first address as the offset and going through to the end of the file.

1
2
3
4
ventus@Ventus-PC:~/atomos$ dd if=fw of=ext_fw.gz skip=764416 bs=256
313856+0 records in
313856+0 records out
80347136 bytes (80 MB, 77 MiB) copied, 0.417453 s, 192 MB/s

After decompressing it, we see that the file is an ext4 filesystem. How interesting. Let’s mount it.

1
2
ventus@Ventus-PC:~/atomos$ file ext_fw
ext_fw: Linux rev 1.0 ext4 filesystem data, UUID=bb14a1a5-f66d-42d2-920d-f8924401a174 (extents) (64bit) (large files) (huge files)

At this point, it’s just appears to be a normal Linux directory structure. This’ll take a bit to poke through.

1
2
3
4
ventus@Ventus-PC:~/atomos$ sudo mount ext_fw ./mnt/
ventus@Ventus-PC:~/atomos$ cd mnt/
ventus@Ventus-PC:~/atomos/mnt$ ls
bin  boot  dev  etc  home  lib  LICENSE  lost+found  media  mnt  proc  run  sbin  sys  tmp  usr  var

fstab

Checking out /etc/fstab, there are interesting mount points for certain things pertaining to assets and boot files.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
ventus@Ventus-PC:~/atomos/mnt/etc$ cat fstab
/dev/root       /                       auto    ro                              1  0
proc            /proc                   proc    defaults                                0  0
devpts          /dev/pts                devpts  mode=0620,gid=5                         0  0
tmpfs           /run                    tmpfs   mode=0755,nodev,nosuid,strictatime      0  0
tmpfs           /var/volatile           tmpfs   defaults                                0  0

/dev/mmcblk0p1  /media/boot             auto    defaults,ro,noatime     0  2
/dev/mmcblk0p2  /media/resources        auto    defaults,ro,noatime     0  2
/dev/mmcblk0p3  /media/static           auto    defaults,ro,noatime     0  2
# TODO not sure how to handle this
/dev/mmcblk0p6  /media/app              ext4    defaults,ro,noauto,noatime      0  2

# nonvolatile is marked as noauto, controlled by external script
/dev/mmcblk0p7  /var/nonvolatile        ext4    defaults,noauto,noatime,nofail,data=journal     0  2

grep-ing for the mmcblk0 devices shows their presence in a few custom scripts and binaries.

1
2
3
4
5
ventus@Ventus-PC:~/atomos/mnt/etc$ sudo grep -rl "mmcblk0p" ../
../usr/sbin/partition-migrate
../usr/share/core_control/migration/migrate_settings
../usr/bin/core_control
../etc/fstab

The first scripts seemed to organize partitions after a system update. The next two were binaries possibly related to the update process, but I haven’t disassembled them yet.


/etc/init.d

Lots of stuff in here, but I removed a lot of entries that had uninteresting functionality. The ones listed appear to manage codec daemons responsible for audio and video recording.

1
2
3
4
5
6
7
8
9
10
ventus@Ventus-PC:~/atomos/mnt/etc/init.d$ ls -alh
total 212K
drwxr-xr-x  2 root root 4.0K Mar  9  2018 .
drwxr-xr-x 37 root root 4.0K Mar  9  2018 ..
-rwxr-xr-x  1 root root 3.0K Mar  9  2018 audio_encoder.sh
-rwxr-xr-x  1 root root 1.7K Mar  9  2018 core_control.sh
-rwxr-xr-x  1 root root 2.2K Mar  9  2018 partition-migrate.sh
-rwxr-xr-x  1 root root 7.1K Mar  9  2018 streaming_pl.sh
-rwxr-xr-x  1 root root 3.0K Mar  9  2018 talkback_pl.sh
-rwxr-xr-x  1 root root 4.2K Mar  9  2018 video_pipeline.sh

Looking in video_pipeline.sh, sounds like debug files lived in /home/root.

1
2
3
4
5
6
DAEMON=/usr/bin/video_pipeline
WRAPPER="$DAEMON.sh"
DEBUG_DAEMON=/home/root/video_pipeline
PERF_DAEMON=/home/root/perf/video_pipeline
NAME=video_pipeline
DESC="Video Pipeline service"

OS Info - /etc/issue

  • /etc/issue - PetaLinux 2022.2_release_S10071807

/etc/passwd and /etc/shadow

Click to expand

```sh # /etc/passwd root:x:0:0:root:/home/root:/bin/sh daemon:x:1:1:daemon:/usr/sbin:/bin/sh bin:x:2:2:bin:/bin:/bin/sh sys:x:3:3:sys:/dev:/bin/sh sync:x:4:65534:sync:/bin:/bin/sync games:x:5:60:games:/usr/games:/bin/sh man:x:6:12:man:/var/cache/man:/bin/sh lp:x:7:7:lp:/var/spool/lpd:/bin/sh mail:x:8:8:mail:/var/mail:/bin/sh news:x:9:9:news:/var/spool/news:/bin/sh uucp:x:10:10:uucp:/var/spool/uucp:/bin/sh proxy:x:13:13:proxy:/bin:/bin/sh www-data:x:33:33:www-data:/var/www:/bin/sh backup:x:34:34:backup:/var/backups:/bin/sh list:x:38:38:Mailing List Manager:/var/list:/bin/sh irc:x:39:39:ircd:/var/run/ircd:/bin/sh gnats:x:41:41:Gnats Bug-Reporting System (admin):/var/lib/gnats:/bin/sh core_control:x:800:800::/home/core_control:/bin/sh pipeline:x:801:801::/home/pipeline:/bin/sh cloud:x:802:802::/home/cloud:/bin/sh avahi:x:998:998::/run/avahi-daemon:/bin/false messagebus:x:999:999::/var/lib/dbus:/bin/false nobody:x:65534:65534:nobody:/nonexistent:/bin/sh # /etc/shadow root:*:15069:0:99999:7::: daemon:*:15069:0:99999:7::: bin:*:15069:0:99999:7::: sys:*:15069:0:99999:7::: sync:*:15069:0:99999:7::: games:*:15069:0:99999:7::: man:*:15069:0:99999:7::: lp:*:15069:0:99999:7::: mail:*:15069:0:99999:7::: news:*:15069:0:99999:7::: uucp:*:15069:0:99999:7::: proxy:*:15069:0:99999:7::: www-data:*:15069:0:99999:7::: backup:*:15069:0:99999:7::: list:*:15069:0:99999:7::: irc:*:15069:0:99999:7::: gnats:*:15069:0:99999:7::: core_control:!:15069:::::: pipeline:!:15069:::::: cloud:!:15069:::::: avahi:!:15069:::::: messagebus:!:15069:::::: nobody:*:15069:0:99999:7::: ```

/etc/petalinux/

Click to expand

```sh # board atomos-ninjav # board-variant aarch64:armv8a:cortexa72-cortexa53:mali400:vcu:zynqmp:zynqmp-ev:atomos-ninjav # bsp atomos-ninjav # product atomos-ninjav-2022.2 # soc zynqmp-ev # version 1.00 ```

Some other interesting things grep-ing the fw file. Sounds like the FPGA was designed through Intel Quartus.

1
2
3
4
5
6
7
8
9
10
11
12
ventus@Ventus-PC:~/atomos$ strings fw | grep -A2 -B2 luke
!refer to the applicable agreement for further details.
!Quartus Prime SVF converter 18.0
!Device #1: 10M04SC - /home/luke/projects/phoenix_scratch/fpga/syn/expansion/exp_common/output_files/exp_common.pof Sat Apr 25 10:32:03 2020
!NOTE "USERCODE" "002BB7BE";
!NOTE "CHECKSUM" "02896B90";
--
!refer to the applicable agreement for further details.
!Quartus Prime SVF converter 18.0
!Device #1: 10M04SC - /home/luke/projects/phoenix_scratch/fpga/syn/expansion/exp_common/output_files/exp_common.pof Tue Apr 28 18:50:25 2020
!NOTE "USERCODE" "002D0A80";
!NOTE "CHECKSUM" "0284F936";

Disassembly

PCB BOM

  • EFM32-TG222F32 (TinyGecko 32bit ARM Cortex-M3 MCU) - Link (this points back to presence of a Gecko bootloader in the firmware)
  • H26M31001HPR (4G NAND MMC) - Link
  • H5AN4G6NAFR (256M UHC) - Link
  • 2x PS8409A (6Gbps HDMI 2.0 repeater/retimer) - Link

Headers

UART:
UART

Debug: DBG

This post is licensed under CC BY 4.0 by the author.