Today I have decided to take a look into the internals of the Roku Streaming Stick, the version model 3840X released in 2025. I checked the sides of the housing and, after a while with the usage of the triangular openers, the plastic cover was opened. Below is a photo:
We see that the bottom part has a built-in small metal plate that is connected to the PCB via thermal paste and acts as an additional heatsink. Near the HDMI socket there is an IC which is storage: Macronix MX30LF4G28AD-XKI. It is a SLC NAND flash with 4 Gb (512 MB x 8 bits) and uses a parallel interface for communication. Below is a photo after removing the top plastic cover:
We can see a USB-C connector used to power the device, along with an aluminum plate that acts as the top lid of the shielding cans. Fortunately, it wasn't glued down and could be fully removed. Here is a photo with the top lid removed:
Also, in this case, the aluminum lid was connected via thermal paste with SoC and acts as heatsink. The SoC in this case is Realtek RTD1212CRB. I have not found much information about it, but it seems to be 4 x ARM Cortex-A55 SoC (probably similar to RTD1311). Near the SoC is located RAM marked with D9SHB. It is Micron MT41K512M8DA-093:P which is 4Gb (512 MB) 1066MHz 2133MTPS DDR3 DRAM. The last can near the USB was fully soldered and might contain the WiFi/BT chipset. I briefly inspected the test pins with a logic analyzer, but I did not find appearance of an accessible UART. Apart from that, I performed a small test in which I set a Wi-Fi point for which I captured all packets and used that point to connect that device. The device was in FTI and additionally asked for new firmware. For a quick test, I checked whether there was any HTTP traffic and found these two requests:


The first one was a JPEG image for some purpose. This could probably be a good entry point for exploring whether the device properly parses a suspicious JPEG delivered via a man-in-the-middle attack. The second request was for the firmware upgrade package from the upgrade server. I downloaded the file to take a closer look at its contents. The initial inspection was performed using Binwalk, a tool commonly used to analyze binary files and identify embedded files, compressed data, and other recognizable file formats.
binwalk JWG.34E02402A DECIMAL HEXADECIMAL DESCRIPTION -------------------------------------------------------------------------------- 0 0x0 Roku aimage SB 40119864 0x2642E38 Cisco IOS experimental microcode, for "" 56676352 0x360D000 Roku aimage SB 56913920 0x3647000 Roku aimage SB 57520128 0x36DB000 Roku aimage SB 60231680 0x3971000 Roku aimage SB 61657088 0x3ACD000 Roku aimage SB 62263296 0x3B61000 Roku aimage SB 62263552 0x3B61100 Flattened device tree, size: 3936735 bytes, version: 17 62263780 0x3B611E4 gzip compressed data, maximum compression, has original file name: "Image32", from Unix, last modified: 2026-08-22 18:26:59 66152520 0x3F16848 Flattened device tree, size: 46437 bytes, version: 17 66211840 0x3F25000 Roku aimage SB 66214216 0x3F25948 Zlib compressed data, default compression 66215185 0x3F25D11 Zlib compressed data, default compression 66216680 0x3F262E8 Zlib compressed data, default compression 66217649 0x3F266B1 Zlib compressed data, default compression 66219148 0x3F26C8C Zlib compressed data, default compression 66220117 0x3F27055 Zlib compressed data, default compression 66221616 0x3F27630 Zlib compressed data, default compression 66222585 0x3F279F9 Zlib compressed data, default compression 66224084 0x3F27FD4 Zlib compressed data, default compression 66225053 0x3F2839D Zlib compressed data, default compression 66226552 0x3F28978 Zlib compressed data, default compression 66227524 0x3F28D44 Zlib compressed data, default compression 66229036 0x3F2932C Zlib compressed data, default compression 66230018 0x3F29702 Zlib compressed data, default compression 66231528 0x3F29CE8 Zlib compressed data, default compression 66232496 0x3F2A0B0 Zlib compressed data, default compression 66234012 0x3F2A69C Zlib compressed data, default compression 66234981 0x3F2AA65 Zlib compressed data, default compression 66236480 0x3F2B040 Zlib compressed data, default compression 66237449 0x3F2B409 Zlib compressed data, default compression 66238948 0x3F2B9E4 Zlib compressed data, default compression 66239917 0x3F2BDAD Zlib compressed data, default compression 66241416 0x3F2C388 Zlib compressed data, default compression 66242388 0x3F2C754 Zlib compressed data, default compression 66243900 0x3F2CD3C Zlib compressed data, default compression 66244882 0x3F2D112 Zlib compressed data, default compression 66246392 0x3F2D6F8 Zlib compressed data, default compression 66247360 0x3F2DAC0 Zlib compressed data, default compression 66248876 0x3F2E0AC Zlib compressed data, default compression 66249848 0x3F2E478 Zlib compressed data, default compression 66251356 0x3F2EA5C Zlib compressed data, default compression 66252100 0x3F2ED44 Zlib compressed data, default compression 66252244 0x3F2EDD4 Zlib compressed data, default compression 66252388 0x3F2EE64 Zlib compressed data, default compression 66252532 0x3F2EEF4 Zlib compressed data, default compression 66252924 0x3F2F07C Zlib compressed data, default compression 66252940 0x3F2F08C Zlib compressed data, default compression 66253344 0x3F2F220 Zlib compressed data, default compression 66254172 0x3F2F55C Zlib compressed data, default compression 66254552 0x3F2F6D8 Zlib compressed data, default compression 66254728 0x3F2F788 Zlib compressed data, default compression 66255744 0x3F2FB80 Zlib compressed data, default compression 66255852 0x3F2FBEC Zlib compressed data, default compression 66255964 0x3F2FC5C Zlib compressed data, default compression 66256108 0x3F2FCEC Zlib compressed data, default compression 66256268 0x3F2FD8C Zlib compressed data, default compression 66256436 0x3F2FE34 Zlib compressed data, default compression 66256596 0x3F2FED4 Zlib compressed data, default compression 66256708 0x3F2FF44 Zlib compressed data, default compression 66256820 0x3F2FFB4 Zlib compressed data, default compression 66256932 0x3F30024 Zlib compressed data, default compression 66257096 0x3F300C8 Zlib compressed data, default compression 66257264 0x3F30170 Zlib compressed data, default compression 66257428 0x3F30214 Zlib compressed data, default compression 66257592 0x3F302B8 Zlib compressed data, default compression 66258212 0x3F30524 Zlib compressed data, default compression 66258588 0x3F3069C Zlib compressed data, default compression 66258860 0x3F307AC Zlib compressed data, default compression 66258952 0x3F30808 Zlib compressed data, default compression 66259052 0x3F3086C Zlib compressed data, default compression 66259148 0x3F308CC Zlib compressed data, default compression 66259264 0x3F30940 Zlib compressed data, default compression 66259360 0x3F309A0 Zlib compressed data, default compression 66259452 0x3F309FC Zlib compressed data, default compression 66259568 0x3F30A70 Zlib compressed data, default compression 66260992 0x3F31000 Roku aimage SB 66273280 0x3F34000 Roku aimage SB
Based on a few minutes of analysis, I found that the upgrade package consists of a series of blocks, each containing a header followed by a payload. The payload contains various parts of the firmware, such as initfs_cramfs, uImage, and others. Based on information I found online, the header has the following structure:
| Offset (from block start) | Size | Field |
| 0 | 8 | (reserved/prefix, zeros before magic) |
| 8 | 8 | Magic string "imgARMcC" |
| 16 | 4 | Release ID |
| 20 | 4 | Platform ID |
| 24 | 4 | Image type enum (text/env/initfs_cramfs/app_cramfs/firmware_blob/uimage/uboot) |
| 28 | 4 | Total length (header + data + trailing padding) |
| 32 | 4 | Data start offset to start padding length |
| 36 | 4 | Data start offset |
| 40 | 4 | Data link address |
| 44 | 4 | Data entry point offset |
| 48 | 1 | Encryption flag (0x00 none, 0x40 cbc_onepass, 0x80 cbc_4kblocks, 0xC0 ctr) |
| 52 | 4 | Build date (Unix time, must be > 1990) |
| 56 | 4 | Build host string pointer/flag |
| 64-95 | 32 | Signature/hash region |
| 240 | 4 | IV1 |
| 244 | 4 | IV2 |
| 246 | 4 | IV3 |
| 250 | 4 | IV4 |
With this information, I asked AI to create a simple python parser that would print this information and extract the payload from each segment. The result is shown below:
python3 roku_aimage_parser.py JWG.34E02402A -o extracted File: JWG.34E02402A File size: 70819840 bytes Found 10 Roku aimage SB segment(s) --- Segment 0 --- Block start offset : 0x0 (0) Segment end (next hdr/EOF) : 0x360D000 (56676352) Segment size : 56676352 bytes Release ID : 0.0-1 (0x1) Platform ID : Default Type : initfs_cramfs (10) Total length (hdr+data+pad) : 56676352 bytes Data-start-to-padding offset: 56676352 Data start offset : 0 Data link address : 0x0 Data entry point offset : 0x0 Encryption : cbc_4kblocks Build date : 2026-08-22 18:30:32 UTC IVs : 28a46eb2 86a476c6 8d4186a4 0000c922 Payload range : 0x100 - 0x360D000 (56676096 bytes) -> extracted to : extracted/segment_00_0x0000000_initfs_cramfs.bin --- Segment 1 --- Block start offset : 0x360D000 (56676352) Segment end (next hdr/EOF) : 0x3647000 (56913920) Segment size : 237568 bytes Release ID : Dev Platform ID : Default Type : unknown(0x101) (257) Total length (hdr+data+pad) : 237568 bytes Data-start-to-padding offset: 0 Data start offset : 4096 Data link address : 0x0 Data entry point offset : 0x0 Encryption : none Build date : invalid (0) Payload range : 0x360E000 - 0x3647000 (233472 bytes) (declared end out of range, clamped to next segment/EOF) -> extracted to : extracted/segment_01_0x360D000_unknown_0x101_.bin --- Segment 2 --- Block start offset : 0x3647000 (56913920) Segment end (next hdr/EOF) : 0x36DB000 (57520128) Segment size : 606208 bytes Release ID : 0.0-1 (0x1) Platform ID : 0x1f7b2372 Type : unknown(0x3) (3) Total length (hdr+data+pad) : 606208 bytes Data-start-to-padding offset: 599312 Data start offset : 256 Data link address : 0x0 Data entry point offset : 0x0 Encryption : unknown(0x1) Build date : 2026-08-22 18:30:44 UTC IVs : 00000000 00000000 00000000 00000000 Payload range : 0x3647100 - 0x36D9510 (599056 bytes) -> extracted to : extracted/segment_02_0x3647000_unknown_0x3_.bin --- Segment 3 --- Block start offset : 0x36DB000 (57520128) Segment end (next hdr/EOF) : 0x3971000 (60231680) Segment size : 2711552 bytes Release ID : 0.0-1 (0x1) Platform ID : Default Type : unknown(0x110) (272) Total length (hdr+data+pad) : 2711552 bytes Data-start-to-padding offset: 2711296 Data start offset : 256 Data link address : 0x0 Data entry point offset : 0x0 Encryption : none Build date : 2026-08-22 18:30:44 UTC Payload range : 0x36DB100 - 0x3970F00 (2711040 bytes) -> extracted to : extracted/segment_03_0x36DB000_unknown_0x110_.bin --- Segment 4 --- Block start offset : 0x3971000 (60231680) Segment end (next hdr/EOF) : 0x3ACD000 (61657088) Segment size : 1425408 bytes Release ID : 0.0-1 (0x1) Platform ID : Default Type : unknown(0x111) (273) Total length (hdr+data+pad) : 1425408 bytes Data-start-to-padding offset: 1425152 Data start offset : 256 Data link address : 0x0 Data entry point offset : 0x0 Encryption : none Build date : 2026-08-22 18:30:44 UTC Payload range : 0x3971100 - 0x3ACCF00 (1424896 bytes) -> extracted to : extracted/segment_04_0x3971000_unknown_0x111_.bin --- Segment 5 --- Block start offset : 0x3ACD000 (61657088) Segment end (next hdr/EOF) : 0x3B61000 (62263296) Segment size : 606208 bytes Release ID : 0.0-1 (0x1) Platform ID : 0x1f7b2372 Type : firmware_blob (14) Total length (hdr+data+pad) : 606208 bytes Data-start-to-padding offset: 592768 Data start offset : 256 Data link address : 0x20000 Data entry point offset : 0x0 Encryption : unknown(0x1) Build date : 2026-08-22 18:30:44 UTC IVs : 165482b3 098bbda7 aec3098b efc8e948 Payload range : 0x3ACD100 - 0x3B5DB80 (592512 bytes) -> extracted to : extracted/segment_05_0x3ACD000_firmware_blob.bin --- Segment 6 --- Block start offset : 0x3B61000 (62263296) Segment end (next hdr/EOF) : 0x3F25000 (66211840) Segment size : 3948544 bytes Release ID : 0.0-1 (0x1) Platform ID : Default Type : uimage (24) Total length (hdr+data+pad) : 3948544 bytes Data-start-to-padding offset: 3936735 Data start offset : 256 Data link address : 0x3000000 Data entry point offset : 0x0 Encryption : unknown(0x1) Build date : 2026-08-22 18:30:32 UTC IVs : 00000000 00000000 00000000 00000000 Payload range : 0x3B61100 - 0x3F221DF (3936479 bytes) -> extracted to : extracted/segment_06_0x3B61000_uimage.bin --- Segment 7 --- Block start offset : 0x3F25000 (66211840) Segment end (next hdr/EOF) : 0x3F31000 (66260992) Segment size : 49152 bytes Release ID : 0.0-1 (0x1) Platform ID : 0x53b22501 Type : app_cramfs (13) Total length (hdr+data+pad) : 49152 bytes Data-start-to-padding offset: 49152 Data start offset : 0 Data link address : 0x0 Data entry point offset : 0x0 Encryption : none Build date : 2026-08-22 18:30:40 UTC Payload range : 0x3F25100 - 0x3F31000 (48896 bytes) -> extracted to : extracted/segment_07_0x3F25000_app_cramfs.bin --- Segment 8 --- Block start offset : 0x3F31000 (66260992) Segment end (next hdr/EOF) : 0x3F34000 (66273280) Segment size : 12288 bytes Release ID : Dev Platform ID : Default Type : unknown(0x101) (257) Total length (hdr+data+pad) : 12288 bytes Data-start-to-padding offset: 0 Data start offset : 4096 Data link address : 0x0 Data entry point offset : 0x0 Encryption : none Build date : invalid (0) Payload range : 0x3F32000 - 0x3F34000 (8192 bytes) (declared end out of range, clamped to next segment/EOF) -> extracted to : extracted/segment_08_0x3F31000_unknown_0x101_.bin --- Segment 9 --- Block start offset : 0x3F34000 (66273280) Segment end (next hdr/EOF) : 0x438A000 (70819840) Segment size : 4546560 bytes Release ID : 0.0-1 (0x1) Platform ID : 0x53b22501 Type : unknown(0x6) (6) Total length (hdr+data+pad) : 4546560 bytes Data-start-to-padding offset: 4542766 Data start offset : 256 Data link address : 0x0 Data entry point offset : 0x0 Encryption : unknown(0x1) Build date : 2026-08-22 18:30:40 UTC IVs : 00000000 00000000 00000000 00000000 Payload range : 0x3F34100 - 0x438912E (4542510 bytes) -> extracted to : extracted/segment_09_0x3F34000_unknown_0x6_.bin
It seems that the payloads can be extracted successfully. However, there are still some unknown data types and signs of encryption. This is probably because the header description I found online is from a considerably older version of the firmware format. Nevertheless, it is interesting that some of the payloads, such as initfs_cramfs, are encrypted. Additionally, it looks like Roku uses a slightly modified version of CRAMFS, as the app_cramfs payload could not be mounted using a standard CRAMFS implementation. For example, in the block containing the uImage, it was possible to extract some information about the kernel. The extracted information is shown below:
Linux version 4.9.294-grsec (ec2-user@ip-10-215-112-241.us-west-2.compute.internal) (gcc version 14.2.0 (crosstool-NG 1.26.0 - Roku GCC toolchain 20241120)) #1 SMP PREEMPT Sat Aug 22 18:26:56 UTC 2026
Probably, digging deeper would turn this into more security-related research, so I stopped here.