Refactor commonly used utils out of minor_patch_diff
Plus some documentation
This commit is contained in:
+16
-146
@@ -1,140 +1,17 @@
|
||||
import click
|
||||
import json
|
||||
import re
|
||||
import semver
|
||||
|
||||
from analysis_utils import (
|
||||
get_correct_switch,
|
||||
get_packet_handler_addr,
|
||||
get_packet_handler_opcode_offset,
|
||||
get_packet_handler_switch_addr,
|
||||
)
|
||||
|
||||
fucked_distance = 0xFFFFFFFF
|
||||
max_size_diff = 10
|
||||
|
||||
|
||||
def get_sem_ver(exe_file: str):
|
||||
res = re.match(r".*ffxiv_dx11\.(\d).(\d)(\d)(\w?)(\d?)\.exe", exe_file)
|
||||
sem_ver = f"{res.group(1)}.{res.group(2)}.{res.group(3)}"
|
||||
if res.group(4) != "":
|
||||
sem_ver = f"{sem_ver}+{res.group(4)}"
|
||||
return sem_ver
|
||||
|
||||
|
||||
def get_zone_proto_down_sig(sem_ver: str):
|
||||
if semver.compare(sem_ver, "7.3.0") >= 0:
|
||||
return "48 89 5C 24 ? 55 56 57 41 54 41 55 41 56 41 57 48 8D AC 24 ? ? ? ? B8 ? ? ? ? E8 ? ? ? ? 48 2B E0 48 8B 05 ? ? ? ? 48 33 C4 48 89 85 ? ? ? ? 45 0F B7 68 ?"
|
||||
elif semver.compare(sem_ver, "7.2.0") >= 0:
|
||||
return "40 55 53 56 57 41 55 41 56 41 57 48 8D AC 24 ? ? ? ? B8 ? ? ? ? E8 ? ? ? ? 48 2B E0 48 8B 05 ? ? ? ? 48 33 C4 48 89 85 ? ? ? ? 45 0F B7 78 ?"
|
||||
elif semver.compare(sem_ver, "6.4.0") >= 0:
|
||||
return "40 53 56 48 81 EC ? ? ? ? 48 8B 05 ? ? ? ? 48 33 C4 48 89 44 24 ? 8B F2"
|
||||
else:
|
||||
return "48 89 ? 24 ? ? 48 83 EC 50 8B F2 49 8B"
|
||||
|
||||
|
||||
def get_opcode_offset_sig(sem_ver: str):
|
||||
if semver.compare(sem_ver, "7.3.0") >= 0:
|
||||
return "E8 ? ? ? ? 41 83 C5 ? 49 8B FC"
|
||||
elif semver.compare(sem_ver, "7.2.0") >= 0:
|
||||
return "E8 ? ? ? ? 41 83 C7 ? EB 1B"
|
||||
elif semver.compare(sem_ver, "6.4.0") >= 0:
|
||||
return "40 53 56 48 81 EC ? ? ? ? 48 8B 05 ? ? ? ? 48 33 C4 48 89 44 24 ? 8B F2"
|
||||
else:
|
||||
return "48 89 ? 24 ? ? 48 83 EC 50 8B F2 49 8B"
|
||||
|
||||
|
||||
def get_opcode_offset_7_30(r2):
|
||||
orig_loc = r2.cmd("s") # Save original spot
|
||||
r2.cmd("aei") # Initialize ESIL VM
|
||||
r2.cmd("aeim") # Initialize ESIL VM stack
|
||||
r2.cmd("aeip") # Initialize ESIL VM IP to curseek
|
||||
r2.cmd("aeso") # step over call
|
||||
r2.cmd("aer r13=0x500") # set r15 to some arbitrary number
|
||||
r2.cmd("aeso") # step
|
||||
|
||||
regs = r2.cmdj("arj")
|
||||
opcode_offset = 0x500 - regs["r13"]
|
||||
|
||||
# Clear the ESIL environment
|
||||
r2.cmd("ar0")
|
||||
r2.cmd("aeim-")
|
||||
r2.cmd("aei-")
|
||||
r2.cmd(f"s {orig_loc}") # Seek back to original spot
|
||||
|
||||
return opcode_offset
|
||||
|
||||
|
||||
def get_opcode_offset_7_20(r2):
|
||||
orig_loc = r2.cmd("s") # Save original spot
|
||||
r2.cmd("aei") # Initialize ESIL VM
|
||||
r2.cmd("aeim") # Initialize ESIL VM stack
|
||||
r2.cmd("aeip") # Initialize ESIL VM IP to curseek
|
||||
r2.cmd("aeso") # step over call
|
||||
r2.cmd("aer r15=0x500") # set r15 to some arbitrary number
|
||||
r2.cmd("aeso") # step
|
||||
|
||||
regs = r2.cmdj("arj")
|
||||
opcode_offset = 0x500 - regs["r15"]
|
||||
|
||||
# Clear the ESIL environment
|
||||
r2.cmd("ar0")
|
||||
r2.cmd("aeim-")
|
||||
r2.cmd("aei-")
|
||||
r2.cmd(f"s {orig_loc}") # Seek back to original spot
|
||||
|
||||
return opcode_offset
|
||||
|
||||
|
||||
def get_opcode_offset(r2):
|
||||
orig_loc = r2.cmd("s") # Save original spot
|
||||
r2.cmd("aei") # Initialize ESIL VM
|
||||
r2.cmd("aeim") # Initialize ESIL VM stack
|
||||
r2.cmd("aeip") # Initialize ESIL VM IP to curseek
|
||||
|
||||
r2.cmd("aecc") # continue until call
|
||||
r2.cmd("aer rax=0x0") # set rax to 0
|
||||
r2.cmd('"aesue rax,0x0,>"') # continue until rax changes?
|
||||
r2.cmd("aer rdx=0x200") # set rdx to some arbitrary number
|
||||
r2.cmd("aeso") # step
|
||||
|
||||
regs = r2.cmdj("arj")
|
||||
opcode_offset = regs["rdx"] - regs["rax"]
|
||||
|
||||
# Clear the ESIL environment
|
||||
r2.cmd("ar0")
|
||||
r2.cmd("aeim-")
|
||||
r2.cmd("aei-")
|
||||
r2.cmd(f"s {orig_loc}") # Seek back to original spot
|
||||
|
||||
return opcode_offset
|
||||
|
||||
|
||||
def get_correct_switch(approx_ea, switch_cases):
|
||||
switches = dict()
|
||||
|
||||
pattern = re.compile(r"case\.(0x[0-9a-fA-F]+)\.(\d+)")
|
||||
|
||||
for l in switch_cases:
|
||||
match = pattern.match(l["name"])
|
||||
if match is not None:
|
||||
switch_ea = match[1]
|
||||
case_ea = l["addr"]
|
||||
|
||||
if switch_ea not in switches:
|
||||
switches[switch_ea] = dict()
|
||||
if case_ea not in switches[switch_ea]:
|
||||
switches[switch_ea][case_ea] = {
|
||||
"opcodes": [],
|
||||
}
|
||||
switches[switch_ea][case_ea]["opcodes"].append(match[2])
|
||||
|
||||
found_switch = None
|
||||
longest_switch = dict()
|
||||
for switch_ea in switches:
|
||||
int_switch_ea = int(switch_ea, 16)
|
||||
if int_switch_ea < approx_ea or int_switch_ea > approx_ea + 0x100:
|
||||
continue
|
||||
if len(switches[switch_ea].keys()) > len(longest_switch):
|
||||
found_switch = switch_ea
|
||||
longest_switch = switches[switch_ea]
|
||||
|
||||
return found_switch, longest_switch
|
||||
|
||||
|
||||
def get_block_sizes(blocks):
|
||||
block_sizes = dict()
|
||||
for block in blocks:
|
||||
@@ -144,7 +21,7 @@ def get_block_sizes(blocks):
|
||||
|
||||
def generate_opcodes_db(packet_handler_ea, switch, opcode_offset, block_sizes):
|
||||
opcodes_db = dict()
|
||||
|
||||
packet_handler_ea = int(packet_handler_ea, 16)
|
||||
for case_ea, data in switch.items():
|
||||
resolved_opcodes = [int(opcode) + opcode_offset for opcode in data["opcodes"]]
|
||||
opcodes_db[resolved_opcodes[0]] = {
|
||||
@@ -157,7 +34,7 @@ def generate_opcodes_db(packet_handler_ea, switch, opcode_offset, block_sizes):
|
||||
|
||||
|
||||
def extract_opcode_data(exe_file):
|
||||
from utils import eprint, create_r2_byte_pattern, sync_r2_output
|
||||
from utils import eprint, sync_r2_output
|
||||
|
||||
import r2pipe
|
||||
|
||||
@@ -166,10 +43,7 @@ def extract_opcode_data(exe_file):
|
||||
|
||||
sync_r2_output(r2)
|
||||
|
||||
sem_ver = get_sem_ver(exe_file)
|
||||
p = create_r2_byte_pattern(get_zone_proto_down_sig(sem_ver))
|
||||
target = r2.cmd(f"/x {p}").split()[0] # Find byte pattern
|
||||
packet_handler_ea = int(target, 16)
|
||||
target = get_packet_handler_addr(r2, exe_file)
|
||||
r2.cmd(f"s {target}") # Seek to target
|
||||
|
||||
## STEP 1: Grab switch cases
|
||||
@@ -179,16 +53,8 @@ def extract_opcode_data(exe_file):
|
||||
|
||||
eprint(f" Loaded switch cases")
|
||||
|
||||
## STEP 2: Grab opcode offset
|
||||
p = create_r2_byte_pattern(get_opcode_offset_sig(sem_ver))
|
||||
opcode_offset_target = r2.cmd(f"/x {p}").split()[0] # Find byte pattern
|
||||
packet_handler_ea = int(opcode_offset_target, 16)
|
||||
r2.cmd(f"s {opcode_offset_target}") # Seek to target
|
||||
|
||||
if semver.compare(sem_ver, "7.2.0") >= 0:
|
||||
opcode_offset = get_opcode_offset_7_20(r2)
|
||||
else:
|
||||
opcode_offset = get_opcode_offset(r2)
|
||||
## STEP 2: Get opcode offset
|
||||
opcode_offset = get_packet_handler_opcode_offset(r2, exe_file)
|
||||
eprint(f" Found opcode offset: {opcode_offset}")
|
||||
|
||||
r2.cmd(f"s {target}") # Seek to original target
|
||||
@@ -201,6 +67,7 @@ def extract_opcode_data(exe_file):
|
||||
eprint(f" Grabbed blocks from packet handler")
|
||||
|
||||
## STEP 4: Process data
|
||||
packet_handler_ea = get_packet_handler_switch_addr(r2, exe_file)
|
||||
switch_ea, packet_handler_switch = get_correct_switch(
|
||||
packet_handler_ea, switch_cases
|
||||
)
|
||||
@@ -281,6 +148,9 @@ def find_opcode_matches(old_opcodes_db, new_opcodes_db):
|
||||
)
|
||||
def minor_patch_diff(old_exe, new_exe):
|
||||
"""
|
||||
DEPRECATED. Use vtable_diff.py instead. It's a more reliable method of
|
||||
getting the diff for a minor patch.
|
||||
|
||||
Generates an opcode diff file for minor patches (e.g 6.30 => 6.30h).
|
||||
|
||||
This script outputs to stdout, so pipe it to a json file.
|
||||
|
||||
Reference in New Issue
Block a user