BinCAT takes .ini
files as input. The ini format varies quite often
as we introduce new features, so it has a format version, defined in the
[analyzer]
section, in the ini_version
key.
This documentation is partial, the only reference is the parser code in
parser.mly
.
Example files are provided:
- for x86, see get_key_x86.ini
- for armv7, see get_key_armv7.ini
- for armv8, see get_key_armv8.ini
- for powerpc, see get_key_powerpc.ini
The input file is split into various sections:
analyzer
: global configuration for the analyzerprogram
: program (target) specific configurationsections
: input file sectionsimports
: input file importsstate
: initial state for register and memoryoverride
: state overrides- and arch specific sections:
x86
,armv7
andarmv8
Set the loglevel
option in the [analyzer]
section to between 1
and 4
:
- basic info
- more info
- debug
- advanced debug
Allows the user to specify functions which should be skipped over: they will behave as if they are empty.
fun_skip
is a list of functions to skip, separated by a comma:
-
fun_skip = sk(arg_nb, ret_val), ...
:sk
is either a function name or an addressarg_nb
is the number of argumentsret_val
is the value/taint of the return value. The syntax follows the one described in the state syntax for the initialisation of the memory and registers.
For example: fun_skip=kill(2)
will skip calls to kill
, which has 2
arguments. To specify also its return value to be 0, then add fun_skip = kill(2, 0)
.
Users may want to "nop" some instructions, which can be done by using the nop
key.
For example nop=0x1234, 0x9876
will make the analyzer handle the
instructions at addresses 0x1234
and 0x9876
as a "nop", moving to the
next instruction without side effects.
BinCAT can load ELF coredumps, for example:
[program]
mode = protected
[...]
format = elf
load_elf_coredump = "core_get_key_x86"
BinCAT will load the initial state from the specified core file.
When using a coredump, the [state]
section should be empty
An initial value is defined in 3 parts:
- a concrete value
- a top (unknown) mask
- a taint mask (which can be unknown)
For example: 0x12345600?0x000000FF!0xFF000000
defines a value with
0x123456
as a value for the 3 top bytes- unknown value for the least significant byte
- a known taint for the whole value: the MSB is tainted while the rest is not
One can also skip some parts:
0
: concrete value of 00xFF!0xFF
: concrete tainted value of 0xFF0?0xFFFFFFFF
: unknown value, untainted0!0xFF?0xFFFFFF00
: concrete value of 0, with the LSB tainted and the rest with unknown taint
taint can be also specified by using the magic value TAINT_ALL
.
Important remark: our memory model consider global memory and heap as completely separated spaces (without overlap). By default a value is considered to be into the global memory space. If one wants to set a value in the heap space it has to be prefixed with a 'H'.
Registers are defined by adding entries to the [state]
section with the
following syntax:
reg[NAME] = VALUE
where NAME
is any valid register for the target
architecture and VALUE
is defined according to the rules detailed above.
Memory state is also defined in the [state]
section:
REGION[ADDRESS*size] = VALUE
where:
REGION
can be eithermem
orheap
.ADDRESS
is a number*size
is optional and allows to quickly define big slices. read it as amemset
VALUE
can either be as defined above OR use the advanced syntax defined below.
VALUE
can be defined using hexadecimal values using the following syntax:
|hexvalues|?topmask!taintmask
Caveats:
topmask
andtaintmask
must have the same length as hexvalues.
Allows the user to override value and taint for registry and memory data.
Contains one item for each instruction pointer value where a value is to be modified:
[override]
0x4242 = reg[eax], 0x0!TAINT_ALL; reg[esp], 0x00
The key (0x4242
here) is the instruction pointer value. The value is a
semicolon separated list of destination, value!taint
, where either value
or
taint
can be omitted.