Skip to content

Instantly share code, notes, and snippets.

@A2nkF
Last active September 22, 2020 19:08
Show Gist options
  • Save A2nkF/4c5305dce26269bd9d701cda684a3897 to your computer and use it in GitHub Desktop.
Save A2nkF/4c5305dce26269bd9d701cda684a3897 to your computer and use it in GitHub Desktop.
######################################################
# IDApython script to parse the stringliteral.json
# file, Il2CppDumper spits out and load the strings
# into an idadb
#
# Usage:
# Just excute the script from within IDA and
# select the generated `stringliteral.json `
# in the file prompt
#
# Note:
# If you get any errors, make sure you have the
# correct base address. The string offsets Il2CppDumper
# returns assume the base address to be 0x00.
# Rebase the idb to that address or add the load
# address to all the string offsets to make sure
# everything works as expected :D
#
# ~A2nkF
######################################################
import idc
import idautils
import logging
import idaapi
import json
from unicodedata import normalize
from idaapi import *
IWID_STRINGS = 0x4
g_logger = logging.getLogger(__name__)
class FailedToExpandSegmentException(Exception):
def __init__(self, message):
super(FailedToExpandSegmentException, self).__init__(message)
self.message = message
class ArgumentNotFoundException(Exception):
def __init__(self, message):
super(ArgumentNotFoundException, self).__init__(message)
self.message = message
class FailedToAppendSegmentException(Exception):
def __init__(self, message):
super(FailedToAppendSegmentException, self).__init__(message)
self.message = message
class RenamingException(Exception):
def __init__(self, message):
super(RenamingException, self).__init__(message)
self.message = message
class NoInputFileException(Exception):
def __init__(self, message):
super(NoInputFileException, self).__init__(message)
self.message = message
def get_end_of_last_segment():
""" Return the last segment's end address. """
last_ea = 0
for segment in idautils.Segments():
if idc.SegEnd(segment) > last_ea:
last_ea = idc.SegEnd(segment)
return last_ea
def append_segment(segment_name, segment_size):
""" Add a new segment to the IDB file and return its starting address.
Information about function arguments will be stored here. Only works if the
segment name is not used yet. This does not affect the original binary.
Arguments:
segment_name -- the name of the segment to be added
"""
for segment in idautils.Segments():
if idc.SegName(segment) == segment_name:
g_logger.warning('[String resolver] Segment ' + segment_name + ' already exists')
return idc.SegStart(segment)
new_segment_start = get_end_of_last_segment()
print('[String resolver] Adding new segment at 0x%08x' % new_segment_start)
if not idc.AddSeg(new_segment_start, (new_segment_start+segment_size),
0, 1, 0, idaapi.scPub) == 1:
raise FailedToAppendSegmentException('[String resolver] Could not add segment')
# set new segment's attributes
if not idc.RenameSeg(new_segment_start, segment_name):
raise FailedToAppendSegmentException('[String resolver] Could not rename segment')
if not idc.SetSegClass(new_segment_start, 'DATA'):
raise FailedToAppendSegmentException('[String resolver] Could not set segment class')
if not idc.SegAlign(new_segment_start, idc.saRelPara):
raise FailedToAppendSegmentException('[String resolver] Could not align segment')
if not idc.SetSegAddressing(new_segment_start, 1): # 1 -- 32 bit
raise FailedToAppendSegmentException(
'[String resolver] Could not set segment addressing')
return new_segment_start
def cleanData(dirty):
alph = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789_'
return ''.join(c for c in dirty if c in alph)
def main():
# Get h
path = idaapi.ask_file(False, '*.json', 'stringliteral.json')
init_mappings = json.loads(open(path, 'rb').read())
final_mappings = []
# We'll use this segment as a buffer for our strings
base_addr = append_segment("STRING_RESOLVER", 0x10000)
offset = 0
for entry in init_mappings:
addr = int(entry['address'], 0x10)
data = entry['value']
# resolve real addresses of strings
final_mappings.append((addr, offset, data))
print("[String resolver] Next string at idx 0x%x" % offset)
# write string bytes to memory
for c in data:
idaapi.patch_byte(base_addr+offset, ord(c))
offset += 1
# add 0x00 byte
idaapi.patch_byte(base_addr+offset, 0x00)
offset += 1
# magic
idaapi.request_refresh(IWID_STRINGS)
# patch string pointers to new segment
for addr, offset, data in final_mappings:
print("[String resolver] patching from 0x%x -> 0x%x" % (addr, base_addr+offset))
idaapi.patch_qword(addr, base_addr+offset)
data = normalize('NFKD', data).encode('ascii','ignore')
clean = cleanData(data) # we can't have special chars in names
idc.MakeUnknown(addr, 1, idaapi.DOUNK_SIMPLE) # undefine existing pointer
SetType(addr, "char *a%s;" % clean[:8]) # set pointer type to 'char *'
SetType(base_addr+offset, "char a%s;" % clean[:8]) # set data type to 'char []'
i = 1
# There might be two different stings, which start with the same 8 chars.
# That's why we assign each string a unique ID. This is my lazy way of doing this :D
while True:
#ret = idaapi.set_name(addr, ("ptr%s%d" % (cleanData[:8], i)), idaapi.SN_NOWARN | idaapi.SN_NOCHECK)
# use this for debugging
ret = MakeName(addr, ("ptr%s%d" % (clean[:8], i)))
i+= 1
if ret:
break
print("[String resolver] Resolved %d strings!" % len(final_mappings))
if __name__ == "__main__":
main()
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment