123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825(**************************************************************************)(* 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). *)(* *)(**************************************************************************)openDbaopenErrorsopenFormatopenDisasm_optionsmoduleCfg=Instr_cfgmoduleProgram=structtypet={instructions:Cfg.t;callsites:Virtual_address.Set.t;entrypoints:Virtual_address.Set.t;unresolved_jumps:Virtual_address.Set.t;}letempty={instructions=Cfg.create1;callsites=Virtual_address.Set.empty;entrypoints=Virtual_address.Set.empty;unresolved_jumps=Virtual_address.Set.empty;}letcreate?(callsites=Virtual_address.Set.empty)?(entrypoints=Virtual_address.Set.empty)?(unresolved_jumps=Virtual_address.Set.empty)instructions={instructions;callsites;entrypoints;unresolved_jumps}leton_instructionsfp={pwithinstructions=fp.instructions}leton_callsitesfp={pwithcallsites=fp.callsites}letis_callsitevaddrp=Virtual_address.Set.memvaddrp.callsitesletadd_callsitepcallsite=on_callsites(Virtual_address.Set.addcallsite)pletadd_callsitespcallsites=Virtual_address.Set.fold(funcp->add_callsitepc)callsitespletadd_unresolved_jumppaddress={pwithunresolved_jumps=Virtual_address.Set.addaddressp.unresolved_jumps;}letppf_tag_functionsppf=letprint_open_stag_=()andprint_close_stag=function|String_tag"function"->fprintfppf"@;<8 0> ; <_fun>"|_->()inletmark_open_stag=function|String_tag"function"->ifLogger.get_color()then"\027[0;36m"else""|_->""andmark_close_stag=function|String_tag"function"->ifLogger.get_color()then"\027[0m"else""|_->""in{mark_open_stag;mark_close_stag;print_open_stag;print_close_stag}letpp_no_dbappfv=matchCfg.V.instvwith|None->()|Someinst->letvaddr=Cfg.V.addrvinletbinstr=Instruction.to_generic_instructioninstinletopcode_str=asprintf"%a"Instruction.Generic.pp_opcodebinstr|>String.trimin(* The X86 standard says in 2.3.11:
- The maximum length of an Intel 64 and IA-32 instruction
remains 15 bytes.
Assuming the opcode is made of groups of 2 nibbles (1 byte),
separated by 1 space, the max string length is computed to be:
2 * 15 + 15 / 2 = 38
Adjust (upwards) the value whenever other assembly languages
have higher requirements. *)fprintfppf"%a@ %-38s@ %a"Virtual_address.ppvaddropcode_strInstruction.Generic.pp_mnemonicbinstrletppppfp=pp_set_formatter_stag_functionsppf(ppf_tag_functionsppf);pp_set_mark_tagsppftrue;pp_set_print_tagsppftrue;fprintfppf"@[<v 0>";Cfg.iter_vertex_by_address(funv->letvaddr=Cfg.V.addrvinlettag_string=ifis_callsitevaddrpthen"function"else""infprintfppf"@[<h>@{<%s>%a@}@]@ "tag_stringpp_no_dbav)p.instructions;fprintfppf"@]";pp_set_mark_tagsppffalse;pp_set_print_tagsppffalseletcount_program_instructionsp=leth=Hashtbl.create107inletincrease_countmnemonic=matchHashtbl.findhmnemonicwith|n->Hashtbl.replacehmnemonic(n+1)|exceptionNot_found->Hashtbl.addhmnemonic1inCfg.iter_vertex(funv->matchCfg.V.instvwith|None->()|Someinst->increase_countinst.Instruction.mnemonic)p.instructions;hletpp_mnemonic_summaryppfp=lettbl=count_program_instructionspinletordered=Hashtbl.fold(funmnemoniccountl->(mnemonic,count)::l)tbl[]|>(* Sorting in decreasing order *)List.sort(fun(_,c1)(_,c2)->comparec2c1)infprintfppf"@[<v 0>Different instruction count:%d@ %a@]"(Hashtbl.lengthtbl)(funppfl->List.iter(fun(m,c)->(* FIXME: Would be nicer to use tabulation boxes below *)lets=asprintf"%a"Mnemonic.ppminfprintfppf"@[<h>%-50s@ %d@]@ "sc)l)orderedletpp_dbappfp=fprintfppf"@[<v 0>%a@]"(funppfp->Cfg.iter_vertex_by_address(funv->matchCfg.V.instvwith|None->()|Someinst->letdhunk=inst.Instruction.dba_blockinfprintfppf"@[<v 0>@[<h># -- %a@]@ %a@]@ @ "pp_no_dbavDhunk.ppdhunk)p.instructions)pletpp_detailsppfp=letpp_settitleppfs=ifnot(Virtual_address.Set.is_emptys)then(letsize=Virtual_address.Set.cardinalsinpp_open_vboxppf2;fprintfppf"## %s (%d)@,"titlesize;pp_open_hovboxppf0;Virtual_address.Set.iter(funvaddr->fprintfppf"%a;@ "Virtual_address.ppvaddr)s;pp_close_boxppf();pp_close_boxppf();pp_print_cutppf())infprintfppf"@[<v 0>%a%a%a@]"(pp_set"Entry points")p.entrypoints(pp_set"Functions")p.callsites(pp_set"Unresolved jumps")p.unresolved_jumpsendopenProgram(* Should it be here ? *)letsimplify_block=Dhunk.constant_propagation(* Add a block to the program in construction.
This block is simplified.
*)letadd_blockinstrp=lethw_address=instr.Instruction.addressinletblock=instr.Instruction.dba_blockinletsimplified_block=simplify_blockblockinletinstr'=Instruction.set_dba_blockinstrsimplified_blockinCfg.add_instphw_addressinstr';pletjoin_wlwl1wl2b1b2=List.fold_left(funacca->letbv=Virtual_address.to_bigint(Dba_types.Caddress.base_valuea)inifZ.gtb1bv||Z.gtbvb2thena::accelseacc)wl1wl2(* FIXME: Use new blocks *)letextra_infodinstr=letrecaux=function|[]->([],None)|[(_,Dba.Instr.SJump(JOuterdst,(Dba.Calladd_retastag)))]->([dst;add_ret],Some(None,Somedst,tag))|[(_,Dba.Instr.SJump(JOuterdst,tag))]->([dst],Some(None,Somedst,tag))|[(_,Dba.Instr.DJump(dst,(Dba.Calladd_retastag)))]->([add_ret],Some(Somedst,None,tag))|[(_,Dba.Instr.DJump(dst,tag))]->([],Some(Somedst,None,tag))|[(_,Dba.Instr.If(_,JOuterthn,_));(_,Dba.Instr.SJump(JOuternextaddr,_));]->([thn;nextaddr],None)|[(_,Dba.Instr.If_);(_,Dba.Instr.SJump(JOuternextaddr,_))]->([nextaddr],None)|[_]|[_;(_,Dba.Instr.Stop_)]->([],None)(* no recurive successors *)|_::insns->auxinsnsinletinstlist=letopenInstructioninletaddr=dinstr.addressinletblock=dinstr.dba_blockinDba_types.(Dhunk.to_stmtsblockaddr|>List.map(funlocinstr->(Statement.locationlocinstr,Statement.instructionlocinstr)))inauxinstlistletfind_callsinstrjumps=letnexts,tag=extra_infoinstrinletcalls=matchtagwith|None|Some(_,_,(Default|Dba.Return))|Some(None,None,Dba.Call_)->[]|Some(None,Somea,Dba.Call_)->[a]|Some(Some_,None,Dba.Call_)->(letcur_addr=Dba_types.Caddress.block_startinstr.Instruction.addressinmatchDba_types.Caddress.Map.findcur_addrjumpswith|l->l|exceptionNot_found->[])|Some(Some_,Some_,Dba.Call_)->(* Never generated by extra_info *)failwith"Disasm: both static and dynamic jump targets provided"in(nexts,calls)letget_call_targetsinstr=Dhunk.calleesinstr.Instruction.dba_block(* Gather all successors made in block except if the user has specified them.
Another desirable behaviors could be to make the union of both sets.
*)letsuccessorsuser_jumpsfsuccinstr=letcaddr=Instruction.get_caddressinstrinletopenDba_typesinmatchCaddress.Map.findcaddruser_jumpswith|l->List.mapCaddress.to_virtual_addressl|>Virtual_address.Set.of_list|exceptionNot_found->fsuccinstrletget_instructionaddressstops=letcaddress=Dba_types.Caddress.of_virtual_addressaddressinifDba_types.Caddress.Set.memcaddressstopsthen(Instruction.stopaddress,None)elseDisasm_core.decodeaddress(* Insert a node from inst with edgest to its successors in the graph [g] *)letinsertginstsuccs=leti_vaddr=inst.Instruction.addressin(* Add the current instruction to the graph *)letsrc=Cfg.V.of_insti_vaddrinstinCfg.add_vertexgsrc;Virtual_address.Set.iter(funvaddr->Cfg.add_edgegsrc(Cfg.V.of_addrvaddr))succsmoduleRecursive=structletlevel=5letinsert_successorssuccsworklist=Logger.debug~level:3"Inserting succs: @[<hov 0>%a@]"(funppfvaddr_set->Virtual_address.Set.iter(funvaddr->fprintfppf"%a;@ "Virtual_address.ppvaddr)vaddr_set)succs;Virtual_address.Set.foldDisasm_core.W.addsuccsworklistletaux_recvisitedprogramworklistjumpsstops=letrecloopprogramvisitedwl=ifDisasm_core.W.is_emptywlthenprogramelseletaddress,addresses=Disasm_core.W.popwlinifnot(Virtual_address.Set.memaddressvisited)thenletvisited=Virtual_address.Set.addaddressvisitedintryLogger.debug~level"Recursive decoding @%a with %a"Virtual_address.ppaddressDisasm_core.W.ppwl;letinstr,_nextaddr=get_instructionaddressstopsin(* Computing successors *)letcall_targets=get_call_targetsinstrinletsuccessors=successorsjumpsDisasm_core.Successors.recursiveinstrinletwl'=insert_successorssuccessorsaddressesinletp'=add_callsitesprogramcall_targets|>on_instructions(add_blockinstr)inloopp'visitedwl'with|Invalid_addresss->Logger.warning"@[%s %@ %a@]"sVirtual_address.ppaddress;loopprogramvisitedworklist|Invalid_arguments->Logger.fatal"@[invalid argument (%s)@]"selseloopprogramvisitedaddressesinloopprogramvisitedworklistletapply_aux=aux_recVirtual_address.Set.emptyletdisassemble?(jumps=Dba_types.Caddress.Map.empty)?(stops=Dba_types.Caddress.Set.empty)?(visited=Virtual_address.Set.empty)?(worklist=Disasm_core.W.empty)program=aux_recvisitedprogramworklistjumpsstopsletapplyparameters=letopenInfosinletwl=Disasm_core.W.of_setparameters.entry_pointsinletjmps=parameters.jumpsinletstops=parameters.stopsinapply_auxProgram.emptywljmpsstopsmoduleD=Disasm_core.Make(structletsuccessors=Disasm_core.Successors.recursiveend)letslice~start~stop=letopenDisasm_coreinLogger.debug"@[<hov>Recursive disassembly on slice [%a, %a]@]"Virtual_address.ppstartVirtual_address.ppstop;letfilter_px=x<=stopinletfpwlinstvnexts=Logger.debug"@[<hov>Successors %@ %a : %a@]"Virtual_address.ppinst.Instruction.addressVirtual_address.pp_setvnexts;letwl'=W.add_filtered_setfilter_pwlvnextsin(* The instruction should be handled. Succs may be out of bounds *)insertp.Program.instructionsinstvnexts;letp'=lethunk=inst.Instruction.dba_blockinifDhunk.has_indirect_jumphunkthenProgram.add_unresolved_jumppinst.Instruction.addresselsepin(p',wl')inW.singletonstart|>D.foldfProgram.emptyend(* The default interval end is the section's end address *)letcompute_interval_end~from_address~img=let_,section_end=Loader_utils.section_slice_by_address~address:from_addressimginLogger.info"@[<h>Using section until %a@]"Virtual_address.ppsection_end;(from_address,section_end)letcompute_linear_disasm_intervalsimgparameters=letopenInfosinifhas_entry_pointsparametersthenletopenVirtual_address.Setinletrecloopacceps=ifis_emptyepsthenacc@parameters.linear_addresseselseletep,eps=popepsinletep_interval=compute_interval_end~from_address:ep~imginloop(ep_interval::acc)epsinloop[]parameters.entry_pointselseparameters.linear_addressesmoduleExtended_linear=struct(* The recursive linear module implements the following disassembly strategy.
Given a set of address intervals to disassemble linearly, it keeps track
of static jumps. If a jump belongs to the current interval being
disassembled, it is added to the worklist.
The benefit is that we are able to find and disassemble overlapping
instructions.
Another possible strategy would be:
- linearly disassemble intervals, keeping track of the jump targets.
- in the end, gather all jump targets recognized. For those whose address
has not been disassembled, start a recursive disassembly.
*)letaux_reclinearaddriendprogramjumpswlvisitedstops=letinitial_address=Virtual_address.to_bigintaddrinletbigend=Virtual_address.to_bigintiendinletrecloop(addr:Virtual_address.t)program=ifaddr>iendthenprogramelsetryLogger.debug~level:4"Disassembling %a"Virtual_address.ppaddr;letopenDba_typesinletinstr,nextaddr=get_instructionaddrstopsinmatchnextaddrwith|None->program|Somesucc_addr->letwl',calls=find_callsinstrjumpsin(* Add all elemnts from [wl'] that are in the linear interval
[initial_address, iend] *)letwl=join_wlwlwl'initial_addressbigend|>List.mapCaddress.to_virtual_address|>Disasm_core.W.of_listinletvcalls=List.fold_left(funvsetc->letvaddr=Dba_types.Caddress.to_virtual_addresscinVirtual_address.Set.addvaddrvset)Virtual_address.Set.emptycallsinletp=add_callsitesprogramvcallsinletp=Recursive.aux_recvisitedpwljumpsstops|>Program.on_instructions(add_blockinstr)inloopsucc_addrpwith|Invalid_addresss->Logger.error"%s %@ %a"sVirtual_address.ppaddr;program|Invalid_arguments->Logger.error"invalid argument (%s)"s;programinloopaddrprogramletapplyparameters=letopenInfosinletjmps=parameters.jumpsinletstops=parameters.stopsinletfprogram(start,end_)=letvisited=Virtual_address.Set.emptyinaux_reclinearstartend_programjmps[]visitedstopsincompute_linear_disasm_intervals(Kernel_functions.get_img())parameters|>List.fold_leftfProgram.emptyendmoduleLinear=structopenDisasm_coremoduleI=Make(structletsuccessors=Successors.linearend)letaux_linearworklistprogram(stop:Virtual_address.t)=letshould_stop=(<)stopinletsteppworklistinstructiondisasm_succs=assert(Virtual_address.Set.cardinaldisasm_succs<=1);(* The instruction should be handled. Succs may be out of bounds *)letg=program.Program.instructionsin(* There are 2 types of successors:
1. the linear one (aka disasm_succs) giving the next address to
disassemble;
2. flow ones (aka jumps, ...) which should be rendered as edges in the
CFG;
We first add the latter as successors in the CFG.
*)lethunk=instruction.Instruction.dba_blockinletflow_succs=Dhunk.outer_jumpshunkininsertginstructionflow_succs;letp'=ifDhunk.has_indirect_jumphunkthenleti_vaddr=instruction.Instruction.addressinProgram.add_unresolved_jumppi_vaddrelsepin(p',Virtual_address.Set.fold(funvaddrw->ifshould_stopvaddrthenwelse(Cfg.add_vertexg(Cfg.V.of_addrvaddr);W.addvaddrw))disasm_succsworklist)inI.foldstepprogramworklist(* Inelegant solution to a real problem.
15 bytes is the biggest x86 opcode. Thus it should be enough in the linear
case to "catch" any computed successor of the upper bound of the desired
linear interval.
We always have [cur_address + increment] <= upper_bound + 15
*)let_pad_fifteen_bytesfrom_caddrstops=letrecloopincrements=ifincrement=15thenselseletcaddr=Dba_types.Caddress.add_intfrom_caddrincrementinloop(succincrement)(Dba_types.Caddress.Set.addcaddrs)inloop0stopsletapply~(byte_wise:bool)(intervals:Virtual_address.tInterval.tlist)=ifbyte_wisethenDisassembly_mode.setLinear_byte_wiseelseDisassembly_mode.setLinear;letopenIntervalinletauxprogramival=Logger.result"@[<h>Linear disassembly from %a to %a@]"Virtual_address.ppival.loVirtual_address.ppival.hi;letworklist=Disasm_core.W.singletonival.loinaux_linearworklistprogramival.hiinletapprox_cfg_size=List.fold_left(funszival->leth=Virtual_address.to_intival.hiandl=Virtual_address.to_intival.loinh-l+sz+1)0intervalsinletcfg=Instr_cfg.createapprox_cfg_sizeinletp=Program.createcfginList.fold_leftauxpintervalsend(* FIXME: Yes I know program is unused *)[@@@ocaml.warning"-27"]letdisassemble_slice~program~(slice_start:Virtual_address.t)~(slice_end:Virtual_address.t)=matchDisassembly_mode.get()with|Linear->letival={Interval.hi=slice_end;Interval.lo=slice_start}inLinear.apply~byte_wise:false[ival]|Recursive->Recursive.slice~start:slice_start~stop:slice_end|_->assertfalseletdisassemble_section?(program=Program.empty)imgsection_name=letsec_start,sec_end=Loader_utils.section_slice_by_namesection_nameimginLogger.debug"Disassembling section %s : [%a -- %a]"section_nameVirtual_address.ppsec_startVirtual_address.ppsec_end;disassemble_slice~program~slice_start:sec_start~slice_end:sec_endletsection=disassemble_sectionletsections?(program=Program.empty)imgsecs=Basic_types.String.Set.fold(funsection_nameprogram->trydisassemble_section~programimgsection_namewithNot_found->Logger.warning"Skipping unknown section %s"section_name;program)secsprogramletdisassemble_sections()=assert(Disasm_options.Sections.is_set());(* force linear mode *)Disasm_options.Disassembly_mode.setDisasm_options.Linear;letimg=Kernel_functions.get_img()insectionsimg@@Disasm_options.Sections.get()moduleBasics=Basic_typesletdisassemble_functiong~funcentry=letopenDisasm_coreinletwl=W.singletonfuncentryinletmoduleDis=Make(structletsuccessors=Successors.linearend)inDis.iter(funwlisuccs->letsrc=i.Instruction.addressinCfg.add_instgsrci;ifDhunk.is_returni.Instruction.dba_blockthenwlelse(Virtual_address.Set.iter(fundst->Cfg.add_edge_agsrcdst)succs;W.add_setwlsuccs))wl;gletdo_functionsgimgfuncnames=letfunction_addrs=Basics.String.Set.fold(funfuncnameaddrs->matchLoader_utils.address_of_symbol_by_name~name:funcnameimgwith|None->Logger.warning"No function named %s. Skipping."funcname;addrs|Somevaddr->Logger.debug~level:5"Add address %a for function %s"Virtual_address.ppvaddrfuncname;Virtual_address.Set.addvaddraddrs)funcnamesVirtual_address.Set.emptyinVirtual_address.Set.fold(funfuncentryg->disassemble_functiong~funcentry)function_addrsgexceptionEntry_foundofCfg.V.tletpp_cfg?(file="function_cfg.dot")g=letoc=open_out_binfileinletentry=(* We just take the first vertex as given by iter as our entry point.
This might not always be a good idea.
*)tryCfg.iter_vertex(funv->raise(Entry_foundv))g;assertfalsewithEntry_foundv->vinCfg.output_graphocg~entry[];close_outoclethandle_functionsfuncnames=letimg=Kernel_functions.get_img()inletg=do_functions(Cfg.create17)imgfuncnamesinpp_cfgg;Program.creategletpp_modeppf=function|Disasm_options.Recursive->Format.fprintfppf"recursive"|Disasm_options.Extended_linear->Format.fprintfppf"extended linear"|Disasm_options.Linear->Format.fprintfppf"linear"|Disasm_options.Linear_byte_wise->Format.fprintfppf"linear byte wise"(* Get the entry points from the parameters file if they exist,
Otherwise, just take what the loader says is the one entry point.
This function should be removed once we get rid of the Infos module.
*)letget_initial_entry_pointsimgparameters=letopenInfosinifhas_entry_pointsparametersthenparameters.entry_pointselseletep=Loader_utils.entry_pointimginLogger.info"Starting from default entry point %a"Virtual_address.ppep;Virtual_address.Set.singletonepletfile~filename=letimg=Loader.load_filefilenameinletep=Loader_utils.entry_pointimginletslice_start,slice_end=compute_interval_end~from_address:ep~imgindisassemble_slice~program:Program.empty~slice_start~slice_endletdisassembleparameters=letdba_file=Option.value~default:"none"(DbaOutputFile.get_opt())andopcode_file=ifOpcodeOutputFile.is_set()thenOpcodeOutputFile.get()else"stdout"inLogger.debug"Disassembling mode %a (dba file=%s, opcode file=%s)"pp_mode(Disassembly_mode.get())dba_fileopcode_file;ifFunctions.is_set()thenletfuncnames=Functions.get()inhandle_functionsfuncnames(* Section disassembly has priority over specific entrypoints *)elseifSections.is_set()then(ifInfos.has_entry_pointsparametersthenLogger.warning"Section disassembly overrides entry points option";disassemble_sections())elseifKernel_options.Dba_config.is_set()thenletlinear_p~byte_wisep=letopenInfosinletopenIntervalinlet(intervals:Virtual_address.tInterval.tlist)=List.map(fun(lo,hi)->{lo;hi})p.linear_addressesinLinear.apply~byte_wiseintervalsinletdisassembler=matchDisassembly_mode.get()with|Recursive->Recursive.apply|Extended_linear->Extended_linear.apply|Linear->linear_p~byte_wise:false|Linear_byte_wise->linear_p~byte_wise:trueindisassemblerparameterselseletimg=Kernel_functions.get_img()inletrecdisasm_epsprogrameps=ifVirtual_address.Set.is_emptyepsthenprogramelseletep,eps=Virtual_address.Set.popepsinletslice_start,slice_end=compute_interval_end~from_address:ep~imgindisasm_eps(disassemble_slice~program~slice_start~slice_end)epsinleteps=get_initial_entry_pointsimgparametersinLogger.debug~level:2"Entry points: @[%a@]"(funppfvset->Virtual_address.Set.iter(fune->Format.fprintfppf"%a;@ "Virtual_address.ppe)vset)eps;disasm_epsProgram.emptyepsletpp_to_file~filenameppvalue=letoc=open_outfilenameinletppf=Format.formatter_of_out_channelocinfprintfppf"%a@?"ppvalue;close_outocletrun()=letparameters=Infos.defaultinifnot(Virtual_address.Set.is_emptyparameters.Infos.entry_points)thenLogger.result"Entry points: @[%a@]"(funppfvset->Virtual_address.Set.iter(fune->Format.fprintfppf"%a;@ "Virtual_address.ppe)vset)parameters.Infos.entry_points;letprogram=disassembleparametersinifOpcodeOutputFile.is_set()thenpp_to_file~filename:(OpcodeOutputFile.get())Program.ppprogramelseLogger.result"@[<v 0>%a@ %a@]"Program.ppprogramProgram.pp_detailsprogram;ifShowInstructionCount.get()thenLogger.result"@[%a@]"Program.pp_mnemonic_summaryprogram;Option.iter(funfilename->pp_to_file~filenameProgram.pp_dbaprogram)(DbaOutputFile.get_opt())(* Other functionalities *)letcustom_pp_dbainstrsopcppfdba_block=letopenDba_printer.EICUnicodeinletopc=Mnemonic.to_stringopcinletspaces=String.make(String.lengthopc)' 'inpp_set_marginppf250;fprintfppf"%a"Dhunk.ppdba_block;fprintfppf"@[";letpp_ithppfn=Format.pp_print_option~none:(funppf()->Format.pp_print_stringppf"None")pp_instructionppf(Dhunk.instdba_blockn)inletmyppppfi=fprintfppf"@[<h>%2d: %a@]"ipp_ithiin(matchDhunk.lengthdba_blockwith|0->()|1->fprintfppf"@[<h>%s → %a@]"opcpp_ith0|2->fprintfppf"@[<v 0> %s ⎧1: %a@ %s ⎩2: %a@ @]"opcpp_ith0spacespp_ith1|nelts->letmiddle=nelts/2inletpp_barfmti=ifi=middlethenfprintffmt"%s ⎨"opcelsefprintffmt"%s ⎪"spacesinletrecauxi=ifi=0then(fprintfppf"@[<v 0>@[<h>%s ⎧%a@]@ "spacesmyppi;aux1)elseifi=nelts-1thenfprintfppf"@[<h>%s ⎩%a@]@]"spacesmyppielse(fprintfppf"@[<h>%a%a@]@ "pp_barimyppi;aux(i+1))inaux0);fprintfppf"@]"letcheck_hex_strings=letopenString_utilsinmatchlfindis(func->not(is_hex_charc))with|Somei->Logger.fatal"Invalid hexadecimal character '%c' in opcode %s"s.[i]s|None->()let_pp_pretty_utf8i=letopenInstructioninLogger.result"@[<v 0>%a@]"(custom_pp_dbainstrsi.mnemonic)i.dba_blockletinst_of_raw?baseraw=check_hex_stringraw;Binstream.of_nibblesraw|>Disasm_core.decode_binstream?base|>fstletdecoderaw=tryletbase=Virtual_address.of_string(Disasm_at.get())inleti=inst_of_raw~baserawinLogger.result"%a"Instruction.ppiwithDecoder.InstructionUnhandleds->Logger.warning"Not decoded %s"s;exit1letmain()=ifDisasm_options.is_enabled()&&Kernel_options.ExecFile.is_set()thenifDisasm_options.CFG_graph.get()thenDisasm_cfg.run()else(Logger.info"Running disassembly";run())letrun_decode()=ifDisasm_options.Decode_instruction.is_set()thendecode(Disasm_options.Decode_instruction.get())let_=Cli.Boot.enlist~name:"disassembly run"~f:main;Cli.Boot.enlist~name:"decode hex"~f:run_decode