123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412(**************************************************************************)(* This file is part of BINSEC. *)(* *)(* Copyright (C) 2016-2026 *)(* CEA (Commissariat à l'énergie atomique et aux énergies *)(* alternatives) *)(* *)(* you can redistribute it and/or modify it under the terms of the GNU *)(* Lesser General Public License as published by the Free Software *)(* Foundation, version 2.1. *)(* *)(* It is distributed in the hope that it will be useful, *)(* but WITHOUT ANY WARRANTY; without even the implied warranty of *)(* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *)(* GNU Lesser General Public License for more details. *)(* *)(* See the GNU Lesser General Public License version 2.1 *)(* for more details (enclosed in the file licenses/LGPLv2.1). *)(* *)(**************************************************************************)openTypesopenOptionsmoduleV=Virtual_addressmoduleA=structinclude Dba_types.Caddressletof_jump_targeta=functionDba.JInnern->reidan|Dba.JOutert->tendmoduleI=Dba.InstrmoduleE=Dba.ExprmoduleD=DirectivemoduleG=Path_generatormoduleN=Binsec_base.Basic_types.Integers.IntmoduleS=structincludeStatusletpp_expectingppf=function|Unknown->()|t->Format.pp_print_stringppf" expecting ";ppppftletpp_expectedppf=function|Unknown->()|t->Format.pp_print_stringppf" (expected ";ppppft;Format.pp_print_charppf')'endmodulePredicate=structtypet={addr:A.t;mnemonic:string;test:Dba.Expr.t;mutablestatus:S.t;expect:S.t;}endmoduleStat=structletground_truth=reffalseletall_predicates=ref0letdeemed_opaques=ref0lettrue_positives=ref0letfalse_positives=ref0letwrong_positives=ref0letdeemed_clear=ref0lettrue_negatives=ref0letfalse_negatives=ref0letdeemed_unreachable=ref0lettrue_unreachable=ref0letfalse_unreachable=ref0letfailure=ref0letall_paths=ref0typetime={mutablesec:float}letsearch_time={sec=0.}leteval_time={sec=0.}lettotal_time={sec=0.}endmoduleEval=structletrunstate(a,c,j)=incrStat.all_paths;lett=Unix.gettimeofday()inletpath=Path.init~env:state~conditions:c~jump_targets:jinletr=Driver.startpathainStat.eval_time.sec<-Stat.eval_time.sec+.Unix.gettimeofday()-.t;(r,path)endmoduleRunner()=structletcookie=Path.State.Cookie.default()let()=Option.iter(funf->fcookie(Smt_options.backend(Smt_options.Solver.get())))(Path.State.moreSymbolic.State.SetSMTSolver);Option.iter(funt->Option.iter(funf->fcookiet)(Path.State.moreSymbolic.State.SetSMTSolverTimeout))(Smt_options.Timeout.get_opt());Option.iter(funp->Option.iter(funf->fcookiep)(Path.State.moreSymbolic.State.SetSMTDumpDir))(Smt_options.DumpDir.get_opt())letrecloop:Env.t->S.t->'alist->S.t=funstatestatuspaths->Logger.debug~level:2"Current status: %a"S.ppstatus;matchpathswith|[]->status|p::paths->(matchEval.runstatepwith|ErrorUnsatisfiable_assumption,_->loopstatestatuspaths|Error_,_->Unknown|Oktest,path->(letsym=Path.statepathinmatchstatuswith|Opaqueb->(match(Path.is_zero_vpathtest,b)with|True,false|False,true->loopstatestatuspaths|False,false|True,true->Clear|Unknown,_->(letsym=Option.get(Path.State.assume(ifbthenPath.Value.unaryNottestelsetest)sym)inmatchPath.State.check_satcookiesymwith|exceptionSymbolic.State.Unknown->Unknown|None->loopstatestatuspaths|Some_->Clear))|_(* i.e. Unreachable *)->(matchPath.is_zero_vpathtestwith|True->loopstate(Opaquefalse)paths|False->loopstate(Opaquetrue)paths|Unknown->(letenum=Path.State.enumeratecookietestsyminmatchPath.State.Enumeration.nextenumwith|exceptionSymbolic.State.Unknown->Unknown|None->loopstateUnreachablepaths|Some(bv,_)->(matchPath.State.Enumeration.nextenumwith|exceptionSymbolic.State.Unknown->Unknown|None->loopstate(Opaque(Bitvector.to_boolbv))paths|Some_->Path.State.Enumeration.suspendenum;Clear)))))letrunstaten(p:Predicate.t)=letk=Unix.gettimeofday()inLogger.debug"Running BB-SSE at %a (%a)%a"A.pp_basep.addrDba_printer.Ascii.pp_bl_termp.testS.pp_expectingp.expect;lett=Unix.gettimeofday()inletpaths=G.enumerate_pathstatenp.addrinLogger.debug"Found %a paths"(funppfpaths->Format.pp_print_intppf(List.lengthpaths))paths;Stat.search_time.sec<-Stat.search_time.sec+.Unix.gettimeofday()-.t;(matchloopstateUnreachablepathswith|Unknown->p.status<-Unknown;Logger.debug"Failed to handle predicate %a at %a%a"Dba_printer.Ascii.pp_bl_termp.testA.pp_basep.addrS.pp_expectedp.expect|status->p.status<-status;Logger.debug"Predicate %a at %a deemed %a%a"Dba_printer.Ascii.pp_bl_termp.testA.pp_basep.addrS.ppstatusS.pp_expectedp.expect);Stat.total_time.sec<-Stat.total_time.sec+.Unix.gettimeofday()-.kletppppf()=letopenStatinFormat.pp_open_vboxppf2;Format.fprintfppf"total predicate %d"!all_predicates;Format.pp_print_spaceppf();Format.pp_open_vboxppf2;Format.fprintfppf"total opaques predicates %d"(!deemed_opaques+!true_positives+!false_positives+!wrong_positives);if!ground_truththen(Format.pp_print_spaceppf();Format.fprintfppf"predicates deemed opaque %d"!deemed_opaques;Format.pp_print_spaceppf();Format.fprintfppf"true positives %d"!true_positives;Format.pp_print_spaceppf();Format.fprintfppf"false positives %d"!false_positives;Format.pp_print_spaceppf();Format.fprintfppf"wrong positives %d"!wrong_positives);Format.pp_close_boxppf();Format.pp_print_spaceppf();Format.pp_open_vboxppf2;Format.fprintfppf"total unreachable predicates %d"(!deemed_unreachable+!true_unreachable+!false_unreachable);if!ground_truththen(Format.pp_print_spaceppf();Format.fprintfppf"predicates deemed unreachable %d"!deemed_unreachable;Format.pp_print_spaceppf();Format.fprintfppf"true positives %d"!true_unreachable;Format.pp_print_spaceppf();Format.fprintfppf"false positives %d"!false_unreachable);Format.pp_close_boxppf();Format.pp_print_spaceppf();Format.pp_open_vboxppf2;Format.fprintfppf"total clear predicates %d"(!deemed_clear+!true_negatives+!false_negatives);if!ground_truththen(Format.pp_print_spaceppf();Format.fprintfppf"predicates deemed clear %d"!deemed_clear;Format.pp_print_spaceppf();Format.fprintfppf"true negatives %d"!true_negatives;Format.pp_print_spaceppf();Format.fprintfppf"false negatives %d"!false_negatives);Format.pp_close_boxppf();Format.pp_print_spaceppf();Format.fprintfppf"total unfinished (timeout) %d"!failure;Format.pp_close_boxppf();Format.pp_print_spaceppf();Format.pp_open_vboxppf0;Format.pp_print_spaceppf();if!ground_truththenFormat.fprintfppf"@[<h> search cumulative time %fs@]@,"search_time.sec;Format.fprintfppf"@[<h>total explored paths %d@]@,"!all_paths;if!ground_truththenFormat.fprintfppf"@[<h> evaluation cumulative time %fs@]@,"eval_time.sec;Format.fprintfppf"@[<h>total satisfiability queries %d@]@,"(Query_stat.Solver.getSat+Query_stat.Solver.getUnsat+Query_stat.Solver.getUnknown);if!ground_truththenFormat.fprintfppf" @[<h> preprocessing cumulative time %fs@]@,\
@[<h> solving cumulative time %fs@]@,"(Query_stat.Preprocess.Timer.get())(Query_stat.Solver.Timer.get());Format.fprintfppf"@[<h> total cumulative time %fs@]@,@]"total_time.secletrecloopstateboundsnpredicatesi=lets=Array.lengthpredicatesinifi<sthen(letp:Predicate.t=Array.getpredicatesiin(matchp.statuswith|Unknown|Clear->Logger.debug~level:0"Analyzing the target %d / %d (%d step%s)"(i+1)s(n+1)(ifn=0then""else"s");runstatenp|Opaque_|Unreachable->());loopstateboundsnpredicates(i+1))elseifnot(N.Set.is_emptybounds)thenletn=N.Set.min_eltboundsinloopstate(N.Set.removenbounds)(n-1)predicates0elseletopenStatinArray.iteri(funi(p:Predicate.t)->incrall_predicates;Logger.result"%d / %d: Predicate %s at %a deemed %a%a"(i+1)sp.mnemonicA.pp_basep.addrS.ppp.statusS.pp_expectedp.expect;match(p.expect,p.status)with|Unknown,Unknown|Clear,Unknown|Opaque_,Unknown|Unreachable,Unknown->incrfailure|Unknown,Unreachable->incrdeemed_unreachable|Unknown,Opaque_->incrdeemed_opaques|Unknown,Clear->incrdeemed_clear|Clear,Clear->incrtrue_negatives|Clear,Opaque_->incrfalse_positives|Clear,Unreachable->incrfalse_unreachable|Opaque_,Clear|Unreachable,Clear->incrfalse_negatives|Opaquetrue,Opaquetrue|Opaquefalse,Opaquefalse->incrtrue_positives|Unreachable,Unreachable->incrtrue_unreachable|Opaque_,Opaque_|Unreachable,Opaque_->incrwrong_positives|Opaque_,Unreachable->incrfalse_unreachable)predicates;Logger.info"%a"pp()endletfind_jumpsfcfg=letjump_targets=Array.of_list(Ghidra_cfg.fold_vertex(funvjumps->iffvthenmatchGhidra_cfg.succ_ecfgvwith|[(_,Branch,_);(_,Fallthrough,_)]|[(_,Fallthrough,_);(_,Branch,_)]->v::jumps|_->jumpselsejumps)cfg[])inArray.sortV.comparejump_targets;jump_targetsletrun()=ifis_enabled()||FindAllJumps.get()||FindJumpsBetween.is_set()then(letcfg,ims=Ghidra_cfg.import()in(* calls to process, jumps to skip, ground truth *)letctp,jts,gt=matchDirectives.get_opt()with|None->(V.Set.empty,V.Set.empty,V.Map.empty)|Somepath->Logger.debug"Reading directives from %s"path;letic=open_inpathinList.fold_left(fun(ctp,jts,gt)->function|D.ExpectAt(v,x)->(ctp,jts,V.Map.addvxgt)|D.SkipJumpv->(ctp,V.Set.addvjts,gt)|D.ProcessCallv->(V.Set.addvctp,jts,gt))(V.Set.empty,V.Set.empty,V.Map.empty)(Parser.directivesLexer.token(Lexing.from_channelic))inletctp=Binsec_base.Basic_types.Integers.Int.Set.fold(funactp->V.Set.add(V.createa)ctp)(CallsToProceed.get())ctpinStat.ground_truth:=not(V.Map.is_emptygt);letjump_targets=ifFindAllJumps.get()thenfind_jumps(funv->not(V.Set.memvjts))cfgelsematchFindJumpsBetween.get()with|[]->(matchKernel_functions.get_ep()with|None->Logger.fatal"Please specify a start address"|Somev->[|v|])|[lo;hi]->letstart,stop=(Virtual_address.createlo,Virtual_address.createhi)infind_jumps(funv->V.comparevstart>0&&V.comparevstop<0&¬(V.Set.memvjts))cfg|_->Logger.fatal"Find-jumps expects exactly two addresses."inletbbt=A.Htbl.create(Ghidra_cfg.nb_vertexcfg)(* rough estimation *)anddis=A.Htbl.create(Ghidra_cfg.nb_vertexcfg)(* rough estimation *)anddap=A.Htbl.create(Ghidra_cfg.nb_edgescfg)(* rough estimation *)andopa=A.Htbl.create(Array.lengthjump_targets)in(* Corner case: add a default source if executable entry point
has a predecessor *)letentry=Loader.Img.entry(Kernel_functions.get_img())inifGhidra_cfg.in_degreecfgentry<>0then(letroot=Virtual_address.create0inassert(not(V.equalrootentry));Ghidra_cfg.add_edge_ecfg(root,Fallthrough,entry);letroot=A.of_virtual_addressrootandentry=A.of_virtual_addressentryinA.Htbl.adddapentry[root];A.Htbl.adddisroot(I.static_jump(JOuterentry)));letstate:Env.t={cfg;ims;ctp;bbt;dis;dap;opa}inletbounds=matchMaxBB.get()with[]->N.Set.singleton1|l->N.Set.of_listlinletn=N.Set.min_eltboundsinifn<1thenLogger.fatal"The number of basic blocks should be greater or equal to 1.";letbounds=N.Set.removenboundsinletpredicates=Array.map(funv->tryletaddr,test=G.find_conditionstatevinPredicate.{addr;mnemonic=V.Htbl.findstate.imsv;test;status=S.Unknown;expect=(tryV.Map.findvgtwithNot_found->S.Unknown);}withNot_found->Logger.fatal"Can not find a conditional jump at %a."V.ppv)jump_targetsinletmoduleR=Runner()inR.loopstatebounds(n-1)predicates0)let_=Cli.Boot.enlist~name:"BB-SSE"~f:run