def msg_send_from_destination(self, destination, selector): ''' Create a MsgSend from the given `destination` and `selector`. Parameters ---------- selector: Selector destination: to which object to send the message Returns ------- msg_send: MsgSend the created `MsgSend` ''' msg_send = None if selector is not None: if isinstance(destination, MsgSendInterface) or destination == 0: # set 0 to not_initialized if isinstance(destination, IVar): if destination.ivar_ref == 0: destination.ivar_ref = MsgSend(NOT_INITIALIZED_CLASS, []) # set 0 to not_initialized if destination == 0: msg_send = MsgSend(NOT_INITIALIZED_CLASS, [selector]) else: # ivar_ref_lookup kwarg resolves ivar_ref from IVar msg_send = destination.create_msg_send( selector, ivar_ref_lookup=self.ivar_lookup) return msg_send
def create_msg_send(self, selector, ivar_ref_lookup=None, *args, **kwargs): ''' Create a `MsgSend` with the specified `selector`. If the `ivar_ref` is not None, this will be used for the `MsgSend`. Otherwise the `ivar_class` is used. Parameters ---------- selector: Selector the `Selector` with which the `MsgSend` shall be created. ivar_ref_lookup: IVarRefLookup, optional if given, the ivar_ref will be resolved Returns ------- MsgSend the created `MsgSend` with `ivar_ref` or `ivar_class` ''' # ivar_ref_lookup = kwargs.get('ivar_ref_lookup') if ivar_ref_lookup is not None: try: self.resolve_ivar_ref(ivar_ref_lookup) except (IvarRefCouldNotBeResolvedException, IvarRefWrongTypeException) as e: log.exception(e) ivar_ref = self.get_ivar_ref() if ivar_ref is not None: return MsgSend.create_from_msgsend(ivar_ref, selector) else: return MsgSend(self.get_ivar_class(), [selector])
def test_filter_function(self): ''' Test the `mc_filter_function` method ''' func = lambda x: str(x) == '@"foo"' c_func = MsgSend(ObjcClass('Bla'), [Selector('foo:', [NSString('foo')])]) msgsend = MsgSend(ObjcClass('SomeClass'), [Selector('someSelector:2dnarg:3rdarg:', [c_func, NSString('SomeString'), NSString('bar')])]) check_method_correct_working = mc_filter_method_call(msgsend, [func], steps = 3) self.assertTrue(check_method_correct_working, 'The method `mc_filter_function` is not working properly!')
def test_has_any_selector(self): ''' Test the method `mc_has_any_selector` ''' sel_name = 'format' msg_send = MsgSend(ObjcClass('NSString'), [Selector('stringWithFormat:', [FormatString('@"%@", @"foo"')])]) res = mc_has_any_selector(msg_send, [sel_name], search_substring = True) print '%s has selector "%s" = %s' % (msg_send, sel_name, res) # [[UIApplication sharedApplication] beginBackgroundTaskWithExpirationHandler:] sel = 'beginBackgroundTaskWithExpirationHandler:' msg_send2 = MsgSend(ObjcClass('UIApplication'), [Selector('sharedApplication'), Selector(sel)]) res2 = mc_has_any_selector(msg_send2, [sel], search_substring = False) self.assertTrue(res and res2 , 'The method `mc_has_any_selector` is not working properly!')
def test_msg_send_eq(self): objc_class = ObjcClass('Foo') sels = [Selector('alloc'), Selector('init'), Selector('foo:', ['1'])] sels2 = [Selector('foo:', ['1'])] msg_send1 = MsgSend(objc_class, sels) msg_send2 = MethodImplementation(objc_class, sels2) res = msg_send1 == msg_send2 and msg_send2 == msg_send1 print '%s == %s and vice versa = %s' % (msg_send1, msg_send2, res) self.assertTrue(res, 'Node quality not correct! Method calls will not link to the corresponding method implementations and vice versa\n')
def test_filter_method_definition(self): ''' Test `md_filter_method_defintion` ''' selname = 'application:handleOpenURL:' sel = Selector(selname) msg_send = MsgSend(ObjcClass('AppDelegate'), [sel]) res = md_filter_method_defintion(msg_send, class_name=None, selector_name=selname, search_substring=True) print '%s has selector %s = %s' % (msg_send, sel, res) self.assertTrue(res, self.MSG % 'MethodDefFilterUtil')
def msg_send_from_destination(self, destination, selector): ''' Overwritten to accept other ints as destination. Currently annotation in Hopper is not so good, leading often to hex values (parsed to int) in the `destination_register`. ''' # temporary workaround to read a msgSend even if destination is integer (transformed from hex) # because Hopper is currently not annotating all destinations! msg_send = ObjectiveCRuntime.msg_send_from_destination( self, destination, selector) if msg_send is None: arm_fix = isinstance(destination, int) if selector is not None and arm_fix: msg_send = MsgSend(ObjcClass(str(destination)), [selector]) return msg_send
def test_filter_category(self): ''' Tests for the method `md_filter_category` ''' category_on_class_name = 'NSURLRequest' category_on_class = ObjcClass(category_on_class_name) category_name = 'AnyHttpsCert' cclass = CategoryClass(category_name, category_on_class=category_on_class) selector = Selector('allowsAnyHTTPSCertificateForHost') category_msg_send = MsgSend(cclass, [selector]) # test with all properties at once category_match = md_filter_category(category_msg_send, str(selector), category_on_class_name, category_name, search_substring=False) print '%s is category on %s with name %s and selector %s = %s' % ( category_msg_send, category_on_class_name, category_name, selector, category_match) category_match2 = md_filter_category(category_msg_send, str(selector), category_on_class_name, category_name=None, search_substring=False) print '%s is category on %s and selector %s = %s' % ( category_msg_send, category_on_class_name, selector, category_match2) # category_on test category_match3 = md_filter_category( category_msg_send, category_on=category_on_class_name) print '%s is category on %s = %s' % ( category_msg_send, category_on_class_name, category_match3) # category name test category_match4 = md_filter_category(category_msg_send, category_name=category_name) print '%s is category with name %s = %s' % ( category_msg_send, category_name, category_match4) # selector test category_match5 = md_filter_category(category_msg_send, selector=str(selector)) print '%s is category with selector %s = %s' % ( category_msg_send, str(selector), category_match5) self.assertTrue( all((category_match, category_match2, category_match3, category_match4, category_match5)), self.MSG % 'md_filter_category')
def create_msg_send(self, selector, *args, **kwargs): ''' Create a `MsgSend` with the specified `selector`. Parameters ---------- selector: Selector the `Selector` with which the `MsgSend` shall be created. Returns ------- MsgSend the created `MsgSend` ''' return MsgSend(self, [selector])
def test_contains_imp_got(self): ''' Test the `mc_contains_imp_got` method ''' imp_got_to_search = 'NSStreamSocketSecurityLevelNone' args = [ImpGot(imp_got_to_search), ImpGot('NSStreamSocketSecurityLevel')] msg_send = MsgSend(ObjcClass("NSOutputStream"), [Selector('setProperty:forKey:', arguments = args)]) res = mc_contains_imp_got(msg_send, imp_got_to_search) print '%s\n has %s: %s' % (msg_send, imp_got_to_search, res) imp_got_to_search = 'kSBXProfileNoWrite' c_func = Function('sandbox_init', func_arguments = [ImpGot('kSBXProfileNoWrite')]) res2 = mc_contains_imp_got(c_func, imp_got_to_search) print '%s\n has %s: %s' % (c_func, imp_got_to_search, res) self.assertTrue(res and res2, 'Method `mc_contains_imp_got` not working properly!')
def create_msg_send(self, selector, *args, **kwargs): ''' Create a `MsgSend` with the specified `selector`. Parameters ---------- selector: Selector the `Selector` with which the `MsgSend` shall be created. Returns ------- MsgSend the created `MsgSend` ''' from vizasm.model.objc.function.MsgSend import MsgSend return MsgSend(self, [selector])
def test_is_exploitable_log_func(self): ''' Test the method `mc_is_exploitable_log_func` ''' msg_send = MsgSend(ObjcClass('NSString'), [Selector('stringWithFormat:', [MethodSelectorArgument('arg1')])]) exploitable_func = Function('NSLog', [msg_send]) res = mc_is_exploitable_log_func(exploitable_func) print 'is exploitable: %s = %s' % (exploitable_func, res) exploitable_printf = Function('printf', [StackVar('var_216')]) res2 = mc_is_exploitable_log_func(exploitable_printf) print 'is exploitable: %s = %s' % (exploitable_printf, res2) exploitable_printf2 = Function('printf', [MethodSelectorArgument('arg1')]) res3 = mc_is_exploitable_log_func(exploitable_printf2) print 'is exploitable: %s = %s' % (exploitable_printf2, res3) self.assertTrue(all((res, res2, res3)), 'The method `mc_is_exploitable_log_func` is not working properly!')
print 'register: %s' % pu.parse_register( ' r2 ; XREF=0x31ae') print pu.parse_stack_push_via_stm('stm.w sp, {r3, r11}', cpu.stack_pointer_register()) print pu.parse_stack_push_via_stm('stm.w sp, {r2, r9}', cpu.stack_pointer_register().register) print pu.parse_stack_access('[sp, #0x8]') print pu.parse_stack_access('[sp]') print pu.parse_offset_addring_offset_needed('[r0, r4]') print pu.parse_offset_addressing('[r0]') # VarAssignment test cpu.memory.registers.set_value_for_register( reg('r5'), IVar(ObjcClass('SelfClass'), MsgSend(ObjcClass('Foo'), [Selector('alloc'), Selector('init')]))) cpu.memory.registers.set_value_for_register(reg('r0'), ObjcClass('Foo')) cpu.memory.registers.set_value_for_register(reg('r4'), ObjcClass('SelfClass')) print pu.parse_var_assignment_with_ivar_ref_from_asmline( 'str r0, [r4, r5]') print pu.parse_var_assignment_without_ivar_ref_from_asmline('[sp, r5]') print pu.parse_var_assignment_with_ivar_ref_from_asmline( 'str r0, [sp, r5]') print 'stack access: %s' % str(pu.parse_stack_access('[sp, #0x4]')) print 'stack access: %s' % str(pu.parse_stack_access('[sp], #0x4')) print 'stack access: %s' % str(pu.parse_stack_access('[sp]')) print pu.parse_add('add r7, sp, #0xc') print pu.parse_add('add r7, sp') print pu.parse_add('add r7, #0xc')
from vizasm.analysis.asm.cpu.x86.Cpu_x86 import Cpu_x86 from vizasm.analysis.asm.cpu.x86.Register_x86 import Register_x86 as reg cpu = Cpu_x86({}) pu = ParseUtil_x86(cpu, reg) imp_stub_x86 = pu.parse_imp( 'call imp___symbol_stub__objc_setProperty') stackvar = pu.parse_stackvar( '00002caf 8B4D14 mov ecx, dword [ss:ebp-0x48+arg_4]' ) cls_ref = pu.parse_objc_class_from_classref('[ds:cls_NSAssertionHandler]') cls_ref2 = pu.parse_objc_class_from_classref( 'dword [ds:eax-0x25e1+cls_Object1]') cpu.read_line('mov ecx, dword [ds:eax-0x25e1+cls_Object1]') var_assignment_without_ivar_ref = pu.parse_var_assignment_without_ivar_ref_from_asmline( 'dword [ds:ecx+0x8]') cpu.memory.registers.set_value_for_register( reg('eax'), MsgSend(ObjcClass('Object1'), [Selector('alloc'), Selector('init')])) var_assignment_with_ivar_ref = pu.parse_var_assignment_with_ivar_ref_from_asmline( 'mov dword [ds:ecx+0x8], eax') print imp_stub_x86 print cls_ref print cls_ref2 print stackvar print var_assignment_without_ivar_ref print var_assignment_with_ivar_ref print pu.parse_imp( '[ds:eax-0x221e+imp___nl_symbol_ptr__NSStreamSocketSecurityLevelKey]')
class MethodCall(object, AddMethodCallToGraphInterface): ''' The `Methodcall` stores messages a sender has sent. This model is uses for a method (sender) that sends several messages and is the output of the `Cpu`. If the sender is not present at creation, using None as argument for the sender creates a pseudo none sender. You can check if this sender is still present with the method has_no_sender(). Parameters ---------- __sender: FunctionInterface, optional (default is PSEUDO_NONE_SENDER) __calls: list<MethodCallItem>, optional (default is []) ''' PSEUDO_NONE_NAME = 'pseudo none' PSEUDO_NONE_SENDER = MsgSend(ObjcClass(PSEUDO_NONE_NAME), []) def __init__(self, sender = None, calls = None): object.__init__(self) if calls is None: calls = [] # fix for creation with None as sender if sender is None: sender = MethodCall.PSEUDO_NONE_SENDER self.__sender = sender self.__calls = calls def __len__(self): return len(self.get_calls()) def __eq__(self, other): if isinstance(other, MethodCall): return self is other or ((self.get_sender(), self.get_calls())) == ((other.sender(), other.get_msg())) return False def __ne__(self, other): return not self == other def __hash__(self): return hash((self.get_sender().__hash__(), tuple(self.get_calls()).__hash__())) def __iter__(self): return iter(sorted(self.calls)) def format_head(self): ''' Format the head (first line) of the `MethodCall`. No newline will be appended. ''' return 'Method: %s' % (self.get_sender()) def __str__(self): head = self.format_head() return '%s:\n%s\n%s' % (head, (len(head) + 1) * '-', ''.join(str(call) for call in self)) def __repr__(self): return '%s(%s: %s)' % (self.__class__.__name__, self.get_sender(), self.get_calls()) def get_sender(self): return self.__sender def get_calls(self): return self.__calls def set_sender(self, value): self.__sender = value def set_calls(self, value): self.__calls = value sender = property(get_sender, set_sender, None, "__sender(MsgSend, optional (default is PSEUDO_NONE_SENDER))") calls = property(get_calls, set_calls, None, "__calls:(list<MethodCallItem>, optional (default is []))") def add_methodcall(self, call, linenr, address = None): ''' Add a call. Parameters: ----------- call: Function or MsgSend linenr: int address: int ''' self.add_methodcallitem(MethodCallItem(call, linenr, address)) def add_methodcallitem(self, methodcallitem): ''' Add the `MethodCallItem`. Parameters: ----------- methodcallitem: MethodCallItem the `MethodCallItem` which shall be added ''' self.get_calls().append(methodcallitem) def idx_methodcallitem(self, linenr): ''' Return the index of the `MethodCallItem` with the given line number''' for i, methocallitem in enumerate(self.calls): if methocallitem.linenr == linenr: return i return None def has_no_sender(self): ''' Check if the `Methodcall` has a sender. If the `MethodCall` is initialized with None as sender, a pseudo none sender is created and used. ''' return self.get_sender() == MethodCall.PSEUDO_NONE_SENDER def is_empty(self): ''' Returns if the methodcall does not contain any calls ''' return len(self.get_calls()) == 0 def create_gephi_attr_dicts(self, asm_lines, filtered_methodcall = None): ''' Construct the attribute dictionary describing the graph style. Use the created dictionaries with the `add_to_graph` method. The attributes are: Calling Method: assembler code the method calls (filtered or not) Method: line number in the asm file address in the asm file surrounding lines Parameters ---------- asm_lines: string the assembler method as string filtered_methodcall: MethodCall, optional (default is None) the filtered `MethodCall` Returns ------- methodcall_sender_attr_dict: dict methodcall_calls_attr_list_dict: dict ''' method_lines_list = [str(methodcallitem) for methodcallitem in self.calls] if filtered_methodcall is None: filtered_methodcall = self # construct calls attribute dictionary methodcall_calls_attr_list_dict = [] cnt_surrounding_lines = setting_for_key(SETTINGS_CNT_SURROUNDING_LINES) if cnt_surrounding_lines >= 0: for i, methodcallitem in enumerate(filtered_methodcall.calls): # construct leading and trailing lines # index of the current line (filtered `MethodCall`) in the list of method lines current_line = str(filtered_methodcall.calls[i].call) linenr = methodcallitem.linenr idx_line = self.idx_methodcallitem(linenr) # idx_line = method_lines_list.index(current_line) lines_before, lines_after = Util.surrounding_elements_from_list(method_lines_list, idx_line, cnt_surrounding_lines) surrounding_lines = NodeAttributes.NPATTERN_SURROUND_LINES % (Util.strlist_to_str(lines_before), current_line, Util.strlist_to_str(lines_after)) methodcall_attr_dict = {NodeAttributes.NATTR_METHOD_SURROUNDING_LINES % cnt_surrounding_lines : surrounding_lines} methodcall_attr_dict.update(methodcallitem.get_gexf_viz_attr_dict()) methodcall_calls_attr_list_dict.append(methodcall_attr_dict) # add method signature to list of method lines method_lines_list.insert(0, filtered_methodcall.format_head() + ":\n") # construct sender attribute dictionary method_lines = Util.strlist_to_str(method_lines_list) methodcall_sender_attr_dict = {NodeAttributes.NATTR_METHOD : method_lines, NodeAttributes.NATTR_ASM_CODE : asm_lines} return (methodcall_sender_attr_dict, methodcall_calls_attr_list_dict) ##################################################################################### # AddToGraphInterface # ##################################################################################### def add_to_graph(self, graph, methodcall_sender_attr_dict = None, methodcall_calls_attr_list_dict = None, sender_methodcall_edge_attr_dict = None): ''' Add the `MethodCall` to the graph Parameters ---------- methodcall_sender_attr_dict: dict, optional (Default {}) methodcall_calls_attr_list_dict: dict, optional (Default {}) methodcall_calls_attr_list_dict: dict, optional (Default {}) ''' if methodcall_sender_attr_dict is None: methodcall_sender_attr_dict = {} if methodcall_calls_attr_list_dict is None: methodcall_calls_attr_list_dict = [] if sender_methodcall_edge_attr_dict is None: sender_methodcall_edge_attr_dict = {} if len(self) > 0: key = self.sender graph.add_node(key) graph.add_attributes(key, methodcall_sender_attr_dict) for idx, methodcallitem in enumerate(self): # only set the next attribute dictionary if list is not empty if methodcall_calls_attr_list_dict: methodcall_attr_dict = deepcopy(methodcall_calls_attr_list_dict[idx]) else: methodcall_attr_dict = {} edge_label = methodcallitem.call graph.add_node(edge_label) graph.add_edge(self.sender, edge_label, key = None, attr_dict = deepcopy(sender_methodcall_edge_attr_dict)) methodcall_attr_dict.update({NodeAttributes.NATTR_LINENUMBER : str(methodcallitem.linenr)}) methodcall_attr_dict.update({NodeAttributes.NATTR_ADDRESS : str(hex(methodcallitem.address))}) graph.add_attributes(edge_label, methodcall_attr_dict)
def __init__(self, msg_send_class): MsgSend.__init__(self, msg_send_class, [])
def create_msg_send(self, selector, *args, **kwargs): from vizasm.model.objc.function.MsgSend import MsgSend return MsgSend(self, [selector])