Last active
September 22, 2020 19:08
-
-
Save A2nkF/4c5305dce26269bd9d701cda684a3897 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
###################################################### | |
# 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