sig_checker/sig_checker.py

115 lines
2.7 KiB
Python
Executable File

# 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')