To run the examples below, open baptop, then:
open Bap.Std;;
- https://binaryanalysisplatform.github.io/bap/api/master/Bap.Std.Bil.html
- https://binaryanalysisplatform.github.io/bap/api/master/Bap.Std.Bil.Types.html
- https://binaryanalysisplatform.github.io/bap/api/master/Bap.Std.Var.html
Or:
https://binaryanalysisplatform.github.io/bap/api/master/Bap.Std.Size.html
Sizes are bit-widths. They are 8, 16, ..., 256:
`r8 | `r16 | ... | `r256
Create a size of width 16-bits:
let s = Size.of_int s;;
That returns error or Ok. Get just the value:
let s = Size.of_int_exn 16;;
Get the number of bits of the size:
let s_width = Size.in_bits s;;
Get the number of bytes in the size:
let s_bytes = Size.in_bytes s;;
Address sizes (Std.addr_size) are sizes for addresses.
They can be 32 or 64 bits:
`r32 | `r64
Create an address size of width 32:
let a = Size.addr_of_int 32;;
Get just the value:
let a = Size.addr_of_int_exn 32;;
Get the number of bytes in the address size:
let a_bytes = Size.in_bytes a;;
There are two types of values in BIL.
- Immediate values
Imm of Int: These are just n-bit words. - A storage slot
Mem Std.addr_size * Std.Size: A pair of address size, and value size. E.g.,Mem (32, 8)= addresses are 32-bit words, and values are 8-bit words.
See type typ in:
https://binaryanalysisplatform.github.io/bap/api/master/Bap.Std.Bil.Types.html.
To construct instances of these types, use these functions: https://binaryanalysisplatform.github.io/bap/api/master/Bap.Std.Type.html
For example, to create an immedate 3-bit type:
let imm_type = Type.imm 3;;
To create a type of storage slot that's 32 * 8:
let size_32 = Size.addr_of_int_exn 32;;
let size_8 = Size.of_int_exn 8;;
let mem_type = Type.mem size_32 size_8;;
Or more concisely:
let mem_type = Type.mem `r32 `r8;;
See https://binaryanalysisplatform.github.io/bap/api/master/Bap.Std.Var.html.
A variable in BIL represents a symbolic name that can have different values at different times during program execution. It can also have versions (i.e. different indices).
Typically, a variable represents a register or a memory location. But, you can also create virtual variables, that correspond to no actual physical location in the program. These are artificially added variables.
When you create a variable, you specify the variable's type, which defines
the values it can range over. There are two possible types, the Imm and
Mem types mentioned above.
Imm nvariables: a container that can hold anyn-bit word.Mem a * svariables: a container for storage slotsa * s.
Identity of variables is defined as same name, same version, and same type. Sameness of variables is defined as same name and type.
So different versions of the same variable are not identical, but there
is a function called same that will ignore the versions and check
simply if two variables have the same name.
Note that the Var module doesn't involve assigning any values to variables.
It just lets you define the container (variable) that values can be put into.
Create a type for 3-bit words:
let v1_type = Type.imm 3;;
Now create a virtual variable called "v1" with that type:
let v1_var = Var.create ~is_virtual:true "v1" v1_type;;
Get its name:
let v1_name = Var.name v1_var;;
Get its type:
let v1_typ = Var.typ v1_var;;
Check if it's physical:
let v1_is_physical = Var.is_physical v1_var;;
Get it's index:
let v1_index = Var.index v1_var;;
Create a new version of the variable:
let v1_var' = Var.with_index v1_var 1;;
Get it's index:
let v1_index' = Var.index v1_var';;
Confirm that the two variables are not identical:
let is_ident = v1_var = v1_var';;
Confirm that they are the same (in name):
let is_same = Var.same v1_var v1_var';;
Get the original version (index 0), called the base:
let v1_base = Var.base v1_var';;
Confirm that it's identical to the original v1_var:
let base_is_ident = v1_var = v1_base;;
Confirm that it's identical to version 1:
let base_is_ident' = v1_var' = v1_base;;
Confirm that it's the same (in name) to the other variants:
let is_same' = Var.same v1_base v1_var;;
let is_same'' = Var.same v1_base v1_var';;
Create a new variable with the same name:
let v1_new = Var.create ~is_virtual:true "v1" v1_type;;
Confirm that it's identical to the original:
let new_is_ident = v1_var = v1_new;;
And confirm that it's the same in name:
let is_same_new = Var.same v1_var v1_new;;
Create a new 4-bit word type:
let v1_type' = Type.imm 4;;
Create a variable named "v1" with that type:
let v1_new' = Var.create ~is_virtual:true "v1" v1_type';;
Confirm that this new one is not the same (because it's type is different):
let is_same_new' = Var.same v1_var v1_new';;
Create a type for 32-bit wide memory slots with 8-bit wide cells (?):
let size_32 = Size.addr_of_int_exn 32;;
let size_8 = Size.of_int_exn 8;;
let r1_type = Type.mem size_32 size_8;;
Or:
let r1_type = Type.mem `r32 `r8;;
Now create a physical variable called "r1" with that type:
let r1_var = Var.create "r1" r1_type;;
Get its name:
let r1_name = Var.name r1_var;;
Get its type:
let r1_typ = Var.typ r1_var;;
Check if it's physical:
let r1_is_physical = Var.is_physical r1_var;;
Get it's index:
let r1_index = Var.index r1_var;;
Etc.
BIL expressions include integers, variables, casting, let expressions, load/store from memory, and so on.
See the type exp here:
https://binaryanalysisplatform.github.io/bap/api/master/Bap.Std.Bil.Types.html
Statements include moves, jumps, while and if, and exceptions.
The simplest expression is just an integer (as a binary word).
Create the integer 1 as an 8-bit word:
let w1 = Word.of_int ~width:8 1;;
Create a BIL expression of this integer:
let w1_exp = Bil.int w1;;
The Type module has an infer function that can be used
to type check the expression:
let w1_is_typed = Type.infer w1_exp;;
Create another integer, 5 as an 8-bit word:
let w5 = Word.of_int ~width:8 5;;
Create a BIL expression of this integer:
let w5_exp = Bil.int w5;;
Type check it:
let w5_is_typed = Type.infer w5_exp;;
BIL has plus, minus, and other binary operators.
To create an expression with a binary operator, use Bil.binop,
and pass it the operator, then the two expressions.
E.g., create an expression that adds w1_exp and w5_exp:
let w1_plus_w5 = Bil.binop Bil.plus w1_exp w5_exp;;
Type check it:
let w1_plus_w5_is_typed = Type.infer w1_plus_w5;;
Assigning expressions to variables is done with the move statement.
Create a variable that can hold 8-bit words:
let v1_type = Type.imm 8;;
let v1_var = Var.create ~is_virtual:true "v1" v1_type;;
Create a BIL statement that assigns w1_exp to this variable:
let v1_assgn = Bil.move v1_var w1_exp;;
A BIL program is a list of statements.
A program with one statement:
let bil_prog = [v1_assgn];;
The Type module has function to type check a BIL program.
E.g., check that the program is well typed, or get the first error:
let prog_is_typed = Type.check bil_prog;;