3

When calling WinAPI functions that take callbacks as arguments, there's usually a special parameter to pass some arbitrary data to the callback. In case there's no such thing (e.g. SetWinEventHook) the only way we can understand which of the API calls resulted in the call of the given callback is to have distinct callbacks. When we know all the cases in which the given API is called at compile-time, we can always create a class template with static method and instantiate it with different template arguments in different call sides. That's a hell of a work, and I don't like doing so.

How do I create callback functions at runtime so that they have different function pointers?

I saw a solution (sorry, in Russian) with runtime assembly generation, but it wasn't portable across x86/x64 archtectures.

polkovnikov.ph
  • 6,256
  • 6
  • 44
  • 79

2 Answers2

2

I've come up with this solution which should be portable (but I haven't tested it):

#define ID_PATTERN          0x11223344
#define SIZE_OF_BLUEPRINT   128   // needs to be adopted if uniqueCallbackBlueprint is complex...

typedef int (__cdecl * UNIQUE_CALLBACK)(int arg);


/* blueprint for unique callback function */
int uniqueCallbackBlueprint(int arg)
{    
    int id = ID_PATTERN;

    printf("%x: Hello unique callback (arg=%d)...\n", id, arg);

    return (id);
}


/* create a new unique callback */
UNIQUE_CALLBACK createUniqueCallback(int id)
{
    UNIQUE_CALLBACK result = NULL;
    char *pUniqueCallback;
    char *pFunction;
    int pattern = ID_PATTERN;
    char *pPattern;
    char *startOfId;
    int i;
    int patterns = 0;


    pUniqueCallback = malloc(SIZE_OF_BLUEPRINT);

    if (pUniqueCallback != NULL)
    {
        pFunction = (char *)uniqueCallbackBlueprint;
#if defined(_DEBUG)
        pFunction += 0x256;  // variable offset depending on debug information????
#endif /* _DEBUG */
        memcpy(pUniqueCallback, pFunction, SIZE_OF_BLUEPRINT);
        result = (UNIQUE_CALLBACK)pUniqueCallback;


        /* replace ID_PATTERN with requested id */
        pPattern = (char *)&pattern;
        startOfId = NULL;
        for (i = 0; i < SIZE_OF_BLUEPRINT; i++)
        {
            if (pUniqueCallback[i] == *pPattern)
            {
                if (pPattern == (char *)&pattern)
                    startOfId = &(pUniqueCallback[i]);
                if (pPattern == ((char *)&pattern) + sizeof(int) - 1)
                {
                    pPattern = (char *)&id;
                    for (i = 0; i < sizeof(int); i++)
                    {
                        *startOfId++ = *pPattern++;
                    }
                    patterns++;
                    break;
                }

                pPattern++;
            }
            else
            {
                pPattern = (char *)&pattern;
                startOfId = NULL;
            }
        }

        printf("%d pattern(s) replaced\n", patterns);

        if (patterns == 0)
        {
            free(pUniqueCallback);
            result = NULL;
        }
    }

    return (result);
}

Usage is as follows:

int main(void)
{
    UNIQUE_CALLBACK callback;
    int id;
    int i;

    id = uniqueCallbackBlueprint(5);
    printf("  -> id = %x\n", id);

    callback = createUniqueCallback(0x4711);
    if (callback != NULL)
    {
        id = callback(25);
        printf("  -> id = %x\n", id);
    }

    id = uniqueCallbackBlueprint(15);
    printf("  -> id = %x\n", id);

    getch();
    return (0);
}

I've noted an interresting behavior if compiling with debug information (Visual Studio). The address obtained by pFunction = (char *)uniqueCallbackBlueprint; is off by a variable number of bytes. The difference can be obtained using the debugger which displays the correct address. This offset changes from build to build and I assume it has something to do with the debug information? This is no problem for the release build. So maybe this should be put into a library which is build as "release".

Another thing to consider whould be byte alignment of pUniqueCallback which may be an issue. But an alignment of the beginning of the function to 64bit boundaries is not hard to add to this code.

Within pUniqueCallback you can implement anything you want (note to update SIZE_OF_BLUEPRINT so you don't miss the tail of your function). The function is compiled and the generated code is re-used during runtime. The initial value of id is replaced when creating the unique function so the blueprint function can process it.

Lukas Thomsen
  • 3,089
  • 2
  • 17
  • 23
  • Looks interesting. Replacing unique patterns like that is dangerous as hell for a number of reasons, but that could work. Let me think on this for a while. – polkovnikov.ph Oct 27 '14 at 21:44
  • This has the same problem as the answer linked to at the top. 64-bit structured exception handling is table-based. All code needs table entries to describe how to unwind the stack when an exception occurs. If you generate or copy code at runtime without updating the SEH tables your application may crash in odd ways. – arx Oct 28 '14 at 13:38
2

You can use the closure API of libffi. It allows you to create trampolines each with a different address. I implemented a wrapping class here, though that's not finished yet (only supports int arguments and return type, you can specialize detail::type to support more than just int). A more heavyweight alternative is LLVM, though if you're dealing only with C types, libffi will do the job fine.