BAP Memory


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, and section.

  • 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 for Image 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.