def parse(self):
        if ida_segment.get_segm_by_name('DYLD_CACHE_HEADER'):
            seg = ida_segment.get_first_seg()

            def handle(seg):
                name = ida_segment.get_segm_name(seg)
                try:
                    mod, segname = name.split(':')
                except ValueError:
                    return

                if segname == '__objc_protolist':
                    self.handle_proto_seg(seg)
                elif segname == '__objc_classlist':
                    self.handle_class_seg(seg)

            while seg:
                handle(seg)
                seg = ida_segment.get_next_seg(seg.start_ea)

            return

        protocols = ida_segment.get_segm_by_name('__objc_protolist')
        if protocols:
            self.handle_proto_seg(protocols)

        classes = ida_segment.get_segm_by_name('__objc_classlist')
        if classes:
            self.handle_class_seg(classes)
Beispiel #2
0
    def build():
        seg = ida_segment.get_segm_by_name('__text')
        for f in idautils.Functions(start=seg.startEA, end=seg.endEA):
            name = idc.GetFunctionName(f)
            DB.shared.add_node(f, name)
            if name[0] in ['-', '+']:
                MachO.oc_methods.append(f)
            elif name.startswith('sub_'):
                MachO.subroutines.append(f)

            func_type = MachO.get_func_type(f)  # 根据IDA提供的func prototype(可能会失败)
            if func_type:
                MachO.func_data_frm_ida[f] = func_type
                if f in MachO.oc_methods:
                    cls, sel = name[1:].strip('[]').split()
                    rec = func_type[0].type.__str__()
                    if rec not in MachO.funcs_index_by_rec:
                        MachO.funcs_index_by_rec[rec] = {sel: f}
                    else:
                        MachO.funcs_index_by_rec[rec][sel] = f
                    if sel not in MachO.funcs_index_by_sel:
                        MachO.funcs_index_by_sel[sel] = {rec: f}
                    else:
                        MachO.funcs_index_by_sel[sel][rec] = f

                    if len(func_type) == 2:
                        MachO.func_has_no_args.append(f)

                    ret_type = func_type.rettype.__str__()
                    if ret_type == 'id' and cls in OCClass.pool_indexed_by_name:  # TODO category
                        if not OCClass.pool_indexed_by_name[cls].is_method_a_getter(sel, f):
                            MachO.funcs_ret_id.append(f)
Beispiel #3
0
def main():
    """
    for each function in .text segment, we extract
    name, start_ea, end_ea, code, size attributes from IDA
    """
    results = {}

    start = time.time()
    funcs = get_funcs()

    text_segm = ida_segment.get_segm_by_name(".text")
    for func in funcs:
        # we only care about functions in .text segment
        if not in_range(text_segm, func.start_ea):
            continue

        name = ida_funcs.get_func_name(func.start_ea)
        code = ida_hexrays.decompile_func(func, None)
        results[name] = {   "name": name,
                            "start_ea": func.start_ea,
                            "end_ea": func.end_ea,
                            "code": code.__str__(),
                            "size": func.size()
                            }
    print("JSON:")
    print(json.dumps({"time": time.time() - start, "data": results}))
 def fill_mem(self, op, dtype, addr):
   op.type = ida_ua.o_mem
   op.dtype = dtype
   #op.addr = addr
   # add data segment base addr
   ds = ida_segment.get_segm_by_name('VM_DATA')        
   op.addr = ds.start_ea + addr
def _get_seg(possible_seg_names):
    for seg_name in possible_seg_names:
        seg = ida_segment.get_segm_by_name(seg_name)
        if seg:
            return seg

    return None
Beispiel #6
0
 def build_from_IDA():
     seg = ida_segment.get_segm_by_name('__objc_protolist')
     for ea in range(seg.startEA, seg.endEA, 8):
         p = Protocol(ida_bytes.get_qword(ea))
         p.extract_info_from_IDA()
         Protocol.pool_indexed_by_ea[p.ea] = p
         Protocol.pool_indexed_by_name[p.name] = Protocol.pool_indexed_by_name[p.name] + [
             p, ] if p.name in Protocol.pool_indexed_by_name else [p, ]
Beispiel #7
0
def SegByName(n):
	t = ida_segment.get_segm_by_name(n)
	if (t.start_ea != ida_idaapi.BADADDR):
		start = t.start_ea
		end = t.end_ea
	else:
		start = ida_idaapi.BADADDR
		end = ida_idaapi.BADADDR
	return (start,end)
Beispiel #8
0
 def build_from_IDA():
     seg = ida_segment.get_segm_by_name('__objc_classlist')
     for class_ea in range(seg.startEA, seg.endEA, 8):
         objc_data = ida_bytes.get_qword(class_ea)
         cls = OCClass(objc_data)
         cls.extract_info_from_IDA()
         OCClass.pool_indexed_by_name[cls.name] = cls if cls.name not in OCClass.pool_indexed_by_name else \
             OCClass.pool_indexed_by_name[cls.name]
         OCClass.pool_indexed_by_ea[cls.ea] = cls
def find_mclib_jumper():
    """Retrieves the address of the McLib jumper fills by the loader during
    the trustlet loading process

    :return: McLib jumper address
    :rtype: int
    """
    segm = ida_segment.get_segm_by_name("rtm")
    mclib_jumper = ida_bytes.get_dword(segm.start_ea + 0x8C)

    return mclib_jumper
Beispiel #10
0
    def find_in_segment(self, value, seg_name_or_ea):
        """
        Searches memory for given value within the range of a specific segment.

        :param bytes value: byte string to search for
        :param seg_name_or_ea: segment name or address within segment.

        :return int: address where value was located or -1 if not found
        """
        if isinstance(seg_name_or_ea, str):
            segment = ida_segment.get_segm_by_name(seg_name_or_ea)
        else:
            segment = ida_segment.getseg(seg_name_or_ea)

        return self.find(value, start=segment.start_ea, end=segment.end_ea)
Beispiel #11
0
def iter_dynamic_functions() -> Iterable[Tuple[int, str]]:
    """
    Iterates the dynamically resolved function signatures.

    :yield: (ea, name)
    """
    # Look for data elements in the .data segment in which IDA has placed
    # a function signature element on.
    data_segment = ida_segment.get_segm_by_name(".data")
    for ea in idautils.Heads(start=data_segment.start_ea,
                             end=data_segment.end_ea):
        flags = ida_bytes.get_flags(ea)
        if idc.is_data(flags) and not idc.is_strlit(flags) and _is_func_type(
                ea):
            yield ea, ida_name.get_name(ea)
Beispiel #12
0
    def _get_segments(self, segname=None):
        """
        Obtain the bytes of the segment specified in segname or all segments as an iterable.

        :param str segname: segment name or None

        :yield: seg_start, seg_bytes
        """
        if segname:
            seg_starts = [ida_segment.get_segm_by_name(segname).start_ea]
        else:
            seg_starts = idautils.Segments()

        for ea in seg_starts:
            yield ea, segments.get_bytes(ea)
Beispiel #13
0
def get_start(name_or_addr):
    """
    Retrieves the starting address for the segment containing the given name or address.

    :param string|int name_or_addr: either the name of a segment or an EA within a segment
    """
    if isinstance(name_or_addr, str):
        segment = ida_segment.get_segm_by_name(name_or_addr)
        if segment is None:
            raise AssertionError(
                "could not find segment for {}".format(name_or_addr))
        return segment.start_ea
    elif isinstance(name_or_addr, numbers.Number):
        return idc.get_segm_attr(name_or_addr, idc.SEGATTR_START)
    else:
        raise ValueError("Invalid value: {}".format(name_or_addr))
Beispiel #14
0
def main(argv=None):
    if argv is None:
        argv = sys.argv[:]

    try:
        seg_spec = prompt_for_segment()
    except BadInputError:
        logger.error('bad input, exiting...')
        return -1

    seg = ida_segment.get_segm_by_name(seg_spec.name)
    if not seg:
        logger.error("bad segment, exiting...")

    buf = ida_bytes.get_bytes(seg.start_ea, seg.end_ea - seg.start_ea)
    with open(seg_spec.path, "wb") as f:
        f.write(buf)

    logger.info("wrote %x bytes", len(buf))
Beispiel #15
0
from idaapi import get_bytes, get_inf_structure
from ida_name import set_name
from ida_segment import get_segm_by_name

from find_kallsyms import find_kallsyms_in_rodata

rodata_segm = get_segm_by_name('.rodata')
rodata_size = rodata_segm.end_ea - rodata_segm.start_ea + 1
rodata = bytearray(get_bytes(rodata_segm.start_ea, rodata_size))
inf = get_inf_structure()
endianness = '>' if inf.is_be() else '<'
for address, name in find_kallsyms_in_rodata(rodata, endianness):
    if name[0] != 'A':
        set_name(address, str(name[1:]))
Beispiel #16
0
for name, (addr, size) in data["peripherals"].items():
    seg = ida_segment.getseg(addr)
    if seg:
        old_name = ida_segment.get_segm_name(seg)
        ida_segment.set_segm_name(seg, "%s_%s" % (old_name, name))
    else:
        add_segment(addr, size, name)

for name, (addr, reg_count, reg_size, clu_count, clu_size) in data["addresses"].items():
    for m in range(clu_count):
        for n in range(reg_count):
            reg_name = name.replace('<m>', str(m)).replace('<n>', str(n))
            reg_addr = addr + m * clu_size + n * reg_size

            ida_bytes.create_data(reg_addr, {
                1: ida_bytes.byte_flag(),
                2: ida_bytes.word_flag(),
                4: ida_bytes.dword_flag()
            }[reg_size], reg_size, ida_netnode.BADNODE)
            ida_name.set_name(reg_addr, reg_name)

base_addr = ida_segment.get_segm_by_name("ROM").start_ea
for name, offset in data["interrupts"].items():
    addr = base_addr + (16 + offset) * 4
    name = "%s_%s" % ("arm" if offset < 0 else "irq", name.lower())
    ida_bytes.del_items(addr, 0, 4)
    ida_bytes.create_dword(addr, 4, True)
    ida_name.set_name(addr, name, 0)
    if ida_bytes.get_dword(addr) > 0:
        ida_offset.op_plain_offset(addr, 0, 0)
Beispiel #17
0
def dump_segment(segm_name, from_debugger=True):
    segm = ida_segment.get_segm_by_name(segm_name)
    data_length = segm.end_ea - segm.start_ea
    return ida_bytes.get_bytes(segm.start_ea, data_length, from_debugger)
Beispiel #18
0
#Attempts to find & name callbacks registered with REGISTERCLASS
import ida_funcs
import ida_idaapi
import ida_name
import ida_offset
import ida_segment
import ida_ua
import ida_xref

res = ida_name.get_name_value(ida_idaapi.BADADDR, "REGISTERCLASS")
code_ea = ida_segment.get_segm_by_name("_TEXT").start_ea

procs = {
    0x18: "Create",
    0x1C: "Destroy",
    0x20: "Icon",
    0x24: "Paint",
    0x28: "Size",
    0x2C: "Input",
    0x30: "Focus",
    0x34: "Scroll",
    0x38: "Data",
    0x3C: "Help"
}

if res[0] == 1:
    regfunc = res[1]
    regcall = ida_xref.get_first_cref_to(regfunc)
    while regcall != ida_idaapi.BADADDR:
        insn = ida_ua.insn_t()
        ida_ua.decode_insn(insn, regcall)
Beispiel #19
0
import os
import ida_segment
import ida_kernwin
import ida_name

filename = ida_kernwin.ask_file(0, "", "FILTER *.MAP\nSelect MAP file")
if filename is not None and os.path.isfile(filename):

    real_base = {}
    map_base = {}

    real_base['DGROUP'] = ida_segment.get_segm_by_name("DATA").start_ea
    real_base['IGROUP'] = ida_segment.get_segm_by_name("TEXT").start_ea

    with open(filename, "r") as f:
        data = f.read().split('\n')

    insection = None

    for line in data:
        if line.strip() == 'Origin   Group':
            insection = "groups"
        elif line.strip() == 'Address         Publics by Value':
            insection = "publics_"
        elif line.strip() == '' and insection == "publics_":
            insection = "publics"
        elif line.strip() == '':
            insection = None
        else:
            if insection == "groups":
                (addr, name) = line.strip().split('   ')
ROOT_PATH = os.path.abspath(os.path.dirname(__file__))

log_file = open(os.path.join(ROOT_PATH, "./output.log"), "w")

binary_info = idaapi.get_inf_structure()
if binary_info.is_64bit():
    BITS = 64
elif binary_info.is_32bit():
    BITS = 32
else:
    BITS = 16
ENDIAN = "big" if binary_info.is_be() else "little"
ARCH = binary_info.procname

text_seg = ida_segment.get_segm_by_name(".text")
if not text_seg:
    log_file.write("<%s>: text_seg is not exists!!!!\n" %
                   get_input_file_path())
#     text_seg = ida_segment.get_segm_by_name("LOAD")

if ARCH == "ARM" and ENDIAN == "little" and BITS == 32:
    # Identify up to four parameters
    ARGU_REGS = ['R0', 'R1', 'R2', 'R3', 'ENDs']
    JMP_INSTRUCTION = [
        'B', 'BLT', 'BLE', 'BEQ', 'BVC', 'BVS', 'BLS', 'BX', 'BLO', 'BCS',
        'BCC', 'BMI', 'BPL', 'BGT', 'BGE', 'BNE', 'BHI', 'BHS'
    ]
    RETURN_REG = 0  # R0
elif ARCH == "metapc" and ENDIAN == "little" and BITS == 32:
    RETURN_REG = 0  # eax
Beispiel #21
0
from idaapi import get_bytes, require
from ida_segment import get_segm_by_name

require('find_kallsyms')
require('ida_utils')
rodata_segm = get_segm_by_name('.rodata')
if rodata_segm is None:
    rodata_segm = get_segm_by_name('.text')
rodata_size = rodata_segm.end_ea - rodata_segm.start_ea + 1
rodata = b''.join(get_bytes(rodata_segm.start_ea, rodata_size))
kallsyms = find_kallsyms.find_kallsyms_in_rodata(rodata)  # noqa: F821
ida_utils.apply_kallsyms(kallsyms)  # noqa: F821
class FfxivSigmaker:
    DATASTORE: ClientStructsData
    GENERATED_DATASTORE: GeneratedData
    STANDARD_IMAGE_BASE = 0x1_4000_0000

    # The ".text" segment, only look for xrefs within this range
    TEXT_SEGMENT: ida_segment.segment_t = ida_segment.get_segm_by_name(".text")

    # Max number of XREFs to search through for each address
    XREFS_TO_SEARCH = 10

    # Sigs that are not unique, cache having to search short sigs multiple times
    _NOT_UNIQUE_SIGNATURES: Set[str] = set()

    def __init__(self):
        data_path = os.path.join(os.path.dirname(os.path.realpath(__file__)),
                                 "data.yml")
        with Path(data_path).open('r') as fd:
            data_dict = yaml.load(fd)
            self.DATASTORE = dacite.from_dict(ClientStructsData, data_dict)

        self._rebase_datastore()

        generated_data_path = os.path.join(
            os.path.dirname(os.path.realpath(__file__)), "generated_data.yml")
        with Path(generated_data_path).open('r') as fd:
            data_dict = yaml.load(fd)
            self.GENERATED_DATASTORE = data_dict  #dacite.from_dict(GeneratedData, data_dict)

    def run(self) -> None:
        """
        Run the sigmaker
        :return: None
        """
        Log.debug('generating global sigs')
        total_globals = len(self.DATASTORE.globals)
        for (i, (global_ea,
                 global_name)) in enumerate(self.DATASTORE.globals.items()):
            status = f'{i}/{total_globals} ({i / total_globals:0.2%})'

            if global_name in self.GENERATED_DATASTORE.global_sigs:
                existing_sig = self.GENERATED_DATASTORE.global_sigs[
                    global_name]
                if existing_sig != "None":
                    Log.debug(
                        f'{status} // {global_name} // {global_ea:X} // has existing sig: {existing_sig}'
                    )
                else:
                    Log.debug(
                        f'{status} // {global_name} // {global_ea:X} // failed sig generation on previous run'
                    )
                continue

            sig = self._sig_address(int(global_ea), False)
            Log.debug(f'{status} // {global_name} // {global_ea:X} // {sig}')
            if sig:
                self.GENERATED_DATASTORE.global_sigs[global_name] = sig
            else:
                self.GENERATED_DATASTORE.global_sigs[global_name] = "None"

        Log.debug('generating class sigs')
        class_data: GameClass
        total = len(self.DATASTORE.classes)
        for (i, (class_name,
                 class_data)) in enumerate(self.DATASTORE.classes.items()):
            # skip stubs
            if not class_data:
                continue

            status = f'{i}/{total} ({i / total:0.2%})'

            for (func_ea, func_name) in class_data.funcs.items():
                existing_sig = self.GENERATED_DATASTORE.get_function_signature(
                    class_name, func_name)
                if existing_sig is not None:
                    if existing_sig == "None":
                        Log.debug(
                            f'{status} // {class_name}::{func_name} // {func_ea:X} // failed sig generation on previous run'
                        )
                    else:
                        Log.debug(
                            f'{status} // {class_name}::{func_name} // {func_ea:X} // has existing sig: {existing_sig}'
                        )
                    continue
                sig = self._sig_address(int(func_ea), True)
                Log.debug(
                    f'{status} // {class_name}::{func_name} // {func_ea:X} // {sig}'
                )
                if sig:
                    self.GENERATED_DATASTORE.set_function_signature(
                        class_name, func_name, sig)
                else:
                    self.GENERATED_DATASTORE.set_function_signature(
                        class_name, func_name, "None")

            # global_ea = class_data.g_pointer
            # if global_ea:
            #     sig = self._sig_address(global_ea, False)
            #     Log.debug(f'{status} // {class_name}::gPointer // {global_ea:X} // {sig}')
            #     if sig:
            #         class_data.g_pointer_sig = sig
            #
            # global_ea = class_data.g_instance
            # if global_ea:
            #     sig = self._sig_address(global_ea, False)
            #     Log.debug(f'{status} // {class_name}::gInstance // {global_ea:X} // {sig}')
            #     if sig:
            #         class_data.g_instance_sig = sig

        Log.debug(f'{total}/{total} (100.00%)')

    def export(self) -> None:
        """
        Export generated data to json
        :return: None
        """
        data_path = os.path.join(os.path.dirname(os.path.realpath(__file__)),
                                 "generated_data.yml")

        with Path(data_path).open('w') as fd:
            yaml.dump(self.GENERATED_DATASTORE, fd)

    # region Rebasing

    def _rebase_datastore(self, undo: bool = False) -> None:
        """
        Rebase all the addresses in the datastore.
        :param undo: Revert a rebasing instead
        :return: None
        """
        current_image_base = idaapi.get_imagebase()
        if self.STANDARD_IMAGE_BASE == current_image_base:
            return

        rebase_offset = current_image_base - self.STANDARD_IMAGE_BASE
        if undo:
            rebase_offset *= -1

        self.__rebase_dict(self.DATASTORE.globals, rebase_offset)
        self.__rebase_dict(self.DATASTORE.functions, rebase_offset)

        class_data: GameClass
        for (class_name, class_data) in self.DATASTORE.classes.items():
            if not class_data:
                continue

            self.__rebase_dict(class_data.funcs, rebase_offset)

            for vtbl in class_data.vtbls:
                vtbl.ea += rebase_offset

            if class_data.g_pointer:
                class_data.g_pointer += rebase_offset

            if class_data.g_instance:
                class_data.g_instance += rebase_offset

    def __rebase_dict(self, mapping: Dict[int, str],
                      rebase_offset: int) -> None:
        """
        Rebase all addresses in a [addr, _] mapping
        :param mapping: Mapping to rebase
        :param rebase_offset: Rebase offset
        :return: None
        """
        for (addr, name) in list(mapping.items()):
            mapping[addr + rebase_offset] = mapping.pop(addr)

    # endregion

    # region Signature creation

    def _sig_address(self, addr: int, is_func: bool) -> Optional[str]:
        """
        Find the best sig available for a given address
        :param addr: Address to sig the xrefs of
        :param is_func: Address is a func start
        :return: A signature, or None
        """
        sigs = self.__get_xref_sigs(addr, is_func)
        sig = self.__find_best_sig(sigs)
        return sig

    def __get_xref_sigs(self, addr: int, is_func: bool) -> Iterator[str]:
        """
        Create a signature from a function address
        XRef sigs are preferred, however if none are available the func itself will be used
        :param addr: Function address
        :param is_func: Indicates that this address is a func, and can be signatured directly
        :return: A series of Dalamud compatible signatures
        """
        xref_addrs = [xref.frm for xref in idautils.XrefsTo(addr)]

        if is_func and not ida_funcs.get_func(addr):
            Log.warn(
                f'Address at {addr:X} is identified as a func, but is not in IDA, attempting to make a subroutine'
            )
            ida_funcs.add_func(addr)

        # This should prune xrefs in places like .pdata by only keeping xrefs in a function
        xref_addrs = list(filter(ida_funcs.get_func_name, xref_addrs))

        # Grab the first N xrefs
        xref_addrs = xref_addrs[:self.XREFS_TO_SEARCH]

        if is_func:
            # Try to sig the func itself as well
            xref_addrs.insert(0, addr)

        for xref_addr in xref_addrs:
            yield from SigGen(xref_addr)

    def __find_best_sig(self, sigs: Iterator[str]) -> Optional[str]:
        """
        Find the shortest signature, with only one match in the .TEXT segment.
        :param sigs: Signatures to check
        :return: The shortest signature, or None
        """
        chosen_sig = None
        chosen_sig_size = sys.maxsize

        for sig in sigs:
            # Already have a sig shorter than this one
            if len(sig) >= chosen_sig_size:
                continue

            if sig in self._NOT_UNIQUE_SIGNATURES:
                continue

            if self.__is_sig_unique(sig):
                chosen_sig = sig
                chosen_sig_size = len(sig)
            else:
                self._NOT_UNIQUE_SIGNATURES.add(sig)

        return chosen_sig

    def __is_sig_unique(self, sig: str) -> bool:
        """
        Perform a binary sig search and determine if the signature has one unique match
        :param sig: Sig to search
        :return: True if only one address matches
        """

        # Expects a byte array
        fmt_sig = [
            int(s, 16).to_bytes(1, 'little') if s != '??' else b'\0'
            for s in sig.split(' ')
        ]
        fmt_sig = b''.join(fmt_sig)

        # Another byte array, 0 = "??" wildcard
        sig_mask = [
            int(b != '??').to_bytes(1, 'little') for b in sig.split(' ')
        ]
        sig_mask = b''.join(sig_mask)

        result_count = 0
        sig_addr = self.TEXT_SEGMENT.start_ea  # noqa
        while True:
            sig_addr = idaapi.bin_search(
                sig_addr,
                self.TEXT_SEGMENT.end_ea,  # noqa
                fmt_sig,
                sig_mask,
                ida_bytes.BIN_SEARCH_FORWARD,
                ida_bytes.BIN_SEARCH_NOCASE)

            # No more results
            if sig_addr == idaapi.BADADDR:
                break

            # We have a match
            result_count += 1

            # But more than one fails the signature
            if result_count > 1:
                return False

            # Starting at the match, returns the same match. So go to the next byte
            sig_addr += 1

        return True