33from collections import defaultdict , Counter
44from functools import cmp_to_key
55
6+ import claripy
67import networkx as nx
78from angr .errors import SimUnsatError
89
1516
1617l = logging .getLogger (__name__ )
1718
19+ class ConcreteRegSetter (Builder ):
20+ """
21+ a chain builder that aims to find best gadgets that can set registers to
22+ concrete values
23+ """
24+ def __init__ (self , chain_builder ):
25+ super ().__init__ (chain_builder )
26+ self ._concrete_setting_gadgets = None
27+ self ._concrete_setting_cache = defaultdict (list )
28+
29+ def bootstrap (self ):
30+ self ._concrete_setting_gadgets = self .filter_gadgets (self .chain_builder .gadgets )
31+ for g in self ._concrete_setting_gadgets :
32+ reg = list (g .concrete_regs )[0 ]
33+ self ._concrete_setting_cache [reg ].append (g )
34+
35+ def _effect_tuple (self , g ):
36+ assert len (g .concrete_regs ) == 1
37+ reg = list (g .concrete_regs )[0 ]
38+ return (reg , g .concrete_regs [reg ])
39+
40+ def _comparison_tuple (self , g ):
41+ assert len (g .concrete_regs ) == 1
42+ reg = list (g .concrete_regs )[0 ]
43+ return (len (g .changed_regs - {reg }), g .stack_change , g .num_sym_mem_access ,
44+ g .isn_count , int (g .has_conditional_branch is True ))
45+
46+ def filter_gadgets (self , gadgets ):
47+ gadgets = [g for g in gadgets if len (g .concrete_regs ) == 1 and not g .num_sym_mem_access ]
48+ return self ._filter_gadgets (gadgets )
49+
50+ class ConcreteRegChanger (Builder ):
51+ """
52+ a chain builder that aims to craft register values using concrete value
53+ effect in gadgets
54+ """
55+ def __init__ (self , chain_builder ):
56+ super ().__init__ (chain_builder )
57+ self ._concrete_reg_changing_gadgets = None
58+ self ._concrete_reg_changing_cache = defaultdict (list )
59+
60+ def bootstrap (self ):
61+ self ._concrete_reg_changing_gadgets = self .filter_gadgets (self .chain_builder .gadgets )
62+ for g in self ._concrete_reg_changing_gadgets :
63+ reg = list (g .concrete_reg_changes )[0 ]
64+ self ._concrete_reg_changing_cache [reg ].append (g )
65+
66+ def _effect_tuple (self , g ):
67+ reg = list (g .concrete_reg_changes .keys ())[0 ]
68+ init_ast , final_ast = g .concrete_reg_changes [reg ]
69+ val = claripy .algorithm .replace (expr = final_ast ,
70+ old = init_ast ,
71+ new = claripy .BVV (0 , self .project .arch .bits ))
72+ op = final_ast .op
73+ if op in ('ZeroExt' , 'SignExt' ):
74+ op = final_ast .args [1 ].op
75+ return (reg , op , val .concrete_value )
76+
77+ def _comparison_tuple (self , g ):
78+ reg = list (g .concrete_reg_changes )[0 ]
79+ return (len (g .changed_regs - {reg }), g .stack_change , g .num_sym_mem_access ,
80+ g .isn_count , int (g .has_conditional_branch is True ))
81+
82+ def filter_gadgets (self , gadgets ):
83+ gadgets = [g for g in gadgets if len (g .concrete_reg_changes ) == 1 and not g .num_sym_mem_access ]
84+ return self ._filter_gadgets (gadgets )
85+
1886class RegSetter (Builder ):
1987 """
20- a chain builder that aims to set registers using different algorithms
21- 1. algo1: graph-search, fast, not reliable
22- 2. algo2: pop-only bfs search, fast, reliable, can generate chains to bypass bad-bytes
23- 3. algo3: riscy-rop inspired backward search, slow, can utilize gadgets containing conditional branches
88+ a chain builder that aims to set registers using the graph search algorithm
2489 """
2590
2691 #### Inits ####
@@ -32,8 +97,12 @@ def __init__(self, chain_builder):
3297 # Estimate of how difficult it is to set each register.
3398 # all self-contained and not symbolic access
3499 self ._reg_setting_dict : dict [str , list ] = defaultdict (list )
100+ self ._concrete_reg_setter = ConcreteRegSetter (chain_builder )
101+ self ._concrete_reg_changer = ConcreteRegChanger (chain_builder )
35102
36103 def bootstrap (self ):
104+ self ._concrete_reg_setter .bootstrap ()
105+ self ._concrete_reg_changer .bootstrap ()
37106 self ._reg_setting_gadgets = self .filter_gadgets (self .chain_builder .gadgets )
38107
39108 # update reg_setting_dict
@@ -603,7 +672,7 @@ def _handle_hard_regs(self, gadgets, registers, preserve_regs) -> list[RopGadget
603672 if hard_chains :
604673 hard_chain = hard_chains [0 ]
605674 else :
606- hard_chain = self ._find_add_chain (gadgets , reg , val )
675+ hard_chain = self ._find_add_chain (reg , val )
607676 if hard_chain :
608677 self .hard_chain_cache [key ] = hard_chain # we cache the result even if it fails
609678
@@ -627,16 +696,22 @@ def _find_concrete_chains(gadgets, registers):
627696 chains .append ([g ])
628697 return chains
629698
630- def _find_add_chain (self , gadgets , reg , val ) -> list [RopGadget | RopBlock ]:
699+ def _find_add_chain (self , reg , val ) -> list [RopGadget | RopBlock ]:
631700 """
632701 find one chain to set one single register to a specific value using concrete values only through add/dec
633702 """
634703 val = rop_utils .cast_rop_value (val , self .project )
635- concrete_setter_gadgets = [ x for x in gadgets if reg in x . concrete_regs ]
636- delta_gadgets = [ x for x in gadgets if len ( x . reg_dependencies ) == 1 and reg in x . reg_dependencies \
637- and len ( x . reg_dependencies [ reg ]) == 1 and reg in x . reg_dependencies [reg ] ]
704+ arch_bits = self . project . arch . bits
705+ concrete_setter_gadgets = self . _concrete_reg_setter . _concrete_setting_cache [ reg ]
706+ delta_gadgets = self . _concrete_reg_changer . _concrete_reg_changing_cache [reg ]
638707 for g1 in concrete_setter_gadgets :
639708 for g2 in delta_gadgets :
709+ init_ast , final_ast = g2 .concrete_reg_changes [reg ]
710+ ast = claripy .algorithm .replace (expr = final_ast ,
711+ old = init_ast ,
712+ new = claripy .BVV (g1 .concrete_regs [reg ], arch_bits ))
713+ if ast .concrete_value != val .concreted :
714+ continue
640715 try :
641716 chain = self ._build_reg_setting_chain ([g1 , g2 ], {reg : val })
642717 state = chain .exec ()
@@ -653,13 +728,9 @@ def _find_add_chain(self, gadgets, reg, val) -> list[RopGadget|RopBlock]:
653728
654729 def _effect_tuple (self , g ):
655730 v1 = tuple (sorted (g .popped_regs ))
656- v2 = tuple (sorted (g .concrete_regs .items ()))
657- v3 = []
658- for x ,y in g .reg_dependencies .items ():
659- v3 .append ((x , tuple (sorted (y ))))
660- v3 = tuple (sorted (v3 ))
661- v4 = g .transit_type
662- return (v1 , v2 , v3 , v4 )
731+ v2 = tuple (sorted (g .reg_moves ))
732+ v3 = g .transit_type
733+ return (v1 , v2 , v3 )
663734
664735 def _comparison_tuple (self , g ):
665736 return (len (g .changed_regs - g .popped_regs ), g .stack_change , g .num_sym_mem_access ,
0 commit comments