This post explores a portion of the BAP API that may be useful when interacting with binary images and their contents. The intention is to guide users with initial steps for interacting with this interface; users may then explore further features of the API depending on their needs. Thus, we elide some details of the full API and data structures.
In this post, we use an ELF binary corresponding to example.c
from the
previous post.
Image contents
How do I print out all of the memory chunks (with labels) in an ELF binary?
Project.memory project
|> Memmap.to_sequence
|> Seq.iter ~f:(fun (mem,v) ->
Format.printf "%s(%a)@.%a@." (Value.tagname v) Value.pp v Memory.pp mem);
Output:
segment(02)
00400000 7F 45 4C 46 02 01 01 00 00 00 00 00 00 00 00 00 |.ELF............|
00400010 02 00 3E 00 01 00 00 00 40 04 40 00 00 00 00 00 |..>.....@.@.....|
00400020 40 00 00 00 00 00 00 00 B0 14 00 00 00 00 00 00 |@...............|
00400030 00 00 00 00 40 00 38 00 09 00 40 00 23 00 20 00 |....@.8...@.#. .|
...
symbol(h)
0040052D 55 48 89 E5 48 83 EC 10 89 7D FC 83 45 FC 01 8B |UH..H....}..E...|
0040053D 45 FC 89 C7 E8 02 00 00 00 C9 C3 |E.......... |
symbol(g)
00400548 55 48 89 E5 48 83 EC 10 89 7D FC 83 7D FC 0A 7E |UH..H....}..}..~|
00400558 05 8B 45 FC EB 0E 83 45 FC 01 8B 45 FC 89 C7 E8 |..E....E...E....|
00400568 C1 FF FF FF C9 C3 |...... |
...
section(.rodata)
0000000000400640: 01 00 02 00 52 65 73 3a 20 25 64 0a 00
section(.eh_frame_hdr)
00400650 01 1B 03 3B 4C 00 00 00 08 00 00 00 B0 FD FF FF |...;L...........|
00400660 98 00 00 00 F0 FD FF FF 68 00 00 00 DD FE FF FF |........h.......|
00400670 C0 00 00 00 F8 FE FF FF E0 00 00 00 1E FF FF FF |................|
...
-
The binary image contents can be accessed with
Project.memory
. This returns a Memmap data structure which is a lookup data structure, mapping memory regions to values. -
We iterate over the Memmap, which gives us tuples
(mem,v)
corresponding to (memory,value). For each of these values associated with memory, we can extract a tagname. -
Tag names correspond to the type (operating somewhat like a category) of the chunks of memory. For instance, in the output we see
symbol
,segment
, andsection
. -
Value.pp
extracts the value of the relevant type, and prints it. For example, a section and it’s corresponding name (.rodata
,.got
, …). -
For the interested reader, see more on universal values.
Image Sections
How do I print the memory contents of an ELF section, such as ‘.rodata’?
let find_section_by_name name =
let open Format in
let memory = Project.memory project in
Memmap.to_sequence memory |> Seq.find_map ~f:(fun (m,v) ->
Option.(Value.get Image.section v >>= fun n ->
Option.some_if (n = name) m)) in
(match find_section_by_name ".rodata" with
| Some mem -> printf "%a" Memory.pp mem
| None -> printf "No memory for this section\n");
Output:
0000000000400640: 01 00 02 00 52 65 73 3a 20 25 64 0a 00
Notes:
-
This time, we use
Value.get
on a special section tag defined forImage
to extract the section name. -
Where the value corresponds to a section name we are looking for, we return the memory
m
.
Reading memory
How can I print out strings in the .rodata section?
In the output of the previous example, we can recognize the hex encoding of a
string starting at 0x400644
. We define a number of helper functions to
extract and print it:
(** Provide a view of the memory area, starting at [addr] *)
let mem_from_addr addr mem =
match Memory.view ~word_size:`r8 ~from:addr mem with
| Ok r -> r
| Error e -> failwith @@ sprintf "Failure: %s\n" @@ Error.to_string_hum e in
(** Given a memory area, start at the beginning and collect characters in the
accumulator string until we reach a until byte. Return the string *)
let read_string mem =
let (!) = Char.to_string in
Memory.foldi ~word_size:`r8 mem ~init:(false,"")
~f:(fun addr word (set,acc) ->
let char = Word.to_chars word LittleEndian |> Seq.hd_exn in
match set,char with
| (false,'\x00') -> (true,acc)
| (false,c) -> (false,acc^(!c))
| (true,c) -> (true,acc)) |> snd in
(** Read from the address *)
let addr = Addr.of_string "0x400644:64" in
(** Get and print the result *)
let result =
let open Option in
find_section_by_name ".rodata" >>= fun mem ->
Option.some_if (Memory.contains mem addr) (
let mem' = mem_from_addr addr mem in
read_string mem') in
(match result with
| Some s -> printf "%s\n%!" s
| None -> failwith "No string could be found");
Output:
Res: %d
Notes:
-
Memory.view
gives us a way to create pieces of memory that we can use in arbitrary ways. -
Memory.foldi
provides an interface for folding over the address range of a memory structure. -
For the interested reader, refer to the memory iterators and memory module.