capdis (short for CAPstone DISassembler) is disassembly tool based on the capstone library, with features specifically supporting CHDK development. It is primarily intended for thumb2 / Digic 6 firmware, but can also be used on earlier ARM firmware or arbitrary ARM or thumb2 code.
The latest version is available in the CHDK tools directory.
A patched version of capstone 3.0.4 is required. The patch and pre-built win32 library are available from https://app.box.com/s/sshu7dv0mebvnee6gvpsex46y2m18w51
NOTE: The bug is fixed in later versions of capstone, but other source adjustments are required to use them.
To build the CHDK capstone tools, the following should be set in your localbuildconf.inc
# Define this to enable building of tools using the capstone library (http://www.capstone-engine.org/) # Required to rebuild stubs for thumb2 / digic 6 firmware OPT_CAPSTONE_TOOLS=1 # Set the following if capstone includes are not in the default search path CAPSTONE_TOOLS_INC=-I/path/to/capstone/include # Set the following to the options required to link to the capstone library. # At a minimum, -lcapstone or similar is required. If the library is not # on the default search path, -L/path/to/capstone is also required CAPSTONE_TOOLS_LINK=-L/path/to/capstone -lcapstone
Replace /path/to/capstone with the location where the capstone library is installed.
To build capdis, make extras or capdis (capdis.exe on windows) in the tools directory.
capdis is a command line tool which disassembles a specified address range, with output in formats suitable for general reverse engineering or use in CHDK code. If invoked without any options, it prints a brief summary.
Options are specified like -x=value with no spaces.
To disassemble, you must specify
- A start address, with -s=<numeric address or known function name> or -o=<byte offset from file start>
- The lowest bit of the start address controls whether code is disassembled as ARM (LSB=0) or thumb (LSB=1). So to disassemble thumb2 code starting at 0xfc020000, you would use -s=0xfc020001
- If RAM code regions are recognized (see stubs_entry.S generated by finsig_thumb2) you can disassemble code in those regions by passing addresses directly, e.g. -s=0x010e1001 to disassemble the start of RAM kernel code.
- An end address or instruction count, with -e=<address> or -c=<count>
- A file to dissemble, typically the PRIMARY.BIN for the camera you are working on.
- The firmware start address (CHDK ROMBASEADDR)
Other useful options
- The -s option allows CHDK "stubs" to be used to annotate known functions from stubs_entry.S, funcs_by_name.csv and stubs_entry_2.s. Running capdis from the root of your CHDK source, you would use something like -s=platform/g7x/sub/100d
- Note that if stubs generation fails, the CHDK build process renames the stubs_entry.S file to stubs_entry.S.err. To allow it to be seen by capdis, you must rename it back to stubs_entry.S.
- The -f option can is used to control the output format. Use -f=chdk to generate code suitable for inline assembly in CHDK. Use -f=objdump to output something roughly similar to GNU objdump / stubs2disv7.pl. If neither is specified, the default is similar to capstone standard disassembly.
Disassembling a full firmware dumpEdit
To generate files suitable for reverse engineering, you can use the following procedure.
In the text below <camera> below refers to your platform name, e.g. g7x. <firmware> refers to your firmware version, e.g. 100d
Create an initial tree to generate stubsEdit
If you started your port by copying an existing port, you can skip this step, but you may need to adjust some of the values in your copied tree.
Set up a minimal platform tree, with platform/<camera> platform/<camera>/sub/<firmware>
Create platform/<camera>/makefile.inc, with at least the following values
THUMB_FW=1 PLATFORMOS=dryos PLATFORMOSVER=<DRYOS version number, does not need to be correct> ROMBASEADDR=<your ROM base address, 0xFC000000 on known Digic 6>
Create platform/<camera>/sub/<firmware>/Makefile containing at least
Create platform/<camera>/sub/<firmware>/makefile.inc (can be empty)
make PLATFORM=<camera> PLATFORMSUB=<firmware> rebuild-stubs
For a new port, this will fail because various functions are not found, but stubs_entry.S.err, funcs_by_name.csv etc should be created.
Rename stubs_entry.S.err to stubs_entry.S
Disassemble code regionsEdit
Known digic 6 firmwares have several code regions: The bootloader, the main ROM code and one or two regions which are copied to RAM (or ITCM) on startup. If your firmware is similar to known firmwares, these (except the bootloader) will be detected by finsig_thumb2, and noted in comments.
To disassemble RAM code, look in the "Detected address ranges section" for items listed as RAM code, and use the address range for start and end, like
tools/capdis platform/g9x/sub/101a/PRIMARY.BIN 0xfc000000 -stubs=platform/g9x/sub/101a -s=0x010e1001 -e=0x010fbd18 -f=objdump -d-const -d-addr -d-bin > platform/g9x/sub/101a/RAMCODE.DIS
To dissemble main ROM code, use the "Main firmware start" address as the start (with the thumb it set) and the address from the DEF(ctypes,...) line as the end, like
tools/capdis platform/g9x/sub/101a/PRIMARY.BIN 0xfc000000 -stubs=platform/g9x/sub/101a -s=0xfc020001 -e=0xfc6572ac -f=objdump -d-const -d-addr -d-bin > platform/g9x/sub/101a/ROMCODE.DIS
Note: disassembling a full ROM can take a long time, (up to a few hours on reasonably modern system), and the resulting file can be larger than 100MB
Disassembling code for CHDK sourceEdit
In CHDK, code for startup and various task hooks uses inline assembly generated from firmware code.
To generate this code, you can use capdis like
tools/capdis ../dumps/g7x/sub/100d/PRIMARY.BIN 0xFC000000 -stubs=platform/g7x/sub/100d -f=chdk -s=0xfc064301 -c=60
Wrapping this in a shell script can be more convenient, something like
#!/bin/bash cam=$1 shift sub=$1 shift capdis platform/$cam/sub/$sub/PRIMARY.BIN 0xfc000000 -stubs=platform/$cam/sub/$sub $*