mirror of
https://github.com/zaksabeast/3ds-Ghidra-Scripts.git
synced 2025-06-18 17:05:47 -04:00
182 lines
6.5 KiB
Python
182 lines
6.5 KiB
Python
# Labels 3ds CTR lib functions
|
|
# @category 3ds
|
|
#
|
|
|
|
from collections import Counter
|
|
from ghidra_utils import bytesToByteArr, nameCtrFunc, findFirstByteArray, readBytes, getCallArgs, getValueFromConstantVarnode, getOrCreateNamespace
|
|
from ctr_services import getCommandName, service_handle_names
|
|
|
|
listing = currentProgram.getListing()
|
|
memory = currentProgram.getMemory()
|
|
|
|
def makeIpcHeader(command_id, normal_params, translate_params):
|
|
return command_id << 16 | (normal_params & 0x3f) << 6 | translate_params & 0x3f
|
|
|
|
def parseIpcVarNode(varnode):
|
|
addr = varnode.getAddress()
|
|
if addr.isRegisterAddress():
|
|
return None
|
|
if addr.isConstantAddress():
|
|
return addr.getOffset()
|
|
return memory.getInt(addr)
|
|
|
|
def nameIpcFunc(ipc_func, ipc_header, handle):
|
|
func_name = getCommandName(ipc_header, handle)
|
|
nameCtrFunc(ipc_func, func_name)
|
|
|
|
def nameIpcWrapperFunc(ipc_func, ipc_header, handle):
|
|
func_name = getCommandName(ipc_header, handle)
|
|
nameCtrFunc(ipc_func, func_name + '_wrapper')
|
|
|
|
def getConstantFromMov(mov_addr):
|
|
inst = listing.getInstructionContaining(mov_addr)
|
|
if inst is not None:
|
|
pcode_ops = inst.getPcode()
|
|
for pcode_op in pcode_ops:
|
|
if pcode_op.getMnemonic() == 'COPY':
|
|
varnode = pcode_op.getInputs()[0]
|
|
return getValueFromConstantVarnode(varnode)
|
|
return None
|
|
|
|
def getIpcHeaderFromAddr(addr):
|
|
ipc_header = getConstantFromMov(addr)
|
|
if ipc_header is None:
|
|
ipc_store_refs = getReferencesFrom(addr)
|
|
if len(ipc_store_refs) > 0:
|
|
ipc_header_addr = ipc_store_refs[0].getToAddress()
|
|
ipc_header = memory.getInt(ipc_header_addr)
|
|
return ipc_header
|
|
|
|
ctr_namespace = getOrCreateNamespace('ctr')
|
|
|
|
handles_by_caller_funcs = {}
|
|
for handle_name in service_handle_names:
|
|
handles = getSymbols(handle_name, ctr_namespace)
|
|
for handle in handles:
|
|
refs = getReferencesTo(handle.getAddress())
|
|
for ref in refs:
|
|
handle_name = handle.getName()
|
|
from_addr = ref.getFromAddress()
|
|
func_ref = getFunctionContaining(from_addr)
|
|
if func_ref is not None:
|
|
func_addr = func_ref.getEntryPoint()
|
|
handles_by_caller_funcs[func_addr] = handle_name
|
|
|
|
def getHandleOfIpcCommand(ipc_func):
|
|
return handles_by_caller_funcs.get(ipc_func.getEntryPoint())
|
|
|
|
# ---------------------------------------------------------------------------
|
|
# IPC functions using ipc_set_header
|
|
ipc_set_header_bytes = [0x10, 0xb5, 0x04, 0x46, 0x08, 0x46, 0x11, 0x46, 0x1a, 0x46, 0x02, 0x9b, 0xff, 0xff, 0xff, 0xff, 0x21, 0x68, 0x08, 0x60, 0x10, 0xbd]
|
|
ipc_set_header_mask = [0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff]
|
|
ipc_set_header_addr = findFirstByteArray(ipc_set_header_bytes, ipc_set_header_mask)
|
|
|
|
if ipc_set_header_addr is None:
|
|
ipc_make_header_refs = []
|
|
else:
|
|
ipc_make_header_refs = getReferencesTo(ipc_set_header_addr)
|
|
|
|
if ipc_make_header_refs is None:
|
|
ipc_make_header_refs = []
|
|
|
|
result = []
|
|
for ref in ipc_make_header_refs:
|
|
func = getFunctionContaining(ref.getFromAddress())
|
|
if func is not None:
|
|
result.append(func)
|
|
else:
|
|
print('Cannot find function for {}. Could this be part of a service command handler jump table?'.format(ref.getFromAddress()))
|
|
|
|
ipc_make_header_refs = result
|
|
|
|
dupliate_ipc_make_header_refs = []
|
|
unique_ipc_make_header_refs = []
|
|
|
|
for ref, count in Counter(ipc_make_header_refs).items():
|
|
if count > 1:
|
|
dupliate_ipc_make_header_refs.append(ref)
|
|
else:
|
|
unique_ipc_make_header_refs.append(ref)
|
|
|
|
# If a function sets a header multiple times, it's likely a command handler
|
|
for ipc_func in dupliate_ipc_make_header_refs:
|
|
nameCtrFunc(ipc_func, 'handleServiceCommand')
|
|
|
|
for ipc_func in unique_ipc_make_header_refs:
|
|
args = getCallArgs(ipc_func, ipc_set_header_addr)
|
|
if len(args) < 4:
|
|
print('Bad number of args at {}'.format(ipc_set_header_addr))
|
|
else:
|
|
command_id = parseIpcVarNode(args[1])
|
|
normal_params = parseIpcVarNode(args[2])
|
|
translate_params = parseIpcVarNode(args[3])
|
|
if command_id is not None and normal_params is not None and translate_params is not None:
|
|
ipc_header = makeIpcHeader(command_id, normal_params, translate_params)
|
|
handle = getHandleOfIpcCommand(ipc_func)
|
|
nameIpcFunc(ipc_func, ipc_header, handle)
|
|
|
|
# ---------------------------------------------------------------------------
|
|
# IPC functions using inlined svc_sync_sync_request
|
|
svc_send_sync_request = bytesToByteArr([0x32, 0x00, 0x00, 0xef])
|
|
svc_send_sync_request_mask = bytesToByteArr([0xff, 0xff, 0xff, 0xff])
|
|
|
|
func_start_bytes = bytesToByteArr([0x00, 0x00, 0x0d, 0xe9])
|
|
func_start_mask = bytesToByteArr([0x00, 0x00, 0x0f, 0xff])
|
|
|
|
get_thread_local_storage_bytes = bytesToByteArr([0x70, 0x4f, 0x1d, 0xee])
|
|
get_thread_local_storage_mask = bytesToByteArr([0xff, 0x0f, 0xff, 0xff])
|
|
|
|
start_address = memory.getExecuteSet().getMaxAddress()
|
|
while start_address is not None:
|
|
svc_send_sync_request_addr = memory.findBytes(start_address, svc_send_sync_request, svc_send_sync_request_mask, False, monitor)
|
|
|
|
if svc_send_sync_request_addr is None:
|
|
# No more functions
|
|
break
|
|
|
|
if readBytes(svc_send_sync_request_addr.add(4), 4) == bytesToByteArr([0x1e, 0xff, 0x2f, 0xe1]):
|
|
# Non-inlined svcSendSyncRequest
|
|
start_address = None
|
|
continue
|
|
|
|
ipc_func_addr = memory.findBytes(svc_send_sync_request_addr, func_start_bytes, func_start_mask, False, monitor)
|
|
|
|
if ipc_func_addr is None:
|
|
# No more functions
|
|
break
|
|
|
|
wrapper_func = None
|
|
ipc_func = getFunctionContaining(ipc_func_addr)
|
|
|
|
if ipc_func is None:
|
|
# Bad data
|
|
# We should probably check the section before assuming this
|
|
break
|
|
|
|
if not ipc_func.getEntryPoint().equals(ipc_func_addr):
|
|
wrapper_func = ipc_func
|
|
ipc_func = createFunction(ipc_func_addr, 'UNKNOWN_CTR_IPC_FN')
|
|
|
|
ipc_func_body = ipc_func.getBody()
|
|
ipc_func_start = ipc_func_body.getMinAddress()
|
|
ipc_func_end = ipc_func_body.getMaxAddress()
|
|
get_thread_local_storage_addr = memory.findBytes(ipc_func_start, ipc_func_end, get_thread_local_storage_bytes, get_thread_local_storage_mask, True, monitor)
|
|
|
|
ipc_store_header_addr = get_thread_local_storage_addr.add(4)
|
|
ipc_header = None
|
|
|
|
while ipc_header is None and ipc_store_header_addr < svc_send_sync_request_addr:
|
|
ipc_header = getIpcHeaderFromAddr(ipc_store_header_addr)
|
|
ipc_store_header_addr = ipc_store_header_addr.add(4)
|
|
|
|
if ipc_header is None:
|
|
print('Cannot find ipc header for {}!'.format(ipc_func_addr))
|
|
continue
|
|
|
|
handle = getHandleOfIpcCommand(ipc_func)
|
|
nameIpcFunc(ipc_func, ipc_header, handle)
|
|
if wrapper_func is not None:
|
|
nameIpcWrapperFunc(wrapper_func, ipc_header, handle)
|
|
|
|
start_address = ipc_func_addr.subtract(4)
|