2022-01-04 08:12:46 +00:00
|
|
|
# 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):
|
2022-01-04 08:22:24 +00:00
|
|
|
sig = sig.strip().replace(' ', '')
|
2022-01-04 08:12:46 +00:00
|
|
|
|
|
|
|
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()
|
2022-09-09 07:03:50 +00:00
|
|
|
sigs = [x.strip() for x in SIG_REGEX.findall(data) if is_valid_sig(x)]
|
2022-01-04 08:12:46 +00:00
|
|
|
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
|
|
|
|
|
2022-01-04 08:30:43 +00:00
|
|
|
# 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)
|
2022-01-04 08:12:46 +00:00
|
|
|
|
2022-01-04 08:30:43 +00:00
|
|
|
# Another byte array, 0 = "??" wildcard
|
|
|
|
sig_mask = [int(b != '??').to_bytes(1, 'little') for b in sig.split(' ')]
|
|
|
|
sig_mask = b''.join(sig_mask)
|
2022-01-04 08:12:46 +00:00
|
|
|
|
2022-01-04 08:30:43 +00:00
|
|
|
while True:
|
2022-01-04 08:12:46 +00:00
|
|
|
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')
|