EasyHook / EasyHook

EasyHook - The reinvention of Windows API Hooking
https://easyhook.github.io
MIT License
3.01k stars 646 forks source link

Hooking a function with variadic arguments #246

Open asaadmohammed74 opened 6 years ago

asaadmohammed74 commented 6 years ago

how can i hook _snprintf ? i don't know how to define its delegate since i don't know what is the alternative of c ...

justinstenning commented 6 years ago

Take a look at this delegate implementation (I haven’t tested this myself): https://www.ownedcore.com/forums/world-of-warcraft/world-of-warcraft-bots-programs/wow-memory-editing/301588-how-hooking-functions-wow-variable-arguments-c.html

...it turned out you can't use the arglist keyword in delegates... ...To my surprise, I discovered that arglist translates directly into a structure. This structure is a special structure and is called RuntimeArgumentHandle. This structure can not be boxed into an object, which means you won't be able to call the original function if you use a late bound delegate, like WhiteMagic does. Fortunately I wrote my own library that uses generics and it works with that.

RuntimeArgumentHandle was originally thought to be used together with ArgIterator to iterate through arguments. This, however, throws an exception when you do it in WoW (?!? why?). Fortunately RuntimeArgumentHandle contains a single member which is an IntPtr and points to the first argument on the stack. Unfortunately Microsoft made this member internal, so my first thought was to use Reflection. But oops, RuntimeArgumentHandle can not be boxed to an object, so you can't get the value on the instance! I fixed this by simply casting a pointer to it to an IntPtr* and dereferencing that.

IntPtr ptr = *(IntPtr*)&args;

Here is a fictional example that is for a function that accepts a string like formatted like %b%d%f and an arg for each of those three format specifiers (e.g. SomeVariadicFunc("%b%d%f", true, 123, 1.123)).

[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
private delegate void SomeVariadicFuncDelegate(string fmt, RuntimeArgumentHandle args);
private static unsafe void SomeVariadicFuncHook(string fmt, RuntimeArgumentHandle args)
{
    // call original
    OriginalSomeVariadicFunc(fmt, args);

    if (fmt == null)
        return;

    IntPtr ptr = *(IntPtr*)&args;
    var vals = new List<object>(fmt.Length / 2);
    for (int i = 1; i < fmt.Length; i += 2)
    {
        switch (fmt[i])
        {
            case 'b':
                vals.Add(Memory.Read<uint>(ptr) != 0);
                ptr += 4;
                break;
            case 'd':
                vals.Add(Memory.Read<int>(ptr));
                ptr += 4;
                break;
            case 'f':
                vals.Add(Memory.Read<double>(ptr));
                ptr += 8;
                break;
            case 'u':
                vals.Add(Memory.Read<uint>(ptr));
                ptr += 4;
                break;
            case 's':
                vals.Add(Memory.Read<string>((uint)ptr, 0));
                ptr += 4;
                break;
        }
    }
}
zengfr commented 3 years ago

it is my code but not working https://github.com/EasyHook/EasyHook/issues/351