Close

Attacking sdvcam

A project log for Cheapy 'rearview mirror' dashcam/Carplay/AAuto dev

Rooting, reverse engineering, and binary patching software on a closed device

mjc506mjc506 08/30/2023 at 21:390 Comments

Cat-ing /sys/devices/virtual/disp/disp/sys/attr displays some data about a couple of the virtual framebuffers. One of these covers the whole screen (shows the buttons etc?) and the other matches the 533 or 588x320 resolution. Unfortunately, I could not find any way of getting the system to change that size. The driver for the screen seems to be compiled into the kernel (and no luck with source code...) and no obvious libraries seem present or are opened by sdvcam (it 'opens' the display device directly). Hmm... Perhaps I could trace the calls, but that means getting gbd etc onto the system (and compiled for arm...) and without any external libraries I can call, would be no use anyway.

I copied the sdvcam binary to the microSD card and transferred it to my laptop. The laptop is x86, and the binary is arm, but perhaps Ghidra can make some sense of it... After a good fight with Radare2, Ghidra and eventually Cutter, I had sdvcam loading into the disassembler. Taking a worryingly long time to load into the disassembler...

Oh my. Of course, a binary this large and this complicated (and apparently all the libraries compiled in) is going to feel a little overwhelming... And I've not done much assembly work before, especially arm. How hard can it be... Fortunately, Ghidra (within Cutter) has managed to decompile the assembly (it was lovely seeing something vaguely understandable!) and I do have the rather helpful debug log from the running application, with logs such as 'requesting 1280x720 stream' and 'setting frame to...', also with what appeared to be function names and perhaps line numbers from the original source code. No function names and certainly no line numbers in the decompiler output, but those strings are there!!

It didn't take too long to trace the section of code responsible for setting the frame size. It basically calculated the aspect ratio of the stream, and then made the frame as big as possible while fitting the screen (or rather, the 1010x320 'free space' between the buttons) and maintaining the aspect ratio. So far, so good, but if AAuto would only send 840x480, 1280x720 or 1920x1080 streams, the frame would never fill the screen. If the function sized the frame to fit the 1010x320 space, but allowing the edges to spill over the edge of the screen, that would work, but the top and bottom of the stream would be invisible, and I bravely decided that trying to edit the lylink binary to request blanking from AAuto that I'd only read a brief description of was probably beyond my abilities... Next best option was just stretching the frame to 1010x320 and hoping the distortion wasn't too bad (the 'stock' frame sizes already resulted in the stream being squashed slightly horizontally, so hopefully nearly doubling the frame width wouldn't make everything unreadable). Also, hopefully the rest of the software would handle the wider frame and translate touches correctly back to the AAuto app on my phone... (but not the absolute end of the world if not, most of it is voice controllable)

The function was laid out in such a way that

  1. The aspect ratio of the stream was calculated, and compared to 1010x320
    1. If the stream was wide (in comparison with 1010x320) it set the width to 1010 and then calculated the height to maintain the aspect ratio
    2. If the stream was narrow compared to 1010x320, it set the height to 320 and then calculated the width.
  2. The next step was calculating an offset so the stream appears centered in the screen.

The AAuto stream would always fall into the second group (b). If I removed the width calculation, and always set it to 1010, the frame should take up the whole (available) screen, and the offsets should be calculated to suit. After a bit of arm assembly revision, I managed to nop the calculation and set the width to an integer 1010...

Discussions