mirror of
https://github.com/pakt/ropc
synced 2026-06-08 16:36:27 +00:00
312 lines
9.6 KiB
OCaml
312 lines
9.6 KiB
OCaml
open Symbeval
|
|
open Printf
|
|
open Sys
|
|
open Int_utils
|
|
|
|
type candidate = Cand of (int * int) * Ast.stmt list
|
|
|
|
let g_start = ref 0
|
|
let g_end = ref 0
|
|
|
|
let mAX_BYTES_BACKWARD = 10
|
|
let hALT = Ast.Halt(Ast.exp_true, [])
|
|
|
|
(* scan for RETs *)
|
|
let scan_section get s s_start s_end =
|
|
let l = ref [] in
|
|
let _ =
|
|
for i=s_start to s_end-1 do
|
|
let j = Int64.of_int i in
|
|
let b = get j in
|
|
if b = char_of_int 0xC3 then
|
|
l := i::!l
|
|
else
|
|
()
|
|
done
|
|
in
|
|
!l
|
|
|
|
let make_scan get = scan_section get
|
|
|
|
let get_range_prog prog i j =
|
|
let (i,j) = cast_range i j in
|
|
let l = Asmir.asmprogram_to_bap_range prog i j in
|
|
l
|
|
|
|
let make_get_range prog = get_range_prog prog
|
|
|
|
(* l must be sorted increasingly *)
|
|
let convert prog positions s_start =
|
|
let get_range = make_get_range prog in
|
|
let get_small_range prev_pos pos =
|
|
let f i =
|
|
let s = (pos-i) in
|
|
let e = pos+1 in (* +1 for RET (0xc3) *)
|
|
let stmts = get_range s e in
|
|
Cand((s,e), stmts)
|
|
in
|
|
let n = min mAX_BYTES_BACKWARD (pos - prev_pos - 1) in
|
|
let _ = if n<0 then failwith "seems like an unsorted positions list" else () in
|
|
(* return list of ranges [[..], [..], ...] *)
|
|
Util.mapn f n
|
|
in
|
|
let f (acc, prev_pos) pos =
|
|
let l = get_small_range prev_pos pos in
|
|
(l::acc, pos)
|
|
in
|
|
let _ = Printf.printf "s_start = 0x%08x\n" s_start in
|
|
let (ranges, _) = List.fold_left f ([], s_start) positions in
|
|
List.concat ranges
|
|
|
|
let make_convert prog = convert prog
|
|
|
|
let dump_range range =
|
|
let _ = Printf.printf "---\n" in
|
|
(*
|
|
let dumper = Pp.ast_short_dump in
|
|
let range = List.map (fun x -> let AsmMeta(_,_,_,l) = x in l) range in
|
|
let range = List.concat range in
|
|
let strings = dumper range in
|
|
let _ = List.map (fun s -> Printf.printf "%s\n" s) strings in
|
|
let _ = Printf.printf "---\n" in
|
|
*)
|
|
()
|
|
|
|
let dump_ranges ranges =
|
|
let _ = Printf.printf "# NEW SECTION\n" in
|
|
let _ = List.map (fun l -> dump_range l) ranges in
|
|
()
|
|
|
|
let write_to_file fn s =
|
|
let oc = open_out fn in
|
|
let _ = fprintf oc "%s" s in
|
|
let _ = close_out oc in
|
|
()
|
|
|
|
let filter_sections prog f =
|
|
let sections = Asmir.get_all_asections prog in
|
|
let sections = Array.to_list sections in
|
|
let sections = List.filter f sections in
|
|
sections
|
|
|
|
let sec_start_end sections =
|
|
let ss s = Asmir.get_section_start s in
|
|
let se s = Asmir.get_section_end s in
|
|
let triples = List.map (fun s -> (s, safe_to_int64 (ss s), safe_to_int64 (se s))) sections in
|
|
triples
|
|
|
|
let candidates_all_sections prog =
|
|
let sections = filter_sections prog Asmir.is_code in
|
|
let get = Asmir.get_prog_contents prog in
|
|
let triples = sec_start_end sections in
|
|
(*let _ = test_get get in*)
|
|
let scan = make_scan get in
|
|
let f (s,ss,se) =
|
|
let positions = scan s ss se in
|
|
let positions = List.sort compare positions in
|
|
(s, positions, ss)
|
|
in
|
|
let sec_positions = List.map f triples in
|
|
let convert_to_bap = make_convert prog in
|
|
let f (s, positions, s_start) =
|
|
let ranges = convert_to_bap positions s_start in
|
|
(s, ranges)
|
|
in
|
|
let sec_ranges = List.map f sec_positions in
|
|
(*
|
|
let _ = List.map (fun (s, ranges) -> dump_ranges ranges) sec_ranges in
|
|
*)
|
|
let ranges = List.map (fun (x,y) -> y) sec_ranges in
|
|
let ranges = List.concat ranges in
|
|
|
|
(* just test *)
|
|
if !g_start > 0 && !g_end > !g_start then
|
|
let s,e = (!g_start, !g_end) in
|
|
let l = get_range_prog prog s e in
|
|
let c = Cand((s,e),l) in
|
|
let ranges = [c] in
|
|
ranges
|
|
else
|
|
ranges
|
|
|
|
(* delta - var,value hash *)
|
|
let dump_ctx_delta delta =
|
|
Concrete.print_values delta
|
|
|
|
(* Special() is a sign of disasm error, execution will fail on these *)
|
|
let good_disasm stmts =
|
|
let p x =
|
|
match x with
|
|
| Ast.Halt(_,_)
|
|
| Ast.Special(_,_) ->
|
|
true
|
|
| _ -> false
|
|
in
|
|
let trash = List.exists p stmts in
|
|
not trash
|
|
|
|
(* don't allow ANY jumps.
|
|
incomplete, but sufficient, to avoid infinite loops *)
|
|
let no_jumps stmts =
|
|
let p x =
|
|
match x with
|
|
| Ast.Jmp(_,_)
|
|
| Ast.CJmp(_,_,_,_) ->
|
|
true
|
|
| _ -> false
|
|
in
|
|
let trash = List.exists p stmts in
|
|
not trash
|
|
|
|
(* filter bad disasms and potential infinite loops *)
|
|
let filter_candidates candidates =
|
|
(* get stmts *)
|
|
let gs cand = let Cand(_,stmts) = cand in stmts in
|
|
let fm cand = let Cand(m,_) = cand in m in
|
|
let candidates = List.filter (fun c -> good_disasm (gs c)) candidates in
|
|
(* RET gets split into few instructions, last of them being jmp *)
|
|
let candidates = List.map (fun c -> Cand(fm c, Common.drop_last (gs c))) candidates in
|
|
let candidates = List.filter (fun c -> no_jumps (gs c)) candidates in
|
|
candidates
|
|
|
|
module GadgetSet = Set.Make(
|
|
struct
|
|
let compare = Pervasives.compare
|
|
type t = Common.gadget
|
|
end
|
|
)
|
|
|
|
let gadget_set_of_list = List.fold_left (fun acc x -> GadgetSet.add x acc) GadgetSet.empty
|
|
|
|
(* g_list is a list of (f_init,f_finish) functions of gadgets *)
|
|
let classify g_list cand =
|
|
let Cand((off_s,off_e), stmts) = cand in
|
|
let org_stmts = stmts in
|
|
let add_halt stmts =
|
|
stmts @ [hALT]
|
|
in
|
|
let stmts = add_halt stmts in
|
|
let cinit = Gdefs.common_init stmts in
|
|
|
|
let f acc g_func =
|
|
let empty_set = GadgetSet.empty in
|
|
let (_, _, _, repeats) = g_func () in
|
|
let co_exec (gset,_,_) i =
|
|
(* g_func might want to initiate regs/mem to random values with each run,
|
|
so this is necessary. computing the tuple just once would make the registers constant *)
|
|
let (f_init, f_finish, mem_hax, _) = g_func () in
|
|
let init = f_init stmts in (* FIXME: should return init, stmts? *)
|
|
let init = cinit @ init in
|
|
let ctx = Symbeval.concretely_execute ~i:init ~mem_hax:mem_hax (stmts) in
|
|
let gadgets, mod_regs = f_finish ctx in
|
|
(* let _ = Printf.printf "len g = %d\n" (List.length gadgets) in *)
|
|
let gset_new = gadget_set_of_list gadgets in
|
|
let gset = if i=0 then gset_new else GadgetSet.inter gset gset_new in
|
|
(* mod_regs are going to be verified later, so we don't have to be very precise *)
|
|
gset, mod_regs, Some(ctx)
|
|
in
|
|
let range = Util.mapn (fun x->x) (repeats-1) in (* len(range) = repeats *)
|
|
let (gset, mod_regs, ct) = List.fold_left co_exec (empty_set, [], None) range in
|
|
let ctx =
|
|
match ct with
|
|
| Some(ctx) -> ctx
|
|
| None -> assert false
|
|
in
|
|
let gadgets = GadgetSet.elements gset in
|
|
(* we are not saving stmts, because marshaling them doesn't work for some reason *)
|
|
(* verifier will not accept Halt/Special statements *)
|
|
let wrapped = Gdefs.wrap_meta ctx gadgets mod_regs off_s off_e in
|
|
wrapped::acc
|
|
in
|
|
let gadgets = List.fold_left f [] g_list in
|
|
let gadgets = List.concat gadgets in
|
|
gadgets
|
|
|
|
let make_classify = classify (Gdefs.g_list ())
|
|
|
|
let biggest_rw_data prog =
|
|
(* let datas = filter_sections prog Asmir.is_rw_data in *)
|
|
let datas = filter_sections prog Asmir.is_bss in
|
|
let sse = sec_start_end datas in
|
|
let sse = List.map (fun (_,s,e) -> (e-s,s,e)) sse in
|
|
let cmp (s1,_,_) (s2,_,_) = compare s1 s2 in
|
|
let sorted = List.sort cmp sse in
|
|
let sorted = List.map (fun (_,s,e) -> (s,e)) sorted in
|
|
let data = List.nth sorted ((List.length sorted)-1) in
|
|
data
|
|
|
|
let parse_exe fn =
|
|
let prog = Asmir.open_program fn in
|
|
let candidates = candidates_all_sections prog in
|
|
let classify = make_classify in
|
|
let candidates = filter_candidates candidates in
|
|
let gmetas = List.map (fun cand -> classify cand) candidates in
|
|
let gmetas = List.concat gmetas in
|
|
(*
|
|
let _ = Printf.printf "len gmetas: %d\n" (List.length gmetas) in
|
|
let pr gm = let Common.GMeta(g,_,_,_)=gm in Printf.printf "# %s\n" (Common.dump_gadget g) in
|
|
let _ = List.map pr gmetas in
|
|
*)
|
|
let data = biggest_rw_data prog in
|
|
(*
|
|
let (s,e) = data in
|
|
let _ = Printf.printf "biggest: %x-%x\n" s e in
|
|
let gmetas = [] in
|
|
*)
|
|
gmetas, data
|
|
|
|
(*
|
|
let dump_gmetas gmetas =
|
|
let dumped = List.map (fun gm -> Gdefs.dump_gmeta gm) gmetas in
|
|
let _ = List.map (fun s -> Printf.printf "---\n%s\n" s) dumped in
|
|
()
|
|
*)
|
|
|
|
let wrap_in_container fn gmetas data =
|
|
let cont = Common.GContainer(fn, data, gmetas) in
|
|
cont
|
|
|
|
let set_start_end_offsets s e =
|
|
let id = fun x -> x in
|
|
begin
|
|
(g_start := Scanf.sscanf s "0x%x" id);
|
|
(g_end := Scanf.sscanf e "0x%x" id);
|
|
Printf.printf "start: %x\n" !g_start;
|
|
Printf.printf "end: %x\n" !g_end;
|
|
end
|
|
|
|
let main () =
|
|
let parse_and_save fn ofn =
|
|
let gmetas, data = parse_exe fn in
|
|
(* let _ = dump_gmetas gmetas in *)
|
|
let container = wrap_in_container fn gmetas data in
|
|
Common.marshal_to_file ofn container
|
|
in
|
|
let argc = Array.length Sys.argv in
|
|
if argc > 1 then
|
|
let fn = Sys.argv.(1) in
|
|
let ofn = if argc = 4 then
|
|
let s = Sys.argv.(2) in
|
|
let e = Sys.argv.(3) in
|
|
let _ = set_start_end_offsets s e in
|
|
"candidates.bin"
|
|
else
|
|
if argc = 3 then
|
|
Sys.argv.(2)
|
|
else
|
|
assert false
|
|
in
|
|
parse_and_save fn ofn
|
|
else
|
|
let err = Printf.sprintf "Usage:\n%s <exe file> <out candidates file>\n" Sys.argv.(0) in
|
|
print_string err
|
|
(*
|
|
let main () =
|
|
try
|
|
_main ()
|
|
with _ ->
|
|
let s = Printexc.get_backtrace () in
|
|
Printf.printf "%s\n" s
|
|
*)
|
|
let _ = main()
|