1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
(** This module contains FFI-functionality for generating serde_json objects on the Rust side
which is used for converting complex Ocaml data structures to Rust data structures.
*)
open Core_kernel
open Bap.Std
open Symbol_utils
type t = nativeint
external rs_finalize_json_builder: t -> unit = "rs_finalize_json_builder"
external rs_build_serde_null: unit -> t = "rs_build_serde_null"
external rs_build_serde_bool: bool -> t = "rs_build_serde_bool"
external rs_build_serde_number: int -> t = "rs_build_serde_number"
external rs_build_serde_string: string -> t = "rs_build_serde_string"
external rs_build_serde_array_from_list: t list -> t = "rs_build_serde_array_from_list"
external rs_build_serde_object: (string * t) list -> t = "rs_build_serde_object"
external rs_build_bitvector: string -> t = "rs_build_serde_bitvector"
external rs_convert_json_to_string: t -> string = "rs_convert_json_to_string"
let add_finalizer value =
(Gc.Expert.add_finalizer_exn value rs_finalize_json_builder) (* TODO: if test throws Invalid_argument exceptions, the values to finalize must be wrapped in ref to ensure heap allocation! *)
let build_null (): t =
let value = rs_build_serde_null () in
let () = add_finalizer value in
value
let build_number (num: int) : t =
let value = rs_build_serde_number num in
let () = add_finalizer value in
value
let build_bool (boolean: bool) : t =
let value = rs_build_serde_bool boolean in
let () = add_finalizer value in
value
let build_string (string_val: string) : t =
let value = rs_build_serde_string string_val in
let () = add_finalizer value in
value
let build_array (obj_list: t list) : t =
let value = rs_build_serde_array_from_list obj_list in
let () = add_finalizer value in
value
let build_object (entries: (string * t) list) : t =
let value = rs_build_serde_object entries in
let () = add_finalizer value in
value
let to_string (serde_json: t) : String.t =
rs_convert_json_to_string serde_json
let of_var_type (var_type: Bil.Types.typ) : t =
match var_type with
| Imm bitsize ->
build_object (
("Immediate", build_number bitsize) :: []
)
| Mem (addr_size, size) ->
build_object (
("Memory", build_object (
("addr_size", build_number (Size.in_bits addr_size)) ::
("elem_size", build_number (Size.in_bits size)) :: []
)) :: [])
| Unk -> build_string "Unknown"
let of_var (var: Var.t) : t =
build_object [
("name", build_string (Var.name var));
("type_", of_var_type (Var.typ var));
("is_temp", build_bool (Var.is_virtual var));
]
let of_cast_type (cast_type: Bil.Types.cast) : t =
build_string (Sexp.to_string (Bil.Types.sexp_of_cast cast_type))
let of_binop_type (binop: Bil.Types.binop) : t =
build_string (Sexp.to_string (Bil.Types.sexp_of_binop binop))
let of_unop_type (unop: Bil.Types.unop) : t =
build_string (Sexp.to_string (Bil.Types.sexp_of_unop unop))
let of_endianness (endianness: Bitvector.endian) : t =
build_string (Sexp.to_string (Bitvector.sexp_of_endian endianness))
let of_bitvector (bitv: Bitvector.t) : t =
let value = rs_build_bitvector (Bitvector.to_string bitv) in
let () = add_finalizer value in
value
let rec of_exp (exp: Exp.t) : t =
begin match exp with
| Var(var) ->
build_object (("Var", of_var var) :: [])
| Int(bitvector) ->
build_object (("Const", of_bitvector bitvector) :: [])
| Load(mem, addr, endian, size) ->
build_object [ ("Load", build_object [
("memory", of_exp mem);
("address", of_exp addr);
("endian", of_endianness endian);
("size", build_number (Size.in_bits size));
]);]
| Store(mem, addr, value, endian, size) ->
build_object [ ("Store", build_object [
("memory", of_exp mem);
("address", of_exp addr);
("value", of_exp value);
("endian", of_endianness endian);
("size", build_number (Size.in_bits size));
]);]
| BinOp(type_, lhs, rhs) ->
build_object [ ("BinOp", build_object [
("op", of_binop_type type_);
("lhs", of_exp lhs);
("rhs", of_exp rhs);
]);]
| UnOp(type_, exp) ->
build_object [ ("UnOp", build_object [
("op", of_unop_type type_);
("arg", of_exp exp);
]);]
| Cast(cast, width, exp) ->
build_object [ ("Cast", build_object [
("kind", of_cast_type cast);
("width", build_number width);
("arg", of_exp exp);
]);]
| Let(var, bound_exp, body_exp) ->
build_object [ ("Let", build_object [
("var", of_var var);
("bound_exp", of_exp bound_exp);
("body_exp", of_exp body_exp)
]);]
| Unknown(text, typ) ->
build_object [ ("Unknown", build_object [
("description", build_string text);
("type_", of_var_type typ);
]);]
| Ite(if_, then_, else_) ->
build_object [ ("IfThenElse", build_object [
("condition", of_exp if_);
("true_exp", of_exp then_);
("false_exp", of_exp else_);
]);]
| Extract(high, low, exp) ->
build_object [ ("Extract", build_object [
("low_bit", build_number low);
("high_bit", build_number high);
("arg", of_exp exp)
]);]
| Concat(left, right) ->
build_object [ ("Concat", build_object [
("left", of_exp left);
("right", of_exp right)
]);]
end
let of_tid (tid: Tid.t) (tid_map: word Tid.Map.t) : t =
build_object [
("id", build_string @@ Tid.name tid);
("address", build_string @@ Address_translation.translate_tid_to_assembler_address_string tid tid_map);
]
let of_def (def: Def.t) (tid_map: word Tid.Map.t) : t =
build_object [
("tid", of_tid (Term.tid def) tid_map);
("term", build_object [
("lhs", of_var (Def.lhs def));
("rhs", of_exp (Def.rhs def));
]);
]
let of_jmp_label (jmp_label: label) (tid_map: word Tid.Map.t) : t =
match jmp_label with
| Direct(tid) ->
build_object [
("Direct", of_tid tid tid_map);
]
| Indirect(exp) ->
build_object [
("Indirect", of_exp exp);
]
let of_call (call: Call.t) (tid_map: word Tid.Map.t) : t =
build_object [
("target", of_jmp_label (Call.target call) tid_map);
("return_", match Call.return call with
| Some(target) -> of_jmp_label target tid_map
| None -> build_null ()
);
]
let of_jmp_kind (kind: jmp_kind) (tid_map: word Tid.Map.t) : t =
match kind with
| Call(call) ->
build_object [
("Call", of_call call tid_map);
]
| Goto(label) ->
build_object [
("Goto", of_jmp_label label tid_map);
]
| Ret(label) ->
build_object [
("Return", of_jmp_label label tid_map);
]
| Int(interrupt_num, tid) ->
build_object [
("Interrupt", build_object [
("value", build_number interrupt_num );
("return_addr", of_tid tid tid_map)
]);
]
let of_jmp (jmp: Jmp.t) (tid_map: word Tid.Map.t) : t =
(* Since BAP 2.0 doesn't emit return statements anymore,
we have check the is_return hint to correct the jump kind for return statements. *)
let is_return = match Term.get_attr jmp Disasm.insn with
| None -> false
| Some(insn) -> Insn.(is return) insn in
let jmp_kind = if is_return then
match Jmp.kind jmp with
| Call(call) -> begin match Call.target call with
| Indirect(exp) -> Ret(Indirect(exp))
| _ -> Jmp.kind jmp
end
| _ -> Jmp.kind jmp
else
Jmp.kind jmp in
build_object [
("tid", of_tid (Term.tid jmp) tid_map);
("term", build_object [
("condition", if Option.is_some (Jmp.guard jmp) then of_exp (Jmp.cond jmp) else build_null ());
("kind", of_jmp_kind jmp_kind tid_map);
]);
]
let of_blk (blk: Blk.t) (tid_map: word Tid.Map.t) : t =
let defs = Seq.to_list (Term.enum def_t blk) in
let defs = List.map defs ~f:(fun def -> of_def def tid_map) in
let jmps = Seq.to_list (Term.enum jmp_t blk) in
let jmps = List.map jmps ~f:(fun jmp -> of_jmp jmp tid_map) in
build_object [
("tid", of_tid (Term.tid blk) tid_map);
("term", build_object [
("defs", build_array defs);
("jmps", build_array jmps);
]);
]
let of_sub (sub: Sub.t) (tid_map: word Tid.Map.t) : t =
let blocks = Seq.to_list (Term.enum blk_t sub) in
let blocks = List.map blocks ~f:(fun block -> of_blk block tid_map) in
build_object [
("tid", of_tid (Term.tid sub) tid_map);
("term", build_object [
("name", build_string (Sub.name sub));
("blocks", build_array blocks);
]);
]
let of_extern_symbol (symbol: extern_symbol) (tid_map: word Tid.Map.t) : t =
build_object [
("tid", of_tid symbol.tid tid_map);
("address", build_string symbol.address);
("name", build_string symbol.name);
("calling_convention", match symbol.cconv with
| Some(cconv) -> build_string cconv
| None -> build_null ()
);
("arguments", build_array (List.map symbol.args ~f:(fun (var, expr, intent) ->
build_object [
("var", of_var var);
("location", of_exp expr);
("intent", match intent with
| Some(In) -> build_string "Input"
| Some(Out) -> build_string "Output"
| Some(Both) -> build_string "Both"
| None -> build_string "Unknown"
)
]
)))
]
let of_program (program: Program.t) (extern_symbols: extern_symbol List.t) (entry_points: Tid.t List.t) (tid_map: word Tid.Map.t) : t =
let subs = Seq.to_list (Term.enum sub_t program) in
let subs = List.map subs ~f:(fun sub -> of_sub sub tid_map) in
build_object [
("tid", of_tid (Term.tid program) tid_map);
("term", build_object [
("subs", build_array subs);
("extern_symbols", build_array (List.map extern_symbols ~f:(fun sym -> of_extern_symbol sym tid_map)));
("entry_points", build_array (List.map entry_points ~f:(fun tid -> of_tid tid tid_map)));
]);
]
let of_project (project: Project.t) (extern_symbols: extern_symbol List.t) (entry_points: Tid.t List.t) (tid_map: word Tid.Map.t) : t =
build_object [
("program", of_program (Project.program project) extern_symbols entry_points tid_map);
("cpu_architecture", build_string (Arch.to_string (Project.arch project)));
("stack_pointer_register", of_var (Symbol_utils.stack_register project));
("callee_saved_registers", build_array (List.map (Cconv.get_register_list project "callee_saved") ~f:(fun reg_name -> build_string reg_name) ));
("parameter_registers", build_array (List.map (Cconv.get_register_list project "params") ~f:(fun reg_name -> build_string reg_name) ))
]