From 41c7ea2f4284fc468f34c78c11a46e082f72ea07 Mon Sep 17 00:00:00 2001 From: Anna Clemens Date: Tue, 4 Jan 2022 03:12:46 -0500 Subject: [PATCH] chore: initial commit --- sig_checker.py | 115 +++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 115 insertions(+) create mode 100755 sig_checker.py diff --git a/sig_checker.py b/sig_checker.py new file mode 100755 index 0000000..aec2e53 --- /dev/null +++ b/sig_checker.py @@ -0,0 +1,115 @@ +# 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() + + 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 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 + + while True: + # 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) + + 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') \ No newline at end of file