frida / frida-python

Frida Python bindings
Other
787 stars 148 forks source link

frida-tracer fails on Windows with long function names #9

Closed guillaume-uH57J9 closed 10 years ago

guillaume-uH57J9 commented 10 years ago

The affects Frida 1.4.0, specifically python file frida/tracer.py

On Windows, Frida crashes with a unhandled file not found exception because of the Windows API path length limitation (260 characters). For details see Maximum Path Length Limitation at http://msdn.microsoft.com/en-us/library/windows/desktop/aa365247%28v=vs.85%29.aspx

This occurs very easily with Java methods that have a very long mangled name (blah_method_blah_type_pointer_paramater_typebisparameterbis...).

See below an extract of a modified tracer.py that solves this issue by taking only 32 characters for the function name in case the total path length is longer then 260.

A proper solution would be always limiting the function name to 32 characters to obtain more consistent and predictable file names. Then append a CRC32 of the function name to make sure there's no collision. There is a real risk of collision if the function names are truncated, especially with function overloading, so you should really consider adding a unique suffix like CRC32.

class FileRepository(Repository):
    def __init__(self):
        super(FileRepository, self).__init__()
        self._handlers = {}
        self._repo_dir = os.path.join(os.getcwd(), '__handlers__')

    def ensure_handler(self, function):
        entry = self._handlers.get(function.absolute_address)
        if entry is not None:
            (function, handler, handler_file, handler_mtime, last_sync) = entry
            return handler

        handler = None
        handler_files_to_try = []

        if isinstance(function, ModuleFunction):
            module_dir = os.path.join(self._repo_dir, to_filename(function.module.name))
            module_handler_file = os.path.join(module_dir, to_filename(function.name) + '.js')
            handler_files_to_try.append(module_handler_file)
            module_handler_file_short = os.path.join(module_dir, to_filename(function.name)[0:32] + '.js')
            handler_files_to_try.append(module_handler_file_short)

        any_module_handler_file = os.path.join(self._repo_dir, to_filename(function.name) + '.js')
        handler_files_to_try.append(any_module_handler_file)
        module_handler_file_short =os.path.join(self._repo_dir, to_filename(function.name)[0:32] + '.js')
        handler_files_to_try.append(module_handler_file_short)

        for handler_file in handler_files_to_try:
            if os.path.isfile(handler_file):
                with open(handler_file, 'r') as f:
                    handler = f.read()
                self._notify_load(function, handler, handler_file)
                break

        if handler is None:
            handler = self._create_stub_handler(function)
            if len(handler_files_to_try[0]) > 260:
                handler_file = handler_files_to_try[1]
            else:
                handler_file = handler_files_to_try[0]
            handler_dir = os.path.dirname(handler_file)
            if not os.path.isdir(handler_dir):
                os.makedirs(handler_dir)
            with open(handler_file, 'w') as f:
                f.write(handler)
            self._notify_create(function, handler, handler_file)

        handler_mtime = os.stat(handler_file).st_mtime
        self._handlers[function.absolute_address] = (function, handler, handler_file, handler_mtime, time.time())

        return handler

    def sync_handler(self, function_address):
        (function, handler, handler_file, handler_mtime, last_sync) = self._handlers[function_address]
        delta = time.time() - last_sync
        if delta >= 1.0:
            changed = False

            try:
                new_mtime = os.stat(handler_file).st_mtime
                if new_mtime != handler_mtime:
                    with open(handler_file, 'r') as f:
                        new_handler = f.read()
                    changed = new_handler != handler
                    handler = new_handler
                    handler_mtime = new_mtime
            except:
                pass

            self._handlers[function_address] = (function, handler, handler_file, handler_mtime, time.time())

            if changed:
                self._notify_update(function, handler, handler_file)
oleavr commented 10 years ago

Great catch! I guess one way would be to add a to_filepath() function that takes care of the truncation of the final path. So if len(path) > 260 then trim off 8 + len(path) - 260 characters at the end of the filename (before the extension), and put a hex-encoded CRC32 there. Feel free to open a PR if you have time (if not I can have a look at it before the next release).

Thanks!

guillaume-uH57J9 commented 10 years ago

Thanks.

The PR will arrive soon.

I believe doing the truncation as a function of len(path) is actually a bad idea. Even if the function name should have a constant length, the complete path length may change (for instance the parent directory is renamed, moved, zip/unzipped...). So frida will be unable to find the scripts in all these situation, which would be really annoying. Or it would have to verify many tests to find the correct length, and we don't want to slow things down with unecessary I/O.

So, my PR will probably include a fixed limit on length + CRC32 for a more deterministic result.

Guillaume

2014-05-12 20:41 GMT+02:00 Ole André Vadla Ravnås notifications@github.com :

Great catch! I guess one way would be to add a to_filepath() function that takes care of the truncation of the final path. So if len(path) > 260then trim off 8

  • len(path) - 260 characters at the end of the filename (before the extension), and put a hex-encoded CRC32 there. Feel free to open a PR if you have time (if not I can have a look at it before the next release).

Thanks!

— Reply to this email directly or view it on GitHubhttps://github.com/frida/frida-python/issues/9#issuecomment-42871012 .

oleavr commented 10 years ago

Ahh yes, that's a very good point! Fixed length is definitely the way to go then. Thanks!