Created
August 7, 2020 20:05
-
-
Save XalconRE/0df7c171ac257c37c775ad8b745dd899 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
""" | |
:: File Name: | |
wow_script_functions.py | |
:: Author: | |
Jadd - https://ntoskr.nl/ | |
:: Target: | |
World of Warcraft x64 - Retail Client 8.0+ | |
World of Warcraft x64 - Classic Client 1.13+ | |
:: Description: | |
Renames Lua scripting functions within the game client, more specifically | |
those referencing one of four common "FrameScript" functions. Generates a | |
function renaming script by outputting the results to lua_functions.py in | |
the IDB's directory. | |
Updated to Python 3 by Xalcon | |
:: License: | |
Free to use under MIT License. | |
""" | |
from idc_bc695 import * | |
def FindBinary(ea, flag, searchstr, radix=16): | |
return find_binary(ea, flag, searchstr, radix, from_bc695=True) | |
# o_reg values. | |
REG_AX = 0; REG_CX = 1; REG_DX = 2; REG_BX = 3; | |
REG_SP = 4; REG_BP = 5; REG_SI = 6; REG_DI = 7; | |
REG_R8 = 8; REG_R9 = 9; REG_R10 = 10; REG_R11 = 11; | |
REG_R12 = 12; REG_R13 = 13; REG_R14 = 14; REG_R15 = 15; | |
def find_prev_op(ea, operand): | |
if ea == BADADDR: | |
return BADADDR | |
search_end = first_func_chunk(ea) | |
while ea != BADADDR: | |
ea = prev_head(ea) | |
if ea < search_end: | |
break | |
if print_insn_mnem(ea) == operand: | |
return ea | |
return BADADDR | |
def find_next_op(ea, operand): | |
if ea == BADADDR: | |
return BADADDR | |
search_end = find_func_end(ea) | |
while ea != BADADDR: | |
ea = next_head(ea) | |
if ea >= search_end: | |
break | |
if print_insn_mnem(ea) == operand: | |
return ea | |
return BADADDR | |
def search_framescript_function(name): | |
if name == 'FrameScript_RegisterFunction': | |
ea = FindBinary(0, SEARCH_DOWN, '48 8B D9 48 8B CF 45 33 C0 E8 ?? ?? ?? ?? 48 8B D3 48 8B CF E8 ?? ?? ?? ?? BA FE FF FF FF') | |
assert(ea != BADADDR) | |
ea = first_func_chunk(ea) | |
return ea | |
if name == 'FrameScript_RegisterGlobalFunction': | |
ea = FindBinary(0, SEARCH_DOWN, '49 8B C0 48 8B DA 48 8B F9 48 8B D0 48 8B 09 45 33 C0 E8 ?? ?? ?? ??') | |
assert(ea != BADADDR) | |
ea = first_func_chunk(ea) | |
return ea | |
if name == 'FrameScript_RegisterTableFunction': | |
ea = FindBinary(0, SEARCH_DOWN, '41 B8 EE D8 FF FF 4C 8B F1 E8 ?? ?? ?? ?? 49 8B 0E E8 ?? ?? ?? ?? 49 8B 0E') | |
assert(ea != BADADDR) | |
ea = first_func_chunk(ea) | |
return ea | |
if name == 'FrameScript_RegisterFunctionNamespaceWithCount': | |
ea = FindBinary(0, SEARCH_DOWN, 'BA EE D8 FF FF 48 8B CB 48 8B 5C 24 ?? 48 8B 6C 24 ?? 48 8B 74 24 ??') | |
assert(ea != BADADDR) | |
ea = first_func_chunk(ea) | |
return ea | |
def read_lua_functions(framescript_function): | |
results = [] | |
reference = get_next_fcref_to(framescript_function, 0) | |
while reference != BADADDR: | |
if print_insn_mnem(reference) == "jmp": | |
ea = prev_head(reference) | |
rcx = get_operand_value(ea, 1) | |
ea = prev_head(ea) | |
rdx = get_operand_value(ea, 1) | |
function_name = get_strlit_contents(get_qword(rcx), -1, ASCSTR_C).decode('ascii') | |
function_addr = get_qword(rdx) - get_first_seg() | |
results.append({'name': function_name, 'address': function_addr}) | |
reference = get_next_fcref_to(framescript_function, reference) | |
continue | |
compare = reference | |
ea = reference | |
registers = {} | |
function_count = 1 | |
mnemonic = '' | |
# Check how it determines the function table length. | |
while mnemonic != 'cmp' and mnemonic != 'sub': | |
compare = next_head(compare) | |
mnemonic = print_insn_mnem(compare) | |
# Possible operations: | |
# cmp rbx, ... -> pointer comparison | |
# cmp rdi, ... -> counter comparison | |
# sub rdi, 1 -> countdown | |
comparison = get_operand_value(compare, 0) | |
countdown = mnemonic == 'sub' | |
if comparison != REG_BX and comparison != REG_DI: | |
raise Exception('Invalid comparison at 0x{:016X}'.format(reference)) | |
if comparison == REG_DI and not countdown: | |
registers[REG_DI] = get_operand_value(compare, 1) | |
while REG_BX not in registers or REG_DI not in registers: | |
mnemonic = '' | |
while mnemonic != 'lea' and mnemonic != 'mov': | |
ea = prev_head(ea) | |
mnemonic = print_insn_mnem(ea) | |
register = get_operand_value(ea, 0) | |
if register not in registers: | |
registers[register] = get_operand_value(ea, 1) | |
if comparison == REG_BX: | |
function_count = (registers[REG_DI] - registers[REG_BX]) / 0x10 | |
else: | |
function_count = registers[REG_DI] | |
for i in range(0, int(function_count)): | |
entry = registers[REG_BX] + (i * 0x10) | |
function_name = get_strlit_contents(get_qword(entry), -1, ASCSTR_C).decode('ascii') | |
function_addr = get_qword(entry + 8) - get_first_seg() | |
results.append({'name': function_name, 'address': function_addr}) | |
reference = get_next_fcref_to(framescript_function, reference) | |
return results | |
def read_lua_global_functions(framescript_function): | |
results = [] | |
reference = get_next_fcref_to(framescript_function, 0) | |
while reference != BADADDR: | |
registers = {} | |
ea = reference | |
while REG_DX not in registers or REG_R8 not in registers: | |
ea = find_prev_op(ea, 'lea') | |
registers[get_operand_value(ea, 0)] = get_operand_value(ea, 1) | |
function_name = get_strlit_contents(registers[REG_DX], -1, ASCSTR_C).decode('ascii') | |
function_addr = registers[REG_R8] - get_first_seg() | |
results.append({'name': function_name, 'address': function_addr}) | |
reference = get_next_fcref_to(framescript_function, reference) | |
return results | |
def read_lua_namespace_functions(framescript_function): | |
results = [] | |
reference = get_next_fcref_to(framescript_function, 0) | |
while reference != BADADDR: | |
registers = {} | |
ea = reference | |
while REG_DX not in registers or REG_R8 not in registers or REG_R9 not in registers: | |
ea = find_prev_op(ea, 'lea') | |
registers[get_operand_value(ea, 0)] = get_operand_value(ea, 1) | |
namespace = get_strlit_contents(registers[REG_DX], -1, ASCSTR_C).decode('ascii') | |
function = get_strlit_contents(registers[REG_R8], -1, ASCSTR_C).decode('ascii') | |
function_name = '{}.{}'.format(namespace, function) | |
function_addr = registers[REG_R9] - get_first_seg() | |
results.append({'name': function_name, 'address': function_addr}) | |
reference = get_next_fcref_to(framescript_function, reference) | |
return results | |
def read_lua_namespace_with_count_functions(framescript_function): | |
results = [] | |
reference = get_next_fcref_to(framescript_function, 0) | |
while reference != BADADDR: | |
registers = {} | |
ea = reference | |
while REG_CX not in registers or REG_DX not in registers or REG_R8 not in registers: | |
mnemonic = '' | |
while mnemonic != 'lea' and mnemonic != 'mov': | |
ea = prev_head(ea) | |
mnemonic = print_insn_mnem(ea) | |
registers[get_operand_value(ea, 0)] = get_operand_value(ea, 1) | |
namespace = get_strlit_contents(registers[REG_R8], -1, ASCSTR_C).decode('ascii') | |
for i in range(0, registers[REG_DX]): | |
function = get_strlit_contents(get_qword(registers[REG_CX] + i * 16), -1, ASCSTR_C).decode('ascii') | |
function_name = '{}.{}'.format(namespace, function) | |
function_addr = get_qword(registers[REG_CX] + 8 + i * 16) - get_first_seg() | |
results.append({'name': function_name, 'address': function_addr}) | |
reference = get_next_fcref_to(framescript_function, reference) | |
return results | |
def log_output(file, definitions): | |
for function in definitions: | |
file.write('MakeNameEx(baseAddr + 0x{address:016X}, "Script_{name}", SN_NOWARN)\n'.format(**function)) | |
file.write('SetType(baseAddr + 0x{address:016X}, "signed int __fastcall Script_{name}(void *L);")\n'.format(**function)) | |
print('0x{address:016X} Script_{name}'.format(**function)) | |
def define_lua_functions(definitions): | |
for function in definitions: | |
MakeNameEx(get_first_seg() + function['address'], 'Script_{name}'.format(**function), SN_NOWARN) | |
SetType(get_first_seg() + function['address'], 'signed int __fastcall Script_{name}(void *L);'.format(**function)) | |
def main(): | |
FrameScript_RegisterFunction = search_framescript_function('FrameScript_RegisterFunction') | |
FrameScript_RegisterGlobalFunction = search_framescript_function('FrameScript_RegisterGlobalFunction') | |
FrameScript_RegisterTableFunction = search_framescript_function('FrameScript_RegisterTableFunction') | |
FrameScript_RegisterFunctionNamespaceWithCount = search_framescript_function('FrameScript_RegisterFunctionNamespaceWithCount') | |
MakeNameEx(FrameScript_RegisterFunction, 'FrameScript_RegisterFunction', SN_NOWARN) | |
SetType(FrameScript_RegisterFunction, 'void __fastcall FrameScript_RegisterFunction(const char *name, signed int (__fastcall *function)(void *L))') | |
MakeNameEx(FrameScript_RegisterGlobalFunction, 'FrameScript_RegisterGlobalFunction', SN_NOWARN) | |
SetType(FrameScript_RegisterGlobalFunction, 'void __fastcall FrameScript_RegisterGlobalFunction(void* L, const char *name, signed int (__fastcall *function)(void *L))') | |
MakeNameEx(FrameScript_RegisterTableFunction, 'FrameScript_RegisterTableFunction', SN_NOWARN) | |
SetType(FrameScript_RegisterTableFunction, 'void __fastcall FrameScript_RegisterTableFunction(void* L, const char *table, const char *name, signed int (__fastcall *function)(void *L))') | |
MakeNameEx(FrameScript_RegisterFunctionNamespaceWithCount, 'FrameScript_RegisterFunctionNamespaceWithCount', SN_NOWARN) | |
SetType(FrameScript_RegisterFunctionNamespaceWithCount, 'void __fastcall FrameScript_RegisterFunctionNamespaceWithCount(__int64* functionTable, int functionCount, const char *table') | |
lua_functions = read_lua_functions(FrameScript_RegisterFunction) | |
define_lua_functions(lua_functions) | |
lua_global_functions = read_lua_global_functions(FrameScript_RegisterGlobalFunction) | |
define_lua_functions(lua_global_functions) | |
lua_namespace_functions = read_lua_namespace_functions(FrameScript_RegisterTableFunction) | |
define_lua_functions(lua_namespace_functions) | |
lua_namespace_with_count_functions = read_lua_namespace_with_count_functions(FrameScript_RegisterFunctionNamespaceWithCount) | |
define_lua_functions(lua_namespace_with_count_functions) | |
with open('lua_functions.py', 'w') as log: | |
log.write('baseAddr = idc.get_first_seg()\n\n') | |
log.write('# Global Lua Functions #\n') | |
log_output(log, lua_functions) | |
log_output(log, lua_global_functions) | |
log.write('\n') | |
log.write('# Namespace Lua Functions #\n') | |
log_output(log, lua_namespace_functions) | |
log_output(log, lua_namespace_with_count_functions) | |
if __name__ == '__main__': | |
main() |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment