@@ -44,6 +44,8 @@ def __init__(
4444 append = None ,
4545 libcache = False ,
4646 multithread = False ,
47+ stop_on_stackpointer = False ,
48+ stop_on_exit_trap = False ,
4749 stdin = 0 ,
4850 stdout = 0 ,
4951 stderr = 0 ,
@@ -77,6 +79,7 @@ def __init__(
7779 self ._platform = ostype_convert (platform .system ().lower ())
7880 self ._internal_exception = None
7981 self ._uc = None
82+ self ._stop_options = QlStopOptions (stackpointer = stop_on_stackpointer , exit_trap = stop_on_exit_trap )
8083
8184 ##################################
8285 # Definition after ql=Qiling() #
@@ -200,6 +203,9 @@ def __init__(
200203 # Setup Outpt
201204 self .os .setup_output ()
202205
206+ # Add extra guard options when configured to do so
207+ self ._init_stop_guard ()
208+
203209
204210 #####################
205211 # Qiling Components #
@@ -659,6 +665,18 @@ def uc(self):
659665 def uc (self , u ):
660666 self ._uc = u
661667
668+ @property
669+ def stop_options (self ) -> "QlStopOptions" :
670+ """ The stop options configured:
671+ - stackpointer: Stop execution on a negative stackpointer
672+ - exit_trap: Stop execution when the ip enters a guarded region
673+ - any: Is any of the options enabled?
674+
675+ Returns:
676+ QlStopOptions: What stop options are configured
677+ """
678+ return self ._stop_options
679+
662680 def __enable_bin_patch (self ):
663681 for addr , code in self .patch_bin :
664682 self .mem .write (self .loader .load_address + addr , code )
@@ -670,7 +688,46 @@ def enable_lib_patch(self):
670688 self .mem .write (self .mem .get_lib_base (filename ) + addr , code )
671689 except :
672690 raise RuntimeError ("Fail to patch %s at address 0x%x" % (filename , addr ))
673-
691+
692+ def _init_stop_guard (self ):
693+ if not self .stop_options .any :
694+ return
695+
696+ # Allocate a guard page, we need this in both cases
697+ # On a negative stack pointer, we still need a return address (otherwise we end up at 0)
698+ # Make sure it is not close to the heap (PE), otherwise the heap cannot grow
699+ self ._exit_trap_addr = self .mem .find_free_space (0x1000 , min_addr = 0x9000000 , alignment = 0x10 )
700+ self .mem .map (self ._exit_trap_addr , 0x1000 , info = '[Stop guard]' )
701+
702+ # Stop on a negative stack pointer
703+ if self .stop_options .stackpointer :
704+ def _check_sp (ql , address , size ):
705+ if not ql .loader .skip_exit_check :
706+ sp = ql ._initial_sp - ql .reg .arch_sp
707+ if sp < 0 :
708+ logging .info ('Process returned from entrypoint (stackpointer)!' )
709+ ql .emu_stop ()
710+
711+ self .hook_code (_check_sp )
712+
713+ # Stop when running to exit trap address
714+ if self .stop_options .exit_trap :
715+ def _exit_trap (ql ):
716+ logging .info ('Process returned from entrypoint (exit_trap)!' )
717+ ql .emu_stop ()
718+
719+ self .hook_address (_exit_trap , self ._exit_trap_addr )
720+
721+ def write_exit_trap (self ):
722+ self ._initial_sp = self .reg .arch_sp
723+ if self .stop_options .any :
724+ if not self .loader .skip_exit_check :
725+ logging .debug (f'Setting up exit trap at 0x{ hex (self ._exit_trap_addr )} ' )
726+ self .stack_write (0 , self ._exit_trap_addr )
727+ elif self .stop_options .exit_trap :
728+ logging .debug (f'Loader { self .loader } requested to skip exit_trap!' )
729+
730+
674731 ###############
675732 # Qiling APIS #
676733 ###############
@@ -683,6 +740,7 @@ def run(self, begin=None, end=None, timeout=0, count=0):
683740 self .exit_point = end
684741 self .timeout = timeout
685742 self .count = count
743+ self .write_exit_trap ()
686744
687745 # init debugger
688746 if self ._debugger != False and self ._debugger != None :
0 commit comments