A forum for reverse engineering, OS internals and malware analysis 

Discussion on reverse-engineering and debugging.
 #28478  by evelyette
 Wed May 11, 2016 1:41 am
Hello,

I'm writing an Ida Pro plugin, more precisely a multi-threaded IDAPython script. Basically the script runs a thread in the background and after calculating something (stumbling upon breakpoint), it will execute some action in the main GUI thread. Basically, I have to ensure that reading/writing to the IDB database is done in a safe manner and it must be done from the main thread, because the plugin will crash when done from the non-main thread.

Ida provides the idaapi.execute_sync function, which ensures the code is run in the main thread even if executed from a non-main thread. The function basically schedule a code that will be run in the main thread when it is safe to access the database. Below are the decorators that we can use for safe reading/writing to the IDB database: http://www.williballenthin.com/blog/201 ... decorator/.
Code: Select all
import functools
import idaapi

def idawrite(f):
    """
    decorator for marking a function as modifying the IDB.
    schedules a request to be made in the main IDA loop to avoid IDB corruption.
    """
    @functools.wraps(f)
    def wrapper(*args, **kwargs):
        ff = functools.partial(f, *args, **kwargs)
        return idaapi.execute_sync(ff, idaapi.MFF_WRITE)
    return wrapper


def idaread(f):
    """
    decorator for marking a function as reading from the IDB.
    schedules a request to be made in the main IDA loop to avoid
      inconsistent results.
    MFF_READ constant via: http://www.openrce.org/forums/posts/1827
    """
    @functools.wraps(f)
    def wrapper(*args, **kwargs):
        ff = functools.partial(f, *args, **kwargs)
        return idaapi.execute_sync(ff, idaapi.MFF_READ)
    return wrapper
In my plugin I'm using the following code:
Code: Select all
def idawrite(f):
    @functools.wraps(f)
    def wrapper(*args, **kwargs):
        logger.debug("WRAPPER1")
        ff = functools.partial(f, *args, **kwargs)
        logger.debug("WRAPPER2")
        val = idaapi.execute_sync(ff, idaapi.MFF_WRITE)
        logger.debug("WRAPPER3")
        return val
    return wrapper

class MyDebugger(idaapi.DBG_Hooks):
    """
    Class for debugging program by using IDA debug hooking functionality.
    """
    def __init__(self):
        pass

    @idawrite
    def process_continue(self):
        """
        Continue the debugging session.
        """
        #idaapi.msg("Continuing process execution.\n")
        logger.debug("G1")
        idaapi.request_continue_process()
        logger.debug("G2")
        idaapi.run_requests()
        logger.debug("G3")

    ...
Ida pro crashes with the following warning dialog, where's it's evident that the crash occurs right after printing "WRAPPER2", but before the "WRAPPER3" - in the "val = idaapi.execute_sync(ff, idaapi.MFF_WRITE)" line.
bug1.png
bug1.png (16 KiB) Viewed 479 times
From long hours of testing this, I've concluded that this happens at random intervals, but more importantly when the main thread is locked and execute_sync cannot execute the code in the main thread - you can try that by running the program and holding a left-mouse button pressed in Ida (which will occupy the main thread), and the program should crash right after being started: this is not always the case, probably because this is a timing issue and it's hard to obtain repeatable results with manual testing.

In any case, does anybody know how to proceed with the problem I've described.