NikonEmulator
(formerly FrEmulator)
Contents |
What is it ?
Trying to understand how the firmware(s) inside Nikon DSLR work, we thought it could be useful to have a software that would take a firmware as input, then imitate what a microcontroller would do with such instructions and data. That's what is generally known as "emulating" a device.
The NikonEmulator started as a simple interpreter of the Fujitsu FR ("B" microcontroller) instruction set (hence the FrEmulator name), simulating only an FR CPU and its memory. Then, as understanding progressed, we added several "components", as well as tracing and debug tools.
Lastly, we thought it would be good to also study the Toshiba chip ("A" microcontroller) and we started over to emulate that chip too. Consequently, version 2 onwards of the emulator really contains two completely separate emulators and toolsets, and was renamed NikonEmulator.
This is a work in progress, obviously.
Getting Started
- Requirement: To run the latest versions of the NikonEmulator, you'll need a Java 7 environment or later (aka Java 1.7+). If you don't have one, please Download and install a Java SE from Oracle's website. The NikonEmulator is known to work under Windows and Linux, but any platform with a Java 7 environment should be OK.
- Download the latest version of NikonEmulator.zip from our Google Code project and unzip it to any folder.
- Download an official firmware from the Nikon website. It is advised to use the D5100 v1.01 firmware (F-D5100-V101W.exe) that serves as a reference for research currently.
- Open that firmware file with an unzipping tool (such as 7-zip for Windows, and extract the D5100_0101.bin file it contains. That's the encoded firmware.
- Start the emulator by double clicking the startEmulator.bat file (or equivalent .sh file under Linux)
- Decode the encoded firmware by using "File > Decode firmware" menu, selecting D5100_0101.bin as the source file, and (for simplicity) NikonEmulator's own directory as target. You should get two files named "a640m010100.bin" and "b640101b.bin". Those are the decoded A and B firmwares.
You are now ready to start hacking.
Uninstalling
The NikonEmulator has no installer and all files are in the unzipped folder, except for a config file that gets created upon first run. This file is called .NikonEmulator and is stored at the root of the user's home directory, e.g. C:\Users\<login>\.NikonEmulator on recent Windows or ~/.NikonEmulator on Linux.
Consequently, full uninstallation is achieved by deleting the unzipped folder and above config file.
User interface
The DSLRs we are interested in share responsibilities between two distinct chips (known as A and B. See Understanding Firmware for more information). Consequently, the NikonEmulator doesn't emulate one chip, but two. This is the reason for the dual-pane interface.
- On the left hand side, with a light blue background, sits the Expeed emulator. The Expeed, or 'B' chip, is based on a Fujitsu Fr family microcontroller, abbreviated FR throughout the interface.
- On the right hand side, with a light green background, sits the I/O chip emulator. The I/O chip, or 'A' chip, is a Toshiba microcontroller of the Tx family, abbreviated TX throughout the interface.
As you can see, both panes look very similar, with the exception of a few features only applicable to one or the other chip.
You can freely move the vertical separation between the panes, or even completely hide one of the panes by clicking on the small arrows on the top of the separator.
Features
The emulator features can be classified in the following groups (roughly corresponding to the application menu):
- File management. This is where you can decode firmware from the cyphered versions provided on Nikon's Website. There is also a basic way to save and reload emulator state, but please be aware that it is limited to memory and CPU, so it can only be used for small tests, not for a complete session.
- Code emulation itself (Run). They correspond to the "Transport" button group (play/pause/etc.). This allows you to execute firmware instructions in different ways, either one-by-one or in sequence, with an optional variable delay between instructions. If the emulator executes instructions in sequence, you can pause it at any time with the corresponding button, or by setting up breakpoints (more on this later). The "STOP" button corresponds to a reset (it is advised then to close all windows that could still reflect obsolete states).
- Component inspection and interaction, grouped under the "Components" menu. Emulation involves, at the very least, a CPU and memory. Each instruction executed by the [CPU has an effect on its internal state (registers) or memory (either actual memory, or memory space mapped components). But other components are also required for a microcontroller to run: Interrupt controller, Timers, etc. Each of these components corresponds to a given Window in the emulator, allowing you to inspect the state of that component, or sometimes even force a given state.
- Tracing. This is about keeping track of what happens. For example, which instructions were executed (real-time disassembly log), what is the current function call nesting (call stack) or what memory areas are being accessed.
- Source code. This is how you can perform a full disassmbly and basic analysis of the firmware code, navigate the resulting assembly code and display a call graph of the functions found
- Tools. This is where all the rest fits, among other a feature to dump part of the emulated memory to file, or reload it, as well as options used for disassembly and general User Interface configuration.
Sample uses
Note: The following use cases assume that you have already performed the "Getting started" steps above.
Getting a firmware disassembly
These are the steps to disassemble one of the firmwares (replace [chip] by FR or TX below or use the buttons on the corresponding side):
- Open the binary firmware using "File > Load [chip] firmware image" or the "Eject" button
- Select your options using "Tools > [chip] disassembly options" or the "Gear" button
- Open the code disassembly window using "Source > Analyse / Disassemble [chip] code" or the "Screwdriver" button
- Select an "options" file, which contains code structure definition and identified symbols. Sample files are provided for the D5100 firmware in the "conf" folder of the NikonEmulator distribution. They use the same base name as the firmware itself, followed by dfr.txt or dtx.txt depending on the chip you work on
- Select a target file to write disassembly to (optional)
- Select the options you want (recommended are "structure", "parameters", plus "int40" for the FR chip)
- Press OK. A log should open, containing many warnings but no errors. This is normal.
- You can now navigate the code using "Source > [chip] source code" or the "Source code" button
- If you chose a target file, you can also open that file in a text editor. Warning, the file can be large. Notepad++ or equivalent is recommended
Eye candy
If you want to be sure the emulator does not just throw garbage at you but is actually executing Nikon code, you can try the following "trick", that rudely forces the processor to execute the function displaying the language selection menu of the camera. Note that as most parameters haven't been initialized, the inter-character spacing is negative, so instead of drawing the string "English", it draws "h s i l g n E" in reverse. The steps are as follows and must only to be performed on the FR (left/blue) side:
- Open the binary firmware using "File > Load FR firmware image" or the "Eject" button
- Let the emulator run so that firmware performs some basic initialization, using "Run > Start (or resume) FR emulator" or the "Play" button. Let it run for "a certain time", so that the status bar shows more than, let's say, 5 000 000 cycles emulated
- Now pause the emulator using "Run > Pause FR emulator" or the "Pause" button
- Open the screen emulator window using "Components > Screen emulator" or the "Screen" button
- Open the CPU state window using "Components > FR CPU State window" or the "CPU" button
- Edit the PC (Program Counter) field and type the value "311942" (valid for D5100 only, of course) and click "Save"
- Now restart the emulator using "Run > Start (or resume) FR emulator" or the "Play" button.
The screen emulator should first be cleared (turn from green to black), then start being filled with characters as described above. You can then pause the emulator again.
These steps are illustrated in this video (with an older emulator version).
Note : you might have to try again if doesn't work the first time. That's due to the fact that depending on when you pause the emulator, you can be in a state where the jump into the screen drawing routine takes another route.
A note on breakpoints and triggers
For people used to development environments, the concept of Breakpoint will sound familiar. Basically, by hitting the "Debug" button instead of the "Play" button, you instruct the emulator to pause the program whenever it is about to execute some instructions you "marked" beforehand. Those "marks" are breakpoints. Such breakpoints can be set by navigating the source code (after disassembly, see first use case above), then right-clicking on an instruction and selecting "Toggle breakpoint".
Alternatively, the breakpoints can also be defined or edited in a dedicated Window accessed using "Run > Setup [chip] breakpoints" (or "Breakpoint" button).
You will notice that when you Add or Edit a breakpoint, the window allows you to specify much more than just a PC address. This is because NikonEmulator breakpoints can do much more than pause on a given instruction:
- they can break when any CPU register matches a given value, and can combine several such register conditions
- for registers presented in binary form (such as the FR's CCR register), you can specify which bits you want to match by using a combination of "1", "0" and "?" (don't care) characters
- you can also break when a given memory position (optionally masked) matches (or doesn't match) a given value
Finally, although the primary goal of a breakpoint is to pause code, the concept was extended so that when the conditions match, the breakpoint (let's call it a trigger) can do any combination of :
- pause code (of course)
- log the condition match in the log tab
- trigger a configurable interrupt
- perform a jump to any part of the code
The last option provides a very effective way to skip code that would fail due to missing or badly emulated components, for example.