Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Pzucker/gdb #70

Merged
merged 63 commits into from
Sep 27, 2019
Merged
Show file tree
Hide file tree
Changes from 26 commits
Commits
Show all changes
63 commits
Select commit Hold shift + click to select a range
90c4ad4
Add Unknown case when pattern matching BIR types
Aug 21, 2019
40f2a2e
Save a binary's program instead of a project
Aug 23, 2019
ac69020
Use last block to determine how much to increment stack pointer
Aug 26, 2019
3134645
Update README with BAP version number
Aug 27, 2019
2ce0faf
Upgrade value_set to BAP 2.0
Aug 28, 2019
ba87d76
Update BAP version number in wp README
Aug 29, 2019
3597cf3
Add makefile for all sample binaries in resources
Sep 3, 2019
cf6bcb7
Look for a jmp to a var with noreturn as return block
Sep 3, 2019
6cc3a85
Basic gdb output might work. It works on the simplest example anyway
philzook58 Sep 20, 2019
bad4c87
added documentation
philzook58 Sep 25, 2019
585e8e9
added labelled parater for filename
philzook58 Sep 25, 2019
906ccf0
fiexed merge conficts
philzook58 Sep 25, 2019
84ea864
Made some suggested fixes in pull request
philzook58 Sep 26, 2019
9ffa399
Use last block to determine how much to increment stack pointer
Aug 26, 2019
fcc4840
Look for a jmp to a var with noreturn as return block
Sep 3, 2019
35a61e4
Basic gdb output might work. It works on the simplest example anyway
philzook58 Sep 20, 2019
1eb8646
added documentation
philzook58 Sep 25, 2019
683fc03
added labelled parater for filename
philzook58 Sep 25, 2019
390248a
Made some suggested fixes in pull request
philzook58 Sep 26, 2019
fb98a4b
added labelled parameter. Moved aorund some comments. Renamed postpro…
philzook58 Sep 26, 2019
10aff45
fixed readme typo
philzook58 Sep 26, 2019
4d6855f
Rebased.
philzook58 Sep 26, 2019
7d8750d
fixed merge conflicts. Again.
philzook58 Sep 26, 2019
666c43e
removed postprocess modules
philzook58 Sep 26, 2019
354ba34
fixed readme
philzook58 Sep 26, 2019
a926039
fixed weird spacing
philzook58 Sep 26, 2019
9cb9cd1
Use last block to determine how much to increment stack pointer
Aug 26, 2019
ed6d6d6
Look for a jmp to a var with noreturn as return block
Sep 3, 2019
f362a3e
Basic gdb output might work. It works on the simplest example anyway
philzook58 Sep 20, 2019
d9833dd
added documentation
philzook58 Sep 25, 2019
c3fc7bc
added labelled parater for filename
philzook58 Sep 25, 2019
9022447
Made some suggested fixes in pull request
philzook58 Sep 26, 2019
6ad1e3a
added labelled parameter. Moved aorund some comments. Renamed postpro…
philzook58 Sep 26, 2019
a1f5e16
fixed readme typo
philzook58 Sep 26, 2019
71c11e7
Rebased.
philzook58 Sep 26, 2019
6afb96e
Look for a jmp to a var with noreturn as return block
Sep 3, 2019
0df653f
Basic gdb output might work. It works on the simplest example anyway
philzook58 Sep 20, 2019
5bb857a
added documentation
philzook58 Sep 25, 2019
14db1b1
Made some suggested fixes in pull request
philzook58 Sep 26, 2019
a7998f2
removed postprocess modules
philzook58 Sep 26, 2019
3c6f4f6
fixed readme
philzook58 Sep 26, 2019
468d426
fixed weird spacing
philzook58 Sep 26, 2019
348145f
Merge branch 'pzucker/gdb' of https://github.com/draperlaboratory/cba…
philzook58 Sep 26, 2019
5aeab89
Use last block to determine how much to increment stack pointer
Aug 26, 2019
f7fced3
Look for a jmp to a var with noreturn as return block
Sep 3, 2019
f171fb7
Basic gdb output might work. It works on the simplest example anyway
philzook58 Sep 20, 2019
93a184a
added documentation
philzook58 Sep 25, 2019
9660ee5
added labelled parater for filename
philzook58 Sep 25, 2019
f0e2e8b
Made some suggested fixes in pull request
philzook58 Sep 26, 2019
e7e34ef
added labelled parameter. Moved aorund some comments. Renamed postpro…
philzook58 Sep 26, 2019
85ea279
fixed readme typo
philzook58 Sep 26, 2019
f189730
Rebased.
philzook58 Sep 26, 2019
526fe04
Look for a jmp to a var with noreturn as return block
Sep 3, 2019
c1d8e37
Basic gdb output might work. It works on the simplest example anyway
philzook58 Sep 20, 2019
658ccfe
added documentation
philzook58 Sep 25, 2019
1e4d12c
Made some suggested fixes in pull request
philzook58 Sep 26, 2019
7855bbc
removed postprocess modules
philzook58 Sep 26, 2019
810bf14
fixed readme
philzook58 Sep 26, 2019
76171ab
fixed weird spacing
philzook58 Sep 26, 2019
b786424
Use last block to determine how much to increment stack pointer
Aug 26, 2019
b0fa7bd
Look for a jmp to a var with noreturn as return block
Sep 3, 2019
3a76268
Rebased.
philzook58 Sep 26, 2019
46f9ee2
Merge branch 'pzucker/gdb' of https://github.com/draperlaboratory/cba…
philzook58 Sep 27, 2019
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
126 changes: 126 additions & 0 deletions wp/lib/bap_wp/src/output.ml
Original file line number Diff line number Diff line change
@@ -0,0 +1,126 @@
(***************************************************************************)
(* *)
(* Copyright (C) 2018/2019 The Charles Stark Draper Laboratory, Inc. *)
(* *)
(* This file is provided under the license found in the LICENSE file in *)
(* the top-level directory of this project. *)
(* *)
(* This work is funded in part by ONR/NAWC Contract N6833518C0107. Its *)
(* content does not necessarily reflect the position or policy of the US *)
(* Government and no official endorsement should be inferred. *)
(* *)
(***************************************************************************)

open !Core_kernel
open Bap.Std

include Self()

module Expr = Z3.Expr
module Arith = Z3.Arithmetic
module BV = Z3.BitVector
module Bool = Z3.Boolean
module Array = Z3.Z3Array
module Env = Environment
module Constr = Constraint


(** Implements {!Model}. *)

open Core_kernel
open Bap.Std

type sign = Unsigned | Signed

let string_of_sign s =
match s with
| Unsigned -> "u"
| Signed -> "s"

exception NoValue of string

let no_value_msg e m =
let e_str = Z3.Expr.to_string e in
let m_str = Z3.Model.to_string m in
Printf.sprintf "No value for symbol '%s' found in model: %s" e_str m_str

let word_of_str s w sign =
let w_str = Printf.sprintf "%s:%d%s" s w (string_of_sign sign) in
Word.of_string w_str

let z3_val_of e m =
match Z3.Model.eval m e true with
| Some v -> v
| None -> raise (NoValue (no_value_msg e m))

let word_of_z3_bv bv =
let bits = Z3.BitVector.get_size (Z3.Expr.get_sort bv) in
let sign = Unsigned in
let s = Z3.Expr.to_string bv in
word_of_str (String.substr_replace_first s ~pattern:"#" ~with_:"0") bits sign

let val_for_reg r m env =
let reg_var = Var.create r (Type.imm 64) in
let reg_value, _ = Environment.get_var env reg_var in
let v = z3_val_of reg_value m in
word_of_z3_bv v

let strip_final_digits s =
let s' = List.rev (String.to_list s) in
let rec pop_digits l =
match l with
| [] -> l
| x :: xs ->
begin
match x with
| '0' .. '9' -> pop_digits xs
| _ -> l
end
in
let s'' = List.rev (pop_digits s') in
String.of_char_list s''

let reg_names m =
m |>
Z3.Model.get_decls |>
List.map ~f:Z3.FuncDecl.get_name |>
List.map ~f:Z3.Symbol.to_string |>
List.filter ~f:(fun s -> String.is_substring_at s 0 "R") |>
List.map ~f:strip_final_digits


let print_result (solver : Z3.Solver.solver) (status : Z3.Solver.status)
(goals: Constr.t) (ctx : Z3.context) : unit =
match status with
| Z3.Solver.UNSATISFIABLE -> Format.printf "\nUNSAT!\n%!"
| Z3.Solver.UNKNOWN -> Format.printf "\nUNKNOWN!\n%!"
| Z3.Solver.SATISFIABLE ->
Format.printf "\nSAT!\n%!";
let model = Z3.Solver.get_model solver
|> Option.value_exn ?here:None ?error:None ?message:None in
Format.printf "\nModel:\n%s\n%!" (Z3.Model.to_string model);
let refuted_goals = Constr.get_refuted_goals goals solver ctx in
Format.printf "\nRefuted goals:\n%!";
Seq.iter refuted_goals ~f:(fun g ->
Format.printf "%s\n%!" (Constr.refuted_goal_to_string g model))

(** [output_gdb] is similar to [print_result] except chews on the model and outputs a gdb script with a
breakpoint at the subroutine and fills the appropriate registers *)

let output_gdb (solver : Z3.Solver.solver) (status : Z3.Solver.status)
(env : Env.t) ~func:(func : string) ~filename:(gdb_filename : string) : unit =
match status with
| Z3.Solver.SATISFIABLE ->
let model = Z3.Solver.get_model solver
|> Option.value_exn ?here:None ?error:None ?message:None in
let regs = reg_names model in
Out_channel.with_file gdb_filename ~f:(fun t ->
Printf.fprintf t "break *%s\n" func; (* The "*" is necessary to break before some slight setup *)

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not super important, but I noticed we use both the Printf modules and the Format modules. What's the difference between these two and would it be a good idea to be uniform?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I have not figured out how to get Format to actually print to a file. Me and jt scratched our heads about this one and couldn't figure it out.

        Out_channel.with_file gdb_filename  ~f:(fun t -> 
            let t = Format.formatter_of_out_channel t in
            Format.fprintf t "break *%s\n" func; (* The "*" is necessary to break before some slight setup *)
            Format.fprintf t "start\n";
            List.iter regs ~f:(fun r -> 
                  let w = val_for_reg r model env in
                  let s = Format.asprintf "set $%s = %a \n" (String.lowercase r) Bap.Std.Word.pp_hex w  in
                  Format.fprintf t "%s" s))   

outputs a blank file. Thoughts? Printf was a workaround.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Tried some various flush commands too.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That's super interesting. Flushing sounds like the intuitive answer to this. If that's the case, keeping it as Printf is all good with me.

Printf.fprintf t "start\n";
List.iter regs ~f:(fun r ->
let w = val_for_reg r model env in
let s = Format.asprintf "set $%s = %a \n" (String.lowercase r) Bap.Std.Word.pp_hex w in
Printf.fprintf t "%s" s))
| _ -> ()


40 changes: 40 additions & 0 deletions wp/lib/bap_wp/src/output.mli
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
(***************************************************************************)
(* *)
(* Copyright (C) 2018/2019 The Charles Stark Draper Laboratory, Inc. *)
(* *)
(* This file is provided under the license found in the LICENSE file in *)
(* the top-level directory of this project. *)
(* *)
(* This work is funded in part by ONR/NAWC Contract N6833518C0107. Its *)
(* content does not necessarily reflect the position or policy of the US *)
(* Government and no official endorsement should be inferred. *)
(* *)
(***************************************************************************)

(**

This module exports types and utilities to process and report results found uisng the wp plugin.

*)

module Expr = Z3.Expr

module Arith = Z3.Arithmetic

module BV = Z3.BitVector

module Bool = Z3.Boolean

module Array = Z3.Z3Array

module Env = Environment

module Constr = Constraint

(** Prints out the result from check, and if the result is [SAT], generate a model that
represents the registers and memory values that lead to a specific program state. *)
val print_result : Z3.Solver.solver -> Z3.Solver.status -> Constr.t -> Z3.context -> unit


(** Prints to file a gdb script that will fill the appropriate registers with the countermodel *)
val output_gdb : Z3.Solver.solver -> Z3.Solver.status -> Env.t -> func:string -> filename:string -> unit
16 changes: 2 additions & 14 deletions wp/lib/bap_wp/src/precondition.ml
Original file line number Diff line number Diff line change
Expand Up @@ -237,6 +237,7 @@ let set_fun_called (post : Constr.t) (env : Env.t) (tid : Tid.t) : Constr.t =
Option.value_exn ?here:None ?error:None ?message:None) in
Constr.substitute_one post fun_name (Bool.mk_true ctx)


let get_stack_ptr_offset (sub : Sub.t) (arch : Arch.t) : Exp.t =
let module Target = (val target_of_arch arch) in
let blks = Term.to_sequence ~rev:true blk_t sub in
Expand Down Expand Up @@ -854,20 +855,7 @@ let check ?refute:(refute = true) (solver : Z3.Solver.solver) (ctx : Z3.context)
in
Z3.Solver.check solver [is_correct]

let print_result (solver : Z3.Solver.solver) (status : Z3.Solver.status)
(goals: Constr.t) (ctx : Z3.context) : unit =
match status with
| Z3.Solver.UNSATISFIABLE -> Format.printf "\nUNSAT!\n%!"
| Z3.Solver.UNKNOWN -> Format.printf "\nUNKNOWN!\n%!"
| Z3.Solver.SATISFIABLE ->
Format.printf "\nSAT!\n%!";
let model = Z3.Solver.get_model solver
|> Option.value_exn ?here:None ?error:None ?message:None in
Format.printf "\nModel:\n%s\n%!" (Z3.Model.to_string model);
let refuted_goals = Constr.get_refuted_goals goals solver ctx in
Format.printf "\nRefuted goals:\n%!";
Seq.iter refuted_goals ~f:(fun g ->
Format.printf "%s\n%!" (Constr.refuted_goal_to_string g model))


let exclude (solver : Z3.Solver.solver) (ctx : Z3.context) ~var:(var : Constr.z3_expr)
~pre:(pre : Constr.t) : Z3.Solver.status =
Expand Down
3 changes: 0 additions & 3 deletions wp/lib/bap_wp/src/precondition.mli
Original file line number Diff line number Diff line change
Expand Up @@ -198,9 +198,6 @@ val visit_sub : Env.t -> Constr.t -> Bap.Std.Sub.t -> Constr.t * Env.t
refute is set to false, it checks for a model instead. *)
val check : ?refute:bool -> Z3.Solver.solver -> Z3.context -> Constr.t -> Z3.Solver.status

(** Prints out the result from check, and if the result is [SAT], generate a model that
represents the registers and memory values that lead to a specific program state. *)
val print_result : Z3.Solver.solver -> Z3.Solver.status -> Constr.t -> Z3.context -> unit

(** Adds a constraint to the Z3 solver in which var does not equal its value from
the original Z3 model, then runs the Z3 solver again.
Expand Down
3 changes: 3 additions & 0 deletions wp/plugin/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -329,6 +329,9 @@ The various options are:
- `--wp-output-vars=var_list`. List of output variables for equivalence checking
by `,` given the same input variables in the case of a comparative analysis.
Defaults to `RAX,EAX` which are the 64- and 32-bit output registers for x86.
- `--wp-gdb-filename=my_exec.gdb` output a gdb script to file `my_exec.gdb`. From
within gdb, run `source my_exec.gdb` to set a breakpoint at the function given by `--wp-function` and fill the appropriate registers with a found counter-model.


## C checking API

Expand Down
26 changes: 18 additions & 8 deletions wp/plugin/wp.ml
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ let analyze_proj (proj : project) (var_gen : Env.var_gen) (ctx : Z3.context)
~inline:(inline : bool)
~to_inline:(to_inline : string list)
~post_cond:(post_cond : string)
: Constr.t =
: Constr.t * Env.t =
let arch = Project.arch proj in
let subs = proj |> Project.program |> Term.enum sub_t in
let main_sub = find_func_err subs func in
Expand Down Expand Up @@ -71,10 +71,10 @@ let analyze_proj (proj : project) (var_gen : Env.var_gen) (ctx : Z3.context)
Pre.mk_smtlib2_post env post_cond
end
in
let pre, _ = Pre.visit_sub env post main_sub in
let pre, env' = Pre.visit_sub env post main_sub in
Format.printf "\nSub:\n%s\nPre:\n%a\n%!"
(Sub.to_string main_sub) Constr.pp_constr pre;
pre
(pre, env')

let compare_projs (proj : project) (file1: string) (file2 : string)
(var_gen : Env.var_gen) (ctx : Z3.context)
Expand All @@ -83,7 +83,7 @@ let compare_projs (proj : project) (file1: string) (file2 : string)
~inline:(inline : bool)
~to_inline:(to_inline : string list)
~output_vars:(output_vars : string list)
: Constr.t =
: Constr.t * Env.t =
let prog1 = In_channel.with_file file1 ~f:Program.Io.load in
let prog2 = In_channel.with_file file2 ~f:Program.Io.load in
(* Currently using the dummy binary's project to determine the architecture
Expand Down Expand Up @@ -117,7 +117,7 @@ let compare_projs (proj : project) (file1: string) (file2 : string)
Pre.mk_default_env ctx var_gen ~subs:subs1 ~arch:arch,
Pre.mk_default_env ctx var_gen ~subs:subs2 ~arch:arch
in
let pre, _ =
let pre, env' =
if check_calls then
Comp.compare_subs_fun ~original:(main_sub1,env1) ~modified:(main_sub2,env2)
else
Expand All @@ -137,7 +137,7 @@ let compare_projs (proj : project) (file1: string) (file2 : string)
in
Format.printf "\nComparing\n\n%s\nand\n\n%s\n%!"
(Sub.to_string main_sub1) (Sub.to_string main_sub2);
pre
(pre, env')

let main (file1 : string) (file2 : string)
~func:(func : string)
Expand All @@ -148,20 +148,27 @@ let main (file1 : string) (file2 : string)
~post_cond:(post_cond : string)
~num_unroll:(num_unroll : int option)
~output_vars:(output_vars : string list)
~gdb_filename:(gdb_filename : string option)
(proj : project) : unit =
Log.start ~logdir:"/dev/stdout" ();
let ctx = Env.mk_ctx () in
let var_gen = Env.mk_var_gen () in
let solver = Z3.Solver.mk_simple_solver ctx in
update_default_num_unroll num_unroll;
let pre =
let pre, env' =
if compare then
compare_projs proj file1 file2 var_gen ctx ~func ~check_calls ~inline ~to_inline ~output_vars
else
analyze_proj proj var_gen ctx ~func ~inline ~to_inline ~post_cond
in
let result = Pre.check solver ctx pre in
Pre.print_result solver result pre ctx
let () = match gdb_filename with
| None -> ()
| Some f ->
Printf.printf "Dumping gdb script to file: %s\n" f;
Output.output_gdb solver result env' ~func:func ~filename:f in
Output.print_result solver result pre ctx


module Cmdline = struct
open Config
Expand Down Expand Up @@ -199,6 +206,8 @@ module Cmdline = struct
~doc:"List of output variables to compare separated by ',' given the same \
input variables in the case of a comparative analysis. Defaults to `RAX,EAX` \
which are the 64- and 32-bit output registers for x86."
let gdb_filename = param (some string) "gdb-filename" ~doc:"Output gdb script file for counterexample"
~default:None

let () = when_ready (fun {get=(!!)} ->
Project.register_pass' @@
Expand All @@ -211,6 +220,7 @@ module Cmdline = struct
~post_cond:!!post_cond
~num_unroll:!!num_unroll
~output_vars:!!output_vars
~gdb_filename:!!gdb_filename
)

let () = manpage [
Expand Down