💾 Archived View for danielc.dev › hacking-the-fujifilm-digital-cameras.gmi captured on 2023-03-20 at 17:43:41. Gemini links have been rewritten to link to archived content

View Raw

More Information

⬅️ Previous capture (2023-01-29)

-=-=-=-=-=-=-

Hacking the Fujifilm Digital Cameras

Jul 30 2021

I recently got my hands on a cheap Fujifilm HS20 EXR. It's a pretty old camera (2012),

but I thought it'd be a good way to practice my hacking skills.

---

The first thing I did was update to the latest firmware. It's pretty simple: just hold

down the back button while powering up, and press a few buttons.

After the update, the camera wouldn't let me upgrade to the same version again.

With that, I decided to dig into the firmware file contents.

In a hex editor, the file doesn't look very interesting. No strings, and a weird

looking header. Given that Fujifilm has made tons of different cameras, I decided

to download as many different firmware files as I could and study them.

This is what I came up with:

Size in bytes  Description
4              Hardware or OS version
512            Model information
8              Firmware version
4              Firmware checksum
4              ??? (always 1)
...            Payload (bit flipped)

It appears that Fujifilm has been using the same firmware format since ~2005, and

continue to use it on their latest cameras.

I noticed another different Fujifilm camera, the S-series, has another firmware

format. These cameras have already been hacked by [CHDK developers](https://chdk.setepontos.com/index.php?topic=6484.0).

It has easy-to-access secret menus and built-in scripting.

Sadly, I didn't have this type of camera, and I would be on my own this time.

Creating a new firmware

Given that I bought this camera specifically for hacking, I knew I *had* to try

and increment the firmware version by 1 and see if the camera accepted it. It seems

risky, but I was willing to do it since I bought the camera specifically for hacking.

Before I did that, I needed to do some testing. It appeared that the firmware

version is printed in hex. Yep, that means when you download firmware version

number "1.22", you are getting 1.32 in base 10. I confirmed this with a simple

"1337" test:

![1337](https://eggnog.theres.life/f/30-8kzf51c2grdyawmk6lhc3rihfu91yf.png)

After fully understanding how this worked, I changed the firmware version from 1.4 to 1.5

and tried it. Amazingly, it installed perfectly. And a bonus: it doesn't increment the system's

firmware version counter. Now, all I need to do is figure out the firmware payload.

![binvis.io](https://eggnog.theres.life/f/31-8kzf51c2grdyawmk6lhc3rihfu91yf.png)

It doesn't look encrypted or compressed, so this one should be relatively easy to

solve. The [people at CHDK have already gotten this far](https://chdk.setepontos.com/index.php?topic=6484.msg88507#msg88507): the firmware is XORed with 0xff.

All I had to do was go write a program to go through the file and invert all the bits. Easy.

I was quickly able to get it disassembled in Ghidra, all the data references pointed

to an empty area in the firmware file (a bunch of 0xff). Although the code was basically

unreadable, the strings in the firmware were very interesting. It mentions SQLite,

"AUTO_ACT.SCR" (automatically activated script?), and possibly a hidden menu.

After that, I decided to try and make my own firmware. The checksum looking number

in the header worried me. Was it CRC32? Or was it some proprietary algorithm? How

would I recreate it?

After a few *very* risky firmware update tests, it turns out it is just a simple case of "add up

all the bytes and make sure it equals X." I had confirmed this by reversing a word

in the firmware.

![backwards](https://eggnog.theres.life/f/32-8kzf51c2grdyawmk6lhc3rihfu91yf.JPG)

In order to inject my own custom text, all I had to do was subtract the checksum of

both files and then change the header checksum accordingly.

![hacked by dan](https://eggnog.theres.life/f/33-8kzf51c2grdyawmk6lhc3rihfu91yf.JPG)

Getting the Correct Disassembly

Completely hacked right? Well, custom code injection would be nice. Since the strings

won't correctly reference to the code, I had no idea where a good place to

inject my own code would be.

Then, I remembered the SQLite strings in the firmware. Since SQLite is open-source,

I could match up some of the source and my decompiled code, and calculate the offset

of the string references.

In other words, see how the code was compiled by matching it up with the source code.

I downloaded a [2010 revision of SQLite source code](https://github.com/sqlite/sqlite/tree/5d4feffe7d96893d6eb339818101615748618673) and searched for "0x".

The plan was to match up a number between my disassembly and the source code,

and figure out function names and string offsets from that. Of course, I needed to find

a number that was unique, but I also needed a string referenced near it.

The first thing I found was sqlite3Realloc(). I was quickly able to find it

since it had the number `0x7fffff00` inside the function. Sadly, no string was

referenced here, so I kept digging.

After finding an integer in sqlite3Realloc(), I was able to find several other allocation

functions around it, such as sqlite3DbMallocZero(). The reason I mention that

function is because in the source code, allocateIndexInfo() contains a call to sqlite3DbMallocZero()

and a reference to the string "out of memory".

Now, all I had to do was look at references to sqlite3DbMallocZero() and find

a call that looked like this:

pIdxInfo = sqlite3DbMallocZero(pParse->db, sizeof(\*pIdxInfo)
                       + (sizeof(\*pIdxCons) + sizeof(\*pUsage))\*nTerm
                       + sizeof(\*pIdxOrderBy)\*nOrderBy + sizeof(\*pHidden) );
if( pIdxInfo==0 ){
  sqlite3ErrorMsg(pParse, "out of memory");
  return 0;
}

Eventually, I found something in the Ghidra decompiler that looked like a worthy candidate:

FUN_0047e7e8(\*param_1, iVar32 \* 0x14 + 0x2c + iVar20 \* 8);

After a bit of poking around, everything seemed to match up. Now that I had the

correct offset, I wrote a program to copy the strings over, and I finally got a

usable disassembly.

Code Injection

Now, that I had the strings correctly referenced, I needed to pick a place to inject

my code. My first idea was to hijack the image saving process. I opened up a JPEG image,

and looked around for some interesting things I could possibly hijack.

The first thing I saw was "PrintIM". I searched it in my disassembly, and went to its

reference to one 200 byte function. After changing a few values and testing the resulting

firmware file, I was confident this was a safe place to test my code.

And indeed, it was. Whenever I took a picture, I could run my own custom assembly

and output 29 bytes of whatever I want. Here's a screenshot where I tested how

many bytes I could output:

![hack](https://eggnog.theres.life/f/34-8kzf51c2grdyawmk6lhc3rihfu91yf.png)

A Conclusion

So what's next?

Of course, writing custom firmwares to ROM is very dangerous, especially

with the risky version update hack. The only reason I did it was because I bought

the camera purely for the joy of hacking.

In order to tweak and add useful modifications to the camera, it should be done in a

non-permanent way, such as via a special [USB/PTP](https://github.com/petabyt/fujiptp) command, or uncovering anything

else that could possibly allow code execution.

And as always, the code is completely open-source:

[https://github.com/petabyt/fujifilm](https://github.com/petabyt/fujifilm)