# This IDA script will find all the signatures in your code and # make sure they're in the FFXIV binary. # these directories will be checked recursively SOURCE_DIRS = [ 'D:\\code\\BetterPartyFinder', 'D:\\code\\Burnt Toast', 'D:\\code\\ChatTwo', 'D:\\code\\DalamudPython', 'D:\\code\\ExpandedSearchInfo', 'D:\\code\\Glamaholic', 'D:\\code\\Globetrotter', 'D:\\code\\GoodMemory', 'D:\\code\\HUD Manager', 'D:\\code\\Macrology', 'D:\\code\\Namingway', 'D:\\code\\NominaOcculta', 'D:\\code\\NoSoliciting', 'D:\\code\\PeepingTom', 'D:\\code\\Quest Map', 'D:\\code\\remote-party-finder', 'D:\\code\\SoundFilter', 'D:\\code\\TextBoxStyler', 'D:\\code\\TheGreatSeparator', 'D:\\code\\Tourist', 'D:\\code\\XIVChat', 'D:\\code\\XivCommon', ] # file extension to check for signatures in FILE_EXT = 'cs' import os import os.path import re import io import idaapi import ida_segment import ida_bytes SIG_REGEX = re.compile(r'"([A-Fa-f0-9\? ]+)"') def is_valid_sig(sig): sig = sig.strip().replace(' ', '') if len(sig) == 0: return False if len(sig) % 2 != 0: return False # gonna say 5 bytes min if len(sig) < 10: return False return True def find_all_signatures(): all_sigs = {} for source_dir in SOURCE_DIRS: for root, dirs, files in os.walk(source_dir): for file_name in files: path = os.path.join(root, file_name) if not path.endswith('.' + FILE_EXT): continue with io.open(path, encoding='utf8') as f: data = f.read() sigs = [x.strip() for x in SIG_REGEX.findall(data) if is_valid_sig(x)] all_sigs[path] = sigs return all_sigs def validate(): text = ida_segment.segment_t = ida_segment.get_segm_by_name('.text') sigs = find_all_signatures() for path, sigs in sigs.items(): for sig in sigs: result_count = 0 sig_addr = text.start_ea # 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) while True: sig_addr = idaapi.bin_search( sig_addr, text.end_ea, fmt_sig, sig_mask, ida_bytes.BIN_SEARCH_FORWARD, ida_bytes.BIN_SEARCH_NOCASE ) if sig_addr == idaapi.BADADDR: break result_count += 1 sig_addr += 1 if result_count == 0: print('[E] ' + path + ': Missing ' + sig) if result_count > 1: print('[W] ' + path + ': Multiple matches for ' + sig) if __name__ == '__main__': validate() print('Done')