mirror of
https://github.com/pakt/ropc
synced 2026-06-08 16:36:27 +00:00
800 lines
28 KiB
OCaml
800 lines
28 KiB
OCaml
(* Assumes the parser file is parser.mly and the lexer file is "lexer.mll". *)
|
|
|
|
open Common
|
|
open Printf
|
|
open Ast (* for types *)
|
|
open Cdefs
|
|
open Analysis
|
|
|
|
let hARDCODED_PRINTF = 0x080484a0
|
|
let cRASH_ADDRESS = 0x1111
|
|
let sTACK_VAR_OFF = 0
|
|
let fRAME_VAR_OFF = 4
|
|
let dATA_OFF = 8 (* start writing tables here *)
|
|
|
|
let nO_NAME_LABEL = "@@"
|
|
let gLOBAL_END_LABEL = "global_end"
|
|
|
|
let trd (_,_,x) = x
|
|
let i32 = Int32.of_int
|
|
let fun_label id = "function_"^id
|
|
let fun_local_label fun_id id = "local_"^fun_id^"_"^id
|
|
|
|
let print_errors errors =
|
|
let errors = Ast.dump_errors errors in
|
|
let rec aux errors =
|
|
match errors with
|
|
| hd::tl ->
|
|
let hd = "ERROR. "^hd^"\n" in
|
|
let _ = printf "%s" hd in
|
|
aux tl
|
|
| [] -> ()
|
|
in
|
|
aux errors
|
|
|
|
let pick_different_reg used =
|
|
let regs = Common.rEGS_NO_ESP in
|
|
let p reg = try let _ = List.find (fun x->x=reg) used in false with Not_found -> true in
|
|
let not_used = List.filter p regs in
|
|
match not_used with
|
|
| hd::tl -> hd
|
|
| _ -> assert false
|
|
|
|
(* every invocation of f_next_reg returns new symbolic reg *)
|
|
let make_rewrite_exp f_next_reg read_local ref_local =
|
|
let rec rewrite_exp exp oreg =
|
|
match exp with
|
|
| BinOp(exp1, op, exp2) ->
|
|
let reg1 = f_next_reg () in
|
|
let iexp1 = rewrite_exp exp1 reg1 in
|
|
let reg2 = f_next_reg () in
|
|
let iexp2 = rewrite_exp exp2 reg2 in
|
|
let o = BinO(oreg, reg1, op, reg2) in
|
|
iexp1 @ iexp2 @ [o]
|
|
| UnOp(op, exp) ->
|
|
begin
|
|
match op with
|
|
| Sub -> rewrite_exp (BinOp(Const(0),op,exp)) oreg
|
|
| Not -> rewrite_exp (BinOp(Const(-1),Xor,exp)) oreg
|
|
| _ -> assert false
|
|
end
|
|
| Var(id) ->
|
|
let rl = read_local id oreg in
|
|
[rl]
|
|
| Ref(id) ->
|
|
let rl = ref_local id oreg in
|
|
[rl]
|
|
| ReadMem(id) ->
|
|
let addr_reg = f_next_reg () in
|
|
let rl = read_local id addr_reg in
|
|
let rm = ReadM(oreg, addr_reg) in
|
|
[rl;rm]
|
|
| Const(x) ->
|
|
let mov = MovRegConst(oreg, x) in
|
|
[mov]
|
|
in
|
|
rewrite_exp
|
|
|
|
let rewrite_stmt stack_ptr frame_ptr locals fun_id stmt =
|
|
let f_next_reg = make_reg_generator () in
|
|
let rw_local id reg f_ctor =
|
|
let id2off id = try Hashtbl.find locals id with Not_found -> assert false in
|
|
let off = id2off id in
|
|
f_ctor off reg
|
|
in
|
|
let write_local id reg =
|
|
rw_local id reg (fun off reg -> WriteLocal(off,reg))
|
|
in
|
|
let read_local id reg =
|
|
rw_local id reg (fun off reg -> ReadLocal(off,reg))
|
|
in
|
|
let ref_local id reg =
|
|
rw_local id reg (fun off reg -> LocalAddr(off, reg))
|
|
in
|
|
let deref_local id reg =
|
|
let f off reg =
|
|
let addr_reg = f_next_reg () in
|
|
let rl = ReadLocal(off, addr_reg) in
|
|
let wm = WriteM(addr_reg, reg) in
|
|
[rl;wm]
|
|
in
|
|
rw_local id reg f
|
|
in
|
|
let rewrite_exp = make_rewrite_exp f_next_reg read_local ref_local in
|
|
let push_arg arg =
|
|
(* just check if args are simple *)
|
|
let _ = match arg with
|
|
| Var(_) | ReadMem(_) | Ref(_) | Const(_) -> true
|
|
| _ -> assert false
|
|
in
|
|
let reg = f_next_reg () in
|
|
let iarg = rewrite_exp arg reg in
|
|
let push = PushReg(reg) in
|
|
iarg @ [push]
|
|
in
|
|
let push_args args =
|
|
let rec aux acc args =
|
|
match args with
|
|
| arg::tl ->
|
|
let pa = push_arg arg in
|
|
aux (pa::acc) tl
|
|
| [] -> acc
|
|
in
|
|
let pushes = aux [] args in
|
|
(* let pushes = List.rev pushes in *)
|
|
List.concat pushes
|
|
in
|
|
let set_eax_on_cond cond =
|
|
(* ah = SF ZF xx AF xx PF xx CF *)
|
|
(* returns mask and the value required to take the jump *)
|
|
let flag_mask_const flag =
|
|
let mask,v =
|
|
match flag with
|
|
| E -> (1 lsl 6), 1 lsl 6 (* jz, ZF = 1 *)
|
|
| A -> (1 lor (1 lsl 6)), 0 (* ja, CF = ZF = 0 *)
|
|
| B -> 1, 1 (* jb, CF = 1 *)
|
|
| _ -> assert false
|
|
in
|
|
(* flags are saved to AH, so shift *)
|
|
let mask,v = mask lsl 8, v lsl 8 in
|
|
mask, v
|
|
in
|
|
let neg, flags =
|
|
match cond with
|
|
| Cond(flags) -> false, flags
|
|
| NCond(flags) -> true, flags
|
|
in
|
|
(* FIXME: just one flag atm *)
|
|
let flag = match flags with hd::[] -> hd | _ -> assert false in
|
|
if flag=MP then
|
|
let mov = MovRegConst(C(EAX), 1) in
|
|
[mov]
|
|
else
|
|
let mask, v = flag_mask_const flag in
|
|
let reg = f_next_reg () in
|
|
let mov1 = MovRegConst(reg, mask) in
|
|
let and_ah = BinO(C(EAX), C(EAX), And, reg) in
|
|
let reg = f_next_reg () in
|
|
let mov2 = MovRegConst(reg, v) in
|
|
let sub = BinO(C(EAX), C(EAX), Sub, reg) in
|
|
let lahf = SaveFlags in
|
|
let reg = f_next_reg () in
|
|
(* ZF position in EAX: 6th bit of AH *)
|
|
let mov3 = MovRegConst(reg, 1 lsl (6+8)) in
|
|
let shr = BinO(C(EAX), C(EAX), Div, reg) in
|
|
let reg = f_next_reg () in
|
|
let mov4 = MovRegConst(reg, 1) in
|
|
(* eax=1 iff cond *)
|
|
let last = [BinO(C(EAX), C(EAX), And, reg)] in
|
|
let last = last @
|
|
if neg then [BinO(C(EAX), C(EAX), Xor, reg)]
|
|
else []
|
|
in
|
|
[mov1;and_ah;mov2;sub;lahf;mov3;shr;mov4]@last
|
|
in
|
|
let rewrite stmt =
|
|
match stmt with
|
|
| Assign(id, exp) ->
|
|
let reg = f_next_reg () in
|
|
let iexp = rewrite_exp exp reg in
|
|
let wl = write_local id reg in
|
|
iexp @ [wl]
|
|
| DerefAssign(id, exp) ->
|
|
let reg = f_next_reg () in
|
|
let iexp = rewrite_exp exp reg in
|
|
let wl = deref_local id reg in
|
|
iexp @ wl
|
|
| WriteMem(id, exp) ->
|
|
let exp_reg = f_next_reg () in
|
|
let iexp = rewrite_exp exp exp_reg in
|
|
let addr_reg = f_next_reg () in
|
|
let rl = read_local id addr_reg in
|
|
let wm = WriteM(addr_reg, exp_reg) in
|
|
iexp @ [rl;wm]
|
|
| Cmp(exp1, exp2) ->
|
|
let reg1, reg2 = f_next_reg (), f_next_reg () in
|
|
let iexp1 = rewrite_exp exp1 reg1 in
|
|
let iexp2 = rewrite_exp exp2 reg2 in
|
|
let reg = f_next_reg () in
|
|
let sub = BinO(reg, reg1, Sub, reg2) in
|
|
let lahf = SaveFlags in
|
|
iexp1 @ iexp2 @ [sub; lahf]
|
|
| Call(id, ExpArgs(exp_args)) ->
|
|
let pushes = push_args exp_args in
|
|
let reg = f_next_reg () in
|
|
let mov = MovRegSymb(reg, FromTo(Named(fun_label id), Unnamed(Forward))) in
|
|
let p = PushReg(reg) in
|
|
let reg = f_next_reg () in
|
|
let mov2 = MovRegSymb(reg, FromTo(Unnamed(Forward), Named(fun_label id))) in
|
|
let add = OpStack(Add, reg) in (* jmp *)
|
|
let lbl = Lbl(nO_NAME_LABEL) in
|
|
pushes @ [mov;p;mov2;add;lbl]
|
|
| ExtCall(id, ExpArgs(exp_args)) ->
|
|
let rec range i j = if i >= j then [] else i :: (range (i+1) j) in
|
|
let make_filler n =
|
|
let m x = let x = x land 0xFF in (x lsl 24) lor (x lsl 16) lor (x lsl 8) lor x in
|
|
let f acc x = RawHex(m x)::acc in
|
|
let nums = range 0 n in
|
|
let filler = List.fold_left f [] nums in
|
|
let filler = List.rev filler in
|
|
filler
|
|
in
|
|
let store_args imp_addr args =
|
|
let addr_reg = f_next_reg () in
|
|
let v_reg = f_next_reg () in
|
|
let off_reg = f_next_reg () in
|
|
let fix_reg = f_next_reg () in
|
|
|
|
let per_arg acc arg =
|
|
let iarg = rewrite_exp arg v_reg in
|
|
let wm = WriteM(addr_reg, v_reg) in
|
|
let set = MovRegConst(off_reg, 4) in
|
|
let add = BinO(addr_reg, addr_reg, Add, off_reg) in
|
|
acc @ (iarg@[wm;set;add]) (* O(n^2) *)
|
|
in
|
|
let tmp_reg = f_next_reg () in
|
|
let lbl = Lbl(nO_NAME_LABEL) in
|
|
let save_esp = MovRegReg(tmp_reg, C(ESP)) in
|
|
let mov = MovRegSymb(fix_reg, FromTo(Unnamed(Backward), Unnamed(Forward))) in
|
|
let fix1 = BinO(addr_reg, tmp_reg, Add, fix_reg) in
|
|
(* Restore import address *)
|
|
let reg = f_next_reg () in
|
|
let set_imp = MovRegConst(reg, imp_addr) in
|
|
let wm = WriteM(addr_reg, reg) in
|
|
let reg = f_next_reg () in
|
|
let set8 = MovRegConst(reg, 8) in
|
|
let fix2 = BinO(addr_reg, addr_reg, Add, reg) in
|
|
let stores = List.fold_left per_arg [] args in
|
|
[save_esp;lbl;mov;fix1;set_imp;wm;set8;fix2] @ stores
|
|
in
|
|
let jmp_over_locals locals_filler =
|
|
let n = List.length locals_filler in
|
|
let reg = f_next_reg () in
|
|
let mov = MovRegConst(reg, n*4) in
|
|
let ops = OpStack(Add, reg) in
|
|
[mov;ops]
|
|
in
|
|
let imp_addr = hARDCODED_PRINTF in
|
|
let cmt_s = sprintf "jmp %s" id in
|
|
(* At least 128 bytes for locals *)
|
|
let n_args = List.length exp_args in
|
|
let locals_filler = make_filler (256/4) in
|
|
let jmp_skip_locals = jmp_over_locals locals_filler in
|
|
(* FIXME: hardcoded printf *)
|
|
let jmp_imp = RawHex(imp_addr) in
|
|
(* FIXME: we don't need equality in AdvStack, just >= *)
|
|
let adv = AdvanceStack(n_args*4+4) in
|
|
let lbl = Lbl(nO_NAME_LABEL) in
|
|
let args_filler = make_filler n_args in
|
|
let write_args = store_args imp_addr exp_args in
|
|
write_args @ jmp_skip_locals @ locals_filler @
|
|
[Comment(cmt_s);lbl;jmp_imp;adv] @ (args_filler)
|
|
| Branch(cond, id) ->
|
|
(* eax=1 iff cond, 0 otherwise *)
|
|
let setz = set_eax_on_cond cond in
|
|
let reg = f_next_reg () in
|
|
let start = Unnamed(Forward) in
|
|
let fin = Named(fun_local_label fun_id id) in
|
|
let mov = MovRegSymb(reg, FromTo(start, fin)) in
|
|
let mul = BinO(C(EAX), C(EAX), Mul, reg) in
|
|
let add = OpStack(Add, C(EAX)) in (* jmp *)
|
|
let lbl = Lbl(nO_NAME_LABEL) in
|
|
setz @ [mov; mul; add; lbl;]
|
|
| Label(id) -> [Lbl(fun_local_label fun_id id)]
|
|
|
|
| Enter(n) ->
|
|
let reg = f_next_reg () in
|
|
let rm1 = ReadMConst(reg, frame_ptr) in
|
|
let push = PushReg(reg) in
|
|
let reg = f_next_reg () in
|
|
let rm2 = ReadMConst(reg, stack_ptr) in
|
|
let wm1 = WriteMConst(frame_ptr, reg) in
|
|
let reg1 = f_next_reg () in
|
|
let rm3 = ReadMConst(reg1, stack_ptr) in
|
|
let reg2 = f_next_reg () in
|
|
let mov = MovRegConst(reg2, n) in
|
|
let reg3 = f_next_reg () in
|
|
let sub = BinO(reg3, reg1, Sub, reg2) in
|
|
let wm2 = WriteMConst(stack_ptr, reg3) in
|
|
[rm1;push;rm2;wm1;rm3;mov;sub;wm2]
|
|
| Leave ->
|
|
let reg = f_next_reg () in
|
|
let rm = ReadMConst(reg, frame_ptr) in
|
|
let wm1 = WriteMConst(stack_ptr, reg) in
|
|
let reg = f_next_reg () in
|
|
let pop = PopReg(reg) in
|
|
let wm2 = WriteMConst(frame_ptr, reg) in
|
|
[rm;wm1;pop;wm2]
|
|
| Ret(id) ->
|
|
let reg1 = f_next_reg () in
|
|
let reg2 = f_next_reg () in
|
|
let reg3 = f_next_reg () in
|
|
let p2 = PopReg(reg1) in
|
|
let mov = MovRegSymb(reg2, FromTo(Unnamed(Forward), Named(fun_label id))) in
|
|
let sub = BinO(reg3, reg2, Add, reg1) in
|
|
let add = OpStack(Add, reg3) in (* jmp *)
|
|
let lbl = Lbl(nO_NAME_LABEL) in
|
|
[p2;mov;sub;add;lbl;]
|
|
(* AssignTab is replaced with Assign(id,C) earlier *)
|
|
| AssignTab(_,_) -> assert false
|
|
in
|
|
let new_instrs = rewrite stmt in
|
|
let comments =
|
|
match stmt with
|
|
| Label(_) -> []
|
|
| _ ->
|
|
let s = Ast.dump_stmt stmt in
|
|
[Comment(s)]
|
|
in
|
|
comments @ new_instrs
|
|
|
|
let rewrite_prog prog stack_ptr frame_ptr =
|
|
let assign_vars func =
|
|
let collect_locals stmts =
|
|
let rec aux acc stmts =
|
|
match stmts with
|
|
(* all locals are initialized before use *)
|
|
| (Assign(id,_))::tl -> aux (id::acc) tl
|
|
| hd::tl -> aux acc tl
|
|
| [] -> acc
|
|
in
|
|
let ids = aux [] stmts in
|
|
let ids = Common.generic_unique ids in
|
|
ids
|
|
in
|
|
let Fun(id, Args(args), FunBody(stmts)) = func in
|
|
let h = Hashtbl.create 32 in
|
|
let f (h,n) arg =
|
|
let _ = Hashtbl.add h arg n in
|
|
(h,n+4)
|
|
in
|
|
(* v1,frame,ret,arg1,...,argN *)
|
|
let (h,_) = List.fold_left f (h,12) args in
|
|
let g (h,n) id =
|
|
let _ = Hashtbl.add h id n in
|
|
(h,n-4)
|
|
in
|
|
let ids = collect_locals stmts in
|
|
let (h,_) = List.fold_left g (h,0) ids in
|
|
h
|
|
in
|
|
let rewrite_func func =
|
|
let add_stack_stuff fun_id locals stmts =
|
|
let locals_count = Hashtbl.length locals in
|
|
(* every local is a dword *)
|
|
let pre = [Enter(locals_count*4)] in
|
|
let suf = [Leave;Ret(fun_id)] in
|
|
let stmts = pre@stmts@suf in
|
|
stmts
|
|
in
|
|
let rewrite_stmt = rewrite_stmt stack_ptr frame_ptr in
|
|
let Fun(fun_id, Args(args), FunBody(stmts)) = func in
|
|
let locals = assign_vars func in
|
|
let stmts = add_stack_stuff fun_id locals stmts in
|
|
let instrs = List.map (fun stmt -> rewrite_stmt locals fun_id stmt) stmts in
|
|
let head = Ast.dump_func_head func in
|
|
let fun_lbl = fun_label fun_id in
|
|
let pre = [Comment(head); Lbl(fun_lbl);] in
|
|
let instrs = [pre]@instrs in
|
|
instrs
|
|
in
|
|
let Prog(func_list) = prog in
|
|
let rew = List.map rewrite_func func_list in
|
|
(* let rew = List.concat (rew) in *)
|
|
rew
|
|
|
|
(* Extract tables and create a stub that writes them to the data section.
|
|
* All AssignTable(id,list) are changed to Assign(id,C), where C is the
|
|
* address in .data section *)
|
|
let handle_tables data_s prog =
|
|
let per_func data_start func =
|
|
let h = Hashtbl.create 8 in
|
|
let per_stmt acc stmt =
|
|
let (off, pairs, rew) = acc in
|
|
match stmt with
|
|
| AssignTab(id, l) ->
|
|
let new_stmt = Assign(id, Const(off)) in
|
|
let new_off = off + List.length l in
|
|
new_off,(off,l)::pairs,new_stmt::rew
|
|
|
|
| _ -> off,pairs,stmt::rew
|
|
in
|
|
let Fun(fun_id, Args(args), FunBody(stmts)) = func in
|
|
let data_end, pairs, stmts = List.fold_left per_stmt (data_start,[],[]) stmts in
|
|
let stmts = List.rev stmts in
|
|
let func = Fun(fun_id, Args(args), FunBody(stmts)) in
|
|
data_end, pairs, func
|
|
in
|
|
let per_func_fold (data_start,l_pairs,funs) func =
|
|
let data_end, f_pairs, new_func = per_func data_start func in
|
|
(data_end, f_pairs::l_pairs, new_func::funs)
|
|
in
|
|
let dump_pairs pairs =
|
|
let pr (off, l) =
|
|
let s = dump_int_list l in
|
|
printf "0x%08x,%s\n" off s
|
|
in
|
|
List.map pr pairs
|
|
in
|
|
let make_stub pairs =
|
|
let store addr v =
|
|
let r = S(-1) in
|
|
let mov = MovRegConst(r, v) in
|
|
let wm = WriteMConst(addr, r) in
|
|
[mov;wm]
|
|
in
|
|
let chop l n =
|
|
let f (i,a,b) x = if i<n then (i+1,x::a,b) else (i+1,a,x::b) in
|
|
let (_,a,b) = List.fold_left f (0,[],[]) l in
|
|
List.rev a, List.rev b
|
|
in
|
|
let to_int l =
|
|
let f acc x = (acc lsl 8)+x in
|
|
List.fold_left f 0 l
|
|
in
|
|
let make_one off l =
|
|
let rec aux acc off l =
|
|
let pre,suf = chop l 3 in
|
|
match pre with
|
|
| hd::tl ->
|
|
let v = to_int (List.rev pre) in
|
|
let s = store off v in
|
|
aux (s::acc) (off+3) suf
|
|
| [] -> List.flatten (List.rev acc)
|
|
in
|
|
let s = aux [] off l in
|
|
s
|
|
in
|
|
let f acc (off,l) =
|
|
let s = make_one off l in
|
|
s::acc
|
|
in
|
|
let ss = List.fold_left f [] pairs in
|
|
List.flatten (List.rev ss)
|
|
in
|
|
let data_start = data_s+dATA_OFF in
|
|
let Prog(func_list) = prog in
|
|
let (_,l_pairs,funs) = List.fold_left per_func_fold (data_start,[],[]) func_list in
|
|
let funs = List.rev funs in
|
|
let pairs = List.rev (List.flatten l_pairs) in
|
|
let _ = dump_pairs pairs in
|
|
let new_prog = Prog(funs) in
|
|
let stub = make_stub pairs in
|
|
stub, new_prog
|
|
|
|
let add_comments f_comment new_instrs prefix instr =
|
|
let comments =
|
|
if f_comment instr then
|
|
let s = dump_instr instr in
|
|
[Comment(prefix^s)]
|
|
else
|
|
[]
|
|
in
|
|
comments @ new_instrs
|
|
|
|
(* concretize symbolic constants *)
|
|
(* IN: (instr,gm) pairs
|
|
* OUT: (instr,gm) pairs without MovRegSymb *)
|
|
let fix_symblic pairs =
|
|
let get_size gm =
|
|
let GMeta(_,_,_,stack_fix) = gm in
|
|
stack_fix
|
|
in
|
|
let check_lbl label instr = match instr with Lbl(lab) -> label=lab | _ -> false in
|
|
let distance_to_generic f_match pairs =
|
|
let rec aux dist pairs =
|
|
match pairs with
|
|
| (instr,gm)::tl ->
|
|
if f_match instr then
|
|
(*
|
|
let _ = Printf.printf "found label %s in: %s\n" label
|
|
(dump_instr instr) in
|
|
*)
|
|
Some(dist)
|
|
else
|
|
begin
|
|
(* Ignore gmetas for labels and comments -_-' *)
|
|
if is_lbl_or_comment instr then
|
|
aux dist tl
|
|
else
|
|
let size = get_size gm in
|
|
aux (size+dist) tl
|
|
end
|
|
| [] -> None
|
|
in
|
|
let dist = aux 0 pairs in
|
|
dist
|
|
in
|
|
let distance_to_lbl lbl pairs =
|
|
let f_match = check_lbl lbl in
|
|
let dist = distance_to_generic f_match pairs in
|
|
dist
|
|
in
|
|
let try_both_ways id pre suf =
|
|
let before = distance_to_lbl id pre in
|
|
let after = distance_to_lbl id suf in
|
|
match before,after with
|
|
| Some(_),Some(_) -> failwith ("Found duplicate: "^id)
|
|
| None, None -> failwith ("Can't find label:"^id)
|
|
| Some(n),None -> -n
|
|
| None,Some(n) -> n
|
|
in
|
|
let distance_to_unnamed dir pre suf =
|
|
let sign,chunk =
|
|
match dir with
|
|
| Forward -> 1,suf
|
|
| Backward -> -1,pre
|
|
in
|
|
let dist = distance_to_lbl nO_NAME_LABEL chunk in
|
|
match dist with
|
|
| Some(n) -> sign*n
|
|
| None -> failwith "Unnamed not found"
|
|
in
|
|
let get_distance symb pre suf =
|
|
match symb with
|
|
| Named(id) -> try_both_ways id pre suf
|
|
| Unnamed(dir) -> distance_to_unnamed dir pre suf
|
|
in
|
|
let rec aux pre suf =
|
|
match suf with
|
|
| (MovRegSymb(reg, FromTo(start, fin)),gm)::tl->
|
|
let dstart = get_distance start pre suf in
|
|
let dfin = get_distance fin pre suf in
|
|
let dist = dfin-dstart in
|
|
let _ = printf "FromTo: (%s,%s)->(%d,%d)->%d\n" (dump_symb start) (dump_symb fin) dstart dfin dist in
|
|
let fix = MovRegConst(reg, dist) in
|
|
aux ((fix,gm)::pre) tl
|
|
| hd::tl -> aux (hd::pre) tl
|
|
| [] -> List.rev pre
|
|
in
|
|
aux [] pairs
|
|
|
|
(* AdvanceStack -> RawHex.
|
|
* to_binary would try to fill the gap before the return address,
|
|
* but we use that space for arguments. *)
|
|
let fix_ext_call_stuff pairs =
|
|
let get_addr gm =
|
|
let GMeta(_, fm, _, _) = gm in
|
|
let FileMeta(off_s, _) = fm in
|
|
off_s
|
|
in
|
|
let set_stack_fix gm sf =
|
|
let GMeta(g,fm,mod_reg,_) = gm in
|
|
GMeta(g,fm,mod_reg,sf)
|
|
in
|
|
let f acc (instr,gmeta) =
|
|
let new_instr =
|
|
match instr with
|
|
| AdvanceStack(n) -> RawHex(get_addr gmeta)
|
|
| _ -> instr
|
|
in
|
|
if new_instr <> instr then
|
|
let cmt = Comment(dump_instr instr) in
|
|
let fake_gm = set_stack_fix gmeta 4 in
|
|
let p1 = (new_instr, fake_gm) in
|
|
let p2 = (cmt, gmeta) in
|
|
p1::p2::acc
|
|
else (instr,gmeta)::acc
|
|
in
|
|
let pairs = List.fold_left f [] pairs in
|
|
List.rev pairs
|
|
|
|
let write_const_const src_reg addr_reg addr value =
|
|
let m1 = MovRegConst(src_reg, value) in
|
|
let m2 = MovRegConst(addr_reg, addr) in
|
|
let wm1 = WriteM(addr_reg, src_reg) in
|
|
[m1; m2; wm1]
|
|
|
|
let global_prefix_suffix data_s data_e =
|
|
let stack_top = data_e in
|
|
let stack_frame = stack_top in
|
|
let st_ptr = data_s+sTACK_VAR_OFF in (* global var holding stack_top *)
|
|
let sf_ptr = data_s+fRAME_VAR_OFF in (* -- stack_frame *)
|
|
let addr_reg, src_reg = S(-1), S(-2) in (* HACK *)
|
|
let write_st = write_const_const src_reg addr_reg st_ptr stack_top in
|
|
let write_sf = write_const_const src_reg addr_reg sf_ptr stack_frame in
|
|
let reg = S(-3) in
|
|
let mov = MovRegSymb(reg, FromTo(Named(fun_label "main"), Named(gLOBAL_END_LABEL))) in
|
|
let push = PushReg(reg) in
|
|
let lbl = Lbl(gLOBAL_END_LABEL) in
|
|
let pre = write_st @ write_sf @ [mov; push] in
|
|
let suf = [lbl] in
|
|
pre, suf, st_ptr, sf_ptr
|
|
|
|
let to_binary_one io (instr,gm) =
|
|
let get_lc_off g =
|
|
match g with
|
|
| LoadConst(_,off) -> off
|
|
| _ -> assert false
|
|
in
|
|
let fill io n =
|
|
let dwords = n / 4 in
|
|
let bytes = n mod 4 in
|
|
let rec aux i f m =
|
|
if i < m then
|
|
let _ = f n io in aux (i+1) f m
|
|
else ()
|
|
in
|
|
let f_d n io = IO.write_i32 io n in
|
|
let f_b n io = IO.write_byte io n in
|
|
let _ = aux 0 f_d dwords in
|
|
let _ = aux dwords f_b (dwords+bytes) in
|
|
()
|
|
in
|
|
let value_to_write instr off_s =
|
|
match instr with
|
|
| RawHex(v) -> v
|
|
| _ -> off_s
|
|
in
|
|
let GMeta(g, fm, _, stack_fix) = gm in
|
|
let FileMeta(off_s, _) = fm in
|
|
let v = value_to_write instr off_s in
|
|
let _ = IO.write_i32 io v in
|
|
|
|
let _ =
|
|
match instr with
|
|
| MovRegConst(r,v) ->
|
|
let off = get_lc_off g in
|
|
let _ = assert (stack_fix - off - 4 >= 0) in
|
|
let _ = fill io off in
|
|
let _ = IO.write_i32 io v in
|
|
fill io (stack_fix - off - 8)
|
|
| RawHex(_) -> assert (stack_fix = 4)
|
|
| _ -> fill io (stack_fix-4)
|
|
in
|
|
(* return string *)
|
|
()
|
|
|
|
let filter_trash pairs =
|
|
let p (i,_) =
|
|
match i with
|
|
| Lbl(_) | Comment(_) -> false
|
|
| _ -> true
|
|
in
|
|
List.filter p pairs
|
|
|
|
let to_binary pairs =
|
|
let io = IO.output_string () in
|
|
let consume acc (instr,gm) =
|
|
to_binary_one io (instr,gm)
|
|
in
|
|
let _ = List.fold_left consume () pairs in
|
|
let _ = List.map (fun i -> IO.write_i32 io cRASH_ADDRESS) [1;2;3;4;5;6;7] in
|
|
IO.close_out io
|
|
|
|
let dump_possible gadgets stack_ptr frame_ptr instrs =
|
|
let implement = make_implement stack_ptr frame_ptr in
|
|
let p_by_arg, p_by_pos = make_possible_regs_funs gadgets implement in
|
|
let f _ instr =
|
|
let _ = Printf.printf "%s - " (dump_instr instr) in
|
|
let args = arg_dumper instr in
|
|
let per_arg _ arg =
|
|
let _ = Printf.printf "| %s: " (dump_sreg arg) in
|
|
let set = p_by_arg instr arg in
|
|
let regs = RegSet.elements set in
|
|
let _ = Common.generic_dumper (fun r -> Common.dump_reg r) regs in
|
|
()
|
|
in
|
|
let _ = List.fold_left per_arg () args in
|
|
Printf.printf "%s" "\n"
|
|
in
|
|
let _ = List.fold_left f () instrs in
|
|
()
|
|
|
|
(* dump 'compiled' program *)
|
|
let dump_instrs cl =
|
|
let print i =
|
|
let s = dump_instr i in
|
|
Printf.printf "%s\n" s in
|
|
let _ = List.map print cl in
|
|
()
|
|
|
|
let dump_pairs pairs =
|
|
let _ = print_endline "~~~~~~~~~~~~~" in
|
|
let pr acc (instr,gmeta) =
|
|
let GMeta(_,_,_,stack_fix) = gmeta in
|
|
let (off, sep) =
|
|
if is_lbl_or_comment instr then
|
|
acc, " "
|
|
else
|
|
acc+stack_fix, "\t"
|
|
in
|
|
let _ = printf "0x%04x%s%s\n" acc sep (dump_instr instr) in
|
|
off
|
|
in
|
|
(* First RET will add 4 *)
|
|
let _ = List.fold_left pr 4 pairs in
|
|
()
|
|
|
|
(* FIXME: main has to be at the beginning *)
|
|
let compile prog container =
|
|
let process_func assign_regs instr_lll =
|
|
let per_stmt acc instrs =
|
|
(* list of instructions, set of regs to preserve *)
|
|
let impl =
|
|
try
|
|
assign_regs instrs SRegSet.empty
|
|
with Not_found ->
|
|
let _ = dump_instrs instrs in
|
|
assert false
|
|
in
|
|
impl::acc
|
|
in
|
|
let per_func acc stmts =
|
|
let impl = List.fold_left per_stmt [] stmts in
|
|
let impl = List.rev impl in
|
|
impl::acc
|
|
in
|
|
let impl_lll = List.fold_left per_func [] instr_lll in
|
|
List.rev impl_lll
|
|
in
|
|
let verify_impl impl =
|
|
let p instr = instr_type instr = T0 in
|
|
let ok = List.for_all p impl in
|
|
if not ok then assert false
|
|
else ()
|
|
in
|
|
let GContainer(fn, (data_s, data_e), gmetas) = container in
|
|
let gadgets = get_gadgets gmetas in
|
|
let prefix, suffix, stack_ptr, frame_ptr = global_prefix_suffix data_s data_e in
|
|
(* Swap AssignTable with Assign (const).
|
|
* stub stores all tables in .data section *)
|
|
let stub, prog = handle_tables data_s prog in
|
|
(* Function to implement instructions in terms of simpler instructions.
|
|
* Ultimately instruction is converted to a list of gadgets. *)
|
|
let implement = make_implement stack_ptr frame_ptr in
|
|
let assign_regs = make_assign_regs gmetas stack_ptr frame_ptr in
|
|
(* instr list list list.
|
|
* 1st level: list of functions
|
|
* 2nd level: list of (rewritten) stmts
|
|
* 3rd level: instructions *)
|
|
let instrs_ll = rewrite_prog prog stack_ptr frame_ptr in
|
|
let instrs_ll = [stub] :: [prefix] :: instrs_ll @ [[suffix]] in
|
|
let instrs_lll = [[[Comment("lol");Lbl("1")]]] in
|
|
let instrs_lll = [[stub]] in
|
|
let impl_lll = process_func assign_regs instrs_ll in
|
|
let impl_ll = List.flatten impl_lll in
|
|
let pairs = List.flatten impl_ll in
|
|
let pairs = fix_ext_call_stuff pairs in
|
|
|
|
let instrs = List.map fst pairs in
|
|
let _ = dump_pairs pairs in
|
|
let _ = verify_impl instrs in
|
|
|
|
let pairs = fix_symblic pairs in
|
|
let pairs = filter_trash pairs in
|
|
let bin_str = to_binary pairs in
|
|
instrs, pairs, bin_str
|
|
|
|
let parse_src src_fn =
|
|
let cin = open_in src_fn in
|
|
let lexbuf = Lexing.from_channel cin in
|
|
let p = Parser.input Lexer.token lexbuf in
|
|
let errors = Ast.verify_prog p in
|
|
(p, errors)
|
|
|
|
let main () =
|
|
let argc = Array.length Sys.argv in
|
|
if argc > 2 then
|
|
let src_fn = Sys.argv.(1) in
|
|
let vg_fn = Sys.argv.(2) in
|
|
let out_fn = "compiled.bin" in
|
|
let (p, errors) = parse_src src_fn in
|
|
if errors <> [] then
|
|
print_errors errors
|
|
else
|
|
let p = Ast.unwrap_prog p in
|
|
let p = Ast.move_main_to_front p in
|
|
let p = Ast.flatten_prog p in
|
|
let container = Common.unmarshal_from_file vg_fn in
|
|
let s = Ast.dump_prog p in
|
|
let cl, pairs, bin_str = compile p container in
|
|
let _ = printf "DUMPED:\n%s\n####\n" s in
|
|
let _ = write_str_to_file out_fn bin_str in
|
|
()
|
|
else
|
|
let err = Printf.sprintf "Usage:\n%s <src fn> <vg fn>\n" Sys.argv.(0) in
|
|
print_string err
|
|
|
|
let _ = main()
|