├── .gitignore ├── .merlin ├── LICENSE ├── README.md ├── callstrings ├── .merlin ├── Makefile ├── README.md └── callstrings.ml ├── check-invariants └── check_invariants.ml ├── checkpath ├── Makefile ├── README.md ├── checkpath.ml └── checkpath.mli ├── deadcode └── deadcode.ml ├── fold_consts ├── .merlin ├── Makefile ├── README.md ├── fold_consts.itarget ├── fold_consts.ml ├── main.ml ├── main.mli ├── memdep.ml ├── memdep.mli ├── parameters.ml ├── utils.ml └── utils.mli ├── lifting-benchmark ├── Makefile ├── README.md ├── bench.ml └── bench.mli ├── mem-printer ├── Makefile ├── README.md ├── mem_printer.ml └── run.sh ├── minos ├── .merlin ├── Makefile ├── README.md ├── args.ml ├── binary │ └── test-system │ │ ├── Makefile │ │ ├── test1 │ │ └── test1.c ├── callstrings.ml ├── check.mli ├── check_suite.ml ├── check_tmpl.ml ├── check_tmpl.mli ├── check_util.ml ├── check_util.mli ├── checks │ ├── memcpy_check.ml │ ├── sql_check.ml │ ├── strtol_check.ml │ ├── system_check.ml │ └── system_simple_check.ml ├── color.ml ├── configs │ └── n-1-@system │ │ ├── sinks.txt │ │ └── srcs.txt ├── ctxt.mli ├── cut.ml ├── cut.mli ├── dependence.ml ├── filter.ml ├── filter.mli ├── fold_consts.ml ├── interprocedural.ml ├── interprocedural.mli ├── mem_to_reg.ml ├── minos.ml ├── minos_types.ml ├── normal_graph_view.dot ├── options.ml ├── options.mli ├── output.ml ├── output.mli ├── path_consumer.ml ├── path_consumer.mli ├── path_producer.ml ├── path_producer.mli ├── pathlib.ml ├── pathlib.mli ├── policy.ml ├── policy.mli ├── profile.ml ├── profile.mli ├── resolve_calls.ml ├── run-test-system.sh ├── simple.ml ├── simple_args.ml ├── simple_args.mli ├── summary.ml ├── test.ml ├── time.ml ├── time.mli ├── trim.ml ├── trim.mli ├── util.ml └── util.mli ├── saluki ├── .merlin ├── Makefile ├── README.md ├── match.ml ├── match.mli ├── paper.pdf ├── predicate.ml ├── predicate.mli ├── saluki.ml ├── solution.ml ├── solution.mli ├── solver.ml ├── solver.mli ├── spec.ml ├── spec.mli ├── spec_types.ml ├── specification.ml ├── state.ml ├── state.mli ├── tainter.ml ├── tainter.mli ├── tests │ ├── test0.c │ ├── test1.c │ ├── test10.c │ ├── test11.c │ ├── test12.c │ ├── test13.c │ ├── test14.c │ ├── test15.c │ ├── test16.c │ ├── test17.c │ ├── test18.c │ ├── test2.c │ ├── test3.c │ ├── test4.c │ ├── test5.c │ ├── test6.c │ ├── test7.c │ ├── test8.c │ └── test9.c ├── utilities.ml ├── utilities.mli ├── v.ml └── v.mli ├── staticstore ├── Makefile ├── README.md ├── printstats.ml ├── staticstore.itarget ├── staticstore.ml ├── staticstore.mli └── toida.ml ├── strings ├── Makefile ├── README.md ├── run.sh └── strings.ml ├── test-expect ├── .merlin ├── Makefile ├── README.md ├── _oasis ├── expect.ml ├── expect.mli └── test.ml ├── toy-debugger ├── Makefile ├── README.md ├── birasm.ml ├── cmdline.ml ├── color.ml ├── color.mli ├── debugger.ml ├── draw.ml ├── flag.ml ├── options.ml ├── output.ml ├── pngs │ └── debugger.png ├── run.sh ├── state_data.ml ├── state_data.mli ├── tests │ └── test1 └── viewer │ └── index.html ├── uaf-checker ├── .merlin ├── Makefile ├── README.md ├── cmdline.ml ├── debugger │ ├── .merlin │ ├── color.ml │ ├── color.mli │ ├── debugger.ml │ ├── debugger.mli │ ├── draw.ml │ ├── flag.ml │ ├── output.ml │ ├── state_data.ml │ └── state_data.mli ├── demo │ ├── gnome-nettool │ ├── microx.patch │ ├── posix.h │ ├── run.sh │ └── test-info_get_nic_information.py ├── expected │ ├── dead-simpl-uaf-arm-2.output │ ├── dead-simpl-uaf-arm.output │ ├── gnome-nettool.output │ ├── gueb-example-ite-uaf-arm-O0.output │ ├── gueb-example-uaf-arm-O0.output │ ├── gueb-full-arm-O0-buggy.output │ ├── gueb-full-arm-O0.output │ ├── simpl-uaf-arm-2.output │ ├── simpl-uaf-arm-bug.output │ ├── simpl-uaf-arm.output │ ├── structs-arm-2.output │ ├── structs-arm.output │ ├── structs.output │ ├── super-simpl-uaf-arm.output │ └── uaf-no-assign-arm.output ├── gen_test.sh ├── main.ml ├── microx.patch ├── options.ml ├── pngs │ └── ida-uaf.png ├── posix.h ├── test.ml ├── tests │ └── all │ │ ├── Makefile │ │ ├── dead-simpl-uaf-2.c │ │ ├── dead-simpl-uaf-arm │ │ ├── dead-simpl-uaf-arm-2 │ │ ├── dead-simpl-uaf.c │ │ ├── gnome-nettool │ │ ├── gueb-example-ite-uaf-arm-O0 │ │ ├── gueb-example-ite-uaf.c │ │ ├── gueb-example-uaf-arm-O0 │ │ ├── gueb-example-uaf.c │ │ ├── gueb-full-arm-O0 │ │ ├── gueb-full-arm-O0-buggy │ │ ├── gueb-full-uaf-buggy.c │ │ ├── gueb-full-uaf.c │ │ ├── libvector.so │ │ ├── simpl-uaf-2.c │ │ ├── simpl-uaf-arm │ │ ├── simpl-uaf-arm-2 │ │ ├── simpl-uaf-arm-bug │ │ ├── simpl-uaf-bug.c │ │ ├── simpl-uaf.c │ │ ├── simple-uaf-2.c │ │ ├── structs │ │ ├── structs-2.c │ │ ├── structs-arm │ │ ├── structs-arm-2 │ │ ├── structs.c │ │ ├── super-simpl-uaf-arm │ │ ├── super-simpl-uaf.c │ │ ├── uaf-no-assign-arm │ │ ├── uaf-no-assign.c │ │ ├── vector.c │ │ └── vector.h ├── toida.ml ├── uaf_error.ml ├── uaf_error.mli └── visual.ml └── validate_cfg └── validate_cfg.ml /.gitignore: -------------------------------------------------------------------------------- 1 | *.annot 2 | *.cmo 3 | *.cma 4 | *.cmi 5 | *.a 6 | *.o 7 | *.cmx 8 | *.cmxs 9 | *.cmxa 10 | *.plugin 11 | 12 | # ocamlbuild working directory 13 | _build/ 14 | 15 | # ocamlbuild targets 16 | *.byte 17 | *.native 18 | 19 | # oasis generated files 20 | setup.data 21 | setup.log 22 | /test-expect/myocamlbuild.ml 23 | /test-expect/setup.ml 24 | /saluki/saluki.plugin 25 | /test-expect/_tags 26 | -------------------------------------------------------------------------------- /.merlin: -------------------------------------------------------------------------------- 1 | PKG core_kernel 2 | PKG bap 3 | PKG cmdliner 4 | PKG ppx_jane 5 | FLG -short-paths 6 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2015 BinaryAnalysisPlatform 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | 23 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # bap-plugins 2 | 3 | [![Join the chat at https://gitter.im/BinaryAnalysisPlatform/bap-plugins](https://badges.gitter.im/Join%20Chat.svg)](https://gitter.im/BinaryAnalysisPlatform/bap-plugins?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge) 4 | BAP Plugins Repository 5 | -------------------------------------------------------------------------------- /callstrings/.merlin: -------------------------------------------------------------------------------- 1 | REC 2 | PKG bap 3 | PKG ppx_bap 4 | PKG regular 5 | PKG graphlib 6 | PKG re.posix -------------------------------------------------------------------------------- /callstrings/Makefile: -------------------------------------------------------------------------------- 1 | 2 | all : 3 | bapbuild callstrings.plugin 4 | bapbundle install callstrings.plugin 5 | 6 | clean : 7 | bapbuild -clean 8 | 9 | uninstall: 10 | bapbundle remove callstrings.plugin 11 | -------------------------------------------------------------------------------- /callstrings/README.md: -------------------------------------------------------------------------------- 1 | # Overview 2 | 3 | This plugin prints a set of tracebacks for a given sink. Each 4 | traceback is represented by an s-expression of the following 5 | syntax: 6 | 7 | ``` 8 | (sink (pred1 [ctx1 ctx2 ...] pred2[ctx1 ctx2 ...] ...)) 9 | ``` 10 | where `sink` is the name of the sink, `pred*` is a name of a 11 | caller, and `ctx*` is the context under which the call was performed, 12 | i.e., a set of calls to other functions, that occurs before the call. 13 | 14 | 15 | # Compilation 16 | ```sh 17 | make 18 | make install 19 | ``` 20 | 21 | # Running 22 | 23 | ```sh 24 | bap --callstrings --callstrings-sink= 25 | ``` 26 | 27 | where `` is a path to an executable, and `` is a posix 28 | regular expression that denotes a set of interesting function names. 29 | -------------------------------------------------------------------------------- /check-invariants/check_invariants.ml: -------------------------------------------------------------------------------- 1 | open Core_kernel 2 | open Bap.Std 3 | open Bap_main 4 | open Graphlib.Std 5 | 6 | let sexp_of_tid tid = Sexp.Atom (Tid.name tid) 7 | type block = Addr of addr | Tid of tid 8 | [@@deriving sexp] 9 | 10 | type error = Duplicating of tid 11 | | Unreachable of string * block 12 | [@@deriving sexp] 13 | exception Broken of error [@@deriving sexp] 14 | 15 | let broken err = raise (Broken err) 16 | 17 | let term_identifiers_are_unique program = 18 | let visitor = object inherit [Tid.Set.t] Term.visitor 19 | method! enter_term _ t visited = 20 | let tid = Term.tid t in 21 | if Set.mem visited tid 22 | then broken (Duplicating tid); 23 | Set.add visited tid 24 | end in 25 | (visitor#run program Tid.Set.empty : Tid.Set.t) |> ignore 26 | 27 | let symtab_is_well_formed symtab = 28 | Symtab.to_sequence symtab |> 29 | Seq.iter ~f:(fun (name,entry,cfg) -> 30 | Graphlib.depth_first_search (module Graphs.Cfg) cfg 31 | ~start:entry ~init:() 32 | ~start_tree:(fun start () -> 33 | if not @@ Block.equal start entry 34 | then broken (Unreachable (name, Addr (Block.addr start))))) 35 | 36 | let program_is_well_formed prog = 37 | Term.enum sub_t prog |> 38 | Seq.iter ~f:(fun sub -> match Term.first blk_t sub with 39 | | None -> () 40 | | Some entry -> 41 | let cfg = 42 | Graphs.Tid.Node.remove Graphs.Tid.start 43 | (Sub.to_graph sub) in 44 | let entry = Term.tid entry in 45 | Graphlib.depth_first_search (module Graphs.Tid) cfg 46 | ~start:entry ~init:() 47 | ~start_tree:(fun start () -> 48 | if not @@ Tid.equal start entry 49 | then broken (Unreachable (Sub.name sub, Tid start)))) 50 | 51 | 52 | 53 | 54 | let check_invariants proj = 55 | symtab_is_well_formed (Project.symbols proj); 56 | term_identifiers_are_unique (Project.program proj); 57 | program_is_well_formed (Project.program proj) 58 | 59 | 60 | let () = Extension.declare @@ fun ctxt -> 61 | Project.register_pass' check_invariants; 62 | Ok () 63 | -------------------------------------------------------------------------------- /checkpath/Makefile: -------------------------------------------------------------------------------- 1 | .PHONY : all 2 | 3 | all: 4 | bapbuild checkpath.plugin 5 | 6 | clean: 7 | bapbuild -clean 8 | -------------------------------------------------------------------------------- /checkpath/README.md: -------------------------------------------------------------------------------- 1 | # Overview 2 | 3 | This plugin reads from standard input (later I will parametrize the 4 | input with command line option) specification of routes and checks, 5 | that whether it is satisfied or not. The route is specified with the 6 | following grammar: 7 | 8 | ``` 9 | route = point, sep, {point}, sep, point, eol; 10 | point = hex | str; 11 | sep = " " | ";" | "," | " "; 12 | eol = "\r" | "\n" | "\n\r" | "\r\n"; 13 | hex = ?conventional hexadecimal number?; 14 | str = ?anything that doesn't starts with 0?; 15 | ``` 16 | 17 | Less formally, route is a sequence of points, where the first point is 18 | the source of the route, last is the destination point and whatever in 19 | between is checkpoints. Point can be and address, or a string denoting 20 | the symbol name. 21 | 22 | The route is valid if it exists, i.e., there is a path from source 23 | point to the destination point (which implies that both points should 24 | also exist), and for each checkpoint there exists a path that passes 25 | through it. This can be different paths, i.e., it is not guaranteed that 26 | there exists a path that visits all specified checkpoints. 27 | 28 | # Usage 29 | 30 | ## Interactive 31 | 32 | For interactive use it is suggested to use `rlwrap` or similiar 33 | utility, 34 | 35 | ```sh 36 | rlwrap bap exe -lcheckpaths 37 | ``` 38 | 39 | Here is the example of interaction: 40 | 41 | ```sh 42 | $ rlwrap bap paths -lcheckpath 43 | > a c b 44 | > a f b 45 | [FAIL]: main@0x40052F:64 violates a -> f -> b 46 | > a happens b 47 | [FAIL]: no such checkpoints: happens 48 | > 49 | ``` 50 | 51 | No output means, that path is valid. If path is invalid, it will be 52 | printed. Possible outputs: 53 | ``` 54 | 1. [FAIL]: violates -> -> 55 | 2. [FAIL]: no such checkpoints: , [] 56 | ``` 57 | The second variant is printed, when requested checkpoint wasn't find 58 | in the file at all, but paths may exists. Although, it is not 59 | guaranteed the path from [src] to [dst] do exist. So this should be fixed. 60 | 61 | ## Batch mode 62 | 63 | Just redirect a file with route specifications to `bap`, e.g., 64 | 65 | ```sh 66 | $ bap paths -lcheckpath < routes 67 | ``` 68 | 69 | 70 | # Compatibilty 71 | 72 | Requires BAP 0.9.6 with #191 changeset applied. 73 | -------------------------------------------------------------------------------- /checkpath/checkpath.mli: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BinaryAnalysisPlatform/bap-plugins/2e9aa5c7c24ef494d0e7db1b43c5ceedcb4196a8/checkpath/checkpath.mli -------------------------------------------------------------------------------- /deadcode/deadcode.ml: -------------------------------------------------------------------------------- 1 | (* Dead code elimination based on SSA form. 2 | 3 | If a program is in a SSA form, then dead code elimination is 4 | trivial. All variables, that are not used, are dead. Sic! 5 | 6 | We perform this optimization in a two passes: first we collect use 7 | and def sets, and afterwards filter out all variables that are 8 | defined, but not used. 9 | *) 10 | 11 | 12 | open Core_kernel 13 | open Bap.Std 14 | 15 | (* note, this function will return all variables, including 16 | non-free *) 17 | let vars_of_exp = Exp.fold ~init:Var.Set.empty (object 18 | inherit [Var.Set.t] Exp.visitor 19 | method! enter_var var vars = Set.add vars var 20 | end) 21 | 22 | let vars_of_label = function 23 | | Indirect exp -> vars_of_exp exp 24 | | Direct _ -> Var.Set.empty 25 | 26 | let collect_vars sub = 27 | let (++) = Set.union in 28 | Term.enum blk_t sub |> 29 | Seq.fold ~init:(Var.Set.empty,Var.Set.empty) ~f:(fun sets blk -> 30 | Blk.elts blk |> Seq.fold ~init:sets ~f:(fun (defs,uses) -> 31 | function 32 | | `Phi phi -> 33 | Set.add defs (Phi.lhs phi), 34 | Seq.fold (Phi.values phi) ~init:uses ~f:(fun uses (_,exp) -> 35 | uses ++ vars_of_exp exp) 36 | | `Def def -> 37 | Set.add defs (Def.lhs def), 38 | uses ++ vars_of_exp (Def.rhs def) 39 | | `Jmp jmp -> 40 | defs, 41 | uses ++ vars_of_exp (Jmp.cond jmp) ++ 42 | match Jmp.kind jmp with 43 | | Ret dst | Goto dst -> vars_of_label dst 44 | | Int (_,_) -> Var.Set.empty 45 | | Call call -> 46 | uses ++ vars_of_label (Call.target call) ++ 47 | match Call.return call with 48 | | None -> Var.Set.empty 49 | | Some dst -> vars_of_label dst)) 50 | 51 | let clean_sub arch sub = 52 | let module Target = (val target_of_arch arch) in 53 | let no_side_effects var = 54 | let open Target.CPU in 55 | Var.is_virtual var || is_flag var in 56 | 57 | let filter dead t lhs blk = 58 | Term.filter t blk ~f:(fun p -> not(Set.mem dead (lhs p))) in 59 | let rec clean sub = 60 | let defs,uses = collect_vars sub in 61 | let dead = Set.diff defs uses |> Set.filter ~f:no_side_effects in 62 | if Set.is_empty dead then sub 63 | else Term.map blk_t sub ~f:(fun blk -> 64 | blk |> 65 | filter dead phi_t Phi.lhs |> 66 | filter dead def_t Def.lhs) |> clean in 67 | clean sub 68 | 69 | let main proj = 70 | Project.program proj |> 71 | Term.map sub_t ~f:(clean_sub (Project.arch proj)) |> 72 | Project.with_program proj 73 | 74 | let () = Project.register_pass ~deps:["ssa"] main 75 | -------------------------------------------------------------------------------- /fold_consts/.merlin: -------------------------------------------------------------------------------- 1 | PKG bap 2 | PKG cmdliner 3 | PKG regular 4 | PKG graphlib 5 | B _build 6 | EXT nonrec 7 | FLG -short-paths -------------------------------------------------------------------------------- /fold_consts/Makefile: -------------------------------------------------------------------------------- 1 | .phony: all 2 | 3 | all: 4 | bapbuild -package cmdliner fold_consts.plugin 5 | 6 | clean: 7 | bapbuild -clean 8 | -------------------------------------------------------------------------------- /fold_consts/README.md: -------------------------------------------------------------------------------- 1 | # Constant Propagation 2 | 3 | This plugin performs global constant propagation. Plugin works on IR 4 | level. Currently the input should be in non SSA-form, but the output 5 | will be in SSA. 6 | 7 | Algorithm relies on SSA form, to perform a propagation. But, before 8 | doing the propagation, we insert definitions that will clobber 9 | registers and memory after calls or interrupts. 10 | 11 | To propagate constants through memory we represent memory as a graph 12 | of memory states with edges annotated by definitions. Each memory state 13 | represents a static state of memory in a particular point of program. 14 | We represent program points with a variable, representing a memory. 15 | 16 | Any write to a non-constant address clobbers all writable addresses. 17 | Whenever control flow is undecided (calls or interrupts) we explicitly 18 | clobber all general purpose registers and writable memory. We do not 19 | rely on calling conventions, since they are usually not followed by 20 | compilers. 21 | 22 | Plugin will also dereference memory access that points to statically 23 | known read-only memory regions. 24 | 25 | # Usage 26 | 27 | ```sh 28 | $ make 29 | $ bap -lfold_consts /bin/true 30 | ``` 31 | 32 | See run with `--fold-consts-help` to get more documentation. 33 | -------------------------------------------------------------------------------- /fold_consts/fold_consts.itarget: -------------------------------------------------------------------------------- 1 | fold_consts.plugin 2 | -------------------------------------------------------------------------------- /fold_consts/fold_consts.ml: -------------------------------------------------------------------------------- 1 | open Core_kernel 2 | open Bap.Std 3 | open Parameters 4 | open Cmdliner 5 | include Self() 6 | 7 | let doc = "Perform global constant folding on each function" 8 | 9 | let man = [ 10 | `S "DESCRIPTION"; 11 | `P "This plugin will perform a constant propagation on each \ 12 | subroutine. Plugin operates on the IR level. The SSA form\ 13 | is required. If program is not in SSA, theт it will be \ 14 | transformed. A propagated definition is not removed from \ 15 | a subroutine, since we can't know for sure, that it is not\ 16 | used outside of the subroutine." 17 | ] 18 | 19 | let info = Term.info ~man ~doc "fold_consts" 20 | 21 | let fix_sp : string option option Term.t = 22 | let doc = "Fix stack pointer to a constant value. If stack \ 23 | pointer address is not specified, then an address that \ 24 | is 1GB above the maximum address is chosen" in 25 | Arg.(value & opt (some (some string)) ~vopt:(Some None) 26 | options.fix_sp & info ["fix-sp"] ~doc) 27 | 28 | module Perm = struct 29 | type t = [`no | `ro | `rw] [@@deriving sexp] 30 | let to_string x = Sexp.to_string (sexp_of_t x) 31 | let variants = List.map [`no;`ro;`rw] ~f:(fun x -> to_string x, x) 32 | end 33 | 34 | let resolve_loads : Perm.t Term.t = 35 | let doc = sprintf 36 | "Resolves loads from memory addresses that are mapped from \ 37 | a provided file. Accepted values are %s. If set to `%s' \ 38 | then only loads from a write-protected memory are \ 39 | considered. If set to %s then all loads are resolved. To \ 40 | disable accessing to the static memory say `%s'" 41 | (Arg.doc_alts_enum Perm.variants) 42 | (Perm.to_string `ro) (Perm.to_string `rw) (Perm.to_string `no) in 43 | Arg.(value & opt (enum Perm.variants) ~vopt:`ro 44 | options.resolve_loads & info ["resolve-loads"] ~doc) 45 | 46 | let set_options fix_sp resolve_loads = 47 | options.fix_sp <- fix_sp; 48 | options.resolve_loads <- resolve_loads 49 | 50 | let top = Term.(pure set_options $fix_sp $resolve_loads) 51 | let main proj = match Term.eval ~argv (top,info) with 52 | | `Ok () -> Main.run proj 53 | | _ -> invalid_arg "Bad user input" 54 | 55 | let () = Project.register_pass main 56 | -------------------------------------------------------------------------------- /fold_consts/main.mli: -------------------------------------------------------------------------------- 1 | open Bap.Std 2 | val run : project -> project 3 | -------------------------------------------------------------------------------- /fold_consts/memdep.mli: -------------------------------------------------------------------------------- 1 | open Bap.Std 2 | 3 | type t 4 | 5 | (** [create ?memory arch sub] computes a memory model of a given 6 | subroutine [sub]. If static memory is provided, then it will 7 | be used to resolve memory accesses.*) 8 | val create : ?memory:value memmap -> arch -> sub term -> t 9 | 10 | 11 | (** [load model ~mem ~addr endian size] statically evaluates 12 | with respect to a computed memory [model].*) 13 | val load : t -> mem:exp -> addr:exp -> endian -> size -> exp option 14 | -------------------------------------------------------------------------------- /fold_consts/parameters.ml: -------------------------------------------------------------------------------- 1 | open Core_kernel 2 | 3 | type options = { 4 | mutable fix_sp : string option option; 5 | mutable resolve_loads : [`no | `ro | `rw]; 6 | } 7 | 8 | let options = { 9 | fix_sp = None; 10 | resolve_loads = `ro; 11 | } 12 | -------------------------------------------------------------------------------- /fold_consts/utils.ml: -------------------------------------------------------------------------------- 1 | open Core_kernel 2 | open Bap.Std 3 | 4 | let mem_elt_size arch = 5 | let module Target = (val target_of_arch arch) in 6 | match Var.typ Target.CPU.mem with 7 | | Type.Imm _ -> assert false 8 | | Type.Mem (_,s) -> s 9 | -------------------------------------------------------------------------------- /fold_consts/utils.mli: -------------------------------------------------------------------------------- 1 | open Bap.Std 2 | 3 | val mem_elt_size : arch -> size 4 | -------------------------------------------------------------------------------- /lifting-benchmark/Makefile: -------------------------------------------------------------------------------- 1 | .PHONY : all 2 | 3 | all: 4 | bapbuild -package core_kernel.caml_unix -package findlib.dynload bench.native 5 | 6 | clean: 7 | bapbuild -clean 8 | -------------------------------------------------------------------------------- /lifting-benchmark/README.md: -------------------------------------------------------------------------------- 1 | Lifter Benchmark 2 | ================ 3 | 4 | This simple tool will take a file and disassemble it from the very 5 | beginning till the last byte (even if it is not code, at all), and try 6 | to lift every possible instruction. It won't try to parse file headers, 7 | find code sections, or anything like this. At the end, if everything 8 | went fine, it will print the descriptive statistics of the lifter. 9 | 10 | The benchmark also disables the optimization pipeline, however there is still 11 | a small overhead over the lifter, because the typechecking is enforced. 12 | In fact, at the end, we typecheck at least trice :) 13 | 14 | Building and using 15 | ================== 16 | 17 | ``` 18 | make 19 | ./bench.native 20 | ``` 21 | 22 | You can also specify an architecture: 23 | ``` 24 | ./bench.native 25 | ``` 26 | 27 | 28 | Examples 29 | ======== 30 | 31 | On Intel(R) Xeon(R) CPU E5-2630 v4 @ 2.20GHz, bap 1.5 built wit OCaml 4.05.0+flambda, 32 | and llvm-3.8, yields the following results, 33 | ``` 34 | $ ./bench.native /lib/x86_64-linux-gnu/libc-2.23.so 35 | Statistics for the x86_64 lifter 36 | Total time: 6.89342 s 37 | Total throughtput: 265 kB/s 38 | Insn throughtput: 74532 I/s 39 | Insn latency: 13.42 mks/I 40 | Bytes processed: 1868831 41 | Data bytes: 24636 42 | Code bytes: 1844195 43 | Code density: 98.68% 44 | Total number of instructions: 513777 45 | Lifted instructions: 497837 46 | Lifting coverage: 96.90% 47 | ``` 48 | 49 | and for Google Chrome 50 | 51 | ``` 52 | $ ./bench.native /opt/google/chrome/chrome 53 | Statistics for the x86_64 lifter 54 | Total time: 571.788 s 55 | Total throughtput: 240 kB/s 56 | Insn throughtput: 75191 I/s 57 | Insn latency: 13.30 mks/I 58 | Bytes processed: 140517351 59 | Data bytes: 1285721 60 | Code bytes: 139231630 61 | Code density: 99.09% 62 | Total number of instructions: 42993264 63 | Lifted instructions: 41657415 64 | Lifting coverage: 96.89% 65 | ``` 66 | 67 | Normalized to the CPU speed (i.e., CPU speed / throughput ), it means 68 | that currently the lifter on average makes 9,000 ops per disassembled 69 | byte and 30,000 operations per disassembled instruction. 70 | 71 | 72 | Caveats 73 | ======= 74 | 75 | For some reason the arm backend in LLVM just stops instead of erroring, 76 | on a malformed data. So for arm we need to feed it with something that 77 | looks like code. 78 | -------------------------------------------------------------------------------- /lifting-benchmark/bench.ml: -------------------------------------------------------------------------------- 1 | open Core_kernel 2 | open Bap.Std 3 | 4 | module Unix = Caml_unix 5 | 6 | let usage () = 7 | eprintf "Performs linear sweep disassembly and lifting of raw bytes\n"; 8 | eprintf "Usage: ./bench [] \n"; 9 | exit 1 10 | 11 | module Dis = Disasm_expert.Basic 12 | 13 | module Stats = struct 14 | let start = ref 0. 15 | let finish = ref 0. 16 | let fails = ref 0 17 | let insns = ref 0 18 | let code = ref 0 19 | let data = ref 0 20 | let update stat data = stat := !stat + data 21 | 22 | 23 | let print arch = 24 | let total = !finish -. !start in 25 | let total_bytes = !code + !data in 26 | let total_insns = !insns + !fails in 27 | let latency = (total /. float total_insns) *. 1e6 in 28 | let speed p = (float p /. total) in 29 | let ratio m n = (float m /. float n) *. 100. in 30 | printf "Statistics for the %a lifter\n" Arch.ppo arch; 31 | printf "Total time: %g s\n" total; 32 | printf "Total throughtput: %.0f kB/s\n" @@ speed (total_bytes / 1024) ; 33 | printf "Insn throughtput: %.0f I/s\n" @@ speed total_insns; 34 | printf "Insn latency: %.2f mks/I\n" latency; 35 | printf "Bytes processed: %d\n" total_bytes; 36 | printf "Data bytes: %d\n" !data; 37 | printf "Code bytes: %d\n" !code; 38 | printf "Code density: %.2f%%\n" @@ ratio !code total_bytes; 39 | printf "Total number of instructions: %d\n" total_insns; 40 | printf "Lifted instructions: %d\n" !insns; 41 | printf "Lifting coverage: %.2f%%\n" @@ ratio !insns total_insns 42 | end 43 | 44 | let disasm arch mem = 45 | let module Target = (val target_of_arch arch) in 46 | Dis.with_disasm ~backend:"llvm" (Arch.to_string arch) ~f:(fun dis -> 47 | Stats.start := Unix.gettimeofday (); 48 | Result.return @@ Dis.run dis mem ~init:() ~return:ident 49 | ~stop_on:[`Valid] 50 | ~stopped:(fun s () -> 51 | Stats.finish := Unix.gettimeofday ()) 52 | ~invalid:(fun s mem () -> 53 | Stats.update Stats.data (Memory.length mem); 54 | Dis.step s ()) 55 | ~hit:(fun s mem insn () -> 56 | Stats.update Stats.code (Memory.length mem); 57 | match Target.lift mem insn with 58 | | Ok _ -> 59 | incr Stats.insns; 60 | Dis.step s (); 61 | | Error _ -> 62 | incr Stats.fails; 63 | Dis.step s ())) 64 | 65 | 66 | let main arch file = 67 | let size = Arch.addr_size arch in 68 | let base = Word.zero (Size.in_bits size) in 69 | match Memory.of_file (Arch.endian arch) base file with 70 | | Error err -> 71 | eprintf "Error: file is not readable or regular - %s\n" 72 | (Error.to_string_hum err); 73 | exit 1 74 | | Ok mem -> match disasm arch mem with 75 | | Error err -> 76 | eprintf "Error: failed to initialize the disassembler - %s\n" 77 | (Error.to_string_hum err) 78 | | Ok () -> 79 | Stats.print arch 80 | 81 | let read_arch s = match Arch.of_string s with 82 | | Some a -> a 83 | | None -> 84 | eprintf "Error: unknown architecture %s\n" s; 85 | eprintf "The list of known architectures:\n"; 86 | List.iter Arch.all ~f:(eprintf "\t%a\n" Arch.ppo); 87 | exit 1 88 | 89 | let read_file s = 90 | if Sys.file_exists s && not (Sys.is_directory s) 91 | then s 92 | else begin 93 | eprintf "Error: `%s' is not a regular file\n" s; 94 | usage () 95 | end 96 | 97 | 98 | let () = 99 | match Bap_main.init () with 100 | | Error err -> 101 | Format.eprintf "Failed to initialize bap: %a@\n%!" 102 | Bap_main.Extension.Error.pp err; 103 | | Ok () -> match Array.length Sys.argv with 104 | | 2 -> main `x86_64 (read_file Sys.argv.(1)) 105 | | 3 -> main (read_arch Sys.argv.(1)) (read_file Sys.argv.(2)) 106 | | _ -> usage () 107 | -------------------------------------------------------------------------------- /lifting-benchmark/bench.mli: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BinaryAnalysisPlatform/bap-plugins/2e9aa5c7c24ef494d0e7db1b43c5ceedcb4196a8/lifting-benchmark/bench.mli -------------------------------------------------------------------------------- /mem-printer/Makefile: -------------------------------------------------------------------------------- 1 | all: 2 | bapbuild mem_printer.plugin 3 | 4 | clean: 5 | rm -rf _build *.plugin 6 | -------------------------------------------------------------------------------- /mem-printer/README.md: -------------------------------------------------------------------------------- 1 | This is a simple plugin which outputs the way bap views the memory layout 2 | for a given executable. 3 | -------------------------------------------------------------------------------- /mem-printer/mem_printer.ml: -------------------------------------------------------------------------------- 1 | open Core_kernel 2 | open Bap.Std 3 | open Format 4 | 5 | let main project = 6 | Project.memory project |> Memmap.to_sequence |> Seq.iter ~f:(fun (mem,x) -> 7 | printf "%s(%a)@.%a@." (Value.tagname x) Value.pp x Memory.pp mem) 8 | 9 | let () = Project.register_pass' main 10 | -------------------------------------------------------------------------------- /mem-printer/run.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | echo "Running plugin on $1..." 4 | bap $1 --loader=bap-elf --use-ida idaq --no-byteweight -lmem_printer 5 | -------------------------------------------------------------------------------- /minos/.merlin: -------------------------------------------------------------------------------- 1 | EXT nonrec 2 | EXT ounit 3 | 4 | S . 5 | B _build 6 | 7 | PKG bap core_kernel cmdliner ocamlgraph re.posix ocamlbuild oUnit FrontC bap-x86-cpu bap-arm 8 | 9 | FLG -short-paths 10 | FLG -w -4-33-40-41-42-43-34-44 11 | -------------------------------------------------------------------------------- /minos/Makefile: -------------------------------------------------------------------------------- 1 | .phony: all 2 | all : 3 | bapbuild minos.plugin -pkg core_kernel.caml_unix -pkg FrontC -I checks -pkg bap-x86-cpu -pkg cmdliner -pkg bap-arm 4 | bapbundle install minos.plugin 5 | 6 | test: 7 | ocaml test.ml 8 | rm *.dot 9 | rm *.py 10 | 11 | clean : 12 | bapbuild -clean 13 | rm *.plugin 14 | -------------------------------------------------------------------------------- /minos/args.ml: -------------------------------------------------------------------------------- 1 | open Bap.Std 2 | open Core_kernel 3 | 4 | let to_reg_x86_64 = function 5 | | "arg1" -> "RDI" 6 | | "arg2" -> "RSI" 7 | | "arg3" -> "RDX" 8 | | "arg4" -> "RCX" 9 | | "arg5" -> "R8" 10 | | "arg6" -> "R9" 11 | | "" -> "" 12 | | _ -> 13 | failwith "Arg not supported [x64]: Use [arg1 | arg2 | arg3 | \ 14 | arg4 | arg5 | arg6" 15 | 16 | (* Can be cdecl or stdcall. This code is a placeholder, and should not 17 | be used. *) 18 | let to_reg_x86 = function 19 | | "ret" -> "EAX" 20 | | "arg1" -> "EBP-4" 21 | | "arg2" -> "EBP-8" 22 | | "arg3" -> "EBP-12" 23 | | "arg4" -> "EBP-16" 24 | | "arg5" -> "EBP-20" 25 | | "arg6" -> "EBP-24" 26 | | "" -> "" 27 | | _ -> 28 | failwith "Arg not supported [x86]: Use [ret | arg1 | arg2 | arg3 | arg4 " 29 | 30 | let to_reg_arm = function 31 | | "ret" -> "R0" 32 | | "arg1" -> "R0" 33 | | "arg2" -> "R1" 34 | | "arg3" -> "R2" 35 | | "arg4" -> "R3" 36 | | "" -> "" 37 | | _ -> 38 | failwith "Arg not supported [ARM]: Use [ret | arg1 | arg2 | arg3 | arg4 " 39 | 40 | let to_reg_name project arg = 41 | match Project.arch project with 42 | | `x86 -> to_reg_x86 arg 43 | | `x86_64 -> to_reg_x86_64 arg 44 | | `armv4 | `armv5 | `armv6 | `armv7 45 | | `armv4eb | `armv5eb | `armv6eb | `armv7eb -> to_reg_arm arg 46 | | arch -> failwith @@ Format.sprintf "Arch not supported: %s" 47 | @@ Arch.to_string arch 48 | 49 | (** returns defs for arguments *) 50 | let infer project blk arg = 51 | let (^::) = Seq.cons in 52 | Blk.elts blk |> 53 | Seq.fold ~init:Seq.empty ~f:(fun acc elt -> 54 | match elt with 55 | | `Def d -> let v = Def.lhs d in 56 | let prefix = to_reg_name project arg in 57 | if String.is_prefix (Var.name v) ~prefix && 58 | (not (String.is_empty prefix)) then 59 | ((Term.tid d), d) ^:: acc 60 | else 61 | acc 62 | | `Phi p -> 63 | acc 64 | | `Jmp j -> 65 | acc) 66 | -------------------------------------------------------------------------------- /minos/binary/test-system/Makefile: -------------------------------------------------------------------------------- 1 | all: 2 | arm-linux-gnueabi-gcc -g -o test1 test1.c 3 | -------------------------------------------------------------------------------- /minos/binary/test-system/test1: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BinaryAnalysisPlatform/bap-plugins/2e9aa5c7c24ef494d0e7db1b43c5ceedcb4196a8/minos/binary/test-system/test1 -------------------------------------------------------------------------------- /minos/binary/test-system/test1.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | int main(int argc, char **argv) { 5 | 6 | char s[100]; 7 | 8 | if (argc > 2) { 9 | sprintf(s, "WRONG should be hidden branch left %s", argv[1]); 10 | sprintf(s, "most recent left %s", argv[1]); 11 | } else { 12 | sprintf(s, "WRONG should be hidden branch right %s", argv[1]); 13 | sprintf(s, "most recent right %s", argv[1]); 14 | } 15 | 16 | system(s); 17 | } 18 | 19 | -------------------------------------------------------------------------------- /minos/callstrings.ml: -------------------------------------------------------------------------------- 1 | open Core_kernel 2 | open Bap.Std 3 | open Pathlib 4 | open Poly 5 | 6 | let (^::) = Seq.cons 7 | 8 | let _v ?(v=false) ?(tag="") s = 9 | if v then 10 | Format.printf "%s %s" tag s 11 | 12 | let already_seen seen sub_tid = 13 | Seq.exists seen ~f:(fun tid -> 14 | if sub_tid = tid then true else false) 15 | 16 | let print_callstring ?(v=false) path = 17 | if v then 18 | (List.iter (Seq.to_list path |> List.rev) ~f:(fun tid -> 19 | if v then Format.printf "|%s|" @@ Tid.name tid); 20 | Format.printf "\n") 21 | 22 | let print_count ?(v=false) count = 23 | _v ~v @@ Format.sprintf "%08d%!" count; 24 | _v ~v @@ Format.sprintf "\x08\x08\x08\x08\x08\x08\x08\x08%!" 25 | 26 | (* Path search and check *) 27 | let do_path_search 28 | ?(v=false) ?(rev=false) start prog max_depth = 29 | 30 | let process_path local_state sub_tid termination = 31 | let current_path,depth = local_state in 32 | let path = sub_tid ^:: current_path in 33 | print_callstring ~v path; 34 | match termination with 35 | | `Terminal (_,count) -> 36 | print_count ~v count; 37 | (path ^:: Seq.empty, 1) 38 | | `Seen (_,count) -> 39 | print_count ~v count; 40 | (path ^:: Seq.empty, 1) 41 | | `Max_depth (_,count) -> 42 | print_count ~v count; 43 | (path ^:: Seq.empty, 1) 44 | in 45 | 46 | let finish local_state sub_tid ctxt = 47 | let paths,count = ctxt in 48 | let r_path,r_count = 49 | process_path local_state sub_tid (`Terminal ctxt) in 50 | (Seq.append paths r_path, count + r_count) 51 | in 52 | 53 | let f local_state sub_tid continue ctxt = 54 | let paths,count = ctxt in 55 | let current_path,depth = local_state in 56 | let process = process_path local_state sub_tid in 57 | if already_seen current_path sub_tid then 58 | let r_path, r_count = 59 | process (`Seen ctxt) in 60 | (Seq.append paths r_path, count + r_count) 61 | else if depth = max_depth then 62 | let r_path, r_count = process (`Max_depth ctxt) in 63 | (Seq.append paths r_path, r_count + count) 64 | else 65 | begin 66 | let state = (sub_tid ^:: current_path, depth+1) in 67 | continue ~state 68 | end 69 | in 70 | 71 | let initial_local_state = (Seq.empty, 0) in 72 | let initial_ctxt = (Seq.empty, 0) in 73 | 74 | fold_paths_prog 75 | ~rev 76 | ~prog 77 | ~state:initial_local_state 78 | ~acc:initial_ctxt 79 | ~sub:(Util.tid_of_sub start) ~finish ~f 80 | -------------------------------------------------------------------------------- /minos/check.mli: -------------------------------------------------------------------------------- 1 | open Bap.Std 2 | open Options 3 | open Trim 4 | 5 | (* This is a ctxt type for check *) 6 | type ctxt = { 7 | sub_path : Sub.t; 8 | num_paths : int; 9 | path_dir : string; 10 | options : options; 11 | project : project; 12 | trim : trim; 13 | trim_dir : string; 14 | count : int; (* Current count *) 15 | } 16 | 17 | type t = { 18 | should_produce : (ctxt -> bool); 19 | run: (ctxt -> int); 20 | reverse : bool; (* reverse the direction from sink to source *) 21 | max_depth : int; (* depth to which to traverse *) 22 | sample : int; (* number of paths to sample *) 23 | timeout : int 24 | } 25 | -------------------------------------------------------------------------------- /minos/check_suite.ml: -------------------------------------------------------------------------------- 1 | 2 | let strtol_check = Strtol_check.check 3 | let memcpy_check = Memcpy_check.check 4 | let system_check = System_check.check 5 | let sql_check = Sql_check.check 6 | let system_simple_check = System_simple_check.check 7 | 8 | let ident : Check.t = Check.( 9 | {should_produce = (fun _ -> true); 10 | run = (fun _ -> 5); 11 | reverse=false; 12 | max_depth=(-1); 13 | sample=(-1); 14 | timeout=(-1)}) 15 | 16 | let select = function 17 | | "memcpy" -> memcpy_check 18 | | "sql" -> sql_check 19 | | "system" -> system_check 20 | | "atoi" -> strtol_check 21 | | "system_simple" -> system_simple_check 22 | | _ -> ident 23 | -------------------------------------------------------------------------------- /minos/check_tmpl.ml: -------------------------------------------------------------------------------- 1 | open Bap.Std 2 | open Check 3 | open Core_kernel 4 | 5 | let (^::) = Seq.cons 6 | 7 | type arg = tid * Def.t * tid seq 8 | 9 | (* 1 indexed *) 10 | type args = {arg1 : arg option; 11 | arg2 : arg option; 12 | arg3 : arg option} 13 | 14 | type path_attrs = {args : args; 15 | jmp_deps : tid seq; 16 | num_blks : int} 17 | 18 | let get_jmp_tids sub = 19 | Term.enum blk_t sub |> 20 | Seq.fold ~init:Seq.empty ~f:(fun acc blk -> 21 | Term.enum jmp_t blk |> Seq.fold ~init:acc ~f:(fun acc jmp -> 22 | (Term.tid jmp) ^:: acc)) 23 | 24 | (** Create an arg type if it exists in this blk *) 25 | let infer_arg (ctxt : ctxt) blk arg : arg option = 26 | let inferred_arg = Args.infer ctxt.project blk arg in 27 | match Seq.hd inferred_arg with 28 | | Some (tid,def) -> 29 | Some (tid,def,Seq.empty) 30 | | None -> None (** No def for this arg in blk *) 31 | 32 | (** do not populate deps *) 33 | let infer_args ctxt sink_blk : args = 34 | let (!) = infer_arg ctxt sink_blk in 35 | let tmp = 36 | List.map ["arg1"; "arg2"; "arg3"] ~f:(!) in 37 | {arg1 = (List.nth_exn tmp 0); 38 | arg2 = (List.nth_exn tmp 1); 39 | arg3 = (List.nth_exn tmp 2)} 40 | 41 | let get_jmp_deps sub_path = 42 | let (!) = Seq.map ~f:Term.tid in 43 | let jmp_tids = get_jmp_tids sub_path in 44 | !(Seq.concat_map jmp_tids ~f:(Dependence.jmp_dep sub_path)) 45 | 46 | (** Fill in the data dependence for an arg *) 47 | let populate_deps sub_path arg : arg = 48 | let (!) = Seq.map ~f:Term.tid in 49 | let tid,def,deps = arg in 50 | let deps = !(Dependence.def_dep sub_path tid) in 51 | (tid,def,deps) 52 | 53 | (** TODO, use infer_args and simplify *) 54 | let infer_args_with_deps ctxt sink_blk = 55 | let (!) = infer_arg ctxt sink_blk in 56 | let tmp = 57 | List.map ["arg1"; "arg2"; "arg3"] ~f:(!) |> 58 | List.map ~f:(fun x -> 59 | Option.(x >>= fun x -> Some (populate_deps ctxt.sub_path x))) in 60 | {arg1 = (List.nth_exn tmp 0); 61 | arg2 = (List.nth_exn tmp 1); 62 | arg3 = (List.nth_exn tmp 2)} 63 | -------------------------------------------------------------------------------- /minos/check_tmpl.mli: -------------------------------------------------------------------------------- 1 | open Check 2 | open Bap.Std 3 | 4 | (** Check template that propvides utility functios *) 5 | 6 | type arg = tid * Def.t * tid seq 7 | 8 | (* 1 indexed *) 9 | type args = {arg1 : arg option; 10 | arg2 : arg option; 11 | arg3 : arg option} 12 | 13 | type path_attrs = {args : args; 14 | jmp_deps : tid seq; 15 | num_blks : int} 16 | 17 | val get_jmp_tids : Sub.t -> tid seq 18 | 19 | val infer_arg : ctxt -> Blk.t -> string -> arg option 20 | 21 | val infer_args : ctxt -> Blk.t -> args 22 | 23 | val get_jmp_deps : Sub.t -> tid seq 24 | 25 | val populate_deps : Sub.t -> tid * Def.t * 'a -> arg 26 | 27 | val infer_args_with_deps : ctxt -> Blk.t -> args 28 | -------------------------------------------------------------------------------- /minos/check_util.ml: -------------------------------------------------------------------------------- 1 | open Bap.Std 2 | open Core_kernel 3 | 4 | let (^::) = Seq.cons 5 | 6 | let get_jmp_tids sub = 7 | Term.enum blk_t sub |> 8 | Seq.fold ~init:Seq.empty ~f:(fun acc blk -> 9 | Term.enum jmp_t blk |> Seq.fold ~init:acc ~f:(fun acc jmp -> 10 | (Term.tid jmp) ^:: acc)) 11 | -------------------------------------------------------------------------------- /minos/check_util.mli: -------------------------------------------------------------------------------- 1 | open Bap.Std 2 | open Minos_types 3 | 4 | (** Check template that provides utility functions *) 5 | 6 | (** Return all the tids that are jumps. Typically used to check the 7 | values that the jmp are dependent on *) 8 | val get_jmp_tids : path -> tid seq 9 | -------------------------------------------------------------------------------- /minos/checks/memcpy_check.ml: -------------------------------------------------------------------------------- 1 | (** Spec: 2 | Emit 0 for paths where 3 | a) there is no data dependence of arg3 on on jumps 4 | b) the dependence span is 1 (subsumes a) 5 | c) the path does not contain an ite 6 | d) the path does not contain a call to malloc (which would imply that 7 | memory is allocated to the right size before the memcpy) 8 | *) 9 | 10 | open Bap.Std 11 | open Core_kernel 12 | open Options 13 | open Args 14 | open Check 15 | open Check_tmpl 16 | open Policy 17 | 18 | module P = Policy.Predicate 19 | 20 | (** CHECK *) 21 | let max_paths = 30 22 | 23 | let should_produce' args sink_blk = 24 | match args with 25 | | {arg3 = Some (tid,_,_)} -> 26 | P.arg_is_not_const sink_blk tid 27 | | _ -> false 28 | 29 | (** CHECK *) 30 | let check_path' inter_dependence path_attrs sub_path sink_blk = 31 | match path_attrs with 32 | | {args = {arg3 = Some (_,_,dep)}; jmp_deps; num_blks} when 33 | P.size_eq_0 inter_dependence && 34 | Policy.dep_blk_span dep sub_path = 1 && 35 | not (P.contains_ite sub_path dep) && 36 | not (P.contains_calls ["@.malloc"; "@malloc"] sub_path) 37 | -> 0 38 | | {args = {arg3 = Some (_,_,dep)}; jmp_deps; num_blks} when 39 | P.size_eq_0 inter_dependence && 40 | Policy.dep_blk_span dep sub_path < 3 && 41 | not (P.contains_ite sub_path dep) && 42 | not (P.contains_calls ["@.malloc"; "@malloc"] sub_path) 43 | -> 1 44 | | {args = {arg3 = Some (_,_,dep)}; jmp_deps; num_blks} when 45 | P.size_eq_0 inter_dependence && 46 | num_blks < 5 47 | -> 2 48 | (** any number of blocks *) 49 | | {args = {arg3 = Some (_,_,dep)}; jmp_deps} when 50 | P.size_eq_0 inter_dependence -> 3 51 | | {args = {arg3 = Some (tid,_,_)}} when P.arg_is_const_0 sink_blk tid -> 5 52 | | _ -> 4 53 | 54 | let check_path sink_blk sub_path (ctxt : Check.ctxt) path_attrs = 55 | (** AUX DATA *) 56 | let inter_dependence,arg_deps = match path_attrs.args with 57 | | {arg3 = Some (_,_,arg_deps)} -> 58 | Dependence.inter_dep arg_deps path_attrs.jmp_deps,arg_deps 59 | | _ -> Seq.empty,Seq.empty in 60 | 61 | let jmp_tids = Check_tmpl.get_jmp_tids sub_path in 62 | (** Output *) 63 | Dependence.output sub_path arg_deps path_attrs.jmp_deps 64 | inter_dependence jmp_tids ctxt; 65 | 66 | check_path' inter_dependence path_attrs sub_path sink_blk 67 | 68 | (** Infer arguments from sink blk, if sink blk is the sink tid. Then 69 | check the path. *) 70 | let run ctxt = 71 | let sink_blk = Term.last blk_t ctxt.sub_path |> Util.val_exn in 72 | let args = Check_tmpl.infer_args_with_deps ctxt sink_blk in 73 | let jmp_deps = Check_tmpl.get_jmp_deps ctxt.sub_path in 74 | let num_blks = Term.enum blk_t ctxt.sub_path |> Seq.length in 75 | let path_attr = {args; jmp_deps; num_blks} in 76 | check_path sink_blk ctxt.sub_path ctxt path_attr 77 | 78 | let should_produce ctxt = 79 | let open Trim in 80 | (** AUX DATA *) 81 | 82 | if ctxt.num_paths < max_paths && ctxt.num_paths > 0 then 83 | let trim = ctxt.trim in 84 | let sink_blk = Term.find blk_t ctxt.trim.trim_sub trim.sink_tid |> 85 | Util.val_exn in 86 | let args = Check_tmpl.infer_args ctxt sink_blk in 87 | should_produce' args sink_blk 88 | else 89 | false 90 | 91 | let check : (Check.t) = 92 | {should_produce; run; 93 | reverse=false; max_depth=(-1); 94 | sample=(-1); timeout=(-1)} 95 | -------------------------------------------------------------------------------- /minos/checks/sql_check.ml: -------------------------------------------------------------------------------- 1 | open Bap.Std 2 | open Core_kernel 3 | open Options 4 | open Args 5 | open Check 6 | open Check_tmpl 7 | open Policy 8 | 9 | module P = Policy.Predicate 10 | 11 | (** CHECK *) 12 | let max_paths = 10000 13 | 14 | let should_produce' ctxt args sink_blk = 15 | true 16 | 17 | (** CHECK *) 18 | let check_path' path_attrs sub_path = 19 | let p = Policy.Predicate.contains_call in 20 | let pred = p "@_ZNSs6appendERKSs" sub_path || 21 | p "@_ZNSs6appendEPKcj" sub_path in 22 | match pred with 23 | | true when path_attrs.num_blks < 10 -> 0 24 | | true when path_attrs.num_blks < 20 -> 1 25 | | true when path_attrs.num_blks < 50 -> 2 26 | | true -> 3 27 | | false -> 5 28 | 29 | let check_path sink_blk sub_path (ctxt : Check.ctxt) path_attrs = 30 | (** AUX DATA *) 31 | Dependence.output sub_path Seq.empty Seq.empty Seq.empty Seq.empty ctxt; 32 | check_path' path_attrs sub_path 33 | 34 | (** Infer arguments from sink blk, if sink blk is the sink tid. Then 35 | check the path. *) 36 | let run ctxt = 37 | let sink_blk = Term.last blk_t ctxt.sub_path |> Util.val_exn in 38 | let args = Check_tmpl.infer_args_with_deps ctxt sink_blk in 39 | let jmp_deps = Check_tmpl.get_jmp_deps ctxt.sub_path in 40 | let num_blks = Term.enum blk_t ctxt.sub_path |> Seq.length in 41 | let path_attr = {args; jmp_deps; num_blks} in 42 | check_path sink_blk ctxt.sub_path ctxt path_attr 43 | 44 | let should_produce ctxt = 45 | let open Trim in 46 | (** AUX DATA *) 47 | 48 | let test = 49 | let trim = ctxt.trim in 50 | let sink_blk = Term.find blk_t ctxt.trim.trim_sub trim.sink_tid |> 51 | Util.val_exn in 52 | let args = Check_tmpl.infer_args ctxt sink_blk in 53 | should_produce' ctxt args sink_blk in 54 | if ctxt.num_paths < max_paths && ctxt.num_paths > 0 && test then 55 | true 56 | else 57 | false 58 | 59 | let check : (Check.t) = 60 | {should_produce; run; 61 | reverse=false; max_depth=(-1); 62 | sample=(-1);timeout=(-1)} 63 | -------------------------------------------------------------------------------- /minos/checks/strtol_check.ml: -------------------------------------------------------------------------------- 1 | (** Spec: 2 | Emit 0 for paths where 3 | a) arg1 is not a const 4 | b) there is no data dependence of arg1 on on jumps 5 | *) 6 | 7 | open Bap.Std 8 | open Core_kernel 9 | open Policy 10 | open Options 11 | open Args 12 | open Check 13 | open Check_tmpl 14 | 15 | module P = Policy.Predicate 16 | 17 | (** CHECK *) 18 | let max_paths = 300 19 | 20 | let should_produce' args sink_blk = 21 | match args with 22 | | {arg1 = Some (tid,_,_)} -> 23 | P.arg_is_not_const sink_blk tid 24 | | _ -> false 25 | 26 | (** CHECK *) 27 | let check_path' inter_dependence path_attrs = 28 | match path_attrs.args with 29 | | {arg1 = Some _} when 30 | P.size_eq_0 inter_dependence -> 0 31 | | _ -> 5 32 | 33 | let check_path sink_blk sub_path (ctxt : Check.ctxt) path_attrs = 34 | let module P = Policy.Predicate in 35 | 36 | (** Calculate inter dependence with arg1 *) 37 | let inter_dependence,arg_deps = match path_attrs.args with 38 | | {arg1 = Some (_,_,arg_deps)} -> 39 | Dependence.inter_dep arg_deps path_attrs.jmp_deps,arg_deps 40 | | _ -> Seq.empty,Seq.empty in 41 | 42 | let jmp_tids = Check_tmpl.get_jmp_tids sub_path in 43 | Dependence.output sub_path arg_deps path_attrs.jmp_deps 44 | inter_dependence jmp_tids ctxt; 45 | check_path' inter_dependence path_attrs 46 | 47 | (** Infer arguments from sink blk, if sink blk is the sink tid. Then 48 | check the path. *) 49 | let run ctxt = 50 | let sink_blk = Term.last blk_t ctxt.sub_path |> Util.val_exn in 51 | let args = Check_tmpl.infer_args_with_deps ctxt sink_blk in 52 | let jmp_deps = Check_tmpl.get_jmp_deps ctxt.sub_path in 53 | let num_blks = Term.enum blk_t ctxt.sub_path |> Seq.length in 54 | let path_attr = {args; jmp_deps; num_blks} in 55 | check_path sink_blk ctxt.sub_path ctxt path_attr 56 | 57 | let should_produce ctxt = 58 | let open Trim in 59 | if ctxt.num_paths < max_paths && ctxt.num_paths > 0 then 60 | let trim = ctxt.trim in 61 | let sink_blk = Term.find blk_t ctxt.trim.trim_sub trim.sink_tid |> 62 | Util.val_exn in 63 | let args = Check_tmpl.infer_args ctxt sink_blk in 64 | should_produce' args sink_blk 65 | else 66 | false 67 | 68 | let check : (Check.t) = 69 | {should_produce; run; 70 | reverse=false; max_depth=(-1); 71 | sample=(-1); timeout=(-1)} 72 | -------------------------------------------------------------------------------- /minos/checks/system_simple_check.ml: -------------------------------------------------------------------------------- 1 | open Bap.Std 2 | open Core_kernel 3 | open Options 4 | open Simple_args 5 | open Check 6 | open Check_util 7 | open Policy 8 | open Format 9 | open Poly 10 | 11 | let (^::) = Seq.cons 12 | 13 | module P = Policy.Predicate 14 | 15 | (** This is before paths. We will fold const on the sub with 16 | respect to system *) 17 | let should_produce' ctxt args sink_blk = 18 | match args with 19 | | {arg1 = Some (tid,_)} 20 | when P.arg_is_not_string ctxt.project sink_blk tid -> 21 | Output.trim_priority ctxt.trim_dir 0; 22 | Output.misc (Format.sprintf "Producing for symbolic\n"); 23 | true 24 | | {arg1 = Some (tid,_)} -> (** Arg must be a string *) 25 | let str = 26 | Policy.get_arg_as_string ctxt.project sink_blk tid |> Util.val_exn in 27 | if String.is_substring ~substring:"%" str then ( 28 | Output.misc (Format.sprintf 29 | "Found system argument with format specifier %S\n" str); 30 | Output.trim_priority ctxt.trim_dir 0) 31 | else ( 32 | Output.misc (Format.sprintf "Found system argument %S\n" str); 33 | Output.trim_priority ctxt.trim_dir 1); 34 | false 35 | | _ -> 36 | Output.trim_priority ctxt.trim_dir 5; 37 | false 38 | 39 | (** helper function to resolve constant string arguments to snprintf *) 40 | let printf_arg (ctxt : ctxt) sub = 41 | (** get the blk closest at the end of the path that calls snprintf *) 42 | let blks = 43 | Term.enum blk_t sub |> Seq.fold ~init:Seq.empty ~f:(fun acc blk -> 44 | if List.exists (Util.calls_of_blk_str blk) ~f:(fun s -> 45 | s = "@snprintf") then 46 | (blk ^:: acc) else acc) in 47 | match Seq.hd blks with 48 | | Some blk -> 49 | (** Infer args *) 50 | (** return the string it contains *) 51 | let args = Simple_args.infer_args ctxt.project blk in 52 | (match args with 53 | | {arg3 = Some (tid,_)} 54 | when P.arg_is_string ctxt.project blk tid -> 55 | Policy.get_arg_as_string ctxt.project blk tid 56 | | _ -> None) 57 | | None -> None 58 | 59 | (** CHECK *) 60 | (** Path contains a system with symbolic argument. Check if 61 | it is preceded by a call to snprintf *) 62 | let check_path' ctxt args sub_path sink_blk = 63 | match args with 64 | | {arg1 = Some _} when 65 | P.contains_calls ["@snprintf"] sub_path 66 | -> (match printf_arg ctxt sub_path with 67 | | Some s -> 68 | Output.misc (Format.sprintf 69 | "%04d s[n]printf arg: %S\n" ctxt.count s); 70 | 1 71 | | None -> 5) 72 | | _ -> 0 73 | 74 | let check_path sink_blk sub_path (ctxt : Check.ctxt) path_attrs = 75 | check_path' ctxt path_attrs sub_path sink_blk 76 | 77 | (** Infer arguments from sink blk, if sink blk is the sink tid. Then 78 | check the path. *) 79 | let run ctxt = 80 | let sink_blk = Term.last blk_t ctxt.sub_path |> Util.val_exn in 81 | let args = Simple_args.infer_args ctxt.project sink_blk in 82 | check_path sink_blk ctxt.sub_path ctxt args 83 | 84 | let should_produce ctxt = 85 | let open Trim in 86 | (** AUX DATA *) 87 | 88 | let test = 89 | let sink_blk = Term.find blk_t ctxt.trim.trim_sub ctxt.trim.sink_tid |> 90 | Util.val_exn in 91 | 92 | let args = Simple_args.infer_args ctxt.project sink_blk in 93 | should_produce' ctxt args sink_blk in 94 | 95 | if test then 96 | true 97 | else 98 | false 99 | 100 | let check : (Check.t) = 101 | let max_depth = 1000 in 102 | let sample = 10 in 103 | let timeout = 3 in 104 | let reverse = true in 105 | {should_produce; run; 106 | reverse; 107 | max_depth; 108 | sample; 109 | timeout} 110 | -------------------------------------------------------------------------------- /minos/color.ml: -------------------------------------------------------------------------------- 1 | open Core_kernel 2 | 3 | type t = 4 | | Pink | Aqua | Red | Green | Blue | Orange | Brown | Gray | Purple | White 5 | 6 | let monochrome_gradient = 7 | [0x212121; 0x474747; 0x6b6b6b; 0x8c8c8c; 8 | 0xaaaaaa; 0xc4c4c4; 0xd3d3d3; 0xe8e8e8] 9 | 10 | let to_int = function 11 | | Pink -> 0xff0099 12 | | Aqua -> 0x009999 13 | | Red -> 0xff0000 14 | | Green -> 0x009900 15 | | Blue -> 0x3366ff 16 | | Orange -> 0xFF6633 17 | | Brown -> 0x663300 18 | | Gray -> 0x666666 19 | | Purple -> 0x330066 20 | | White -> 0xffffff 21 | 22 | let to_terminal = function 23 | | Pink -> "\x1b[95m" 24 | | Aqua -> "\x1b[46m" 25 | | Red -> "\x1b[41m" 26 | | Green -> "\x1b[42m" 27 | | Blue -> "\x1b[44m" 28 | | Orange -> "\x1b[43m" 29 | | Brown -> "\x1b[40m" (* normal *) 30 | | Gray -> "\x1b[40m" (* normal *) 31 | | Purple -> "\x1b[35m" 32 | | White -> "\x1b[37m" 33 | 34 | 35 | let (!) = to_int 36 | let (!!) = to_terminal 37 | -------------------------------------------------------------------------------- /minos/configs/n-1-@system/sinks.txt: -------------------------------------------------------------------------------- 1 | @system 2 | -------------------------------------------------------------------------------- /minos/configs/n-1-@system/srcs.txt: -------------------------------------------------------------------------------- 1 | LCA:1 2 | -------------------------------------------------------------------------------- /minos/ctxt.mli: -------------------------------------------------------------------------------- 1 | (** Global path state/context, needs renaming *) 2 | 3 | open Bap.Std 4 | open Options 5 | open Trim 6 | open Graphlib.Std 7 | 8 | type t = { 9 | check : Check.t; 10 | path_dir: string; 11 | trim_dir : string; 12 | count : int; 13 | project : project; 14 | options: options; 15 | trim : trim; 16 | max_depth : int; (** depth to go to before terminating path *) 17 | sample: int; (** number of paths to sample *) 18 | g : (module Graph with type edge = Graphs.Tid.edge and 19 | type node = tid and type t = Graphs.Tid.t) 20 | } 21 | -------------------------------------------------------------------------------- /minos/cut.mli: -------------------------------------------------------------------------------- 1 | open Bap.Std 2 | 3 | (** A cut is a subgraph that contains a call to a sink we are 4 | interested in. Each cut has a unique caller (and callstring up to the 5 | root of the program). We define cut *groups* since a single cut may 6 | contain multiple sinks within the final calling sub. 7 | 8 | E.g., bar below will form a single cut group, which calls sink1 and sink2. 9 | bazz forms a second cut group. 10 | 11 | foo 12 | / \ 13 | / bazz 14 | / -> calls sink3 on some path 15 | bar 16 | -> calls sink1 on some path 17 | -> calls sink 2 on some other path 18 | 19 | *) 20 | 21 | type src_config = 22 | {src_at_root : bool; 23 | src_at_nth : int; 24 | src : string } 25 | 26 | type cut_group = { 27 | (* Stores the blks that call a given source *) 28 | src_caller_blks : Blk.t seq; 29 | sink_caller_blks : Blk.t seq; 30 | (* The callstring from src to the lca *) 31 | src_callstring : tid seq; 32 | sink_callstring : tid seq; 33 | (* The sub that calls src. src_caller_blks are all contained in 34 | this sub *) 35 | src_caller_sub : Sub.t; 36 | sink_caller_sub : Sub.t; 37 | lca_sub : Sub.t; (* root *) 38 | lca_name : string; 39 | depth: int; (* max depth we would have to inline to hit this *) 40 | id: int; (* group id, for output *) 41 | } 42 | 43 | val print_cut_group : cut_group -> unit 44 | 45 | val output_cut_group : cut_group -> unit 46 | 47 | val cuts : project -> Graphs.Callgraph.t -> src_config -> string -> cut_group seq 48 | -------------------------------------------------------------------------------- /minos/filter.ml: -------------------------------------------------------------------------------- 1 | open Core_kernel 2 | open Bap.Std 3 | 4 | type t = string -> bool 5 | 6 | let whitelist = 7 | [ "@\\..*" ; (* plt for x86 *) 8 | "@memcpy"; (* plt for ARM *) 9 | "@recvfrom"; 10 | "@recv"; 11 | "@system"; 12 | "@sprintf"; 13 | "@strcpy"; 14 | "@strcat"] 15 | 16 | let compile s = 17 | s |> List.map ~f:Re.Posix.re 18 | |> Re.alt 19 | |> Re.compile 20 | |> Re.execp 21 | 22 | let apply f str = f str 23 | 24 | let plt_filter ~extra : t = 25 | extra @ whitelist |> compile 26 | 27 | let cpp_filter ~extra : t = 28 | [ "@_ZNS.*"] @ extra @ whitelist |> compile 29 | -------------------------------------------------------------------------------- /minos/filter.mli: -------------------------------------------------------------------------------- 1 | type t 2 | 3 | (** A standard filter for plt calls. [extra] will add strings to the 4 | filtered set *) 5 | val plt_filter : extra:string list -> t 6 | 7 | (** Includes plt_filter, and adds everything matching @_ZNS.* *) 8 | val cpp_filter : extra:string list -> t 9 | 10 | (** Given a filter and a string, return whether the string is 11 | filtered *) 12 | val apply : t -> string -> bool 13 | -------------------------------------------------------------------------------- /minos/fold_consts.ml: -------------------------------------------------------------------------------- 1 | open Core_kernel 2 | open Bap.Std 3 | open Poly 4 | 5 | let fix_sp arch sub = 6 | let module Target = (val target_of_arch arch) in 7 | let width = Size.in_bits (Arch.addr_size arch) in 8 | (* TODO Def.create (Target.CPU.sp)... *) 9 | let make_addr x = Addr.of_int64 ~width x in 10 | let stack_offset = 0x40000000L in 11 | let sp_base = make_addr stack_offset in 12 | let first_blk = Term.first blk_t sub |> Util.val_exn in 13 | let mod_blk = Term.prepend def_t first_blk 14 | (Def.create (X86_cpu.AMD64.sp) (Bil.int sp_base)) in 15 | Term.map blk_t sub ~f:(fun blk -> 16 | if Term.name blk = Term.name first_blk then 17 | mod_blk else blk) 18 | 19 | (** Needs to be fixed. If you see Not_found it's because Sub.ssa 20 | fails. No idea why. *) 21 | let propagate_consts sub = 22 | let (!) = Exp.fixpoint Exp.fold_consts in 23 | let propagate sub = 24 | Term.enum blk_t sub |> Seq.fold ~init:sub ~f:(fun sub blk -> 25 | Term.enum def_t blk |> Seq.fold ~init:sub ~f:(fun sub def -> 26 | let lhs = Bil.var (Def.lhs def) in 27 | let rhs = !(Def.rhs def) in 28 | match rhs with (* if after !ing we have a const, substitute *) 29 | | Bil.Int _ -> 30 | Term.map blk_t sub ~f:(fun blk -> 31 | let res = 32 | Blk.substitute blk lhs rhs in 33 | res) 34 | | _ -> sub)) in 35 | (* after substitution, simplify everything we can in the sub *) 36 | let simplify sub = Term.map blk_t sub ~f:(fun blk -> 37 | (*Format.printf "Problem block: %s\n%!" @@ Blk.to_string blk;*) 38 | Blk.map_exp blk ~f:(fun e -> 39 | (*Format.printf "doing: e: %s!\n%!" @@ Exp.to_string e;*) 40 | let res = !e in 41 | (*Format.printf "DONE\n%!";*) 42 | res)) in 43 | let step sub = sub |> propagate |> simplify in 44 | let rec fp sub = 45 | let sub' = step sub in 46 | if Sub.equal sub sub' then sub else fp sub' in 47 | (*Format.printf "This sub will fail to SSA:\n%s\n" @@ Sub.to_string sub;*) 48 | try 49 | fp (Sub.ssa sub) 50 | with 51 | | Caml.Not_found -> failwith "Sub could not be ssa'd" 52 | 53 | let analyze ?(fixsp=true) arch sub = 54 | if fixsp then 55 | fix_sp arch sub |> propagate_consts 56 | else 57 | propagate_consts sub 58 | -------------------------------------------------------------------------------- /minos/interprocedural.mli: -------------------------------------------------------------------------------- 1 | open Bap.Std 2 | open Filter 3 | 4 | val inline_fixpoint : ?warn:bool -> project -> Sub.t -> Filter.t -> 5 | Sub.t * (string * int) list 6 | 7 | val inline_n : ?warn:bool -> project -> 8 | sub term -> Filter.t -> int -> Sub.t * (string * int) list 9 | -------------------------------------------------------------------------------- /minos/minos_types.ml: -------------------------------------------------------------------------------- 1 | open Bap.Std 2 | 3 | (** Paths are initially represented as a sequence of tids. If it 4 | reaches a checking stage, the check is passed the path in the form of 5 | a Sub.t. So, in the context of minos, an end-result path is a Sub.t. 6 | We use this for convenience *) 7 | type path = Sub.t 8 | -------------------------------------------------------------------------------- /minos/normal_graph_view.dot: -------------------------------------------------------------------------------- 1 | digraph G { 2 | "\00000162"; 3 | "\00000175"; 4 | "\0000017f"; 5 | "\00000189"; 6 | "\000001a2"; 7 | "\000001ac"; 8 | "\000001b6"; 9 | 10 | 11 | "\00000162" -> "\00000175" [label="%00000173", ]; 12 | "\00000162" -> "\000001a2" [label="%00000174", ]; 13 | "\00000175" -> "\0000017f" [label="%0000017e", ]; 14 | "\0000017f" -> "\00000189" [label="%00000188", ]; 15 | "\000001a2" -> "\000001ac" [label="%000001ab", ]; 16 | "\000001ac" -> "\000001b6" [label="%000001b5", ]; 17 | "\000001b6" -> "\00000189" [label="%000001b7", ]; 18 | 19 | } -------------------------------------------------------------------------------- /minos/options.ml: -------------------------------------------------------------------------------- 1 | type options = { 2 | check : string; 3 | config : string; 4 | with_dots: bool; 5 | cuts_only: bool; 6 | trims_only: bool; 7 | path_counts_only: bool; 8 | srcs_f : string; 9 | sinks_f : string; 10 | single_trim : int; 11 | single_cut : int; 12 | single_case : int; 13 | mem_to_reg : bool; 14 | fold_consts : bool; 15 | output_dot_path : bool; 16 | out_dir : string; 17 | no_inline : bool; 18 | verbose : bool 19 | } 20 | -------------------------------------------------------------------------------- /minos/options.mli: -------------------------------------------------------------------------------- 1 | (** The order of processing is: 2 | 1) Generate cut groups (all pairs of sources and sinks) 3 | 2) Generate trims (valid cut groups where sources occur before sinks) 4 | 3) Generate paths over trims (includes invalid paths that may not end 5 | with sink) 6 | 4) Produce paths for analysis 7 | *) 8 | 9 | type options = { 10 | check : string; (* the check to use *) 11 | config : string; (* config file, unused currently *) 12 | with_dots: bool; (* output dots for trims and cuts *) 13 | cuts_only: bool; (* produce only cut groups *) 14 | trims_only: bool; (* produce only trims *) 15 | path_counts_only: bool; (* perform only path counts *) 16 | srcs_f : string; (* file containing name of srcs *) 17 | sinks_f : string; (* file containing name of sinks *) 18 | single_trim : int; (* consider only a single trim case (needs case) *) 19 | single_cut : int; (* consider only a single cut *) 20 | single_case : int; (* consider only a single case *) 21 | mem_to_reg : bool; (* perform mem_to_reg *) 22 | fold_consts : bool; (* perform fold_consts *) 23 | output_dot_path : bool; (* generate dot outputs for paths *) 24 | out_dir : string; (* specify an analysis output directory *) 25 | no_inline : bool; (* default behavior is to inline. this turns it off *) 26 | verbose : bool (* switch for outputting analysis results to terminal *) 27 | } 28 | -------------------------------------------------------------------------------- /minos/output.mli: -------------------------------------------------------------------------------- 1 | open Bap.Std 2 | open Core_kernel 3 | open Profile 4 | 5 | (* Output meta information of analysis for cut groups *) 6 | (* `Valid groups have source before sink. 7 | `Invalid groups have source after sink. 8 | `Skipped groups have multiple block structures matching 9 | the sink. We skip these for now *) 10 | type typ = [`Valid | `Invalid | `Skipped] 11 | 12 | (* With root directory *) 13 | val init : string -> unit 14 | 15 | (* Output to meta.txt *) 16 | val meta : string -> unit 17 | 18 | (* Output to misc.txt *) 19 | val misc : string -> unit 20 | 21 | (* Output to cuts.txt *) 22 | val cuts : string -> unit 23 | 24 | (* Output the graphs of a cut group, and create directories depending 25 | on valid or invalid *) 26 | val cut_graph: typ -> tid -> tid -> Sub.t -> int -> int -> unit 27 | 28 | (* Output to trims.txt *) 29 | val trims : string -> unit 30 | 31 | (* Output a trim group's cases and graphs *) 32 | val trim_graph : tid -> tid -> Sub.t -> int -> int -> unit 33 | 34 | (* Output to paths.txt *) 35 | val paths : string -> string -> unit 36 | 37 | (* Output the path trace (with data dependence *) 38 | val path : string -> int -> string -> unit 39 | 40 | (* Output the .dot of the path trace *) 41 | val path_dot : string -> Sub.t -> tid seq -> int -> unit 42 | 43 | (* Output the priority for checking a path *) 44 | val path_priority : string -> int -> int -> unit 45 | 46 | (* Output trim priority *) 47 | val trim_priority : string -> int -> unit 48 | 49 | (* Output bulk trim information *) 50 | val output_trim : bool -> tid -> tid -> Sub.t -> int -> int -> int -> Profile.t -> unit 51 | -------------------------------------------------------------------------------- /minos/path_consumer.ml: -------------------------------------------------------------------------------- 1 | open Core_kernel 2 | open Bap.Std 3 | open Check 4 | open Util 5 | 6 | let consume sub_path (check : Check.t) (ctxt : Check.ctxt) = 7 | (** Priority type may change, so stick with pattern matching *) 8 | (** Without SSA, dependence matching fails and so does my check. 9 | comment out for now*) 10 | try 11 | match Util.timeout ~secs:3 ~f:check.run ~x:ctxt with 12 | | p -> Output.path_priority ctxt.path_dir ctxt.count p 13 | with 14 | | Timeout -> Format.printf "TIMEOUT!\n%!"; () 15 | -------------------------------------------------------------------------------- /minos/path_consumer.mli: -------------------------------------------------------------------------------- 1 | open Bap.Std 2 | open Trim 3 | open Check 4 | 5 | (** Path consumer dispatches path to a module that checks the path and 6 | returns an analysis result *) 7 | 8 | (** Consume takes a path (after simplification) as a Sub.t. It also 9 | gets the global state. Consume dispatches the information to 10 | a checking module, and outputs the result in the path directory. *) 11 | val consume : Sub.t -> Check.t -> Check.ctxt -> unit 12 | -------------------------------------------------------------------------------- /minos/path_producer.mli: -------------------------------------------------------------------------------- 1 | open Bap.Std 2 | open Options 3 | open Ctxt 4 | open Trim 5 | 6 | (** Start the path runner *) 7 | (** project, options, path_dir, trim_dir, max_depth, check *) 8 | val produce : project -> options -> string -> string -> trim -> Check.t -> Ctxt.t 9 | -------------------------------------------------------------------------------- /minos/pathlib.ml: -------------------------------------------------------------------------------- 1 | open Core_kernel 2 | open Bap.Std 3 | open Graphlib.Std 4 | 5 | type exit_type = [`Seen | `Max_depth | `Terminal] 6 | 7 | (* Printers *) 8 | (* -------- *) 9 | (** Print calls of a blk seq path*) 10 | let print_calls_of_blk_seq blk_seq = 11 | Seq.iter blk_seq ~f:(fun blk -> 12 | let jmps = Term.enum jmp_t blk in 13 | let names = Seq.map jmps ~f:Util.resolve_jmp_name in 14 | Seq.iter names ~f:(fun x -> match x with 15 | | Some x -> 16 | Format.printf "%s\n" x 17 | | None -> ())) 18 | 19 | let print_path path = 20 | Format.printf "path:\n"; 21 | Seq.iter path ~f:(fun node -> 22 | Format.printf "Tid: %a\n" Tid.pp node) 23 | 24 | let print_path_content sub path = 25 | Format.printf "path:\n"; 26 | Seq.iter path ~f:(fun node -> 27 | Format.printf "Tid: %a\n" Tid.pp node; 28 | let blk = Util.blk_of_tid sub node in 29 | Format.printf "%a\n" Blk.pp blk; 30 | Term.enum jmp_t blk |> Seq.iter ~f:(fun jmp -> 31 | let e = Jmp.cond jmp in 32 | Format.printf "[--Jmp cond: %a : %a--]\n" Jmp.pp jmp Exp.pp e)) 33 | 34 | (** given a path (and sub, so we can resolve tid's), print the 35 | things it calls *) 36 | let print_path_calls path sub = 37 | print_calls_of_blk_seq (Seq.map path ~f:(fun tid -> Util.blk_of_tid sub tid)) 38 | 39 | let print_blk_path path = 40 | Format.printf "Blk path:\n"; 41 | Seq.iter path ~f:(fun node -> 42 | Format.printf "Blk: %a" Blk.pp node) 43 | 44 | let blks_of_path path sub = 45 | Seq.map path ~f:(fun tid -> Util.blk_of_tid sub tid) 46 | 47 | (* Utils *) 48 | (* ----- *) 49 | let blk_seq_of_tid_seq path sub = 50 | Seq.map path ~f:(fun tid -> Util.blk_of_tid sub tid) 51 | 52 | let rec fold_paths_prog ?(rev=false) ~prog ~state ~acc ~sub ~finish ~f = 53 | let next = if rev 54 | then Graphs.Callgraph.Node.preds 55 | else Graphs.Callgraph.Node.succs in 56 | match next sub prog with 57 | | l when Seq.is_empty l -> finish state sub acc 58 | | children -> 59 | Seq.fold ~init:acc children 60 | ~f:(fun acc child -> 61 | let continue = fold_paths_prog ~rev ~prog ~acc ~sub:child ~finish ~f 62 | in 63 | f state sub continue acc) 64 | 65 | let debug succs blk = 66 | Format.printf "Current: %s Succs:%!" @@ Tid.to_string blk; 67 | Seq.iter succs ~f:(fun s -> 68 | Format.printf "%s| " @@ Tid.to_string s); 69 | Format.printf "\n%!" 70 | 71 | let rec fold_paths_graph ?(rev=false) 72 | (module G : Graph with 73 | type edge = Graphs.Tid.edge and 74 | type node = tid and 75 | type t = Graphs.Tid.t) 76 | ~(sub : Graphs.Tid.t) ~state ~acc ~(blk : tid) ~finish ~f = 77 | let next = if rev 78 | then G.Node.preds 79 | else G.Node.succs in 80 | match next blk sub with 81 | | l when Seq.is_empty l -> finish state blk acc 82 | | children -> 83 | Seq.fold ~init:acc children 84 | ~f:(fun acc child -> 85 | let continue = fold_paths_graph ~rev (module G) ~sub ~acc ~blk:child 86 | ~finish ~f in 87 | f state blk continue acc) 88 | 89 | let rec fold_paths ~sub ~state ~acc ~blk ~finish ~f = 90 | match Util.succs_of_blk_tid sub blk with 91 | | l when Seq.is_empty l -> finish state blk acc 92 | | children -> 93 | Seq.fold ~init:acc children 94 | ~f:(fun acc child -> 95 | let continue = fold_paths ~sub ~acc ~blk:child ~finish ~f in 96 | f state blk continue acc) 97 | 98 | (** pass the accumulator along if we terminate early *) 99 | type 'a termination = 100 | Terminal of 'a 101 | | Seen of 'a 102 | | Max_depth of 'a 103 | 104 | let calls_of_path path = 105 | Seq.fold ~init:0 path ~f:(fun acc blk -> 106 | let jmps = Term.enum jmp_t blk in 107 | let names = Seq.map jmps ~f:Util.resolve_jmp_name in 108 | Seq.fold ~init:acc (Seq.zip names jmps) ~f:(fun acc (name,jmp) -> 109 | match name with 110 | | Some name -> 111 | Format.printf "%s\n" name; 112 | acc 113 | | None -> acc)) 114 | -------------------------------------------------------------------------------- /minos/pathlib.mli: -------------------------------------------------------------------------------- 1 | open Bap.Std 2 | open Graphlib.Std 3 | 4 | (** A path terminates if it has been [`Seen], reaches a [`Max_depth] 5 | or hits a [`Terminal] point *) 6 | type exit_type = [`Seen | `Max_depth | `Terminal] 7 | 8 | (** [state] is the local state passed to each path. I typically use it 9 | to preserve two things I care about: the current path, and the 10 | current path depth. [acc] is the global state that holds while 11 | traversing along all paths. I use it to store project, the total 12 | path counts, and any other data (such as output directory) for 13 | paths. Note that acc is returned by this function (so you get back 14 | your global state). 15 | 16 | [blk] is the blk to start at. [finish] is called at the 17 | termination of a path. [f] is called on each node except the final 18 | node (which is called by finish). A continuation function is 19 | passed in [f]: (state:'a -> 'b). This allows propagating local 20 | state forward. *) 21 | 22 | val fold_paths: 23 | sub:Sub.t -> 24 | state:'a -> 25 | acc:'b -> 26 | blk:tid -> 27 | finish:('a -> tid -> 'b -> 'b) -> 28 | f:('a -> tid -> (state:'a -> 'b) -> 'b -> 'b) -> 'b 29 | 30 | (** This is the variant of fold_paths that operates over the graph 31 | representation. Supports reverse traversal. *) 32 | val fold_paths_graph : 33 | ?rev:bool -> 34 | (module Graph 35 | with type edge = Graphs.Tid.edge and 36 | type node = tid and 37 | type t = Graphs.Tid.t) -> 38 | sub:Graphs.Tid.t -> 39 | state:'a -> 40 | acc:'b -> 41 | blk:tid -> 42 | finish:('a -> tid -> 'b -> 'b) -> 43 | f:('a -> tid -> (state:'a -> 'b) -> 'b -> 'b) -> 'b 44 | 45 | (** Operates in the same manner as fold_paths, but starts at a given 46 | [sub] and traverses over the callgraph. This can optionally be done 47 | in reverse *) 48 | val fold_paths_prog: 49 | ?rev:bool -> 50 | prog:Graphs.Callgraph.t -> 51 | state:'a -> 52 | acc:'b -> 53 | sub:tid -> 54 | finish:('a -> tid -> 'b -> 'b) -> 55 | f:('a -> tid -> (state:'a -> 'b) -> 'b -> 'b) -> 'b 56 | 57 | (** Convert a sequence of tids to blks *) 58 | val blks_of_path : tid seq -> Sub.t -> Blk.t seq 59 | -------------------------------------------------------------------------------- /minos/policy.mli: -------------------------------------------------------------------------------- 1 | open Bap.Std 2 | 3 | (** Priority of analysis result. Should probably not be in policy module *) 4 | type priority 5 | 6 | module Predicate : sig 7 | (** Provides a set of predicates that can be used in an analysis over paths *) 8 | 9 | (** Whether an argument is a constant (0) for a given def in a blk *) 10 | val arg_is_const_0 : Blk.t -> tid -> bool 11 | 12 | (** Argument is some const *) 13 | val arg_is_const : Blk.t -> tid -> bool 14 | 15 | (** Argument is not a const (symbolic) *) 16 | val arg_is_not_const : Blk.t -> tid -> bool 17 | 18 | (** Deprecated: use _string_ version. Argument is a memory value 19 | that points to rodata *) 20 | val arg_is_rodata : project -> Blk.t -> tid -> bool 21 | 22 | (** Deprecated: use _string_ version. Argument is not a memory value 23 | that points to rodata *) 24 | val arg_is_not_rodata : project -> Blk.t -> tid -> bool 25 | 26 | (** Deprecated: use _string_ version. Argument is a memory value 27 | that points to rodata, and contains string *) 28 | val arg_rodata_contains : project -> blk term -> tid -> string -> bool 29 | 30 | val arg_is_string : project -> Blk.t -> tid -> bool 31 | 32 | val arg_is_not_string : project -> Blk.t -> tid -> bool 33 | 34 | val arg_string_contains : project -> blk term -> tid -> string -> bool 35 | 36 | (** The path (subroutine) contains a call, e.g., "@.malloc" *) 37 | val contains_call : string -> Sub.t -> bool 38 | 39 | (** The path (subroutine) contains any of these call, e.g., "@.malloc" *) 40 | val contains_calls : string list -> Sub.t -> bool 41 | 42 | (** Check whether defs containt an ITE (dependencies) *) 43 | val contains_ite : Sub.t -> tid seq -> bool 44 | 45 | val size_gt : 'a seq -> int -> bool 46 | val size_lt : 'a seq -> int -> bool 47 | val size_eq : 'a seq -> int -> bool 48 | val size_eq_0 : 'a seq -> bool 49 | val size_eq_1 : 'a seq -> bool 50 | end 51 | 52 | val dep_blk_span : tid seq -> Sub.t -> int 53 | 54 | val get_arg_as_string : project -> blk term -> tid -> string option 55 | 56 | val get_arg_as_const : project -> Blk.t -> Tid.t -> int option 57 | -------------------------------------------------------------------------------- /minos/profile.mli: -------------------------------------------------------------------------------- 1 | open Core_kernel 2 | open Bap.Std 3 | open Graphlib.Std 4 | 5 | type t = { 6 | name : string; 7 | num_blks : int; 8 | num_calls : int; 9 | num_loops : int; 10 | cyc_comp : int; 11 | } 12 | 13 | val sub_profile_with_view : 14 | (module Graph with type edge = Graphs.Tid.edge and 15 | type node = tid and type t = Graphs.Tid.t) -> 16 | Sub.t -> t 17 | 18 | val sub_profile : Sub.t -> t 19 | 20 | val print_sub_profile : Sub.t -> unit 21 | 22 | val output_dot_cfg_path: ?special:(string, int) List.Assoc.t -> 23 | ?highlight:(string, int) List.Assoc.t -> 24 | ?v:bool -> Sub.t -> tid seq -> filename:string -> unit 25 | -------------------------------------------------------------------------------- /minos/resolve_calls.ml: -------------------------------------------------------------------------------- 1 | open Core_kernel 2 | open Bap.Std 3 | 4 | (** If sub is a path with constants folded to goto term constants, 5 | this will resolve the goto's to the correct jump target, if it 6 | exists *) 7 | let resolve_calls project sub = 8 | let symtab = Project.symbols project in 9 | Term.map blk_t sub ~f:(fun blk -> 10 | Term.map jmp_t blk ~f:(fun jmp -> 11 | match Jmp.kind jmp with 12 | | Call c -> (match Call.target c with 13 | | Indirect (Bil.Int addr) -> 14 | (Symtab.find_by_start symtab addr |> function 15 | | Some fn -> 16 | let fn_target = Symtab.name_of_fn fn in 17 | let t = Tid.(!("@"^fn_target)) in 18 | Jmp.create_call ~tid:(Term.tid jmp) 19 | ~cond:(Jmp.cond jmp) 20 | (Call.with_target c (Label.direct t)) 21 | | None -> jmp) 22 | | _ -> jmp) 23 | | _ -> jmp)) 24 | -------------------------------------------------------------------------------- /minos/run-test-system.sh: -------------------------------------------------------------------------------- 1 | rm -rf analysis-test-system-* 2 | 3 | bap --symbolizer=ida --no-byteweight --minos binary/test-system/test1 \ 4 | --minos-check=system \ 5 | --minos-out_dir=analysis-test-system-1 \ 6 | --minos-srcs=configs/n-1-@system/srcs.txt \ 7 | --minos-sinks=configs/n-1-@system/sinks.txt \ 8 | --minos-with_dots 9 | -------------------------------------------------------------------------------- /minos/simple.ml: -------------------------------------------------------------------------------- 1 | open Bap.Std 2 | open Core_kernel 3 | open Options 4 | open Ctxt 5 | 6 | let simplify ctxt sub_path = 7 | let arch = Project.arch ctxt.project in 8 | let mem_to_reg = Mem_to_reg.analyze in 9 | let fold_consts = 10 | Fold_consts.analyze ~fixsp:false arch in 11 | let o = ctxt.options in 12 | match (o.mem_to_reg, o.fold_consts) with 13 | | true,true -> 14 | let res = sub_path in 15 | let res2 = mem_to_reg res in 16 | let res3 = fold_consts res2 in 17 | res3 18 | | true,false -> sub_path |> mem_to_reg |> Sub.ssa 19 | | false,true -> sub_path |> fold_consts (* fold_consts ssa's by default *) 20 | | _,_ -> sub_path |> Sub.ssa 21 | -------------------------------------------------------------------------------- /minos/simple_args.ml: -------------------------------------------------------------------------------- 1 | open Bap.Std 2 | open Core_kernel 3 | open X86_cpu 4 | open ARM 5 | open Poly 6 | 7 | let (^::) = Seq.cons 8 | 9 | type arg = tid * Def.t 10 | 11 | type args = {arg1 : arg option; 12 | arg2 : arg option; 13 | arg3 : arg option; 14 | arg4 : arg option} 15 | 16 | (* Can be cdecl or stdcall. This code is a placeholder, and should not 17 | be used. *) 18 | let to_reg_x86 = function 19 | | "ret" -> IA32.rax 20 | | "arg1" -> IA32.rbp (*-4*) 21 | | "arg2" -> IA32.rbp (*-8*) 22 | | "arg3" -> IA32.rbp (*-12*) 23 | | "arg4" -> IA32.rbp (*-16*) 24 | | _ -> 25 | failwith "Arg not supported [x86]: Use [ret | arg1 | arg2 | arg3 | arg4]" 26 | 27 | let to_reg_x86_64 = function 28 | | "arg1" -> AMD64.rdi 29 | | "arg2" -> AMD64.rsi 30 | | "arg3" -> AMD64.rdx 31 | | "arg4" -> AMD64.rcx 32 | | _ -> failwith "Arg not supported [x64]: Use [arg1 | arg2 | arg3 | arg4]" 33 | 34 | let to_reg_arm = function 35 | | "ret" -> ARM.CPU.r0 36 | | "arg1" -> ARM.CPU.r0 37 | | "arg2" -> ARM.CPU.r1 38 | | "arg3" -> ARM.CPU.r2 39 | | "arg4" -> ARM.CPU.r3 40 | | _ -> 41 | failwith "Arg not supported [ARM]: Use [ret | arg1 | arg2 | arg3 | arg4]" 42 | 43 | let to_reg project arg = 44 | match Project.arch project with 45 | | `x86 -> to_reg_x86 arg 46 | | `x86_64 -> to_reg_x86_64 arg 47 | | `armv4 | `armv5 | `armv6 | `armv7 48 | | `armv4eb | `armv5eb | `armv6eb | `armv7eb -> to_reg_arm arg 49 | | arch -> failwith @@ Format.sprintf "Arch not supported: %s" 50 | @@ Arch.to_string arch 51 | 52 | let infer_arg project blk arg : arg option = 53 | Blk.elts blk |> 54 | Seq.fold ~init:Seq.empty ~f:(fun acc elt -> 55 | match elt with 56 | | `Phi _ -> acc 57 | | `Jmp _ -> acc 58 | | `Def d -> 59 | let (!) = Var.name in 60 | let r = to_reg project arg in 61 | if !(Def.lhs d) = !r then 62 | ((Term.tid d),d) ^:: acc else acc) |> 63 | Seq.hd 64 | 65 | (** return a record containing all inferred args *) 66 | let infer_args project blk = 67 | let f = infer_arg project blk in 68 | let args = Array.map [|"arg1"; "arg2"; "arg3"; "arg4"|] ~f in 69 | {arg1 = (args.(0)); 70 | arg2 = (args.(1)); 71 | arg3 = (args.(2)); 72 | arg4 = (args.(3))} 73 | -------------------------------------------------------------------------------- /minos/simple_args.mli: -------------------------------------------------------------------------------- 1 | open Bap.Std 2 | 3 | (** arg tid and def *) 4 | type arg = tid * Def.t 5 | 6 | (** A selection of args we can pattern match against in checks. 1 indexed *) 7 | type args = {arg1 : arg option; 8 | arg2 : arg option; 9 | arg3 : arg option; 10 | arg4 : arg option} 11 | 12 | 13 | (** Given a blk, return an arg option corresponding to the string, 14 | where the string is "arg1", "arg2", ... *) 15 | val infer_arg : project -> Blk.t -> string -> arg option 16 | 17 | (** Return all the args found in this blk *) 18 | val infer_args : project -> Blk.t -> args 19 | -------------------------------------------------------------------------------- /minos/summary.ml: -------------------------------------------------------------------------------- 1 | open Core_kernel 2 | open Bap.Std 3 | open Graphlib.Std 4 | 5 | (** sub, with summary information *) 6 | type 'a t = 7 | {subroutine : sub term; 8 | data : 'a} 9 | 10 | let to_table seq = 11 | let table = Sub.Table.create () in 12 | Seq.iter seq ~f:(fun summary -> 13 | Sub.Table.add_exn table ~key:summary.subroutine ~data:summary.data); 14 | table 15 | 16 | (** populate summary information for subroutines in the callgraph, 17 | bottom-up (post-order). Assume no recursive structures for 18 | now. *) 19 | (** Graphlib.Ir -> nodes are always blks. *) 20 | let summarize_bottom_up callgraph ~f = 21 | Graphlib.postorder_traverse (module Graphs.Callgraph) callgraph |> 22 | (** produce a seq of summaries *) 23 | Seq.filter_map ~f |> to_table 24 | -------------------------------------------------------------------------------- /minos/test.ml: -------------------------------------------------------------------------------- 1 | open Core_kernel 2 | open Filename 3 | 4 | (* any $(var) can be changed with TEST_VAR environment variable, 5 | for example, TEST_ARCH=x86, for $(arch).*) 6 | let compile = 7 | "$(arch)-$(abi)-$(cc)-$(ver) $opt -g $(file).c -o $(dir)$(file)" 8 | 9 | let bap = 10 | "bap $(options) $(dir)$(file) $(ida) $(symfile) $(plugin)" 11 | 12 | let test_folder = "/tmp/bap/tests/" 13 | 14 | let subst = [ 15 | "arch", "arm"; 16 | "abi", "linux-gnueabi"; 17 | "cc", "gcc"; 18 | "ver", "4.7"; 19 | "opt", ""; 20 | "dir", test_folder; 21 | "options", ""; 22 | "ida", ""; 23 | "symfile", ""; 24 | "plugin", ""; 25 | 26 | ] 27 | 28 | let verbose = try Sys.getenv "VERBOSE" with Not_found -> "0" 29 | 30 | exception Command_failed of string [@@deriving sexp] 31 | 32 | 33 | let result_of_string line = 34 | try Scanf.sscanf line "//! %s@\n" Option.some with exn -> None 35 | 36 | let expected_results file = 37 | In_channel.with_file file ~f:(fun inc -> 38 | In_channel.fold_lines inc ~init:String.Set.empty ~f:(fun rs str -> 39 | match result_of_string str with 40 | | None -> rs 41 | | Some res -> Set.add rs (String.strip res))) 42 | 43 | let expand pat map = 44 | let buf = Buffer.create 64 in 45 | Buffer.add_substitute buf (fun key -> 46 | try Sys.getenv ("TEST_"^String.uppercase key) with 47 | Not_found -> try List.Assoc.find_exn map key with 48 | Not_found -> failwithf "no subst for %s" key ()) 49 | pat; 50 | Buffer.contents buf 51 | 52 | let pipe cmd = 53 | let inp = Unix.open_process_in cmd in 54 | let r = In_channel.input_lines inp in 55 | In_channel.close inp; r 56 | 57 | let sh cmd = 58 | if Sys.command cmd <> 0 then 59 | raise (Command_failed cmd) 60 | 61 | let dump_syms f = 62 | expand (sprintf "--use-ida --dump-symbols=$(dir)%s.syms \ 63 | -dbir > $(dir)%s.bir" f f) subst 64 | 65 | let with_syms f = 66 | expand (sprintf "--syms=$(dir)%s.syms" @@ chop_extension f) subst 67 | 68 | let build_file file = 69 | let file = chop_extension file in 70 | sh @@ expand compile @@ ["file", file] @ subst; 71 | sh @@ expand bap @@ ["file", file; "options", dump_syms file] @ 72 | subst 73 | 74 | let mtime file = 75 | let file = expand (sprintf "%s" file) subst in 76 | try Unix.((stat file).st_mtime) with exn -> Float.nan 77 | 78 | let needs_rebuild f = 79 | let s = 80 | expand (sprintf "$(dir)%s.syms" @@ chop_extension f) subst in 81 | mtime f > mtime s 82 | 83 | let pipe_bap plugin file = 84 | sh @@ expand "mkdir -p $(dir)$(file)" @@ 85 | ["file", dirname file] @ subst; 86 | if needs_rebuild file then build_file file; 87 | pipe @@ expand bap @@ [ 88 | "file", chop_extension file; 89 | "symfile", with_syms file; 90 | "options", sprintf "-l%s" plugin] @ 91 | subst 92 | 93 | let print_result ~exp ~got = 94 | printf "Expected:\n"; 95 | Set.iter ~f:print_endline exp; 96 | printf "Got:\n"; 97 | Set.iter ~f:print_endline got 98 | 99 | let ok plugin file = 100 | let exp = expected_results file in 101 | let got = String.Set.of_list (pipe_bap plugin file) in 102 | if verbose <> "0" then print_result ~exp ~got; 103 | Set.equal exp got 104 | 105 | let check plugin file = 106 | if file <> Sys.argv.(0) then 107 | let r = ok plugin file in 108 | if r 109 | then printf "%-40s%s\n%!" file "ok" 110 | else printf "%-40s%s\n%!" file "fail"; 111 | not r 112 | else false 113 | 114 | let run plugin files = 115 | List.count files ~f:(check plugin) |> function 116 | | 0 -> printf "all ok\n"; exit 0 117 | | 1 -> printf "one failure\n"; exit 1 118 | | n -> printf "%d failures\n" n; exit 1 119 | 120 | let main () = 121 | match Array.to_list Sys.argv with 122 | | _ :: plugin :: files -> run plugin files 123 | | _ -> 124 | eprintf "Usage: bapbuild test.native -- .. "; 125 | exit 3 126 | 127 | let () = main () 128 | -------------------------------------------------------------------------------- /minos/time.ml: -------------------------------------------------------------------------------- 1 | open Bap.Std 2 | open Core_kernel 3 | 4 | let time tag f = 5 | let t = Unix.gettimeofday () in 6 | let res = Lazy.force f in 7 | Format.printf "[%s] Execution time: %f seconds\n%!" tag 8 | (Unix.gettimeofday () -. t); 9 | res 10 | 11 | let test = 12 | let f = lazy (Unix.sleep 3) in 13 | time "test" @@ f 14 | -------------------------------------------------------------------------------- /minos/time.mli: -------------------------------------------------------------------------------- 1 | (* Wrap a function with lazy and pass to time, with a tag, to time the 2 | operation *) 3 | val time : string -> 'a lazy_t -> 'a 4 | -------------------------------------------------------------------------------- /minos/trim.ml: -------------------------------------------------------------------------------- 1 | open Bap.Std 2 | open Core_kernel 3 | open Cut 4 | open Options 5 | open Graphlib.Std 6 | open Poly 7 | 8 | let (^::) = Seq.cons 9 | 10 | type trim = { 11 | trim_sub : Sub.t; 12 | cut_group : cut_group; 13 | src_tid : tid; 14 | sink_tid : tid 15 | } 16 | 17 | (** mark the sink_blk with a no_return, otherwise we can't call 18 | Sub.ssa on the blk. Failure can pass silently here, so it's 19 | not ideal. We should check that at least one blk was labeled 20 | with no_return *) 21 | let mark_with_no_return sub sink_blk_tid = 22 | Term.map blk_t sub ~f:(fun blk -> 23 | if Util.tid_of_blk blk = sink_blk_tid then 24 | Term.map jmp_t blk ~f:(fun jmp -> 25 | match Jmp.kind jmp with 26 | | Call c -> 27 | Jmp.create_call ~tid:(Term.tid jmp) 28 | ~cond:(Jmp.cond jmp) 29 | (Call.with_noreturn c) 30 | | _ -> jmp) 31 | else blk) 32 | 33 | (** test for structural equality: string of right hand side of 34 | defs are all the same. This needs serious rethinking. *) 35 | let cmp blk' blk = 36 | let def_str b = 37 | Term.enum def_t b |> Seq.fold ~init:"" ~f:(fun acc def -> 38 | let s = Exp.pps () (Def.rhs def) in 39 | acc^s) in 40 | def_str blk' = def_str blk 41 | 42 | let tid_from_blk_structure blk sub = 43 | let matches = 44 | Term.enum blk_t sub |> Seq.filter ~f:(fun blk' -> 45 | (** find the blk' that matches blk, and get its tid *) 46 | if cmp blk' blk then true else false) in 47 | match Seq.length matches with 48 | | 0 -> 49 | Format.printf " Warning: No blk structure match!"; 50 | (1,Util.tid_of_blk blk) 51 | | 1 -> let blk = Seq.hd_exn matches in 52 | (0,Util.tid_of_blk blk) 53 | (* This happens when there are two or more blocks in *the same* 54 | subroutine that call the sink and are the same structurally. 55 | Weird, but can happen. Unhandled case for now. *) 56 | | _ -> Format.printf "Warning: More than one blk match!"; 57 | (2,Seq.hd_exn matches |> Util.tid_of_blk) 58 | 59 | let trim sub src sink = 60 | let module Cfg = Graphs.Tid in 61 | let cfg = Sub.to_graph sub in 62 | let reachable_src = 63 | Graphlib.fold_reachable (module Cfg) 64 | ~init:Cfg.Node.Set.empty ~f:Set.add 65 | cfg src in 66 | let reachable_sink = 67 | Graphlib.fold_reachable (module Cfg) 68 | ~rev:true ~init:Cfg.Node.Set.empty ~f:Set.add 69 | cfg sink in 70 | let cut_set = 71 | Set.inter reachable_src reachable_sink in 72 | Term.filter blk_t sub ~f:(fun blk -> 73 | Set.mem cut_set (Term.tid blk)) 74 | 75 | (* returns a sequence of subs that start at the source and go to 76 | the sink. sub is lca in this case *) 77 | let trims sub g highlight with_dots = 78 | let blks_call_src = g.src_caller_blks in 79 | let blks_call_sink = g.sink_caller_blks in 80 | let pairs = Seq.cartesian_product blks_call_src blks_call_sink in 81 | Seq.foldi ~init:Seq.empty pairs ~f:(fun j acc (src_blk, sink_blk) -> 82 | let res_src,src_tid = tid_from_blk_structure src_blk sub in 83 | let res_sink,sink_tid = tid_from_blk_structure sink_blk sub in 84 | if res_src = 0 && res_sink = 0 then (* If the source and sink exist *) 85 | ( let sub' = trim sub src_tid sink_tid in 86 | match Term.length blk_t sub' with 87 | | 0 -> 88 | if with_dots then 89 | Output.cut_graph `Invalid src_tid sink_tid sub g.id j; 90 | Format.printf "Sink is before source!\n%!"; acc 91 | | _ -> 92 | if with_dots then 93 | Output.cut_graph `Valid src_tid sink_tid sub g.id j; 94 | let sub' = mark_with_no_return sub' sink_tid in 95 | let t = {trim_sub = sub'; 96 | src_tid; 97 | sink_tid; 98 | cut_group = g} in 99 | t ^:: acc) 100 | else 101 | (if with_dots then 102 | Output.cut_graph `Skipped src_tid sink_tid sub g.id j; 103 | Format.printf "[x] Skipping pair %d->%d\n" res_src res_sink; 104 | acc)) 105 | -------------------------------------------------------------------------------- /minos/trim.mli: -------------------------------------------------------------------------------- 1 | (** A trim is a subgraph composed of unique source/sink pairs. One or 2 | more trims may be produced from a cut group: one trim for each 3 | unique source/sink pair. 4 | *) 5 | 6 | open Bap.Std 7 | open Cut 8 | open Options 9 | 10 | type trim = { 11 | trim_sub : Sub.t; 12 | cut_group : cut_group; 13 | src_tid : tid; 14 | sink_tid : tid 15 | } 16 | 17 | (* 'a is debug. 'b is highlight. *) 18 | (* A given cut_group returns a sequence of trims (trim group) *) 19 | val trims : sub term -> cut_group -> 'b -> bool -> trim seq 20 | -------------------------------------------------------------------------------- /minos/util.mli: -------------------------------------------------------------------------------- 1 | open Bap.Std 2 | open Core_kernel 3 | open Graphlib.Std 4 | 5 | exception Timeout 6 | 7 | (** Find a section by name, e.g. ".rodata" *) 8 | val find_section_by_name : project -> string -> mem option 9 | 10 | (** This just prints the difference between non-constant folded 11 | expressions and folded expressions. Does not update *) 12 | val print_constant_fold : project -> unit -> unit 13 | 14 | (** Expects '@' in sub name *) 15 | val sub_from_name : project -> string -> sub term option 16 | 17 | (** Gets the subroutine name of a jmp target *) 18 | val resolve_jmp_name : Jmp.t -> string option 19 | 20 | (** Given a blk, return the tid successors *) 21 | val succs_of_blk : sub term -> blk term -> tid seq 22 | 23 | (** Given a blk_tid, return the tid successors *) 24 | val succs_of_blk_tid : sub term -> tid -> tid seq 25 | 26 | val blk_of_tid : sub term -> tid -> blk term 27 | 28 | val tid_of_blk : blk term -> tid 29 | 30 | val tid_of_sub : sub term -> tid 31 | 32 | val val_exn : 'a option -> 'a 33 | 34 | val calls_of_blk : blk term -> call list 35 | 36 | val calls_of_blk_str : blk term -> string list 37 | 38 | val calls_of_sub : sub term -> tid list 39 | 40 | val get_exit_blocks : sub term -> blk term seq 41 | 42 | val is_exit_block : sub term -> blk term -> bool 43 | 44 | val contains_call : blk term -> string -> bool 45 | 46 | val blks_with_calls : sub term -> string list 47 | 48 | val contains_indirect_goto: blk term -> bool 49 | 50 | val ends_with_lr : blk term -> bool 51 | 52 | val calls_of_blk_with_tid : blk term -> (tid * call) list 53 | 54 | val target_tid_of_call :call -> tid option 55 | 56 | val sub_of_tid : project -> tid -> sub term option 57 | 58 | val get_return_target : call -> tid option 59 | 60 | val deep_clone_sub : sub term -> sub term * tid Tid.Table.t 61 | 62 | val calls_self : sub term -> bool 63 | 64 | val callgraph_of_sub : project -> tid -> program term 65 | 66 | val is_mutually_recursive : Program.t -> bool 67 | 68 | val make_exported_calls_explicit : project -> project 69 | 70 | val make_implicit_jmp_conds_explicit : project -> project 71 | 72 | (** Insert R0 or RAX after a call for a given sub *) 73 | val make_call_returns_explicit : Sub.t -> Sub.t 74 | 75 | (** Find number of paths in a dag. Uses DP on dfs. MUST be a DAG with 76 | a single exit node. Undefined behavior for multiple exit nodes. *) 77 | val num_paths_dag : 78 | (module Graph with type edge = Graphs.Tid.edge and 79 | type node = tid and type t = Graphs.Tid.t) -> 80 | Graphs.Tid.t -> tid -> int 81 | 82 | val timeout : secs:int -> f:('a -> 'b) -> x:'a -> 'b 83 | -------------------------------------------------------------------------------- /saluki/.merlin: -------------------------------------------------------------------------------- 1 | REC 2 | B _build -------------------------------------------------------------------------------- /saluki/Makefile: -------------------------------------------------------------------------------- 1 | ifeq (is_${TAINT_ENGINE}, is_primus) 2 | OPTS = ${options} \ 3 | --saluki-print-models --propagate-taint-print-coverage \ 4 | --passes=trivial-condition-form,saluki-taint,run,saluki-solve \ 5 | --primus-propagate-taint-from-attr --primus-propagate-taint-to-attr \ 6 | --primus-promiscuous-mode --primus-greedy-scheduler \ 7 | --primus-limit-max-visited=64 --primus-limit-max-length=4096 \ 8 | --run-entry-points=all-subroutines \ 9 | --run-with-repetitions \ 10 | --saluki-print-coverage \ 11 | --primus-lisp-load=init 12 | else 13 | OPTS = ${options} --saluki --saluki-print-models \ 14 | --propagate-taint-print-coverage 15 | endif 16 | 17 | case = * 18 | TEST = tests/test${case}.c 19 | 20 | all : build install 21 | build : saluki 22 | 23 | saluki : *.ml 24 | bapbuild -package cmdliner saluki.plugin 25 | 26 | install: saluki 27 | bapbundle install saluki.plugin 28 | 29 | test-expect: 30 | make -C ../test-expect 31 | 32 | test : all test-expect 33 | TEST_OPTIONS="${OPTS}" bap-test-expect $(TEST) 34 | 35 | 36 | bap: all 37 | bap ${binary} ${OPTS} 38 | 39 | clean: 40 | bapbuild -clean 41 | -------------------------------------------------------------------------------- /saluki/match.mli: -------------------------------------------------------------------------------- 1 | open Core_kernel 2 | open Bap.Std 3 | open Spec 4 | 5 | (** constraints generated by pattern matchers *) 6 | type t = 7 | | Eql of v * var (** v bounds to var *) 8 | | Any of t list (** disjunction of matches *) 9 | | All of t list (** conjunction of matches *) 10 | [@@deriving variants] 11 | 12 | 13 | (** [Any []] is a bot value (never matches) *) 14 | val bot : t 15 | 16 | (** [All []] is a top value (always matches) *) 17 | val top : t 18 | 19 | 20 | class matcher : object 21 | method arg : arg term -> pat -> t 22 | method phi : phi term -> pat -> t 23 | method def : def term -> pat -> t 24 | method jmp : jmp term -> pat -> t 25 | end 26 | 27 | (** returns a list of matcher that implements 28 | all patterns. *) 29 | val patterns : program term -> matcher list 30 | 31 | val pp : Format.formatter -> t -> unit 32 | -------------------------------------------------------------------------------- /saluki/paper.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BinaryAnalysisPlatform/bap-plugins/2e9aa5c7c24ef494d0e7db1b43c5ceedcb4196a8/saluki/paper.pdf -------------------------------------------------------------------------------- /saluki/predicate.ml: -------------------------------------------------------------------------------- 1 | open Core_kernel 2 | open Bap.Std 3 | open Poly 4 | 5 | 6 | type t = { 7 | sat : 'a. 'a term -> exp -> bool 8 | } 9 | 10 | exception Unbound_predicate of string [@@deriving sexp] 11 | 12 | 13 | let preds = String.Table.create () 14 | 15 | 16 | let register name (p : t) = 17 | Hashtbl.add_exn preds ~key:name ~data:p 18 | 19 | 20 | let lookup = Hashtbl.find preds 21 | 22 | let is_marked = { 23 | sat = fun t _ -> Term.has_attr t mark 24 | } 25 | 26 | let has_color c = { 27 | sat = fun t _ -> match Term.get_attr t color with 28 | | Some c' -> c = c' 29 | | _ -> false 30 | } 31 | 32 | let test name term var = 33 | match lookup name with 34 | | None -> raise (Unbound_predicate name) 35 | | Some {sat} -> sat term var 36 | 37 | 38 | let () = 39 | register "is_marked" is_marked; 40 | register "is_black" (has_color `black); 41 | register "is_red" (has_color `red); 42 | register "is_green" (has_color `green); 43 | register "is_yellow" (has_color `yellow); 44 | register "is_blue" (has_color `blue); 45 | register "is_magenta" (has_color `magenta); 46 | register "is_cyan" (has_color `cyan); 47 | register "is_white" (has_color `white); 48 | -------------------------------------------------------------------------------- /saluki/predicate.mli: -------------------------------------------------------------------------------- 1 | open Bap.Std 2 | 3 | type t = {sat : 'a. 'a term -> exp -> bool} 4 | 5 | val register : string -> t -> unit 6 | val lookup : string -> t option 7 | val test : string -> 'a term -> exp -> bool 8 | -------------------------------------------------------------------------------- /saluki/saluki.ml: -------------------------------------------------------------------------------- 1 | let doc = {| 2 | # DESCRIPTION 3 | 4 | Saluki is a proof-based verificiation engine that uses a 5 | simple embedded DSL for property specification. Saluki relies on an 6 | external taint propagation engine for data flow facts. Based on these 7 | facts Saluki will search for models for each specified property. 8 | 9 | For more information about Saluki please refer to its homepage [1] and 10 | paper [2]. 11 | 12 | 13 | [1]: https://github.com/BinaryAnalysisPlatform/bap-plugins/tree/master/saluki 14 | [2]: https://www.ndss-symposium.org/wp-content/uploads/2018/07/bar2018_19_Gotovchits_paper.pdf 15 | 16 | |} 17 | 18 | open Core_kernel 19 | open Bap.Std 20 | open Bap_main 21 | open Format 22 | open Spec 23 | open Poly 24 | 25 | let taint spec proj = 26 | let prog = Project.program proj in 27 | Project.with_program proj (Tainter.seed spec prog) 28 | 29 | let pp_models sol defn = 30 | printf "@[models %s@." (Defn.name defn); 31 | printf "%a" (Solution.pp_sat defn) sol 32 | 33 | let filter checks defs = 34 | Spec.filter defs ~f:(fun defn -> 35 | List.exists checks ~f:(fun pattern -> 36 | String.substr_index (Defn.name defn) ~pattern <> None)) 37 | 38 | let has_visited blk = 39 | Term.has_attr blk Term.visited 40 | 41 | let compute_coverage prog = 42 | Term.enum sub_t prog |> 43 | Seq.fold ~init:(0,0) ~f:(fun state sub -> 44 | Term.enum blk_t sub |> 45 | Seq.fold ~init:state ~f:(fun (visited,total) blk -> 46 | if not (has_visited blk) 47 | then printf "%a" Blk.pp blk; 48 | if has_visited blk then (visited+1,total+1) 49 | else (visited,total+1))) |> function 50 | | (0,0) -> 0,0,0 51 | | (v,t) -> 52 | v,t,Float.(to_int @@ round @@ (of_int v /. of_int t) *. 100.) 53 | 54 | let pp_coverage ppf prog = 55 | let visited,total,percentage = compute_coverage prog in 56 | Format.fprintf ppf "[%d/%d] %3d%%" visited total percentage 57 | 58 | 59 | let solve models spec coverage proj = 60 | let prog = Project.program proj in 61 | let tainter = Tainter.reap prog in 62 | let state = State.create spec tainter in 63 | let state = Solver.run state prog in 64 | let sol = State.solution state spec in 65 | printf "* Specification@.%a" Spec.pp spec; 66 | List.iter (Spec.defns spec) ~f:(fun defn -> 67 | printf "@[assert %s@." (Defn.name defn); 68 | printf "%a" (Solution.pp_unsat defn) sol; 69 | printf "@]"; 70 | if models then pp_models sol defn); 71 | if coverage then printf "Coverage: %a@\n" pp_coverage prog; 72 | Project.with_program proj (Solution.annotate sol prog) 73 | 74 | (* Command line interface *) 75 | 76 | open Extension.Configuration 77 | open Extension.Type 78 | 79 | let check = parameter (some (list string)) "check" 80 | ~doc:"Check the specified list of properties. The list 81 | may contain a full property name, or just some substring. For 82 | example,if $(b,malloc) is specified, then all properties that 83 | contain $(b,malloc) in their name will be checked." 84 | 85 | let models = flag "print-models" 86 | ~doc:"Output found models for each property. Usefull for testing 87 | and debugging." 88 | 89 | let print_coverage = flag "print-coverage" 90 | ~doc:"Prints the percentage of visited blocks" 91 | 92 | let () = Extension.declare ~doc @@ fun ctxt -> 93 | let spec = match get ctxt check with 94 | | None -> Specification.spec 95 | | Some checks -> filter checks (Specification.spec) in 96 | let coverage = get ctxt print_coverage in 97 | let models = get ctxt models in 98 | Project.register_pass ~name:"solve" (solve models spec coverage); 99 | Project.register_pass ~deps:["callsites"] ~name:"taint" (taint spec); 100 | Project.register_pass' ignore 101 | ~deps:["saluki-taint"; "propagate-taint"; "saluki-solve"]; 102 | Ok () 103 | -------------------------------------------------------------------------------- /saluki/solution.mli: -------------------------------------------------------------------------------- 1 | open Core_kernel 2 | open Bap.Std 3 | open Spec 4 | open Format 5 | 6 | type t [@@deriving bin_io, compare, sexp] 7 | 8 | type hypothesis = tid Pat.Map.t [@@deriving bin_io, compare, sexp] 9 | 10 | 11 | val create : spec -> (defn * hypothesis) seq -> t 12 | 13 | val annotate : t -> program term -> program term 14 | 15 | val sat : t -> defn -> bool option 16 | 17 | val pp_sat : defn -> formatter -> t -> unit 18 | 19 | val pp_unsat : defn -> formatter -> t -> unit 20 | -------------------------------------------------------------------------------- /saluki/solver.ml: -------------------------------------------------------------------------------- 1 | open Core_kernel 2 | open Bap.Std 3 | open Spec 4 | open Format 5 | open Utilities 6 | 7 | module SM = Monad.State 8 | open SM.Monad_infix 9 | 10 | class type ['a] vis = object 11 | method arg : arg term -> (unit,'a) SM.t 12 | method phi : phi term -> (unit,'a) SM.t 13 | method def : def term -> (unit,'a) SM.t 14 | method jmp : jmp term -> (unit,'a) SM.t 15 | end 16 | 17 | let update f = SM.get () >>= fun s -> SM.put (f s) 18 | 19 | let iterm ~f = 20 | Seq.fold ~init:(SM.return ()) ~f:(fun m v -> 21 | m >>= fun () -> f v) 22 | 23 | let foreach cls t ~f = Term.enum cls t |> iterm ~f 24 | 25 | let run on mrs f t = 26 | let step mr = 27 | update (fun s -> on s; State.step s t (f mr t)) in 28 | Seq.of_list mrs |> iterm ~f:step 29 | 30 | let solver prog on : 'a vis = 31 | let rs = Match.patterns prog in 32 | object 33 | method arg = run on rs (fun r -> r#arg) 34 | method phi = run on rs (fun r -> r#phi) 35 | method def = run on rs (fun r -> r#def) 36 | method jmp = run on rs (fun r -> r#jmp) 37 | end 38 | 39 | 40 | let search_sub vis sub = 41 | foreach arg_t sub ~f:vis#arg >>= fun () -> 42 | foreach blk_t sub ~f:(fun blk -> 43 | foreach phi_t blk ~f:vis#phi >>= fun () -> 44 | foreach def_t blk ~f:vis#def >>= fun () -> 45 | foreach jmp_t blk ~f:vis#jmp) 46 | 47 | let search (vis : 'a vis) prog = 48 | foreach sub_t prog ~f:(search_sub vis) >>= fun () -> 49 | SM.update State.start_conclusions >>= fun () -> 50 | foreach sub_t prog ~f:(search_sub vis) 51 | 52 | 53 | let do_nothing _ = () 54 | 55 | let run ?(on_step=do_nothing) state prog = 56 | let solver = solver prog on_step in 57 | SM.exec (search solver prog) state 58 | -------------------------------------------------------------------------------- /saluki/solver.mli: -------------------------------------------------------------------------------- 1 | open Core_kernel 2 | open Bap.Std 3 | open Spec 4 | 5 | 6 | (** [run ?on_step init prog] searches for solutions using [State.step] in 7 | the program term. Function [on_state] will be called each time a 8 | step is finished (usefull for fancy progress bars). 9 | 10 | The worst-case complexity is [O((r+1)^n)], where [r] is total 11 | number of different rules in specification. The worst case 12 | complexity is achieved when every term matches every rule, of 13 | course, so in general the complexity is closer to linear, since 14 | the problem state tends to remain of the same size. 15 | 16 | Note: equal rules in different definitions are still considered 17 | distinct. *) 18 | val run : ?on_step:(State.t -> unit) -> State.t -> program term -> State.t 19 | -------------------------------------------------------------------------------- /saluki/spec_types.ml: -------------------------------------------------------------------------------- 1 | open Core_kernel 2 | open Bap.Std 3 | 4 | type id = string 5 | [@@deriving bin_io, compare, sexp] 6 | 7 | type v = V.t [@@deriving bin_io, compare, sexp] 8 | 9 | module Constr = struct 10 | type t = 11 | | Dep of v * v 12 | | Var of v * var 13 | | Fun of id * v 14 | [@@deriving bin_io, compare, sexp, variants] 15 | end 16 | 17 | type constr = Constr.t 18 | [@@deriving bin_io, compare, sexp] 19 | 20 | module S = struct 21 | type t = 22 | | Reg 23 | | Ptr 24 | [@@deriving bin_io, compare, sexp, variants] 25 | end 26 | 27 | type s = S.t 28 | [@@deriving bin_io, compare, sexp] 29 | 30 | module Pat = struct 31 | type t = 32 | | Never 33 | | Call of id * v * v list 34 | | Jump of [`call | `goto | `ret | `exn | `jmp] * v * v 35 | | Move of v * v 36 | | Load of v * v 37 | | Wild of v 38 | | Store of v * v 39 | [@@deriving bin_io, compare, sexp, variants] 40 | end 41 | 42 | type pat = Pat.t 43 | [@@deriving bin_io, compare, sexp] 44 | -------------------------------------------------------------------------------- /saluki/specification.ml: -------------------------------------------------------------------------------- 1 | open Core_kernel 2 | open Bap.Std 3 | open Spec.Language 4 | open Spec 5 | 6 | let maybe_checked name = 7 | define ("ERR/"^name^"_maybe_checked") [ 8 | rule "if_some_jmp_depends" 9 | [p := call name[_']] 10 | [case c jmp _'] 11 | ] vars [reg p; reg c] such that [c/p] 12 | 13 | let data_sanitized src san sink = 14 | define ("BYPASS/data_may_passthrough_"^san^"_before_"^sink) [ 15 | rule ("if_data_passthrough_"^san) 16 | [sub src[p;_';_';_']; sub sink[t]] 17 | [sub san[s;r]] 18 | ] vars [reg *p; reg *t; reg *r; reg *s] such 19 | that [s/p; t/r; t/p] 20 | 21 | let untrusted_input src sink = 22 | define ("TAINT/"^src^"_may_leak_into_"^sink) [ 23 | rule ("if_there_is_data_dependency") 24 | [sub src[p]; sub sink[q]] 25 | [never] 26 | ] vars [reg *p; reg *q] such that [q/p;] 27 | 28 | let magic source is_magic = 29 | define ("MAGIC/magic_door_via_"^source^"_may_exist") [ 30 | rule ("when_magic_meets_"^source) [ 31 | p := use v; 32 | sub source[_';x;_']; 33 | case c jmp _' 34 | ][never] 35 | ] vars [reg *x; reg c; reg p; reg v] such 36 | that [ 37 | forall v such that is_magic; 38 | c / x; 39 | c / p; 40 | ] 41 | 42 | let magic_may_leak_into n is_magic sink args = 43 | define ("MAGIC/magic_may_leak_into_"^sink^"_"^n) [ 44 | rule "when_there_is_data_dependency" 45 | [p := use v; sub sink args] 46 | [never] 47 | ] vars [reg p; reg x; reg v] such 48 | that [forall v such that is_magic; x/p] 49 | 50 | 51 | let escape = "_ZN7OpenDBX4Conn6escapeERKSsRSs" 52 | let append_s = "_ZNSs6appendERKSs" 53 | let append_n = "_ZNSs6appendEPKcj" 54 | let create = "_ZN7OpenDBX4Conn6createERKSsNS_4Stmt4TypeE" 55 | 56 | let unescaped_sql append = 57 | define ("TAINT/"^append^"_may_spoil_input") [ 58 | rule "and_leak_into_stmt" 59 | [sub append[p;_']; sub create[_';_';q]] 60 | [never] 61 | ] vars [reg *p; reg *q] such that [q/p;] 62 | 63 | let magic_leaks_into_malloc = 64 | define "MAGIC/magic_leaks" [ 65 | rule "when_leaks" 66 | [p := use v; sub "malloc"[q]] 67 | [never] 68 | ] vars [reg p; reg q; reg v] such 69 | that [forall v such that is_black; q/p] 70 | 71 | let recv_to x x_args = 72 | define ("TAINT/recv_to_"^x) [ 73 | rule "if_data_dep" 74 | [sub "recv" [_';p;_';_']; sub x x_args] 75 | [never] 76 | ] vars [reg *p; reg *q] such that [q/p] 77 | 78 | 79 | let unsafe_to_strcpy cat untrusted = 80 | define (cat^"/"^untrusted^"_to_strcpy") [ 81 | rule "if_leaks" 82 | [p := call untrusted []; sub "strcpy"[q]] [never] 83 | ] vars [reg *p; reg *q] such that [q/p] 84 | 85 | (* a function that returns a string without any bound 86 | so the only safe way to handle it, is to use strlen.*) 87 | let returns_dynamic_string cat untrusted = 88 | define (cat^"/"^untrusted^"-must-be-strlened") [ 89 | rule "if_leaks" 90 | [p := call untrusted []] [sub "strlen"[q]] 91 | ] vars [reg p; reg q] such that [q/p] 92 | 93 | 94 | 95 | let spec = specification [ 96 | unescaped_sql append_n; 97 | unescaped_sql append_s; 98 | maybe_checked "malloc"; 99 | maybe_checked "calloc"; 100 | untrusted_input "strcpy" "system"; 101 | untrusted_input "sprintf" "system"; 102 | untrusted_input "snprintf" "system"; 103 | recv_to "strcpy" [_';q]; 104 | data_sanitized "fgets" "realpath" "fopen"; 105 | ] 106 | -------------------------------------------------------------------------------- /saluki/state.mli: -------------------------------------------------------------------------------- 1 | open Bap.Std 2 | open Spec 3 | 4 | type t 5 | 6 | val create : spec -> Tainter.t -> t 7 | 8 | val start_conclusions : t -> t 9 | 10 | (** [step state term matcher] given a function [matcher] that for each 11 | pattern returns a set of matching variables, performs one step of 12 | search of matching terms.*) 13 | val step : t -> 'a term -> (pat -> Match.t) -> t 14 | 15 | (** [solutions state spec] returns current solution *) 16 | val solution : t -> spec -> Solution.t 17 | 18 | 19 | val pp : Format.formatter -> t -> unit 20 | -------------------------------------------------------------------------------- /saluki/tainter.mli: -------------------------------------------------------------------------------- 1 | open Bap.Std 2 | open Spec 3 | 4 | type t 5 | 6 | val seed : spec -> program term -> program term 7 | 8 | val reap : program term -> t 9 | 10 | val ptrs_of_var : t -> tid -> var -> Taint.set 11 | 12 | val regs_of_var : t -> tid -> var -> Taint.set 13 | 14 | val ptr_seed_of_var : t -> tid -> var -> Taint.t option 15 | 16 | val reg_seed_of_var : t -> tid -> var -> Taint.t option 17 | -------------------------------------------------------------------------------- /saluki/tests/test0.c: -------------------------------------------------------------------------------- 1 | //! ........: p := malloc() 2 | //! unproved: when c jmp . 3 | //! Coverage: .* 100% 4 | 5 | #include 6 | #include 7 | 8 | int print_result(FILE *output, const char *msg) { 9 | fputs(msg,output); 10 | } 11 | 12 | int main() { 13 | malloc(0); 14 | print_result(stderr, "hello, world\n"); 15 | } 16 | -------------------------------------------------------------------------------- /saluki/tests/test1.c: -------------------------------------------------------------------------------- 1 | //! 00000...: p := malloc() 2 | //! unproved: when c jmp _ 3 | //! Coverage: .* 100% 4 | 5 | 6 | #include 7 | 8 | int init(void *p) { 9 | p = NULL; 10 | } 11 | 12 | int main() { 13 | char *p; 14 | init(p); 15 | p = (char *) malloc(42); 16 | p[0] = '\n'; 17 | } 18 | -------------------------------------------------------------------------------- /saluki/tests/test10.c: -------------------------------------------------------------------------------- 1 | /* example of sanitization via call (CWE-22) */ 2 | 3 | //! 00000...: fgets.p,_,_,_. 4 | //! 00000...: fopen.t. 5 | //! unproved: realpath.s,r. 6 | 7 | #include 8 | #include 9 | #include 10 | 11 | int main(void) { 12 | int size = PATH_MAX + 1; 13 | char input[size]; 14 | char fixed[size]; 15 | 16 | fgets(input, size, stdin); 17 | 18 | if (!realpath(input,fixed)) { 19 | exit(1); 20 | } else { 21 | FILE *f = fopen(input, "r"); 22 | while (fgets(input,size,f)) 23 | puts(input); 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /saluki/tests/test11.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | int main(void) { 5 | int x = getchar(); 6 | char *p = malloc(x); 7 | puts(p); 8 | } 9 | -------------------------------------------------------------------------------- /saluki/tests/test12.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | int main(int argc, const char *argv[]) { 5 | char *p; 6 | int i; 7 | int n = argc * (argc + 1); 8 | if (n <= 0 || n > 4096) 9 | n = 4096; 10 | 11 | p = malloc(n); 12 | if (!p) { 13 | exit(1); 14 | } 15 | 16 | for (i = 0; i <= n; i++) { 17 | putchar(p[i]); 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /saluki/tests/test13.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | //! 000.....: strcpy\(p\) 6 | //! 000.....: system\(q\) 7 | //! Coverage: .* 100% 8 | 9 | int max_cmd = 511; 10 | 11 | int main(int argc, const char *argv[]) { 12 | const char *shell = "/bin/sh -c "; 13 | char buf[max_cmd+1]; 14 | if (argc < 2 || strlen(argv[1]) + strlen(shell) > max_cmd) 15 | exit(1); 16 | strcpy(buf, argv[1]); 17 | return system(buf); 18 | } 19 | -------------------------------------------------------------------------------- /saluki/tests/test14.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | //! 00......: strcpy\(p\) 6 | //! unproved: system\(q\) 7 | //! Coverage: .* 100% 8 | 9 | int max_cmd = 511; 10 | 11 | int main(int argc, const char *argv[]) { 12 | const char *shell = "/bin/sh -c "; 13 | char buf[max_cmd+1]; 14 | if (argc < 2 || strlen(argv[1]) + strlen(shell) > max_cmd) 15 | exit(1); 16 | strcpy(buf, argv[1]); 17 | return system(argv[1]); 18 | } 19 | -------------------------------------------------------------------------------- /saluki/tests/test15.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | 7 | 8 | int init_connection(const char *name) { 9 | struct addrinfo *addr; 10 | int fd = -1; 11 | if (getaddrinfo(name, "80",NULL,&addr) < 0) return -1; 12 | if (addr == NULL) return -2; 13 | fd = socket(addr->ai_family, addr->ai_socktype, addr->ai_protocol); 14 | if (fd < 0) return -3; 15 | if (connect(fd, addr->ai_addr, addr->ai_addrlen) < 0) return -4; 16 | freeaddrinfo(addr); 17 | return fd; 18 | } 19 | 20 | 21 | int main(void) { 22 | int fd; 23 | char buf[BUFSIZ] = {0}; 24 | printf("Enter address: "); 25 | if (fgets(buf,BUFSIZ - 1, stdin) == NULL) return 1; 26 | fd = init_connection(buf); 27 | if (fd < 0) return fd; 28 | printf("Enter message: "); 29 | if (fgets(buf, BUFSIZ - 1, stdin) == NULL) return 1; 30 | if (send(fd, buf, BUFSIZ, 0) < 0) return 2; 31 | close(fd); 32 | } 33 | -------------------------------------------------------------------------------- /saluki/tests/test16.c: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | //! 00000...: p := malloc() 4 | //! 00000...: when c jmp _ 5 | //! Coverage: .* 100% 6 | 7 | 8 | int init(void *p) { 9 | p = NULL; 10 | } 11 | 12 | int main(int argc, const char* argv[]) { 13 | char *p; 14 | init(p); 15 | p = (char *) malloc(42); 16 | if (argc > 0) { 17 | if (!p) { 18 | return 1; 19 | } 20 | } else { 21 | return 2; 22 | } 23 | 24 | p[0] = '\0'; 25 | } 26 | -------------------------------------------------------------------------------- /saluki/tests/test17.c: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | //! 00000...: p := malloc() 4 | //! 00000...: when c jmp _ 5 | //! Coverage: .* 100% 6 | 7 | int init(void *p) { 8 | p = NULL; 9 | } 10 | 11 | int main(int argc, const char* argv[]) { 12 | char *p; 13 | init(p); 14 | p = (char *) malloc(42); 15 | if (argc > 0) { 16 | if (!p) { 17 | return 1; 18 | } 19 | } else { 20 | return 2; 21 | } 22 | 23 | p[0] = '\0'; 24 | } 25 | -------------------------------------------------------------------------------- /saluki/tests/test18.c: -------------------------------------------------------------------------------- 1 | //! 00000...: p := malloc() 2 | //! 00000...: when c jmp _ 3 | //! Coverage: .* 100% 4 | 5 | #include 6 | 7 | int init(void *p) { 8 | p = NULL; 9 | } 10 | 11 | int main(int argc, const char* argv[]) { 12 | char *p; 13 | init(p); 14 | p = (char *) malloc(42); 15 | if (argc > 0) { 16 | exit(1); 17 | } else { 18 | if (!p) { 19 | exit(2); 20 | } 21 | } 22 | 23 | p[0] = '\0'; 24 | } 25 | -------------------------------------------------------------------------------- /saluki/tests/test2.c: -------------------------------------------------------------------------------- 1 | //! 000.....: p := malloc() 2 | //! 000.....: when c jmp _ 3 | //! Coverage: .* 100% 4 | 5 | #include 6 | 7 | int init(void *p) { 8 | p = NULL; 9 | } 10 | 11 | int main() { 12 | char *p; 13 | init(p); 14 | p = (char *) malloc(42); 15 | if (p) 16 | p[0] = '\0'; 17 | } 18 | -------------------------------------------------------------------------------- /saluki/tests/test3.c: -------------------------------------------------------------------------------- 1 | //! 000.....: p := malloc() 2 | //! 000.....: when c jmp _ 3 | //! Coverage: .* 100% 4 | 5 | #include 6 | 7 | int init(void *p) { 8 | p = NULL; 9 | } 10 | 11 | int main(int argc, const char* argv[]) { 12 | char *p; 13 | init(p); 14 | p = (char *) malloc(42); 15 | if (argc > 0) { 16 | if (!p) { 17 | exit(1); 18 | } 19 | } else { 20 | if (!p) 21 | exit(2); 22 | } 23 | 24 | p[0] = '\0'; 25 | } 26 | -------------------------------------------------------------------------------- /saluki/tests/test4.c: -------------------------------------------------------------------------------- 1 | //! 000.....: p := malloc() 2 | //! 000.....: when c jmp _ 3 | //! Coverage: .* 100% 4 | 5 | #include 6 | 7 | int init(void *p) { 8 | p = NULL; 9 | } 10 | 11 | int check(void *p) { 12 | if (!p) exit(1); 13 | } 14 | 15 | int main() { 16 | char *p; 17 | init(p); 18 | p = (char *) malloc(42); 19 | check(p); 20 | p[0] = '\n'; 21 | } 22 | -------------------------------------------------------------------------------- /saluki/tests/test5.c: -------------------------------------------------------------------------------- 1 | //! 000.....: p := malloc() 2 | //! 000.....: when c jmp _ 3 | //! Coverage: .* 100% 4 | 5 | #include 6 | 7 | void init(void *p) { 8 | p = NULL; 9 | } 10 | 11 | char *check(void *p) { 12 | if (!p) exit(1); 13 | return p; 14 | } 15 | 16 | char *c_malloc(size_t size) { 17 | return check(malloc(size)); 18 | } 19 | 20 | int main() { 21 | char *p; 22 | init(p); 23 | p = c_malloc(42); 24 | p[0] = '\n'; 25 | } 26 | -------------------------------------------------------------------------------- /saluki/tests/test6.c: -------------------------------------------------------------------------------- 1 | //! 000.....: p := malloc() 2 | //! unproved: when c jmp . 3 | //! 000.....: p := malloc() 4 | //! 000.....: when c jmp . 5 | //! Coverage: .* 100% 6 | 7 | 8 | #include 9 | 10 | void init(void *p) { 11 | p = NULL; 12 | } 13 | 14 | char *check(void *p) { 15 | if (!p) exit(1); 16 | return p; 17 | } 18 | 19 | char *c_malloc(size_t size) { 20 | return check(malloc(size)); 21 | } 22 | 23 | int main() { 24 | char *p,*q; 25 | init(p); 26 | q = malloc(42); 27 | p = c_malloc(42); 28 | p[0] = '\n'; 29 | q[0] = '\n'; 30 | } 31 | -------------------------------------------------------------------------------- /saluki/tests/test7.c: -------------------------------------------------------------------------------- 1 | //! 00000...: p := malloc() 2 | //! unproved: when c jmp . 3 | //! 00000...: p := malloc() 4 | //! 00000...: when c jmp . 5 | //! Coverage: .* 100% 6 | 7 | 8 | #include 9 | 10 | void init(void *p) { 11 | p = NULL; 12 | } 13 | 14 | char *check(void *p) { 15 | if (!p) exit(1); 16 | return p; 17 | } 18 | 19 | char *c_malloc(size_t size) { 20 | return check(malloc(size)); 21 | } 22 | 23 | int main() { 24 | char *p; 25 | init(p); 26 | p = malloc(42); 27 | p = c_malloc(42); 28 | p[0] = '\n'; 29 | } 30 | -------------------------------------------------------------------------------- /saluki/tests/test8.c: -------------------------------------------------------------------------------- 1 | //! 000.....: p := malloc() 2 | //! 000.....: when c jmp _ 3 | //! Coverage: .* 100% 4 | 5 | #include 6 | 7 | int main(int argc, const char* argv[]) { 8 | char *ptr = NULL; 9 | int i; 10 | for (i = 0; i < argc + 1; i++) { 11 | if (argv[i][0] == '1') { 12 | ptr = malloc(1); 13 | } else { 14 | if (i == 0) { 15 | ptr = malloc(42); 16 | } else { 17 | ptr = malloc(56); 18 | } 19 | } 20 | } 21 | if (ptr) 22 | free(ptr); 23 | } 24 | -------------------------------------------------------------------------------- /saluki/tests/test9.c: -------------------------------------------------------------------------------- 1 | //! 000.....: p := malloc() 2 | //! unproved: when c jmp . 3 | //! 000.....: p := malloc() 4 | //! 000.....: when c jmp . 5 | //! 000.....: p := malloc() 6 | //! 000.....: when c jmp . 7 | //! Coverage: .* 100% 8 | 9 | #include 10 | #include 11 | 12 | void check() { 13 | printf("There is nothing to check\n"); 14 | } 15 | 16 | int main() { 17 | check(); 18 | check(); 19 | check(); 20 | printf("let's get ready for ruuuuumble\n"); 21 | printf("first malloc\n"); 22 | if (malloc(1)) { 23 | printf("second malloc\n"); 24 | if (malloc(2)) { 25 | printf("third malloc \n"); 26 | malloc(3); 27 | } 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /saluki/utilities.ml: -------------------------------------------------------------------------------- 1 | open Core_kernel 2 | open Bap.Std 3 | open Poly 4 | 5 | let label_matches l id = match l with 6 | | Indirect _ -> false 7 | | Direct tid -> 8 | id = "_" || Tid.name tid = "@"^id 9 | 10 | let callee call prog = match Call.target call with 11 | | Indirect _ -> None 12 | | Direct tid -> Term.find sub_t prog tid 13 | 14 | let call_of_jmp jmp = match Jmp.kind jmp with 15 | | Ret _ | Int _ | Goto _ -> None 16 | | Call call -> Some call 17 | 18 | let call_matches call id = 19 | label_matches (Call.target call) id 20 | 21 | 22 | let return caller sub = 23 | match Call.return caller with 24 | | None | Some (Indirect _) -> None 25 | | Some (Direct tid) -> Term.find blk_t sub tid 26 | 27 | let intent_matches x y = match Arg.intent x with 28 | | None -> true 29 | | Some x -> match x,y with 30 | | In,In | Out,Out -> true 31 | | Both,_| _,Both -> true 32 | | _ -> false 33 | 34 | let require x = Option.some_if x () 35 | -------------------------------------------------------------------------------- /saluki/utilities.mli: -------------------------------------------------------------------------------- 1 | (** Useful utility functions 2 | 3 | Mostly on terms. Candidates for library promotion.*) 4 | open Core_kernel 5 | open Bap.Std 6 | 7 | 8 | (** [name_matches l id] returns true if label [l] is direct label 9 | with a given name. Example: [name_matches (Call.target c) "main"].*) 10 | val label_matches : label -> string -> bool 11 | 12 | (** [call_matches c id] is true if its target label matches *) 13 | val call_matches : call -> string -> bool 14 | 15 | (** [intent_matches a in] is true if either argument's [a] 16 | intent is unknown, or if they have intersecting intents.*) 17 | val intent_matches : arg term -> intent -> bool 18 | 19 | (** [call_of_jmp j] is [Some call] if jmp is a call *) 20 | val call_of_jmp : jmp term -> call option 21 | 22 | (** [callee c prog] returns a callee procedure of a given call [c] *) 23 | val callee : call -> program term -> sub term option 24 | 25 | (** [return c sub] finds a block to which call [c] will return *) 26 | val return : call -> sub term -> blk term option 27 | 28 | (** [require x] is [Some ()] if [x] and [None] otherwise. 29 | Useful, in option monad (c.f. haskell's [guard]): 30 | {[ 31 | require (label_matches l "malloc") >>= fun () -> 32 | require (intent_matches a In) >>= fun () -> 33 | ]} 34 | 35 | *) 36 | val require : bool -> unit option 37 | -------------------------------------------------------------------------------- /saluki/v.ml: -------------------------------------------------------------------------------- 1 | open Core_kernel 2 | open Regular.Std 3 | open Bap.Std 4 | open Format 5 | 6 | module Vars = struct 7 | let pool = Vector.create ~capacity:64 "" 8 | 9 | let lookup = function 10 | | 0 -> "_" 11 | | n when n < 0 -> "_" 12 | | n when n < Vector.length pool -> Vector.get pool n 13 | | _ -> "_" 14 | 15 | let register = function 16 | | "" | "_" -> 0 17 | | var -> match Vector.index ~equal:String.equal pool var with 18 | | Some i -> i 19 | | None -> 20 | Vector.append pool var; 21 | Vector.length pool - 1 22 | end 23 | 24 | type t = int [@@deriving bin_io, compare, sexp] 25 | 26 | module Assoc = struct 27 | let find = List.Assoc.find ~equal:Int.equal 28 | let mem = List.Assoc.mem ~equal:Int.equal 29 | end 30 | 31 | let null = 0 32 | let create = Vars.register 33 | include Regular.Make(struct 34 | type t = int [@@deriving bin_io, compare, sexp] 35 | let module_name = None 36 | let pp ppf idx = fprintf ppf "%s" (Vars.lookup idx) 37 | let hash = ident 38 | let version = "0.1" 39 | end) 40 | -------------------------------------------------------------------------------- /saluki/v.mli: -------------------------------------------------------------------------------- 1 | open Core_kernel 2 | open Regular.Std 3 | open Bap.Std 4 | 5 | type t = int 6 | val null : t 7 | val create : string -> t 8 | module Assoc : sig 9 | val find : (t * 'a) list -> t -> 'a option 10 | val mem : (t * 'a) list -> t -> bool 11 | end 12 | include Regular.S with type t := t 13 | -------------------------------------------------------------------------------- /staticstore/Makefile: -------------------------------------------------------------------------------- 1 | .phony: all 2 | all : 3 | bapbuild -package cmdliner staticstore.otarget 4 | 5 | clean : 6 | bapbuild -clean 7 | -------------------------------------------------------------------------------- /staticstore/README.md: -------------------------------------------------------------------------------- 1 | # Overview 2 | 3 | This plugin will classify all functions into three categories: 4 | 5 | - red 6 | - yellow 7 | - green 8 | 9 | `green` functions perform all writes to memory only to a statically 10 | known offsets, i.e, a compile time constants. SP is also considered a 11 | constant iff it is only defined with constant in the ENTRY or EXIT 12 | blocks. If it is defined by a non-constant value, like `SP := SP - R0` 13 | it is considered unsafe. If it is defined by a constant value, but 14 | outside of the ENTRY or EXIT blocks, but the overall function is 15 | classified as green, then such function will be classified as 16 | `yellow`. 17 | 18 | # Input 19 | 20 | Should work fine on any input data. The plugin is not architecture 21 | specific and should work on any first tier architecture. 22 | 23 | 24 | # Output 25 | 26 | The core plugin tags memory with `color` tag. The core plugin is 27 | accompanied with two helper plugins: 28 | - printstats - will output statistics, i.e., the percentage of green 29 | and red functions 30 | - toida - will emit appropriate annotations to the project data 31 | structure, that can be used to produce python script that will color 32 | functions inside IDA. 33 | 34 | # Compilation 35 | ```sh 36 | $ make clean 37 | $ make 38 | ``` 39 | 40 | # Example 41 | ```sh 42 | bap --use-ida -L ~/bap-plugins/staticstore -ltoida --emit-ida-script=color.py 1241.exe 43 | ``` 44 | 45 | The resulting script can be loaded into IDA PRO with `Alt-F7` keybinding. 46 | 47 | # Implementation Details 48 | 49 | The implementation relies on a constant propagation, that is made for 50 | each basic block independently. The only information that is 51 | propagated to the input of the block, is the value of SP and/or BP, 52 | defined in the entry block. Actually, even this is not needed, as it is 53 | a part of future development of this algorithm, when we will address, 54 | locals and structures more accurately. 55 | -------------------------------------------------------------------------------- /staticstore/printstats.ml: -------------------------------------------------------------------------------- 1 | open Core_kernel 2 | open Bap.Std 3 | open Format 4 | 5 | let () = Project.register_pass' ~deps:["staticstore"] 6 | (fun p -> 7 | Memmap.to_sequence (Project.memory p) |> 8 | Seq.fold ~init:(0,0,0) ~f:(fun (r,g,y) (_,v) -> 9 | match Value.get color v with 10 | | Some `red -> (r+1,g,y) 11 | | Some `green -> (r,g+1,y) 12 | | Some `yellow -> (r,g,y+1) 13 | | _ -> (r,g,y)) |> fun (r,g,y) -> 14 | let print name var = 15 | eprintf "%-7s %d/%d (%2.2g %%)@." 16 | name var (r+g+y) (100. *. (float var /. float (r+g+y))) in 17 | print "Green" g; 18 | print "Yellow" y; 19 | print "Red" r) 20 | -------------------------------------------------------------------------------- /staticstore/staticstore.itarget: -------------------------------------------------------------------------------- 1 | staticstore.plugin 2 | printstats.plugin 3 | toida.plugin -------------------------------------------------------------------------------- /staticstore/staticstore.mli: -------------------------------------------------------------------------------- 1 | (** Classifies functions based on storage to unbound offsets. 2 | See README.md for more details. 3 | *) 4 | -------------------------------------------------------------------------------- /staticstore/toida.ml: -------------------------------------------------------------------------------- 1 | open Core_kernel 2 | open Bap.Std 3 | 4 | let code_of_color = function 5 | | `green -> 0x99ff99 6 | | `red -> 0xCCCCFF 7 | | `yellow -> 0xC2FFFF 8 | | _ -> invalid_arg "unexpected color" 9 | 10 | let cmd c = 11 | sprintf "SetFunctionAttr($symbol_addr, FUNCATTR_COLOR, 0x%x)\n" 12 | (code_of_color c) 13 | 14 | let () = Project.register_pass ~deps:["staticstore"] (fun p -> 15 | Project.memory p |> Memmap.to_sequence |> 16 | Seq.fold ~init:p ~f:(fun p (mem,v) -> 17 | Option.value_map ~default:p (Value.get color v) 18 | ~f:(fun c -> Project.substitute p mem python (cmd c)))) 19 | -------------------------------------------------------------------------------- /strings/Makefile: -------------------------------------------------------------------------------- 1 | .phony: all 2 | all : 3 | bapbuild strings.plugin 4 | 5 | clean : 6 | bapbuild -clean 7 | rm *.plugin 8 | -------------------------------------------------------------------------------- /strings/README.md: -------------------------------------------------------------------------------- 1 | Initial pass for resolving strings. Annotates project with the comment tag. 2 | 3 | `bap --print-bir-attr=comment --symbols=ida -lstrings --strings -dbir /bin/ls` 4 | 5 | ``` 6 | .comment "/usr/share/locale" 7 | 000001a2: RSI := 0x41381C:64 8 | .comment "coreutils" 9 | 000001a3: RDI := 0x413800:64 10 | 000001a4: RSP := RSP - 0x8:64 11 | 000001a5: mem64 := mem64 with [RSP, el]:u64 <- 0x40290F:64 12 | 000001a6: call @.bindtextdomain with return %000001a7 13 | ``` 14 | 15 | -------------------------------------------------------------------------------- /strings/run.sh: -------------------------------------------------------------------------------- 1 | bap -lstrings -dbir --emit-attr=comment $1 2 | -------------------------------------------------------------------------------- /strings/strings.ml: -------------------------------------------------------------------------------- 1 | open Core_kernel 2 | open Bap.Std 3 | open Format 4 | open Option 5 | open Poly 6 | 7 | let find_section_by_name project name = 8 | let memory = Project.memory project in 9 | Memmap.to_sequence memory |> Seq.find_map ~f:(fun (m,x) -> 10 | Option.(Value.get Image.section x >>= fun n -> 11 | Option.some_if (n = name) m)) 12 | 13 | let read_string mem w = 14 | let open Or_error.Monad_infix in 15 | let (!) = Char.to_string in 16 | Memory.view ~word_size:`r8 ~from:w mem >>= fun mem' -> 17 | Memory.foldi ~word_size:`r8 mem' ~init:(false,"") 18 | ~f:(fun addr word (fin,acc) -> 19 | let char = Word.enum_chars word LittleEndian |> Seq.hd_exn in 20 | match fin,char with 21 | | (false,'\x00') -> (true,acc) 22 | | (false,c) -> (false,acc^(!c)) 23 | | (true,c) -> (true,acc)) |> snd |> Or_error.return 24 | 25 | let get_rodata_str project w = 26 | find_section_by_name project ".rodata" >>= fun mem -> 27 | if Memory.contains mem w then 28 | match read_string mem w with 29 | | Ok res -> Some res 30 | | Error _ -> None 31 | else None 32 | 33 | let try_get_rodata project addr : 'a option = 34 | let scale = match Arch.addr_size (Project.arch project) with 35 | | `r32 -> `r32 36 | | `r64 -> `r64 in 37 | find_section_by_name project ".text" >>= fun mem -> 38 | match Memory.get ~scale ~addr mem with 39 | | Ok w -> 40 | let get_str = get_rodata_str project in 41 | Option.first_some (get_str w) 42 | (find_section_by_name project ".got" >>= fun got -> 43 | let got_addr = Memory.min_addr got in 44 | Option.first_some 45 | (Addr.add addr got_addr |> get_str) 46 | (Addr.add w got_addr |> get_str)) 47 | | Error _ -> None 48 | 49 | let resolve_string project def_tid = 50 | Program.lookup def_t (Project.program project) def_tid >>= fun def -> 51 | match Def.rhs def with 52 | | Bil.Int w -> 53 | Option.first_some ((get_rodata_str) project w) (try_get_rodata project w) 54 | | Bil.Load (_,Bil.Int w,_,_) -> try_get_rodata project w 55 | | _ -> None 56 | 57 | let stringify project sub = 58 | Term.map blk_t sub ~f:(fun blk -> 59 | Term.map def_t blk ~f:(fun def -> 60 | match resolve_string project @@ Term.tid def with 61 | | Some data -> Term.set_attr def comment (sprintf "%S" data) 62 | | None -> def)) 63 | 64 | let main project : project = 65 | let prog = Project.program project in 66 | (Term.map sub_t prog ~f:(fun sub -> stringify project sub) |> some) 67 | |> function 68 | | Some prog -> Project.with_program project prog 69 | | None -> failwith "Could not stringify!" 70 | 71 | let () = Project.register_pass main 72 | -------------------------------------------------------------------------------- /test-expect/.merlin: -------------------------------------------------------------------------------- 1 | REC 2 | PKG ocamlgraph 3 | PKG re 4 | B _build -------------------------------------------------------------------------------- /test-expect/Makefile: -------------------------------------------------------------------------------- 1 | install : build 2 | ocaml setup.ml -install 3 | 4 | build : setup.data *.ml 5 | ocaml setup.ml -build 6 | 7 | 8 | setup.data : _oasis 9 | oasis setup 10 | ocaml setup.ml -configure --prefix `opam config var prefix` 11 | -------------------------------------------------------------------------------- /test-expect/README.md: -------------------------------------------------------------------------------- 1 | ## Overview 2 | 3 | This is a micro testing framework, usefull in binary analysis. In short, 4 | you write a program in `C` language, and put expected output in the comments. 5 | The framework will compile the C program, run your passes over it, and verify, 6 | that all expectations are met. 7 | 8 | For example, consider the following `C` program: 9 | 10 | ```c 11 | //! 000.....: p := malloc() 12 | //! 000.....: when c jmp _ 13 | 14 | #include 15 | 16 | int init(void *p) { 17 | p = NULL; 18 | } 19 | 20 | int main() { 21 | char *p; 22 | init(p); 23 | p = (char *) malloc(42); 24 | if (p) 25 | p[0] = '\0'; 26 | } 27 | 28 | ``` 29 | 30 | Strings starting with `//!` are expectations. The expectation is a posix regular expression, 31 | so `.` means here match with any character. 32 | 33 | ## Invoking 34 | 35 | The program accepts a list of c-files, where each file constitutes a test. All parameter to 36 | bap are passed via environment variable `TEST_OPTIONS`. A compiler can be controlled by setting the following five variables: 37 | 38 | - TEST_ARCH -- architecture, e.g, arm 39 | - TEST_ABI -- used abi, e.g., linux-gnueabi 40 | - TEST_CC -- C compiler name, e.g., gcc 41 | - TEST_VER -- C compiler version, e.g., 4.7 42 | - TEST_OPT -- compiler options, e.g., -O3 43 | 44 | 45 | For example, to check saluki we can use: 46 | 47 | TEST_OPTIONS='--saluki-seed --qurantine --saluki-solve' bap-test-expect saluki/tests/* 48 | 49 | 50 | To get a more verbose output, with explanations on what and why didn't match, set 51 | `VERBOSE` environment variable to `1`. 52 | 53 | 54 | ## Meeting expectations 55 | 56 | Each expectation, specified in the input file, should be met at least once. A line 57 | in the program output can satisfy only one expectation. Formally, this is the Maximum 58 | Bipartile Matching Problem. If solution to the problem is not found, then it is marked 59 | as a test failure, and missing expectations are optionally printed. 60 | 61 | 62 | ## Building 63 | -------------------------------------------------------------------------------- /test-expect/_oasis: -------------------------------------------------------------------------------- 1 | OASISFormat: 0.4 2 | Name: "bap-test-expect" 3 | Version: 0.1 4 | Synopsis: Test expectations escripted in the C file 5 | Authors: BAP Team 6 | Maintainers: Ivan Gotovchits 7 | License: MIT 8 | Copyrights: (C) 2016 Carnegie Mellon University 9 | BuildTools: ocamlbuild 10 | 11 | Executable "bap-test-expect" 12 | Path: . 13 | MainIs: test.ml 14 | Install: true 15 | BuildDepends: ppx_bap, core_kernel, ocamlgraph, re.posix, core_kernel.caml_unix -------------------------------------------------------------------------------- /test-expect/expect.ml: -------------------------------------------------------------------------------- 1 | open Core_kernel 2 | open Graph 3 | 4 | type trial = { 5 | regexp : Re.re; 6 | string : string; 7 | } 8 | type expect = trial array 9 | 10 | type misses = expect 11 | type t = expect 12 | 13 | 14 | let create = 15 | Array.of_list_map ~f:(fun s -> { 16 | regexp = Re.compile (Re.Posix.re s); 17 | string = s; 18 | }) 19 | 20 | let sat e s = Re.execp e.regexp s 21 | 22 | 23 | module G = struct 24 | type t = expect * string array 25 | module V = struct 26 | type t = Source | Sink | Person of int | Task of int [@@deriving compare] 27 | let hash = Hashtbl.hash 28 | let equal x y = compare x y = 0 29 | end 30 | module E = struct 31 | type label = unit 32 | 33 | type t = {src : V.t; dst : V.t} [@@deriving fields] 34 | let make src dst = {src; dst} 35 | let label _ = () 36 | end 37 | type dir = Succ | Pred 38 | 39 | let iter dir f (workers,jobs) v = 40 | match v,dir with 41 | | V.Source,Pred -> () 42 | | V.Source,Succ -> 43 | Array.iteri workers ~f:(fun i _ -> 44 | f @@ E.make V.Source (V.Person i)) 45 | | V.Sink,Pred -> 46 | Array.iteri jobs ~f:(fun i _ -> 47 | f @@ E.make (V.Task i) V.Sink) 48 | | V.Sink,Succ -> () 49 | | V.Person i as p,Pred -> f @@ E.make V.Source p 50 | | V.Person i as p,Succ -> 51 | Array.iteri jobs ~f:(fun j job -> 52 | if sat workers.(i) job then f (E.make p (V.Task j))) 53 | | V.Task j as t,Succ -> f @@ E.make t V.Sink 54 | | V.Task j as t,Pred -> 55 | Array.iteri workers ~f:(fun i worker -> 56 | if sat worker jobs.(j) then f (E.make (V.Person i) t)) 57 | 58 | let iter_succ_e = iter Succ 59 | let iter_pred_e = iter Pred 60 | end 61 | 62 | module F = struct 63 | type t = int 64 | type label = unit 65 | let max_capacity () = 1 66 | let min_capacity () = 0 67 | let flow () = min_capacity () 68 | let add = (+) 69 | let sub = (-) 70 | let zero = 0 71 | let compare = Int.compare 72 | end 73 | 74 | module FFMF = Flow.Ford_Fulkerson(G)(F) 75 | 76 | let all_matches expect workers = 77 | let workers = Array.of_list workers in 78 | let (flow,_) = FFMF.maxflow (expect,workers) G.V.Source G.V.Sink in 79 | Array.filteri expect ~f:(fun i _ -> 80 | Sequence.range 0 (Array.length workers) |> 81 | Sequence.for_all ~f:(fun j -> 82 | flow (G.E.make (G.V.Person i) (G.V.Task j)) = 0)) |> function 83 | | [| |] -> `Yes 84 | | ms -> `Missed ms 85 | 86 | 87 | let pp_one ppf ms = 88 | Format.fprintf ppf "@[Expectation %S is not satisfied@]" ms.(0).string 89 | 90 | let pp_many ppf ms = 91 | Format.fprintf ppf "@[Expectations ["; 92 | Array.iter ms ~f:(fun {string=s} -> Format.fprintf ppf "@;%S;" s); 93 | Format.fprintf ppf "@]@.] are not satisfied" 94 | 95 | let pp_misses ppf ms = 96 | if Array.length ms = 1 97 | then pp_one ppf ms 98 | else pp_many ppf ms 99 | -------------------------------------------------------------------------------- /test-expect/expect.mli: -------------------------------------------------------------------------------- 1 | (** Given a sequence of expected strings [E] and test corpora [S], we 2 | want to ensure, that each expected string matches at least one 3 | substring in a testing corpora, that is not matched by other 4 | expected string. 5 | 6 | This is a Maximum Bipartile Matching problem. First we find a MBP 7 | solution, and if in this solution all persons got a job, then we 8 | are satisfied, otherwise we give a set of expectations, that were 9 | not satisfied. 10 | 11 | An example, may clarify the problem. Given a following expectation 12 | specification: [x;x;y], we will any input that has at least two 13 | [x] and at least one [y]. 14 | *) 15 | 16 | type t 17 | type misses 18 | 19 | (** [create regexp] takes a list of POSIX regular expressions 20 | and converts it into an expectation *) 21 | val create : string list -> t 22 | 23 | 24 | (** [all_matches expectation data] checks that provided list of 25 | strings [data] satisfies given [expectation] *) 26 | val all_matches : t -> string list -> [`Yes | `Missed of misses] 27 | 28 | (** [pp_misses ppf misses] prints missed expectation into a given 29 | formatter. *) 30 | val pp_misses : Format.formatter -> misses -> unit 31 | -------------------------------------------------------------------------------- /test-expect/test.ml: -------------------------------------------------------------------------------- 1 | open Core_kernel 2 | open Filename 3 | open Format 4 | open Poly 5 | 6 | module Buffer = Caml.Buffer 7 | module Unix = Caml_unix 8 | 9 | (* any $(var) can be changed with TEST_VAR environment variable, 10 | for example, TEST_ARCH=x86, for $(arch).*) 11 | let compile = 12 | "$(arch)-$(abi)-$(cc)-$(ver) $opt -g $(file).c -o $(dir)$(file)" 13 | 14 | let bap = "bap $(dir)$(file) $(options)" 15 | 16 | let test_folder = "/tmp/bap/tests/" 17 | 18 | let subst = [ 19 | "arch", "arm"; 20 | "abi", "linux-gnueabi"; 21 | "cc", "gcc"; 22 | "ver", "5"; 23 | "opt", ""; 24 | "dir", test_folder; 25 | "options", ""; 26 | ] 27 | 28 | type expect = Expect.t 29 | 30 | let verbose () = match Sys.getenv_opt "VERBOSE" with 31 | | Some x -> x 32 | | None -> "0" 33 | 34 | exception Command_failed of string [@@deriving sexp] 35 | 36 | let assoc = List.Assoc.find ~equal:String.equal 37 | 38 | 39 | let result_of_string line = 40 | try Scanf.sscanf line "//! %s@\n" Option.some with exn -> None 41 | 42 | let expected_results file : expect = 43 | In_channel.read_lines file |> 44 | List.filter_map ~f:result_of_string |> 45 | Expect.create 46 | 47 | let expand pat map = 48 | let buf = Buffer.create 64 in 49 | Buffer.add_substitute buf (fun key -> 50 | match Sys.getenv_opt ("TEST_"^String.uppercase key) with 51 | | Some x -> x 52 | | None -> match assoc map key with 53 | | Some x -> x 54 | | None -> failwithf "no subst for %s" key ()) 55 | pat; 56 | Buffer.contents buf 57 | 58 | let pipe cmd : string list = 59 | let env = Unix.environment () in 60 | let out,inp,err = Unix.open_process_full cmd env in 61 | Out_channel.close inp; 62 | let res = List.concat [ 63 | In_channel.input_lines out; 64 | In_channel.input_lines err; 65 | ] in 66 | List.iter ~f:In_channel.close [out;err]; 67 | res 68 | 69 | let sh cmd = 70 | if Sys.command cmd <> 0 then 71 | raise (Command_failed cmd) 72 | 73 | let build_file file = 74 | let file = chop_extension file in 75 | sh @@ expand compile @@ ["file", file] @ subst 76 | 77 | let mtime file = 78 | let file = expand (sprintf "%s" file) subst in 79 | try Unix.((stat file).st_mtime) with exn -> 0.0 80 | 81 | let needs_rebuild f = 82 | let s = expand (sprintf "$(dir)%s" @@ chop_extension f) subst in 83 | mtime f > mtime s 84 | 85 | let pipe_bap file = 86 | sh @@ expand "mkdir -p $(dir)$(file)" @@ 87 | ["file", dirname file] @ subst; 88 | if needs_rebuild file then build_file file; 89 | pipe @@ expand bap @@ [ 90 | "file", chop_extension file; 91 | ] @ subst 92 | 93 | let print_result misses got = 94 | eprintf "@.@.%a in the following output:@.@." 95 | Expect.pp_misses misses; 96 | List.iter ~f:print_endline got 97 | 98 | let set_of_list xs = 99 | List.fold xs ~init:String.Set.empty ~f:(fun set s -> 100 | Set.add set (String.strip s)) 101 | 102 | let ok file = 103 | let exp = expected_results file in 104 | let got = pipe_bap file in 105 | match Expect.all_matches exp got with 106 | | `Yes -> true 107 | | `Missed misses -> 108 | if verbose () <> "0" then print_result misses got; 109 | false 110 | 111 | 112 | let check file = 113 | if file <> Sys.argv.(0) then 114 | let r = ok file in 115 | if r 116 | then printf "%-60s%s\n%!" file "ok" 117 | else printf "%-60s%s\n%!" file "fail"; 118 | not r 119 | else false 120 | 121 | let run files = 122 | List.count files ~f:check |> function 123 | | 0 -> printf "all ok\n"; exit 0 124 | | 1 -> printf "one failure\n"; exit 1 125 | | n -> printf "%d failures\n" n; exit 1 126 | 127 | let main () = 128 | match Array.to_list Sys.argv with 129 | | _ :: files -> run files 130 | | _ -> 131 | eprintf "Usage: TEST_OPTIONS='..' bapbuild test.native -- .. "; 132 | exit 3 133 | 134 | let () = main () 135 | -------------------------------------------------------------------------------- /toy-debugger/Makefile: -------------------------------------------------------------------------------- 1 | all : 2 | bapbuild birasm.plugin 3 | bapbuild debugger.plugin -pkg bap-microx -pkg cmdliner 4 | 5 | clean: 6 | bapbuild -clean 7 | rm viewer/*.txt viewer/*.svg viewer/*.dot 8 | -------------------------------------------------------------------------------- /toy-debugger/README.md: -------------------------------------------------------------------------------- 1 | ## What 2 | 3 | This is a toy debugger for various IR interpreter extensions. Right now it 4 | extends concretizer/conqueror. The basic functionality is dumping the CFG and 5 | memory/register values as csv viles. A simple web wrapper let's you navigate 6 | these. 7 | 8 | ## Using 9 | 10 | Have `dot` installed (Needed for svg conversion). 11 | 12 | `./run.sh`
13 | `cd viewer && firefox index.html` or whatever browser you prefer. 14 | 15 | The viewer is rudimentary. Use arrow keys (left, right) to navigate forward and 16 | backwards. 17 | 18 | Example: 19 | 20 | 21 | 22 | ## Commandline 23 | 24 | See the reference in `run.sh`: 25 | 26 | ``` 27 | bap tests/$binary --symbolizer=ida \ 28 | --callsites \ 29 | -lbirasm --birasm \ 30 | -ldebugger --debugger \ 31 | --debugger-fname=main \ 32 | --debugger-memory="enter_term" \ 33 | --debugger-regs="enter_term" \ 34 | --debugger-dot="enter_term" \ 35 | --debugger-path-count="enter_term" \ 36 | --debugger-myself="enter_term" \ 37 | --debugger-checkpoints="enter_term" \ 38 | --debugger-dir="viewer" 39 | ``` 40 | 41 | The grammar is 42 | 43 | ``` 44 | let grammar () = {| 45 | flag ::= 46 | directives ::= .. 47 | directive ::= 48 | | 49 | | 50 | | 51 | | 52 | | 53 | | 54 | hooks ::= .. 55 | hook ::= 56 | | 57 | | 58 | | 59 | | 60 | | 61 | | 62 | | 63 | | 64 | | 65 | | 66 | | 67 | |} 68 | ``` 69 | 70 | For instance, 71 | 72 | To print the trace after a path terminates, use option 73 | `--debugger-trace="path_terminates"`. 74 | 75 | To print the memory state after each load and store, use option 76 | `--debugger-memory="(load store)"`. 77 | 78 | Full command: 79 | `bap tests/test --symbolizer=ida -ldebugger --debugger --debugger-fname=main --debugger-memory="(load store)" --debugger-dir="viewer"` 80 | 81 | ## Future 82 | 83 | Note this can serve as a reference for hooking into interpreters and 84 | other extensions (such as taint propagation). 85 | 86 | The interpeter hooks may also serve as a reference for developing a proper 87 | debugger in future. Basically, the right way to do this is for each hook to 88 | respond to requests according to a debugging protocol a la V8 89 | https://github.com/v8/v8/wiki/Debugging-Protocol. 90 | -------------------------------------------------------------------------------- /toy-debugger/birasm.ml: -------------------------------------------------------------------------------- 1 | open Core_kernel 2 | open Bap.Std 3 | include Self () 4 | open Format 5 | open Regular.Std 6 | 7 | let go (type t) (module T: Regular.S with type t = t) get_addr proj = 8 | let f x elt = 9 | match get_addr elt with 10 | | Some addr -> 11 | Bytes.cat (Bytes.of_string @@ sprintf "%a: " Word.pps addr) x 12 | | None -> x in 13 | match T.default_printer () with 14 | | None -> () 15 | | Some (super,_,_) -> 16 | let to_bytes elt = f (T.to_bytes ~fmt:super elt) elt in 17 | let writer = Data.Write.create ~to_bytes () in 18 | let name = sprintf "decorated_%s" super in 19 | T.add_writer name ~ver:"1.0" writer; 20 | T.set_default_printer name 21 | 22 | let main proj = 23 | let get_addr t = Term.get_attr t address in 24 | go (module Def) get_addr proj; 25 | go (module Jmp) get_addr proj; 26 | proj 27 | 28 | let () = Project.register_pass main 29 | -------------------------------------------------------------------------------- /toy-debugger/cmdline.ml: -------------------------------------------------------------------------------- 1 | open Core_kernel 2 | open Format 3 | 4 | open Options 5 | open Flag 6 | 7 | include Cmdliner 8 | 9 | module T = struct 10 | open Sexp.O 11 | open Result.Monad_infix 12 | 13 | let expect exp ~got = Error (sprintf "expected %s got %S" exp got) 14 | 15 | (** Takes `eval_blk` and capitalizes it, so we can just use of_sexp *) 16 | let atom (s : Sexp.t) = 17 | match s with 18 | | Atom s -> 19 | String.capitalize s 20 | |> Sexp.of_string 21 | |> Hook.t_of_sexp 22 | |> fun x -> Ok x 23 | | s -> expect (grammar ()) ~got:(Sexp.to_string s) 24 | 25 | (** Parses list of sexps into hook types *) 26 | let sexp = function 27 | | List x -> List.map ~f:atom x 28 | | x -> [atom x] 29 | 30 | let start s = Result.all (sexp s) 31 | 32 | let parse s = 33 | try (start (Sexp.of_string s)) 34 | with exn -> expect (grammar ()) ~got:s 35 | 36 | let parser s = match parse s with 37 | | Ok r -> `Ok r 38 | | Error e -> `Error e 39 | 40 | let to_string hook = 41 | Sexp.to_string (Hook.sexp_of_t hook) 42 | 43 | let pp ppf s = 44 | List.map ~f:to_string s |> 45 | String.concat ~sep:" " |> 46 | Format.fprintf ppf "%s" 47 | 48 | let go : 'a Cmdliner.Arg.converter = parser,pp 49 | end 50 | 51 | let fname : string option Term.t = 52 | let doc = "only one function" in 53 | Arg.(value & opt (some string) None & info ["fname"] ~doc) 54 | 55 | let flags kind : hook list list Term.t = 56 | let doc = sprintf "Flags" in 57 | Arg.(value & opt_all T.go [] & info [kind] ~doc) 58 | 59 | let dir : string option Term.t = 60 | let doc = "output directory" in 61 | Arg.(value & opt (some string) None & info ["dir"] ~doc) 62 | 63 | let verbose : bool Term.t = 64 | let doc = "Verbose option" in 65 | Arg.(value & flag & info ["verbose"] ~doc) 66 | 67 | let process_args fname dir memory regs dot path_counts trace self 68 | checkpoints verbose = 69 | let open Variantslib in 70 | let directives = 71 | let unbox var = var.Variantslib.Variant.constructor in 72 | let add hooks acc v = 73 | let hooks' = List.concat hooks in 74 | let v' = unbox v in 75 | (Flag (v',hooks')) :: acc in 76 | Flag.Directive.Variants.fold ~init:[] 77 | ~memory:(add memory) 78 | ~regs:(add regs) 79 | ~path_counts:(add path_counts) 80 | ~dot:(add dot) 81 | ~trace:(add trace) 82 | ~myself:(add self) 83 | ~checkpoints:(add self) 84 | in 85 | {fname; 86 | dir; 87 | directives; 88 | verbose} 89 | 90 | let info = Term.info ~doc:"" "Debugger" 91 | 92 | let parse argv = 93 | let args = Term.(pure process_args 94 | $fname 95 | $dir 96 | $(flags "memory") 97 | $(flags "regs") 98 | $(flags "dot") 99 | $(flags "path-counts") 100 | $(flags "trace") 101 | $(flags "myself") 102 | $(flags "checkpoints") 103 | $verbose) in 104 | match Term.eval ~argv (args,info) with 105 | | `Ok res -> res 106 | | `Error err -> exit 1 107 | | `Version | `Help -> exit 0 108 | -------------------------------------------------------------------------------- /toy-debugger/color.ml: -------------------------------------------------------------------------------- 1 | open Core_kernel 2 | 3 | type t = 4 | | Pink | Aqua | Red | Green | Blue | Orange | Brown | Gray | Purple 5 | | White | Black 6 | 7 | let to_int = function 8 | | Pink -> 0xff0099 9 | | Aqua -> 0x009999 10 | | Red -> 0xff0000 11 | | Green -> 0x009900 12 | | Blue -> 0x3366ff 13 | | Orange -> 0xFF6633 14 | | Brown -> 0x663300 15 | | Gray -> 0x666666 16 | | Purple -> 0x330066 17 | | White -> 0xffffff 18 | | Black -> 0x000000 19 | 20 | let (!) = to_int 21 | -------------------------------------------------------------------------------- /toy-debugger/color.mli: -------------------------------------------------------------------------------- 1 | open Core_kernel 2 | 3 | type t = 4 | | Pink | Aqua | Red | Green | Blue | Orange | Brown | Gray | Purple 5 | | White | Black 6 | 7 | val to_int : t -> int 8 | 9 | val ( ! ) : t -> int 10 | -------------------------------------------------------------------------------- /toy-debugger/draw.ml: -------------------------------------------------------------------------------- 1 | open Core_kernel 2 | open Bap.Std 3 | open Graphlib.Std 4 | open Color 5 | open Poly 6 | 7 | let left_justify = 8 | String.concat_map ~f:(fun c -> 9 | if c = '\n' then "\\l" else Char.to_string c) 10 | 11 | (** filter html and turn \n into html align left *) 12 | let format = 13 | String.concat_map ~f:(function 14 | | '<' -> "<" 15 | | '>' -> ">" 16 | | '&' -> "'&" 17 | | '\n' -> "
" 18 | | c -> Char.to_string c) 19 | 20 | (** grep from the start of a tid to t a newline, and color that. *) 21 | let color_pc ?pc node_str = 22 | match pc with 23 | | Some tid -> 24 | let pc = Tid.to_string tid in 25 | String.substr_replace_first 26 | ~pattern:pc ~with_:(sprintf "%s" pc) 27 | node_str 28 | | None -> node_str 29 | 30 | let dot_cfg ?pc ?(highlight_node=[]) sub ~filename = 31 | let module Cfg = Graphs.Ir in 32 | let cfg = Sub.to_cfg sub in 33 | 34 | let string_of_node node = 35 | sprintf "\"%s\"" @@ Blk.to_string @@ Cfg.Node.label node |> left_justify 36 | in 37 | 38 | let node_attrs node = 39 | let node_tid = Term.tid (Cfg.Node.label node) in 40 | let node_str = 41 | sprintf "%s" @@ Blk.to_string @@ Cfg.Node.label node 42 | in 43 | if List.Assoc.mem ~equal:Tid.equal highlight_node node_tid then 44 | [`Shape `Box; `Style `Filled; `Fontcolor !White; 45 | `Fillcolor (List.Assoc.find_exn ~equal:Tid.equal highlight_node node_tid); 46 | `HtmlLabel (node_str |> format |> color_pc ?pc); 47 | `Fontname "Monospace"] 48 | else 49 | [`Shape `Box; `Style `Filled; `Fillcolor !White; `Fontname "Monospace"] in 50 | Graphlib.to_dot (module Cfg) ~node_attrs ~string_of_node ~filename 51 | cfg 52 | 53 | let save_cfg ?pc ~filename sub trace = 54 | let highlight_node = List.map trace ~f:(fun tid -> (tid,!Gray)) in 55 | dot_cfg ?pc ~highlight_node sub ~filename 56 | -------------------------------------------------------------------------------- /toy-debugger/flag.ml: -------------------------------------------------------------------------------- 1 | let grammar () = {| 2 | flag ::= 3 | directives ::= .. 4 | directive ::= 5 | | 6 | | 7 | | 8 | | 9 | | 10 | | 11 | hooks ::= .. 12 | hook ::= 13 | | 14 | | 15 | | 16 | | 17 | | 18 | | 19 | | 20 | | 21 | | 22 | | 23 | | 24 | |} 25 | 26 | module Directive = struct 27 | type t = [ 28 | | `Memory 29 | | `Regs 30 | | `Path_counts 31 | | `Trace 32 | | `Dot 33 | | `Myself (* Usually the arguments supplied to the callback. *) 34 | | `Checkpoints 35 | ] [@@deriving sexp,variants] 36 | end 37 | 38 | type directive = Directive.t 39 | 40 | module Hook = struct 41 | type t = [ 42 | | `Enter_term 43 | | `Eval_sub 44 | | `Eval_blk 45 | | `Eval_jmp 46 | | `Eval_def 47 | | `Undefined_var 48 | | `Undefined_addr 49 | | `Update 50 | | `Load 51 | | `Store 52 | | `Lookup 53 | | `Path_terminates (* Invoked right after a path terminates *) 54 | ] [@@deriving sexp] 55 | end 56 | 57 | type hook = Hook.t 58 | 59 | type t = Flag of directive * hook list 60 | -------------------------------------------------------------------------------- /toy-debugger/options.ml: -------------------------------------------------------------------------------- 1 | open Flag 2 | 3 | type t = {fname : string option; 4 | dir : string option; 5 | directives : Flag.t list; 6 | verbose : bool;} 7 | -------------------------------------------------------------------------------- /toy-debugger/output.ml: -------------------------------------------------------------------------------- 1 | open Core_kernel 2 | open Format 3 | 4 | let to_csv ?filename data = 5 | let open Out_channel in 6 | let write chan = 7 | output_lines chan @@ List.map data ~f:(String.concat ~sep:",") in 8 | match filename with 9 | | Some filename -> with_file filename ~f:write 10 | | None -> write stdout 11 | -------------------------------------------------------------------------------- /toy-debugger/pngs/debugger.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BinaryAnalysisPlatform/bap-plugins/2e9aa5c7c24ef494d0e7db1b43c5ceedcb4196a8/toy-debugger/pngs/debugger.png -------------------------------------------------------------------------------- /toy-debugger/run.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | trap convert_svg INT 4 | 5 | binary=test1 6 | 7 | bap tests/$binary \ 8 | --no-byteweight \ 9 | --symbolizer=ida \ 10 | --callsites \ 11 | -lbirasm --birasm \ 12 | -ldebugger --debugger \ 13 | --debugger-fname=main \ 14 | --debugger-memory="enter_term" \ 15 | --debugger-regs="enter_term" \ 16 | --debugger-dot="enter_term" \ 17 | --debugger-path-count="enter_term" \ 18 | --debugger-myself="enter_term" \ 19 | --debugger-checkpoints="enter_term" \ 20 | --debugger-dir="viewer" 21 | 22 | function convert_svg() { 23 | echo "Converting..." 24 | cd viewer && ls *.dot | xargs -L 1 -I % bash -c "dot -Tsvg % > %.svg" 25 | cd .. 26 | } 27 | 28 | convert_svg 29 | -------------------------------------------------------------------------------- /toy-debugger/state_data.ml: -------------------------------------------------------------------------------- 1 | open Core_kernel 2 | open Bap.Std 3 | open Format 4 | open Poly 5 | open Flag 6 | 7 | let to_csv ?dirname ~invoker data_type step_count data = 8 | let prefix = 9 | Sexp.to_string (Hook.sexp_of_t invoker) 10 | |> String.uncapitalize in 11 | match dirname with 12 | | Some dirname -> 13 | let (!!) = format_of_string in 14 | let filename = 15 | (match data_type,invoker with 16 | | `Regs,_ -> !!"%s_regs%04d.txt" 17 | | `Memory,_ -> !!"%s_memory%04d.txt" 18 | | `Path_counts,_ -> !!"%s_path_count%04d.txt" 19 | | `Trace,_ -> !!"%s_trace%04d.txt" 20 | | `Checkpoints,_ -> !!"%s_checkpoints%04d.txt" 21 | | `Myself,invoker -> !!"%s_self%04d.txt") |> fun x -> 22 | dirname^"/"^(sprintf x prefix step_count) in 23 | Output.to_csv ~filename data 24 | | None -> Output.to_csv data 25 | 26 | let to_dot ?pc ?dirname sub step_count trace = 27 | match dirname with 28 | | None -> failwith "Please specify a directory with -dir option" 29 | | Some dirname -> 30 | let filename = dirname^"/"^(sprintf "cfg_frame%04d.dot" step_count) in 31 | Draw.save_cfg ?pc ~filename sub trace; 32 | -------------------------------------------------------------------------------- /toy-debugger/state_data.mli: -------------------------------------------------------------------------------- 1 | open Core_kernel 2 | open Bap.Std 3 | open Flag 4 | 5 | (** if no directory is sepcified, it prints to standard out *) 6 | val to_csv : ?dirname:string -> invoker:hook -> 7 | [< `Memory | `Path_counts | `Regs | `Trace | `Myself | `Checkpoints] 8 | -> int -> string list list -> unit 9 | 10 | val to_dot : ?pc:tid -> ?dirname:string -> sub term -> int -> tid list -> unit 11 | -------------------------------------------------------------------------------- /toy-debugger/tests/test1: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BinaryAnalysisPlatform/bap-plugins/2e9aa5c7c24ef494d0e7db1b43c5ceedcb4196a8/toy-debugger/tests/test1 -------------------------------------------------------------------------------- /toy-debugger/viewer/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 11 | 12 | 68 | 69 | 70 | 71 |

0000

72 | 73 | 74 |
75 | 76 |
77 |
78 | 79 |
80 |
81 |

Current

82 | 83 | 84 |
85 |
86 |

Regs

87 | 88 |

Checkpoints

89 | 90 |
91 | 92 |
93 |

Memory

94 | 95 |
96 |
97 |
98 |
99 | 100 | 101 | 102 | 103 | -------------------------------------------------------------------------------- /uaf-checker/.merlin: -------------------------------------------------------------------------------- 1 | EXT nonrec 2 | EXT ounit 3 | 4 | S . 5 | S debugger 6 | B _build 7 | B _build/debugger 8 | 9 | S /home/vagrant/core_kernel.113.33.03/src 10 | 11 | PKG bap bap-arm core_kernel cmdliner ocamlgraph re.posix ocamlbuild oUnit FrontC bap-microx 12 | 13 | FLG -short-paths 14 | FLG -w -4-33-40-41-42-43-34-44 15 | -------------------------------------------------------------------------------- /uaf-checker/Makefile: -------------------------------------------------------------------------------- 1 | all : 2 | bapbuild -no-hygiene main.plugin -pkg cmdliner -pkg bap-microx -pkg bap-arm -I debugger 3 | 4 | test: 5 | ocaml test.ml 6 | 7 | clean: 8 | bapbuild -clean 9 | rm *.plugin 10 | -------------------------------------------------------------------------------- /uaf-checker/debugger/.merlin: -------------------------------------------------------------------------------- 1 | EXT nonrec 2 | EXT ounit 3 | 4 | S . 5 | B ../_build 6 | B ../_build/debugger 7 | 8 | S /home/vagrant/core_kernel.113.33.03/src 9 | 10 | PKG bap bap-arm core_kernel cmdliner ocamlgraph re.posix ocamlbuild oUnit FrontC bap-microx 11 | 12 | FLG -short-paths 13 | FLG -w -4-33-40-41-42-43-34-44 14 | -------------------------------------------------------------------------------- /uaf-checker/debugger/color.ml: -------------------------------------------------------------------------------- 1 | open Core_kernel 2 | 3 | type t = 4 | | Pink | Aqua | Red | Green | Blue | Orange | Brown | Gray | Purple 5 | | White | Black 6 | 7 | let to_int = function 8 | | Pink -> 0xff0099 9 | | Aqua -> 0x009999 10 | | Red -> 0xff0000 11 | | Green -> 0x009900 12 | | Blue -> 0x3366ff 13 | | Orange -> 0xFF6633 14 | | Brown -> 0x663300 15 | | Gray -> 0x666666 16 | | Purple -> 0x330066 17 | | White -> 0xffffff 18 | | Black -> 0x000000 19 | 20 | let (!) = to_int 21 | -------------------------------------------------------------------------------- /uaf-checker/debugger/color.mli: -------------------------------------------------------------------------------- 1 | open Core_kernel 2 | 3 | type t = 4 | | Pink | Aqua | Red | Green | Blue | Orange | Brown | Gray | Purple 5 | | White | Black 6 | 7 | val to_int : t -> int 8 | 9 | val ( ! ) : t -> int 10 | -------------------------------------------------------------------------------- /uaf-checker/debugger/debugger.mli: -------------------------------------------------------------------------------- 1 | open Bap.Std 2 | open Microx.Std 3 | open Concretizer 4 | open Flag 5 | 6 | (** TODO: split base_context and into expi_ and biri_ contexts, then 7 | propagate expi_ to concretizer and biri_ to conqueror *) 8 | 9 | (** The base_context is for biri, since we are using the trace part of 10 | it. Also, conqueror uses the next/set_next part of it. It could 11 | be split to a expi_base_context and biri_base_context, but that 12 | would make things tricky.*) 13 | class base_context : 14 | ?dir:string -> 15 | ?directives:Flag.t list -> 16 | program term -> object('s) 17 | inherit Biri.context 18 | 19 | method cstack : sub term list 20 | method addrs : word list 21 | method blk_count : int 22 | method path_count : int 23 | method branch_count : int 24 | method step_count : int 25 | 26 | method add_addr : word -> 's 27 | 28 | method update_callstack_enter : sub term -> 's 29 | method update_callstack_leave : sub term -> 's 30 | 31 | method inc_blk_count : 's 32 | method inc_step_count : 's 33 | 34 | method log : extras:string list -> Flag.hook -> unit 35 | end 36 | 37 | (** Helper debugger classes. The conqueror class uses base_context *) 38 | (** TODO: add path_terminates callback *) 39 | class conqueror_debugger_context : 40 | ?dir:string -> 41 | ?directives:Flag.t list -> 42 | ?max_steps:int -> 43 | ?max_loop:int -> 44 | program term -> 45 | object('s) 46 | inherit Conqueror.context 47 | inherit base_context 48 | 49 | method merge_counts : 's -> 's 50 | method update_path_count : 's 51 | end 52 | 53 | (** Debuggers do not need options! options are triggered in the debug 54 | l context *) 55 | class ['a] concretizer_debugger : 56 | ?memory:(addr -> word option) -> 57 | ?lookup:(var -> word option) -> 58 | ?random_seed:int -> 59 | ?reg_policy:Concretizer.policy -> 60 | ?mem_policy:Concretizer.policy -> 61 | project -> 62 | object('s) 63 | (*concretizer debugger is lifted to Biri.context, not 64 | Expi.context, since base_context must derive from Biri.context. 65 | Ideally, this should use a expi_debug_context.*) 66 | constraint 'a = #base_context 67 | inherit ['a] Concretizer.main 68 | end 69 | 70 | class ['a] conqueror_debugger : 71 | ?deterministic:bool -> 72 | project -> 73 | object('s) 74 | constraint 'a = #conqueror_debugger_context 75 | inherit ['a] Conqueror.main 76 | end 77 | 78 | (** TODO: the right context for conqueror_concretizer_debugger is 79 | conqueror_debugger_context, which doesn't feel right *) 80 | (** TODO: should O be giving memory_lookup and register_lookup? *) 81 | class ['a] conqueror_concretizer_debugger : 82 | ?deterministic:bool -> 83 | ?memory:(addr -> word option) -> 84 | ?lookup:(var -> word option) -> 85 | ?random_seed:int -> 86 | ?reg_policy:Concretizer.policy -> 87 | ?mem_policy:Concretizer.policy -> 88 | project -> 89 | object('s) 90 | constraint 'a = #conqueror_debugger_context 91 | inherit ['a] conqueror_debugger 92 | inherit ['a] concretizer_debugger 93 | end 94 | -------------------------------------------------------------------------------- /uaf-checker/debugger/draw.ml: -------------------------------------------------------------------------------- 1 | open Core_kernel 2 | open Bap.Std 3 | open Graphlib.Std 4 | open Color 5 | open Polymorphic_compare 6 | 7 | 8 | let left_justify = 9 | String.concat_map ~f:(fun c -> 10 | if c = '\n' then "\\l" else Char.to_string c) 11 | 12 | (** filter html and turn \n into html align left *) 13 | let format = 14 | String.concat_map ~f:(function 15 | | '<' -> "<" 16 | | '>' -> ">" 17 | | '&' -> "'&" 18 | | '\n' -> "
" 19 | | c -> Char.to_string c) 20 | 21 | (** grep from the start of a tid to t a newline, and color that. *) 22 | let color_pc ?pc node_str = 23 | match pc with 24 | | Some tid -> 25 | let pc = Tid.to_string tid in 26 | String.substr_replace_first 27 | ~pattern:pc ~with_:(sprintf "%s" pc) 28 | node_str 29 | | None -> node_str 30 | 31 | let dot_cfg ?pc ?(highlight_node=[]) sub ~filename = 32 | let module Cfg = Graphs.Ir in 33 | let cfg = Sub.to_cfg sub in 34 | 35 | let string_of_node node = 36 | sprintf "\"%s\"" @@ Blk.to_string @@ Cfg.Node.label node |> left_justify 37 | in 38 | 39 | let node_attrs node = 40 | let node_tid = Term.tid (Cfg.Node.label node) in 41 | let node_str = 42 | sprintf "%s" @@ Blk.to_string @@ Cfg.Node.label node 43 | in 44 | if List.Assoc.mem ~equal highlight_node node_tid then 45 | [`Shape `Box; `Style `Filled; `Fontcolor !White; 46 | `Fillcolor (List.Assoc.find_exn ~equal highlight_node node_tid); 47 | `HtmlLabel (node_str |> format |> color_pc ?pc); 48 | `Fontname "Monospace"] 49 | else 50 | [`Shape `Box; `Style `Filled; `Fillcolor !White; `Fontname "Monospace"] in 51 | Graphlib.to_dot (module Cfg) ~node_attrs ~string_of_node ~filename 52 | cfg 53 | 54 | let save_cfg ?pc ~filename sub trace = 55 | let highlight_node = List.map trace ~f:(fun tid -> (tid,!Gray)) in 56 | dot_cfg ?pc ~highlight_node sub ~filename 57 | -------------------------------------------------------------------------------- /uaf-checker/debugger/flag.ml: -------------------------------------------------------------------------------- 1 | let grammar () = {| 2 | flag ::= 3 | directives ::= .. 4 | directive ::= 5 | | 6 | | 7 | | 8 | | 9 | | 10 | | 11 | hooks ::= .. 12 | hook ::= 13 | | 14 | | 15 | | 16 | | 17 | | 18 | | 19 | | 20 | | 21 | | 22 | | 23 | | 24 | |} 25 | 26 | module Directive = struct 27 | type t = [ 28 | | `Memory 29 | | `Regs 30 | | `Path_counts 31 | | `Trace 32 | | `Dot 33 | | `Myself (* Usually the arguments supplied to the callback. *) 34 | | `Checkpoints 35 | | `Freed_addrs 36 | | `Alloced_addrs 37 | ] [@@deriving sexp,variants] 38 | end 39 | 40 | type directive = Directive.t 41 | 42 | module Hook = struct 43 | type t = [ 44 | | `Enter_term 45 | | `Eval_sub 46 | | `Eval_blk 47 | | `Eval_jmp 48 | | `Eval_def 49 | | `Undefined_var 50 | | `Undefined_addr 51 | | `Update 52 | | `Load 53 | | `Store 54 | | `Lookup 55 | | `Path_terminates (* Invoked right after a path terminates *) 56 | ] [@@deriving sexp] 57 | end 58 | 59 | type hook = Hook.t 60 | 61 | type t = Flag of directive * hook list 62 | -------------------------------------------------------------------------------- /uaf-checker/debugger/output.ml: -------------------------------------------------------------------------------- 1 | open Core_kernel 2 | open Format 3 | 4 | let to_csv ?filename data = 5 | let open Out_channel in 6 | let write chan = 7 | output_lines chan @@ List.map data ~f:(String.concat ~sep:",") in 8 | match filename with 9 | | Some filename -> with_file filename ~f:write 10 | | None -> () (*write stdout*) 11 | -------------------------------------------------------------------------------- /uaf-checker/debugger/state_data.ml: -------------------------------------------------------------------------------- 1 | open Core_kernel 2 | open Bap.Std 3 | open Format 4 | 5 | open Flag 6 | 7 | let to_csv ?dirname ~invoker data_type step_count data = 8 | let prefix = 9 | Sexp.to_string (Hook.sexp_of_t invoker) 10 | |> String.uncapitalize in 11 | match dirname with 12 | | Some dirname -> 13 | let (!!) = format_of_string in 14 | let filename = 15 | (match data_type,invoker with 16 | | `Regs,_ -> !!"%s_regs%04d.txt" 17 | | `Memory,_ -> !!"%s_memory%04d.txt" 18 | | `Path_counts,_ -> !!"%s_path_count%04d.txt" 19 | | `Trace,_ -> !!"%s_trace%04d.txt" 20 | | `Checkpoints,_ -> !!"%s_checkpoints%04d.txt" 21 | | `Myself,invoker -> !!"%s_self%04d.txt" 22 | | `Freed_addrs,_ -> !!"%s_freed_addrs%04d.txt" 23 | | `Alloced_addrs,_ -> !!"%s_alloced_addrs%04d.txt") 24 | |> fun x -> 25 | dirname^"/"^(sprintf x prefix step_count) in 26 | Output.to_csv ~filename data 27 | | None -> Output.to_csv data 28 | 29 | let to_dot ?pc ?dirname sub step_count trace = 30 | match dirname with 31 | | None -> failwith "Please specify a directory with -dir option" 32 | | Some dirname -> 33 | let filename = dirname^"/"^(sprintf "cfg_frame%04d.dot" step_count) in 34 | Draw.save_cfg ?pc ~filename sub trace; 35 | -------------------------------------------------------------------------------- /uaf-checker/debugger/state_data.mli: -------------------------------------------------------------------------------- 1 | open Core_kernel 2 | open Bap.Std 3 | open Flag 4 | 5 | (** if no directory is sepcified, it prints to standard out *) 6 | val to_csv : ?dirname:string -> invoker:hook -> 7 | [< `Memory | `Path_counts | `Regs | `Trace | `Myself | `Checkpoints 8 | | `Freed_addrs | `Alloced_addrs] 9 | -> int -> string list list -> unit 10 | 11 | val to_dot : ?pc:tid -> ?dirname:string -> sub term -> int -> tid list -> unit 12 | -------------------------------------------------------------------------------- /uaf-checker/demo/gnome-nettool: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BinaryAnalysisPlatform/bap-plugins/2e9aa5c7c24ef494d0e7db1b43c5ceedcb4196a8/uaf-checker/demo/gnome-nettool -------------------------------------------------------------------------------- /uaf-checker/demo/microx.patch: -------------------------------------------------------------------------------- 1 | diff --git a/lib/microx/microx_conqueror.ml b/lib/microx/microx_conqueror.ml 2 | index 05bc5df..7e25961 100644 3 | --- a/lib/microx/microx_conqueror.ml 4 | +++ b/lib/microx/microx_conqueror.ml 5 | @@ -139,6 +139,7 @@ class ['a] main ?(deterministic=false) p = 6 | | None -> self#break 7 | | Some ctxt -> 8 | SM.put ctxt >>= fun () -> 9 | + let jmp = Jmp.with_cond jmp (Bil.int Word.b1) in 10 | super#eval_jmp jmp >>= fun () -> 11 | SM.get () >>= fun ctxt -> 12 | match ctxt#next with 13 | -------------------------------------------------------------------------------- /uaf-checker/demo/run.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | echo "Warning: This will not work as intended without applying microx.patch to your BAP" 4 | 5 | mkdir -p viewer 6 | bap --api-add c:posix.h 7 | bap ./gnome-nettool --no-byteweight --symbolizer=ida -l$(pwd)/../main --main --main-precision=2 --main-fname=info_get_nic_information 8 | 9 | # Use ida to visualize ! 10 | # idaq -S$(pwd)/test-info_get_nic_information.py ./gnome-nettool 11 | -------------------------------------------------------------------------------- /uaf-checker/demo/test-info_get_nic_information.py: -------------------------------------------------------------------------------- 1 | from idautils import * 2 | Wait() 3 | idaapi.set_item_color(DecodeInstruction(0x17344).ea, 0xff) 4 | MakeComm(0x17344, "~use") 5 | idaapi.set_item_color(DecodeInstruction(0x166f0).ea, 0xffff) 6 | MakeComm(0x166f0, "~free") 7 | idaapi.set_item_color(DecodeInstruction(0x17138).ea, 0xff00) 8 | MakeComm(0x17138, "~alloc") 9 | idaapi.set_item_color(DecodeInstruction(0x17344).ea, 0xff) 10 | MakeComm(0x17344, "~use") 11 | idaapi.set_item_color(DecodeInstruction(0x166f0).ea, 0xffff) 12 | MakeComm(0x166f0, "~free") 13 | idaapi.set_item_color(DecodeInstruction(0x17138).ea, 0xff00) 14 | MakeComm(0x17138, "~alloc") 15 | idaapi.set_item_color(DecodeInstruction(0x17344).ea, 0xff) 16 | MakeComm(0x17344, "~use") 17 | idaapi.set_item_color(DecodeInstruction(0x166f0).ea, 0xffff) 18 | MakeComm(0x166f0, "~free") 19 | idaapi.set_item_color(DecodeInstruction(0x17138).ea, 0xff00) 20 | MakeComm(0x17138, "~alloc") 21 | idaapi.set_item_color(DecodeInstruction(0x17344).ea, 0xff) 22 | MakeComm(0x17344, "~use") 23 | idaapi.set_item_color(DecodeInstruction(0x166f0).ea, 0xffff) 24 | MakeComm(0x166f0, "~free") 25 | idaapi.set_item_color(DecodeInstruction(0x17138).ea, 0xff00) 26 | MakeComm(0x17138, "~alloc") 27 | -------------------------------------------------------------------------------- /uaf-checker/expected/dead-simpl-uaf-arm-2.output: -------------------------------------------------------------------------------- 1 | =-=-=-=-=-=-=-= UAF! -=-=-=-=-=-=-=- 2 | UAF detected! 3 | Address: 0xDEAD965F:32 4 | Used at: 00000141 5 | Free'd at: 0000019d 6 | Alloc'd at: 0000019c 7 | 8 | 9 | =-=-=-=-=-=-=-= UAF! -=-=-=-=-=-=-=- 10 | UAF detected! 11 | Address: 0xDEAD965E:32 12 | Used at: 00000141 13 | Free'd at: 0000019d 14 | Alloc'd at: 0000019c 15 | 16 | 17 | =-=-=-=-=-=-=-= UAF! -=-=-=-=-=-=-=- 18 | UAF detected! 19 | Address: 0xDEAD965D:32 20 | Used at: 00000141 21 | Free'd at: 0000019d 22 | Alloc'd at: 0000019c 23 | 24 | 25 | =-=-=-=-=-=-=-= UAF! -=-=-=-=-=-=-=- 26 | UAF detected! 27 | Address: 0xDEAD965C:32 28 | Used at: 00000141 29 | Free'd at: 0000019d 30 | Alloc'd at: 0000019c 31 | 32 | 33 | -------------------------------------------------------------------------------- /uaf-checker/expected/dead-simpl-uaf-arm.output: -------------------------------------------------------------------------------- 1 | =-=-=-=-=-=-=-= UAF! -=-=-=-=-=-=-=- 2 | UAF detected! 3 | Address: 0xDEAD965F:32 4 | Used at: 00000138 5 | Free'd at: 00000194 6 | Alloc'd at: 00000193 7 | 8 | 9 | =-=-=-=-=-=-=-= UAF! -=-=-=-=-=-=-=- 10 | UAF detected! 11 | Address: 0xDEAD965E:32 12 | Used at: 00000138 13 | Free'd at: 00000194 14 | Alloc'd at: 00000193 15 | 16 | 17 | =-=-=-=-=-=-=-= UAF! -=-=-=-=-=-=-=- 18 | UAF detected! 19 | Address: 0xDEAD965D:32 20 | Used at: 00000138 21 | Free'd at: 00000194 22 | Alloc'd at: 00000193 23 | 24 | 25 | =-=-=-=-=-=-=-= UAF! -=-=-=-=-=-=-=- 26 | UAF detected! 27 | Address: 0xDEAD965C:32 28 | Used at: 00000138 29 | Free'd at: 00000194 30 | Alloc'd at: 00000193 31 | 32 | 33 | -------------------------------------------------------------------------------- /uaf-checker/expected/gnome-nettool.output: -------------------------------------------------------------------------------- 1 | =-=-=-=-=-=-=-= UAF! -=-=-=-=-=-=-=- 2 | UAF detected! 3 | Address: 0xDEADDCD6:32 4 | Used at: 000011c8 5 | Free'd at: 00007bbb 6 | Alloc'd at: 00007ae0 7 | 8 | 9 | =-=-=-=-=-=-=-= UAF! -=-=-=-=-=-=-=- 10 | UAF detected! 11 | Address: 0xDEADDCD7:32 12 | Used at: 000011c8 13 | Free'd at: 00007bbb 14 | Alloc'd at: 00007ae0 15 | 16 | 17 | =-=-=-=-=-=-=-= UAF! -=-=-=-=-=-=-=- 18 | UAF detected! 19 | Address: 0xDEADDCD8:32 20 | Used at: 000011c8 21 | Free'd at: 00007bbb 22 | Alloc'd at: 00007ae0 23 | 24 | 25 | =-=-=-=-=-=-=-= UAF! -=-=-=-=-=-=-=- 26 | UAF detected! 27 | Address: 0xDEADDCD9:32 28 | Used at: 000011c8 29 | Free'd at: 00007bbb 30 | Alloc'd at: 00007ae0 31 | 32 | 33 | -------------------------------------------------------------------------------- /uaf-checker/expected/gueb-example-ite-uaf-arm-O0.output: -------------------------------------------------------------------------------- 1 | =-=-=-=-=-=-=-= UAF! -=-=-=-=-=-=-=- 2 | UAF detected! 3 | Address: 0xDEAD965F:32 4 | Used at: 00000143 5 | Free'd at: 00000196 6 | Alloc'd at: 00000195 7 | 8 | 9 | =-=-=-=-=-=-=-= UAF! -=-=-=-=-=-=-=- 10 | UAF detected! 11 | Address: 0xDEAD965E:32 12 | Used at: 00000143 13 | Free'd at: 00000196 14 | Alloc'd at: 00000195 15 | 16 | 17 | =-=-=-=-=-=-=-= UAF! -=-=-=-=-=-=-=- 18 | UAF detected! 19 | Address: 0xDEAD965D:32 20 | Used at: 00000143 21 | Free'd at: 00000196 22 | Alloc'd at: 00000195 23 | 24 | 25 | =-=-=-=-=-=-=-= UAF! -=-=-=-=-=-=-=- 26 | UAF detected! 27 | Address: 0xDEAD965C:32 28 | Used at: 00000143 29 | Free'd at: 00000196 30 | Alloc'd at: 00000195 31 | 32 | 33 | -------------------------------------------------------------------------------- /uaf-checker/expected/gueb-example-uaf-arm-O0.output: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BinaryAnalysisPlatform/bap-plugins/2e9aa5c7c24ef494d0e7db1b43c5ceedcb4196a8/uaf-checker/expected/gueb-example-uaf-arm-O0.output -------------------------------------------------------------------------------- /uaf-checker/expected/gueb-full-arm-O0-buggy.output: -------------------------------------------------------------------------------- 1 | =-=-=-=-=-=-=-= UAF! -=-=-=-=-=-=-=- 2 | UAF detected! 3 | Address: 0xDEAD6622:32 4 | Used at: 000001ea 5 | Free'd at: 00000294 6 | Alloc'd at: 00000291 7 | 8 | 9 | =-=-=-=-=-=-=-= UAF! -=-=-=-=-=-=-=- 10 | UAF detected! 11 | Address: 0xDEAD6621:32 12 | Used at: 000001ea 13 | Free'd at: 00000294 14 | Alloc'd at: 00000291 15 | 16 | 17 | =-=-=-=-=-=-=-= UAF! -=-=-=-=-=-=-=- 18 | UAF detected! 19 | Address: 0xDEAD6620:32 20 | Used at: 000001ea 21 | Free'd at: 00000294 22 | Alloc'd at: 00000291 23 | 24 | 25 | =-=-=-=-=-=-=-= UAF! -=-=-=-=-=-=-=- 26 | UAF detected! 27 | Address: 0xDEAD661F:32 28 | Used at: 000001ea 29 | Free'd at: 00000294 30 | Alloc'd at: 00000291 31 | 32 | 33 | =-=-=-=-=-=-=-= UAF! -=-=-=-=-=-=-=- 34 | UAF detected! 35 | Address: 0xDEAD6622:32 36 | Used at: 000001f9 37 | Free'd at: 00000294 38 | Alloc'd at: 00000291 39 | 40 | 41 | =-=-=-=-=-=-=-= UAF! -=-=-=-=-=-=-=- 42 | UAF detected! 43 | Address: 0xDEAD6621:32 44 | Used at: 000001f9 45 | Free'd at: 00000294 46 | Alloc'd at: 00000291 47 | 48 | 49 | =-=-=-=-=-=-=-= UAF! -=-=-=-=-=-=-=- 50 | UAF detected! 51 | Address: 0xDEAD6620:32 52 | Used at: 000001f9 53 | Free'd at: 00000294 54 | Alloc'd at: 00000291 55 | 56 | 57 | =-=-=-=-=-=-=-= UAF! -=-=-=-=-=-=-=- 58 | UAF detected! 59 | Address: 0xDEAD661F:32 60 | Used at: 000001f9 61 | Free'd at: 00000294 62 | Alloc'd at: 00000291 63 | 64 | 65 | =-=-=-=-=-=-=-= UAF! -=-=-=-=-=-=-=- 66 | UAF detected! 67 | Address: 0xDEAD6622:32 68 | Used at: 00000203 69 | Free'd at: 00000294 70 | Alloc'd at: 00000291 71 | 72 | 73 | =-=-=-=-=-=-=-= UAF! -=-=-=-=-=-=-=- 74 | UAF detected! 75 | Address: 0xDEAD6621:32 76 | Used at: 00000203 77 | Free'd at: 00000294 78 | Alloc'd at: 00000291 79 | 80 | 81 | =-=-=-=-=-=-=-= UAF! -=-=-=-=-=-=-=- 82 | UAF detected! 83 | Address: 0xDEAD6620:32 84 | Used at: 00000203 85 | Free'd at: 00000294 86 | Alloc'd at: 00000291 87 | 88 | 89 | =-=-=-=-=-=-=-= UAF! -=-=-=-=-=-=-=- 90 | UAF detected! 91 | Address: 0xDEAD661F:32 92 | Used at: 00000203 93 | Free'd at: 00000294 94 | Alloc'd at: 00000291 95 | 96 | 97 | -------------------------------------------------------------------------------- /uaf-checker/expected/gueb-full-arm-O0.output: -------------------------------------------------------------------------------- 1 | =-=-=-=-=-=-=-= UAF! -=-=-=-=-=-=-=- 2 | UAF detected! 3 | Address: 0xDEAD6622:32 4 | Used at: 000001cc 5 | Free'd at: 0000026c 6 | Alloc'd at: 00000269 7 | 8 | 9 | =-=-=-=-=-=-=-= UAF! -=-=-=-=-=-=-=- 10 | UAF detected! 11 | Address: 0xDEAD6621:32 12 | Used at: 000001cc 13 | Free'd at: 0000026c 14 | Alloc'd at: 00000269 15 | 16 | 17 | =-=-=-=-=-=-=-= UAF! -=-=-=-=-=-=-=- 18 | UAF detected! 19 | Address: 0xDEAD6620:32 20 | Used at: 000001cc 21 | Free'd at: 0000026c 22 | Alloc'd at: 00000269 23 | 24 | 25 | =-=-=-=-=-=-=-= UAF! -=-=-=-=-=-=-=- 26 | UAF detected! 27 | Address: 0xDEAD661F:32 28 | Used at: 000001cc 29 | Free'd at: 0000026c 30 | Alloc'd at: 00000269 31 | 32 | 33 | =-=-=-=-=-=-=-= UAF! -=-=-=-=-=-=-=- 34 | UAF detected! 35 | Address: 0xDEAD6622:32 36 | Used at: 000001db 37 | Free'd at: 0000026c 38 | Alloc'd at: 00000269 39 | 40 | 41 | =-=-=-=-=-=-=-= UAF! -=-=-=-=-=-=-=- 42 | UAF detected! 43 | Address: 0xDEAD6621:32 44 | Used at: 000001db 45 | Free'd at: 0000026c 46 | Alloc'd at: 00000269 47 | 48 | 49 | =-=-=-=-=-=-=-= UAF! -=-=-=-=-=-=-=- 50 | UAF detected! 51 | Address: 0xDEAD6620:32 52 | Used at: 000001db 53 | Free'd at: 0000026c 54 | Alloc'd at: 00000269 55 | 56 | 57 | =-=-=-=-=-=-=-= UAF! -=-=-=-=-=-=-=- 58 | UAF detected! 59 | Address: 0xDEAD661F:32 60 | Used at: 000001db 61 | Free'd at: 0000026c 62 | Alloc'd at: 00000269 63 | 64 | 65 | =-=-=-=-=-=-=-= UAF! -=-=-=-=-=-=-=- 66 | UAF detected! 67 | Address: 0xDEAD6622:32 68 | Used at: 000001e5 69 | Free'd at: 0000026c 70 | Alloc'd at: 00000269 71 | 72 | 73 | =-=-=-=-=-=-=-= UAF! -=-=-=-=-=-=-=- 74 | UAF detected! 75 | Address: 0xDEAD6621:32 76 | Used at: 000001e5 77 | Free'd at: 0000026c 78 | Alloc'd at: 00000269 79 | 80 | 81 | =-=-=-=-=-=-=-= UAF! -=-=-=-=-=-=-=- 82 | UAF detected! 83 | Address: 0xDEAD6620:32 84 | Used at: 000001e5 85 | Free'd at: 0000026c 86 | Alloc'd at: 00000269 87 | 88 | 89 | =-=-=-=-=-=-=-= UAF! -=-=-=-=-=-=-=- 90 | UAF detected! 91 | Address: 0xDEAD661F:32 92 | Used at: 000001e5 93 | Free'd at: 0000026c 94 | Alloc'd at: 00000269 95 | 96 | 97 | -------------------------------------------------------------------------------- /uaf-checker/expected/simpl-uaf-arm-2.output: -------------------------------------------------------------------------------- 1 | =-=-=-=-=-=-=-= UAF! -=-=-=-=-=-=-=- 2 | UAF detected! 3 | Address: 0xDEADD0F1:32 4 | Used at: 00000167 5 | Free'd at: 000001ce 6 | Alloc'd at: 000001cc 7 | 8 | 9 | =-=-=-=-=-=-=-= UAF! -=-=-=-=-=-=-=- 10 | UAF detected! 11 | Address: 0xDEADD0F0:32 12 | Used at: 00000167 13 | Free'd at: 000001ce 14 | Alloc'd at: 000001cc 15 | 16 | 17 | =-=-=-=-=-=-=-= UAF! -=-=-=-=-=-=-=- 18 | UAF detected! 19 | Address: 0xDEADD0EF:32 20 | Used at: 00000167 21 | Free'd at: 000001ce 22 | Alloc'd at: 000001cc 23 | 24 | 25 | =-=-=-=-=-=-=-= UAF! -=-=-=-=-=-=-=- 26 | UAF detected! 27 | Address: 0xDEADD0EE:32 28 | Used at: 00000167 29 | Free'd at: 000001ce 30 | Alloc'd at: 000001cc 31 | 32 | 33 | -------------------------------------------------------------------------------- /uaf-checker/expected/simpl-uaf-arm-bug.output: -------------------------------------------------------------------------------- 1 | =-=-=-=-=-=-=-= UAF! -=-=-=-=-=-=-=- 2 | UAF detected! 3 | Address: 0xDEADD0F1:32 4 | Used at: 00000167 5 | Free'd at: 000001ce 6 | Alloc'd at: 000001cc 7 | 8 | 9 | =-=-=-=-=-=-=-= UAF! -=-=-=-=-=-=-=- 10 | UAF detected! 11 | Address: 0xDEADD0F0:32 12 | Used at: 00000167 13 | Free'd at: 000001ce 14 | Alloc'd at: 000001cc 15 | 16 | 17 | =-=-=-=-=-=-=-= UAF! -=-=-=-=-=-=-=- 18 | UAF detected! 19 | Address: 0xDEADD0EF:32 20 | Used at: 00000167 21 | Free'd at: 000001ce 22 | Alloc'd at: 000001cc 23 | 24 | 25 | =-=-=-=-=-=-=-= UAF! -=-=-=-=-=-=-=- 26 | UAF detected! 27 | Address: 0xDEADD0EE:32 28 | Used at: 00000167 29 | Free'd at: 000001ce 30 | Alloc'd at: 000001cc 31 | 32 | 33 | -------------------------------------------------------------------------------- /uaf-checker/expected/simpl-uaf-arm.output: -------------------------------------------------------------------------------- 1 | =-=-=-=-=-=-=-= UAF! -=-=-=-=-=-=-=- 2 | UAF detected! 3 | Address: 0xDEADD0F1:32 4 | Used at: 0000016c 5 | Free'd at: 000001de 6 | Alloc'd at: 000001db 7 | 8 | 9 | =-=-=-=-=-=-=-= UAF! -=-=-=-=-=-=-=- 10 | UAF detected! 11 | Address: 0xDEADD0F0:32 12 | Used at: 0000016c 13 | Free'd at: 000001de 14 | Alloc'd at: 000001db 15 | 16 | 17 | =-=-=-=-=-=-=-= UAF! -=-=-=-=-=-=-=- 18 | UAF detected! 19 | Address: 0xDEADD0EF:32 20 | Used at: 0000016c 21 | Free'd at: 000001de 22 | Alloc'd at: 000001db 23 | 24 | 25 | =-=-=-=-=-=-=-= UAF! -=-=-=-=-=-=-=- 26 | UAF detected! 27 | Address: 0xDEADD0EE:32 28 | Used at: 0000016c 29 | Free'd at: 000001de 30 | Alloc'd at: 000001db 31 | 32 | 33 | -------------------------------------------------------------------------------- /uaf-checker/expected/structs-arm-2.output: -------------------------------------------------------------------------------- 1 | =-=-=-=-=-=-=-= UAF! -=-=-=-=-=-=-=- 2 | UAF detected! 3 | Address: 0xDEAD72D4:32 4 | Used at: 00000156 5 | Free'd at: 000001b5 6 | Alloc'd at: 000001b8 7 | 8 | 9 | =-=-=-=-=-=-=-= UAF! -=-=-=-=-=-=-=- 10 | UAF detected! 11 | Address: 0xDEAD72D3:32 12 | Used at: 00000156 13 | Free'd at: 000001b5 14 | Alloc'd at: 000001b8 15 | 16 | 17 | =-=-=-=-=-=-=-= UAF! -=-=-=-=-=-=-=- 18 | UAF detected! 19 | Address: 0xDEAD72D2:32 20 | Used at: 00000156 21 | Free'd at: 000001b5 22 | Alloc'd at: 000001b8 23 | 24 | 25 | =-=-=-=-=-=-=-= UAF! -=-=-=-=-=-=-=- 26 | UAF detected! 27 | Address: 0xDEAD72D1:32 28 | Used at: 00000156 29 | Free'd at: 000001b5 30 | Alloc'd at: 000001b8 31 | 32 | 33 | -------------------------------------------------------------------------------- /uaf-checker/expected/structs-arm.output: -------------------------------------------------------------------------------- 1 | =-=-=-=-=-=-=-= UAF! -=-=-=-=-=-=-=- 2 | UAF detected! 3 | Address: 0xDEAD72D0:32 4 | Used at: 0000015d 5 | Free'd at: 000001e0 6 | Alloc'd at: 000001e5 7 | 8 | 9 | =-=-=-=-=-=-=-= UAF! -=-=-=-=-=-=-=- 10 | UAF detected! 11 | Address: 0xDEAD72CF:32 12 | Used at: 0000015d 13 | Free'd at: 000001e0 14 | Alloc'd at: 000001e5 15 | 16 | 17 | =-=-=-=-=-=-=-= UAF! -=-=-=-=-=-=-=- 18 | UAF detected! 19 | Address: 0xDEAD72CE:32 20 | Used at: 0000015d 21 | Free'd at: 000001e0 22 | Alloc'd at: 000001e5 23 | 24 | 25 | =-=-=-=-=-=-=-= UAF! -=-=-=-=-=-=-=- 26 | UAF detected! 27 | Address: 0xDEAD72CD:32 28 | Used at: 0000015d 29 | Free'd at: 000001e0 30 | Alloc'd at: 000001e5 31 | 32 | 33 | -------------------------------------------------------------------------------- /uaf-checker/expected/structs.output: -------------------------------------------------------------------------------- 1 | Pass `main' failed at runtime with: ("validation errors" 2 | (("" "expected width 64, but got 32"))) 3 | -------------------------------------------------------------------------------- /uaf-checker/expected/super-simpl-uaf-arm.output: -------------------------------------------------------------------------------- 1 | =-=-=-=-=-=-=-= UAF! -=-=-=-=-=-=-=- 2 | UAF detected! 3 | Address: 0xDEADD0F1:32 4 | Used at: 0000014c 5 | Free'd at: 000001aa 6 | Alloc'd at: 000001a9 7 | 8 | 9 | =-=-=-=-=-=-=-= UAF! -=-=-=-=-=-=-=- 10 | UAF detected! 11 | Address: 0xDEADD0F0:32 12 | Used at: 0000014c 13 | Free'd at: 000001aa 14 | Alloc'd at: 000001a9 15 | 16 | 17 | =-=-=-=-=-=-=-= UAF! -=-=-=-=-=-=-=- 18 | UAF detected! 19 | Address: 0xDEADD0EF:32 20 | Used at: 0000014c 21 | Free'd at: 000001aa 22 | Alloc'd at: 000001a9 23 | 24 | 25 | =-=-=-=-=-=-=-= UAF! -=-=-=-=-=-=-=- 26 | UAF detected! 27 | Address: 0xDEADD0EE:32 28 | Used at: 0000014c 29 | Free'd at: 000001aa 30 | Alloc'd at: 000001a9 31 | 32 | 33 | -------------------------------------------------------------------------------- /uaf-checker/expected/uaf-no-assign-arm.output: -------------------------------------------------------------------------------- 1 | =-=-=-=-=-=-=-= UAF! -=-=-=-=-=-=-=- 2 | UAF detected! 3 | Address: 0xDEAD965F:32 4 | Used at: 00000138 5 | Free'd at: 00000199 6 | Alloc'd at: 00000198 7 | 8 | 9 | =-=-=-=-=-=-=-= UAF! -=-=-=-=-=-=-=- 10 | UAF detected! 11 | Address: 0xDEAD965E:32 12 | Used at: 00000138 13 | Free'd at: 00000199 14 | Alloc'd at: 00000198 15 | 16 | 17 | =-=-=-=-=-=-=-= UAF! -=-=-=-=-=-=-=- 18 | UAF detected! 19 | Address: 0xDEAD965D:32 20 | Used at: 00000138 21 | Free'd at: 00000199 22 | Alloc'd at: 00000198 23 | 24 | 25 | =-=-=-=-=-=-=-= UAF! -=-=-=-=-=-=-=- 26 | UAF detected! 27 | Address: 0xDEAD965C:32 28 | Used at: 00000138 29 | Free'd at: 00000199 30 | Alloc'd at: 00000198 31 | 32 | 33 | -------------------------------------------------------------------------------- /uaf-checker/gen_test.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | 4 | bap --api-add=c:posix.h 5 | 6 | expected=expected 7 | mkdir -p $expected 8 | for b in `ls --ignore=\*.c --ignore=Makefile --ignore=\*.h --ignore=\*.so --ignore=\*.o tests/all`; do 9 | bap tests/all/${b} --no-byteweight --symbolizer=ida -lmain --main --main-fname=main &> $expected/${b}.output 10 | done 11 | 12 | # overwrite gnome-nettool 13 | bap tests/all/gnome-nettool --no-byteweight --symbolizer=ida -lmain --main --main-precision=2 --main-fname=info_get_nic_information --callsites &> $expected/gnome-nettool.output 14 | -------------------------------------------------------------------------------- /uaf-checker/microx.patch: -------------------------------------------------------------------------------- 1 | diff --git a/lib/microx/microx_conqueror.ml b/lib/microx/microx_conqueror.ml 2 | index 05bc5df..7e25961 100644 3 | --- a/lib/microx/microx_conqueror.ml 4 | +++ b/lib/microx/microx_conqueror.ml 5 | @@ -139,6 +139,7 @@ class ['a] main ?(deterministic=false) p = 6 | | None -> self#break 7 | | Some ctxt -> 8 | SM.put ctxt >>= fun () -> 9 | + let jmp = Jmp.with_cond jmp (Bil.int Word.b1) in 10 | super#eval_jmp jmp >>= fun () -> 11 | SM.get () >>= fun ctxt -> 12 | match ctxt#next with 13 | -------------------------------------------------------------------------------- /uaf-checker/options.ml: -------------------------------------------------------------------------------- 1 | type t = {fname : string option; 2 | log_lookup : bool; 3 | log_update : bool; 4 | log_load : bool; 5 | log_store : bool; 6 | log_memory_save : bool; 7 | log_memory_load : bool; 8 | log_undefined_addr : bool; 9 | log_undefined_var : bool; 10 | log_def : bool; 11 | log_jmp : bool; 12 | log_dump_memory : bool; 13 | log_dump_memory_on_load : bool; 14 | log_dump_memory_on_store : bool; 15 | log_dump_bindings : bool; 16 | log_alloc : bool; 17 | log_free : bool; 18 | verbose : bool; 19 | (** This is temporary *) 20 | precision : int} 21 | -------------------------------------------------------------------------------- /uaf-checker/pngs/ida-uaf.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BinaryAnalysisPlatform/bap-plugins/2e9aa5c7c24ef494d0e7db1b43c5ceedcb4196a8/uaf-checker/pngs/ida-uaf.png -------------------------------------------------------------------------------- /uaf-checker/test.ml: -------------------------------------------------------------------------------- 1 | #use "topfind";; 2 | #require "bap.top";; 3 | #require "oUnit";; 4 | 5 | open Core_kernel 6 | open Bap.Std 7 | open Or_error 8 | open OUnit2 9 | 10 | let test ?(extra=[]) test_path expected ctxt : unit = 11 | let options = 12 | ["--no-byteweight"; "--symbolizer=ida"; "-lmain"; "--main"] @ extra in 13 | assert_command ~ctxt "bap" ([test_path] @ options) ~foutput:(fun stream -> 14 | let str = 15 | let res = ref [] in 16 | let appender v = res := v :: !res in 17 | Stream.iter appender stream; 18 | List.rev !res |> List.map ~f:String.of_char |> String.concat in 19 | assert_equal str expected) 20 | 21 | (** Read test oracle *) 22 | let expected filename = In_channel.read_all filename 23 | 24 | let suite = 25 | "uaf" >::: 26 | [ 27 | "dead-simpl-1" >:: test "tests/all/dead-simpl-uaf-arm" 28 | (expected "expected/dead-simpl-uaf-arm.output") 29 | ~extra:["--main-fname=main"; 30 | "--main-precision=2";]; 31 | "dead-simple-2" >:: test "tests/all/dead-simpl-uaf-arm-2" 32 | (expected "expected/dead-simpl-uaf-arm-2.output") 33 | ~extra:["--main-fname=main"; 34 | "--main-precision=2";]; 35 | "super-simpl" >:: test "tests/all/super-simpl-uaf-arm" 36 | (expected "expected/super-simpl-uaf-arm.output") 37 | ~extra:["--main-fname=main"; 38 | "--main-precision=2";]; 39 | "simpl" >:: test "tests/all/simpl-uaf-arm" 40 | (expected "expected/simpl-uaf-arm.output") 41 | ~extra:["--main-fname=main"; 42 | "--main-precision=2";]; 43 | "simpl-2" >:: test "tests/all/simpl-uaf-arm-2" 44 | (expected "expected/simpl-uaf-arm-2.output") 45 | ~extra:["--main-fname=main"; 46 | "--main-precision=2";]; 47 | "no-assign" >:: test "tests/all/uaf-no-assign-arm" 48 | (expected "expected/uaf-no-assign-arm.output") 49 | ~extra:["--main-fname=main"; 50 | "--main-precision=2";]; 51 | "gueb-example" >:: test "tests/all/gueb-example-uaf-arm-O0" 52 | (expected "expected/gueb-example-uaf-arm-O0.output") 53 | ~extra:["--main-fname=main"; 54 | "--main-precision=2";]; 55 | "gueb-example-ite" >:: test "tests/all/gueb-example-ite-uaf-arm-O0" 56 | (expected "expected/gueb-example-ite-uaf-arm-O0.output") 57 | ~extra:["--main-fname=main"; 58 | "--main-precision=2";]; 59 | "gnome-nettool" >:: test "tests/all/gnome-nettool" 60 | (expected "expected/gnome-nettool.output") 61 | ~extra:["--main-precision=2"; 62 | "--main-fname=info_get_nic_information"; 63 | "--callsites"] 64 | ] 65 | 66 | let () = run_test_tt_main suite 67 | -------------------------------------------------------------------------------- /uaf-checker/tests/all/Makefile: -------------------------------------------------------------------------------- 1 | all: 2 | arm-linux-gnueabi-gcc -o dead-simpl-uaf-arm dead-simpl-uaf.c 3 | arm-linux-gnueabi-gcc -o dead-simpl-uaf-arm-2 dead-simpl-uaf-2.c 4 | arm-linux-gnueabi-gcc -o super-simpl-uaf-arm super-simpl-uaf.c 5 | arm-linux-gnueabi-gcc -o simpl-uaf-arm simpl-uaf.c 6 | arm-linux-gnueabi-gcc -o simpl-uaf-arm-2 simpl-uaf-2.c 7 | arm-linux-gnueabi-gcc -o simpl-uaf-arm-bug simpl-uaf-bug.c 8 | arm-linux-gnueabi-gcc -o uaf-no-assign-arm uaf-no-assign.c 9 | arm-linux-gnueabi-gcc -o gueb-example-uaf-arm-O0 gueb-example-uaf.c 10 | arm-linux-gnueabi-gcc -o gueb-example-ite-uaf-arm-O0 gueb-example-ite-uaf.c 11 | arm-linux-gnueabi-gcc -o gueb-full-arm-O0 gueb-full-uaf.c 12 | arm-linux-gnueabi-gcc -o gueb-full-arm-O0-buggy gueb-full-uaf-buggy.c 13 | arm-linux-gnueabi-gcc -o structs-arm structs.c 14 | 15 | blah: 16 | arm-linux-gnueabi-gcc -c vector.c 17 | arm-linux-gnueabi-gcc -shared -o libvector.so vector.o 18 | arm-linux-gnueabi-gcc -L/home/vagrant/rvantonder-bap-plugins/uaf/tests/all/ -o structs-arm-2 structs-2.c -lvector 19 | 20 | clean: 21 | rm dead-simpl-uaf-arm dead-simpl-uaf-arm-2 super-simpl-uaf-arm simpl-uaf-arm gueb-example-uaf-arm-O0 gueb-example-ite-uaf-arm-O0 gueb-full-arm-O0 gueb-full-arm-O0-buggy uaf-no-assign-arm simpl-uaf-arm-2 simpl-uaf-arm-bug structs-arm 22 | -------------------------------------------------------------------------------- /uaf-checker/tests/all/dead-simpl-uaf-2.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | int main (int argc, char * argv[]) { 5 | int * foo = malloc(4); 6 | int * bar = foo; 7 | 8 | free(foo); 9 | 10 | int number = *bar; 11 | 12 | printf("Use: %d\n", number); 13 | } 14 | -------------------------------------------------------------------------------- /uaf-checker/tests/all/dead-simpl-uaf-arm: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BinaryAnalysisPlatform/bap-plugins/2e9aa5c7c24ef494d0e7db1b43c5ceedcb4196a8/uaf-checker/tests/all/dead-simpl-uaf-arm -------------------------------------------------------------------------------- /uaf-checker/tests/all/dead-simpl-uaf-arm-2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BinaryAnalysisPlatform/bap-plugins/2e9aa5c7c24ef494d0e7db1b43c5ceedcb4196a8/uaf-checker/tests/all/dead-simpl-uaf-arm-2 -------------------------------------------------------------------------------- /uaf-checker/tests/all/dead-simpl-uaf.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | int main (int argc, char * argv[]) { 5 | int * foo = malloc(4); 6 | 7 | free(foo); 8 | 9 | int number = *foo; 10 | 11 | printf("Use: %d\n", number); 12 | } 13 | -------------------------------------------------------------------------------- /uaf-checker/tests/all/gnome-nettool: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BinaryAnalysisPlatform/bap-plugins/2e9aa5c7c24ef494d0e7db1b43c5ceedcb4196a8/uaf-checker/tests/all/gnome-nettool -------------------------------------------------------------------------------- /uaf-checker/tests/all/gueb-example-ite-uaf-arm-O0: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BinaryAnalysisPlatform/bap-plugins/2e9aa5c7c24ef494d0e7db1b43c5ceedcb4196a8/uaf-checker/tests/all/gueb-example-ite-uaf-arm-O0 -------------------------------------------------------------------------------- /uaf-checker/tests/all/gueb-example-ite-uaf.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | int main (int argc , char * argv []) { 5 | int * p_index ,* p_pass ; 6 | 7 | // If I uncomment this, I don't see UAF anymore. Why? It's the return that 8 | // trips it up. 9 | 10 | if (argc !=2) { 11 | return 0; 12 | } 13 | 14 | p_index = (int *) malloc (0xff) ; 15 | *p_index = 100; 16 | free(p_index) ; 17 | int val = *p_index; 18 | 19 | return 0; 20 | } 21 | -------------------------------------------------------------------------------- /uaf-checker/tests/all/gueb-example-uaf-arm-O0: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BinaryAnalysisPlatform/bap-plugins/2e9aa5c7c24ef494d0e7db1b43c5ceedcb4196a8/uaf-checker/tests/all/gueb-example-uaf-arm-O0 -------------------------------------------------------------------------------- /uaf-checker/tests/all/gueb-example-uaf.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | #define MIN 0 5 | #define MAX 10 6 | #define SECRET_PASS 0 7 | #define MODE_EASY 1 8 | 9 | int * p_global; 10 | 11 | int cmp () { return * p_global >= MIN && * p_global <= MAX ; } 12 | 13 | void index_user ( int *p) { 14 | int * p_global_save ; 15 | p_global_save = p_global ; 16 | p_global =p; 17 | if( cmp () <=0) { 18 | return; 19 | } 20 | p_global = p_global_save ; return ; 21 | } 22 | 23 | int main (int argc , char * argv []) { 24 | int * p_index; 25 | int val; 26 | 27 | p_global =( int *) malloc ( sizeof ( int ) ) ; 28 | * p_global = SECRET_PASS ; 29 | 30 | p_index =( int *) malloc ( sizeof ( int ) ) ; 31 | *p_index = 100; 32 | index_user(p_index) ; 33 | free(p_index); 34 | 35 | val = *p_global; 36 | 37 | return 0; 38 | } 39 | -------------------------------------------------------------------------------- /uaf-checker/tests/all/gueb-full-arm-O0: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BinaryAnalysisPlatform/bap-plugins/2e9aa5c7c24ef494d0e7db1b43c5ceedcb4196a8/uaf-checker/tests/all/gueb-full-arm-O0 -------------------------------------------------------------------------------- /uaf-checker/tests/all/gueb-full-arm-O0-buggy: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BinaryAnalysisPlatform/bap-plugins/2e9aa5c7c24ef494d0e7db1b43c5ceedcb4196a8/uaf-checker/tests/all/gueb-full-arm-O0-buggy -------------------------------------------------------------------------------- /uaf-checker/tests/all/gueb-full-uaf-buggy.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | #define MIN 0 5 | #define MAX 10 6 | #define SECRET_PASS 100 7 | #define MODE_EASY 1 8 | 9 | int * p_global; 10 | 11 | int cmp () { return * p_global >= MIN && * p_global <= MAX ; } 12 | 13 | void index_user ( int *p) { 14 | int * p_global_save ; 15 | p_global_save = p_global ; 16 | p_global = p; 17 | if( cmp () <=0) { 18 | printf ("The secret is greater than 50\n"); 19 | return; 20 | } 21 | printf ("The secret is less than 50 \n") ; 22 | p_global = p_global_save; 23 | return; 24 | } 25 | 26 | int main (int argc , char * argv []) { 27 | int * p_index ,* p_pass ; 28 | if( argc !=2) { 29 | printf (" ./ uaf MODE \nMODE EASY =1\nMODE HARD !=1\n"); 30 | return 0; 31 | } 32 | 33 | p_global = (int *) malloc (sizeof (int)) ; 34 | * p_global = SECRET_PASS ; 35 | 36 | if( atoi (argv [1]) == MODE_EASY) { 37 | p_index = (int *) malloc (0xff) ; 38 | printf ("Give a number between 0 and 100\n") ; 39 | scanf ("%d", p_index) ; 40 | index_user (p_index) ; 41 | printf("freeing\n"); 42 | free (p_index) ; 43 | } else { 44 | printf ("Good luck !\n") ; 45 | } 46 | 47 | p_pass = (int *) malloc (0xaa) ; 48 | 49 | printf ("Give the secret\n") ; 50 | scanf ("%d",p_pass ) ; 51 | 52 | printf("pindex: %d",*p_index); 53 | printf("pass: %d",*p_pass); 54 | printf("glbl: %d",*p_global); 55 | 56 | if (* p_pass ==* p_global ) { 57 | printf ("Congrats !\n") ; 58 | } else { 59 | printf ("Sorry ...\n"); 60 | } 61 | 62 | return 0; 63 | } 64 | -------------------------------------------------------------------------------- /uaf-checker/tests/all/gueb-full-uaf.c: -------------------------------------------------------------------------------- 1 | /* This is a non-buggy version. In the buggy version, the return 0 trips up 2 | * conqueror */ 3 | 4 | #include 5 | #include 6 | 7 | #define MIN 0 8 | #define MAX 10 9 | #define SECRET_PASS 100 10 | #define MODE_EASY 1 11 | 12 | int * p_global; 13 | 14 | int cmp () { return * p_global >= MIN && * p_global <= MAX ; } 15 | 16 | void index_user ( int *p) { 17 | int * p_global_save ; 18 | p_global_save = p_global ; 19 | p_global = p; 20 | if( cmp () <=0) { 21 | printf ("The secret is greater than 50\n"); 22 | return; 23 | } 24 | printf ("The secret is less than 50 \n") ; 25 | p_global = p_global_save; 26 | return; 27 | } 28 | 29 | int main (int argc , char * argv []) { 30 | int * p_index ,* p_pass ; 31 | 32 | // if( argc !=2) { 33 | // printf (" ./ uaf MODE \nMODE EASY =1\nMODE HARD !=1\n"); 34 | // return 0; 35 | // } 36 | 37 | p_global = (int *) malloc (sizeof (int)) ; 38 | * p_global = SECRET_PASS ; 39 | 40 | if( atoi (argv [1]) == MODE_EASY) { 41 | p_index = (int *) malloc (0xff) ; 42 | printf ("Give a number between 0 and 100\n") ; 43 | scanf ("%d", p_index) ; 44 | index_user (p_index) ; 45 | printf("freeing\n"); 46 | free (p_index) ; 47 | } else { 48 | printf ("Good luck !\n") ; 49 | } 50 | 51 | p_pass = (int *) malloc (0xaa) ; 52 | 53 | printf ("Give the secret\n") ; 54 | scanf ("%d",p_pass ) ; 55 | 56 | printf("pindex: %d",*p_index); 57 | printf("pass: %d",*p_pass); 58 | printf("glbl: %d",*p_global); 59 | 60 | if (* p_pass ==* p_global ) { 61 | printf ("Congrats !\n") ; 62 | } else { 63 | printf ("Sorry ...\n"); 64 | } 65 | 66 | return 0; 67 | } 68 | -------------------------------------------------------------------------------- /uaf-checker/tests/all/libvector.so: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BinaryAnalysisPlatform/bap-plugins/2e9aa5c7c24ef494d0e7db1b43c5ceedcb4196a8/uaf-checker/tests/all/libvector.so -------------------------------------------------------------------------------- /uaf-checker/tests/all/simpl-uaf-2.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | int * p_global; 5 | 6 | int main (int argc, char * argv[]) { 7 | int * p_global = malloc(4); 8 | *p_global = 100; 9 | int * p_local = malloc(4); 10 | 11 | // save global 12 | int * p_ptr_to_global = p_global; 13 | // overwrite global (aliases p_local) 14 | p_global = p_local; 15 | if (atoi(argv[1]) > 10) { 16 | // safety guaranteed on this path 17 | // restore global, safe is p_local is free'd 18 | p_global = p_ptr_to_global; 19 | } else { 20 | // but not here! UAF on this path 21 | } 22 | 23 | free(p_local); 24 | 25 | int use_after_free = *p_global; 26 | 27 | printf("Hi mom: %d\n", use_after_free); 28 | free(p_global); 29 | 30 | } 31 | -------------------------------------------------------------------------------- /uaf-checker/tests/all/simpl-uaf-arm: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BinaryAnalysisPlatform/bap-plugins/2e9aa5c7c24ef494d0e7db1b43c5ceedcb4196a8/uaf-checker/tests/all/simpl-uaf-arm -------------------------------------------------------------------------------- /uaf-checker/tests/all/simpl-uaf-arm-2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BinaryAnalysisPlatform/bap-plugins/2e9aa5c7c24ef494d0e7db1b43c5ceedcb4196a8/uaf-checker/tests/all/simpl-uaf-arm-2 -------------------------------------------------------------------------------- /uaf-checker/tests/all/simpl-uaf-arm-bug: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BinaryAnalysisPlatform/bap-plugins/2e9aa5c7c24ef494d0e7db1b43c5ceedcb4196a8/uaf-checker/tests/all/simpl-uaf-arm-bug -------------------------------------------------------------------------------- /uaf-checker/tests/all/simpl-uaf-bug.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | int * p_global; 5 | 6 | int main (int argc, char * argv[]) { 7 | int * p_global = malloc(4); 8 | *p_global = 100; 9 | int * p_local = malloc(4); 10 | 11 | // save global 12 | int * p_ptr_to_global = p_global; 13 | // overwrite global (aliases p_local) 14 | p_global = p_local; 15 | if (atoi(argv[1]) > 10) { 16 | // UAF on this path! 17 | } else { 18 | // safety guaranteed on this path 19 | // restore global, safe is p_local is free'd 20 | p_global = p_ptr_to_global; 21 | } 22 | 23 | free(p_local); 24 | 25 | int use_after_free = *p_global; 26 | 27 | printf("Hi mom: %d\n", use_after_free); 28 | free(p_global); 29 | 30 | } 31 | -------------------------------------------------------------------------------- /uaf-checker/tests/all/simpl-uaf.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | int * p_global; 5 | 6 | int main (int argc, char * argv[]) { 7 | int * p_global = malloc(4); 8 | *p_global = 100; 9 | int * p_local = malloc(4); 10 | 11 | // save global 12 | int * p_ptr_to_global = p_global; 13 | // overwrite global (aliases p_local) 14 | p_global = p_local; 15 | if (atoi(argv[1]) > 10) { 16 | // do not restore global, if p_local is free'd, a use of p_global 17 | // will be bad, since p_global aliases p_local 18 | printf("something\n"); // currently a bug in bap, needs content to create block for microx to follow 19 | } else { 20 | // restore global, safe is p_local is free'd 21 | p_global = p_ptr_to_global; 22 | } 23 | 24 | free(p_local); 25 | 26 | int use_after_free = *p_global; 27 | 28 | printf("Hi mom: %d\n", use_after_free); 29 | free(p_global); 30 | 31 | } 32 | -------------------------------------------------------------------------------- /uaf-checker/tests/all/simple-uaf-2.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | int * p_global; 5 | 6 | int main (int argc, char * argv[]) { 7 | int * p_global = malloc(4); 8 | *p_global = 100; 9 | int * p_local = malloc(4); 10 | 11 | // save global 12 | int * p_ptr_to_global = p_global; 13 | // overwrite global (aliases p_local) 14 | p_global = p_local; 15 | if (atoi(argv[1]) > 10) { 16 | // do not restore global, if p_local is free'd, a use of p_global 17 | // will be bad, since p_global aliases p_local 18 | printf("something\n"); // currently a bug in bap, needs content to create block for microx to follow 19 | } else { 20 | // restore global, safe is p_local is free'd 21 | p_global = p_ptr_to_global; 22 | } 23 | 24 | free(p_local); 25 | 26 | int use_after_free = *p_global; 27 | 28 | printf("Hi mom: %d\n", use_after_free); 29 | free(p_global); 30 | 31 | } 32 | -------------------------------------------------------------------------------- /uaf-checker/tests/all/structs: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BinaryAnalysisPlatform/bap-plugins/2e9aa5c7c24ef494d0e7db1b43c5ceedcb4196a8/uaf-checker/tests/all/structs -------------------------------------------------------------------------------- /uaf-checker/tests/all/structs-2.c: -------------------------------------------------------------------------------- 1 | /* 2 | In this test, we hide the explicit mallocs in a separate library, which we won't 3 | expose to BAP. We will manually free the entries however, like in gnu-nettool. 4 | */ 5 | 6 | /* Also: bug in conqueror on return values */ 7 | 8 | #include 9 | #include 10 | #include "vector.h" 11 | 12 | void free_vector (struct Vector *vector) { 13 | if(vector != NULL) { 14 | free(vector->data); 15 | free(vector); 16 | } 17 | } 18 | 19 | int main(void) { 20 | struct Vector *v = malloc_vector(sizeof(struct Vector)); 21 | free_vector(v); 22 | 23 | printf("%f\n", *v->data); 24 | } 25 | -------------------------------------------------------------------------------- /uaf-checker/tests/all/structs-arm: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BinaryAnalysisPlatform/bap-plugins/2e9aa5c7c24ef494d0e7db1b43c5ceedcb4196a8/uaf-checker/tests/all/structs-arm -------------------------------------------------------------------------------- /uaf-checker/tests/all/structs-arm-2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BinaryAnalysisPlatform/bap-plugins/2e9aa5c7c24ef494d0e7db1b43c5ceedcb4196a8/uaf-checker/tests/all/structs-arm-2 -------------------------------------------------------------------------------- /uaf-checker/tests/all/structs.c: -------------------------------------------------------------------------------- 1 | /* 2 | In this test, we see both explicit mallocs, and so detect the use in printf. 3 | In the second example, we will see only one malloc which wraps the real malloc (as with the gnu-nettool example). 4 | In this case, we need to track explicitly the sizes of the original malloc, if we have any hopes of accurately detecting a free. 5 | */ 6 | 7 | /* Also: bug in conqueror on return values */ 8 | 9 | #include 10 | #include 11 | 12 | struct Vector { 13 | double *data; 14 | size_t size; 15 | }; 16 | 17 | struct Vector *newVector (size_t sz) { 18 | // Try to allocate vector structure. 19 | struct Vector *retVal = malloc (sizeof (struct Vector)); 20 | // if (retVal == NULL) 21 | // return NULL; 22 | 23 | // Try to allocate vector data, free structure if fail. 24 | retVal->data = malloc (sz * sizeof (double)); 25 | // if (retVal->data == NULL) { 26 | // free (retVal); 27 | // return NULL; 28 | // } 29 | 30 | // Set size and return. 31 | retVal->size = sz; 32 | return retVal; 33 | } 34 | 35 | void delVector (struct Vector *vector) { 36 | // Can safely assume vector is NULL or fully built. 37 | if (vector != NULL) { 38 | free (vector->data); 39 | free (vector); 40 | } 41 | } 42 | 43 | int main(void) { 44 | struct Vector *v = newVector(1); 45 | delVector(v); 46 | 47 | printf("%f\n", *v->data); 48 | } 49 | 50 | /* 51 | vagrant@vagrant-ubuntu-trusty-64:~/rvantonder-bap-plugins/uaf/tests/all$ gcc -o structs structs.c 52 | vagrant@vagrant-ubuntu-trusty-64:~/rvantonder-bap-plugins/uaf/tests/all$ valgrind --tool=memcheck ./structs 53 | ==27309== Memcheck, a memory error detector 54 | ==27309== Copyright (C) 2002-2013, and GNU GPL'd, by Julian Seward et al. 55 | ==27309== Using Valgrind-3.10.0.SVN and LibVEX; rerun with -h for copyright info 56 | ==27309== Command: ./structs 57 | ==27309== 58 | ==27309== Invalid read of size 8 59 | ==27309== at 0x400686: main (in /home/vagrant/rvantonder-bap-plugins/uaf/tests/all/structs) 60 | ==27309== Address 0x51fc040 is 0 bytes inside a block of size 16 free'd 61 | ==27309== at 0x4C2BDEC: free (in /usr/lib/valgrind/vgpreload_memcheck-amd64-linux.so) 62 | ==27309== by 0x40065D: delVector (in /home/vagrant/rvantonder-bap-plugins/uaf/tests/all/structs) 63 | ==27309== by 0x400681: main (in /home/vagrant/rvantonder-bap-plugins/uaf/tests/all/structs) 64 | ==27309== 65 | ==27309== Invalid read of size 8 66 | ==27309== at 0x400689: main (in /home/vagrant/rvantonder-bap-plugins/uaf/tests/all/structs) 67 | ==27309== Address 0x51fc090 is 0 bytes inside a block of size 8 free'd 68 | ==27309== at 0x4C2BDEC: free (in /usr/lib/valgrind/vgpreload_memcheck-amd64-linux.so) 69 | ==27309== by 0x400651: delVector (in /home/vagrant/rvantonder-bap-plugins/uaf/tests/all/structs) 70 | ==27309== by 0x400681: main (in /home/vagrant/rvantonder-bap-plugins/uaf/tests/all/structs) 71 | ==27309== 72 | 0.000000 73 | ==27309== 74 | ==27309== HEAP SUMMARY: 75 | ==27309== in use at exit: 0 bytes in 0 blocks 76 | ==27309== total heap usage: 2 allocs, 2 frees, 24 bytes allocated 77 | ==27309== 78 | ==27309== All heap blocks were freed -- no leaks are possible 79 | ==27309== 80 | ==27309== For counts of detected and suppressed errors, rerun with: -v 81 | ==27309== ERROR SUMMARY: 2 errors from 2 contexts (suppressed: 0 from 0) 82 | */ 83 | -------------------------------------------------------------------------------- /uaf-checker/tests/all/super-simpl-uaf-arm: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BinaryAnalysisPlatform/bap-plugins/2e9aa5c7c24ef494d0e7db1b43c5ceedcb4196a8/uaf-checker/tests/all/super-simpl-uaf-arm -------------------------------------------------------------------------------- /uaf-checker/tests/all/super-simpl-uaf.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | int * p_global; 5 | 6 | int main (int argc, char * argv[]) { 7 | int * p_global = malloc(4); 8 | *p_global = 100; 9 | int * p_local = malloc(4); 10 | 11 | // save global 12 | int * p_ptr_to_global = p_global; 13 | // overwrite global (aliases p_local) 14 | p_global = p_local; 15 | 16 | free(p_local); 17 | 18 | int use_after_free = *p_global; 19 | 20 | printf("Hi mom: %d\n", use_after_free); 21 | } 22 | -------------------------------------------------------------------------------- /uaf-checker/tests/all/uaf-no-assign-arm: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BinaryAnalysisPlatform/bap-plugins/2e9aa5c7c24ef494d0e7db1b43c5ceedcb4196a8/uaf-checker/tests/all/uaf-no-assign-arm -------------------------------------------------------------------------------- /uaf-checker/tests/all/uaf-no-assign.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | int main (int argc, char * argv[]) { 5 | int * foo = malloc(4); 6 | free(foo); 7 | 8 | if (*foo) 9 | printf("Hi\n"); 10 | } 11 | -------------------------------------------------------------------------------- /uaf-checker/tests/all/vector.c: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | struct Vector { 4 | size_t size; 5 | double *data; 6 | }; 7 | 8 | struct Vector *malloc_vector (size_t sz) { 9 | // Try to allocate vector structure. 10 | struct Vector *retVal = malloc (sizeof (struct Vector)); 11 | // if (retVal == NULL) 12 | // return NULL; 13 | 14 | // Try to allocate vector data, free structure if fail. 15 | retVal->data = malloc (sz * sizeof (double)); 16 | // if (retVal->data == NULL) { 17 | // free (retVal); 18 | // return NULL; 19 | // } 20 | 21 | // Set size and return. 22 | retVal->size = sz; 23 | return retVal; 24 | } 25 | 26 | void lib_free_vector (struct Vector *vector) { 27 | // Can safely assume vector is NULL or fully built. 28 | if (vector != NULL) { 29 | free (vector->data); 30 | free (vector); 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /uaf-checker/tests/all/vector.h: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | struct Vector { 4 | size_t size; 5 | double *data; 6 | }; 7 | 8 | struct Vector *malloc_vector(size_t sz); 9 | 10 | void lib_free_vector(struct Vector *vector); 11 | -------------------------------------------------------------------------------- /uaf-checker/toida.ml: -------------------------------------------------------------------------------- 1 | open Core_kernel 2 | open Bap.Std 3 | open Uaf_error 4 | 5 | let code_of_color = function 6 | | `green -> 0x00ff00 7 | | `red -> 0x0000ff 8 | | `yellow -> 0x00FFFF 9 | | _ -> invalid_arg "unexpected color" 10 | 11 | let comment_cmd addr c = 12 | sprintf "MakeComm(0x%x, %S)\n" (Addr.to_int addr |> ok_exn) c 13 | 14 | let color_cmd addr c = 15 | sprintf "idaapi.set_item_color(DecodeInstruction(0x%x).ea, 0x%x)\n" 16 | (Addr.to_int addr |> ok_exn) 17 | (code_of_color c) 18 | 19 | let output filename addrs = 20 | Out_channel.with_file filename ~f:(fun chan -> 21 | fprintf chan "from idautils import *\n\ 22 | Wait()\n"; 23 | let attrs = [(`red,"~use"); (`yellow,"~free") ; (`green,"~alloc")] in 24 | List.iter2_exn addrs attrs ~f:(fun addr (color,comment) -> 25 | fprintf chan "%s" (color_cmd addr color); 26 | fprintf chan "%s" (comment_cmd addr comment))) 27 | 28 | let output_all filename errors proj = 29 | let open Option in 30 | if List.length errors > 0 then 31 | Out_channel.with_file filename ~f:(fun chan -> 32 | fprintf chan "from idautils import *\n\ 33 | Wait()\n"; 34 | List.iter errors ~f:(fun error -> 35 | (let (!) tid = Program.lookup def_t (Project.program proj) tid 36 | |> fun x -> Option.value_exn x in 37 | Term.get_attr !(error.use_tid) address >>= 38 | fun use_addr -> 39 | Term.get_attr !(error.free_tid) address >>= 40 | fun free_addr -> 41 | Term.get_attr !(error.alloc_tid) address >>= 42 | fun alloc_addr -> 43 | let addrs = [use_addr; free_addr; alloc_addr] in 44 | let attrs = [(`red,"~use"); (`yellow,"~free") ; (`green,"~alloc")] in 45 | List.iter2_exn addrs attrs ~f:(fun addr (color,comment) -> 46 | fprintf chan "%s" (color_cmd addr color); 47 | fprintf chan "%s" (comment_cmd addr comment)); 48 | return ()) |> ignore)) 49 | -------------------------------------------------------------------------------- /uaf-checker/uaf_error.ml: -------------------------------------------------------------------------------- 1 | open Core_kernel 2 | open Bap.Std 3 | open Format 4 | open Options 5 | 6 | type t = { 7 | trace : tid list; 8 | addr : addr; 9 | use_tid : tid; 10 | free_tid : tid; 11 | alloc_tid : tid; 12 | } 13 | 14 | let callstack prog error = 15 | let trace = error.trace in 16 | List.fold ~init:[] trace ~f:(fun acc tid -> 17 | (match Program.lookup sub_t prog tid with 18 | | Some sub -> (Sub.name sub) :: acc 19 | | None -> acc) |> fun acc -> 20 | if Tid.(error.alloc_tid = tid) then "~ALLOC~" :: acc 21 | else if Tid.(error.free_tid = tid) then "~FREE~" :: acc 22 | else if Tid.(error.use_tid = tid) then "~USE~" :: acc 23 | else acc) 24 | 25 | let print_callstack proj options error = 26 | if options.verbose then 27 | let prog = Project.program proj in 28 | List.iter (callstack prog error) ~f:(fun sub -> printf "%s\n%!" sub) 29 | 30 | let print_trace options error = 31 | if options.verbose then 32 | (printf "=-=-=-=-=-=-=-= DUMP -=-=-=-=-=-=-=-\n"; 33 | printf "Trace\n%!"; 34 | List.iter error.trace ~f:(fun tid -> printf "%a@." Tid.pp tid)) 35 | 36 | (** Does not print trace by default *) 37 | let pp ppf e = 38 | fprintf ppf 39 | "UAF detected!@.\ 40 | Address: %a@.\ 41 | Used at: %a@.\ 42 | Free'd at: %a@.\ 43 | Alloc'd at: %a@." 44 | Addr.pp e.addr 45 | Tid.pp e.use_tid 46 | Tid.pp e.free_tid 47 | Tid.pp e.alloc_tid 48 | 49 | let to_string e = 50 | asprintf "%a" pp e 51 | 52 | let pps () e = to_string e 53 | -------------------------------------------------------------------------------- /uaf-checker/uaf_error.mli: -------------------------------------------------------------------------------- 1 | open Bap.Std 2 | open Format 3 | open Options 4 | 5 | type t = { 6 | trace : tid list; 7 | addr : addr; 8 | use_tid : tid; 9 | free_tid : tid; 10 | alloc_tid : tid 11 | } 12 | 13 | val callstack : Program.t -> t -> string list 14 | val print_callstack : Project.t -> Options.t -> t -> unit 15 | val print_trace : Options.t -> t -> unit 16 | val to_string : t -> string 17 | val pp : formatter -> t -> unit 18 | val pps : unit -> t -> string 19 | -------------------------------------------------------------------------------- /uaf-checker/visual.ml: -------------------------------------------------------------------------------- 1 | open Core_kernel 2 | open Bap.Std 3 | open Uaf_error 4 | open Poly 5 | 6 | class marker (error : Uaf_error.t) = object(self) 7 | inherit Term.mapper as super 8 | method! map_term cls t = 9 | super#map_term cls t |> fun t -> 10 | (* Highlight everything visited in the trace *) 11 | (if List.mem ~equal:Tid.equal error.trace (Term.tid t) 12 | then Term.set_attr t foreground `cyan 13 | else t) |> fun t -> 14 | (* Highlight use site *) 15 | (if Term.tid t = error.use_tid 16 | then Term.set_attr t background `red 17 | |> fun t -> Term.set_attr t foreground `white 18 | else t) |> fun t -> 19 | (* Highlight free site *) 20 | (if Term.tid t = error.free_tid 21 | then Term.set_attr t background `yellow 22 | |> fun t -> Term.set_attr t foreground `white 23 | else t) |> fun t -> 24 | (* Highlight alloc site *) 25 | if Term.tid t = error.alloc_tid 26 | then Term.set_attr t background `green 27 | |> fun t -> Term.set_attr t foreground `white 28 | else t 29 | end 30 | 31 | let tag_visited proj error = 32 | let marker = new marker error in 33 | Project.program proj |> marker#run |> Project.with_program proj 34 | -------------------------------------------------------------------------------- /validate_cfg/validate_cfg.ml: -------------------------------------------------------------------------------- 1 | open Core_kernel 2 | open Graphlib.Std 3 | open Bap.Std 4 | 5 | let main proj = 6 | let prog = Project.program proj in 7 | Term.enum sub_t prog |> Seq.iter ~f:(fun sub -> 8 | let cfg = Sub.to_graph sub in 9 | let name = Sub.name sub in 10 | match Term.first blk_t sub with 11 | | None -> 12 | eprintf "sub %s is empty\n%!" name; 13 | | Some blk -> 14 | let n = Graphlib.fold_reachable (module Graphs.Tid) 15 | ~init:0 ~f:(fun s _ -> s + 1) cfg (Term.tid blk) in 16 | if n <> Term.length blk_t sub 17 | then eprintf "sub %s has unreachable blocks\n%!" name) 18 | 19 | let () = Project.register_pass' main 20 | --------------------------------------------------------------------------------