Bugs in EasyHook 2.6 stable

Oct 19, 2010 at 1:49 PM

I've found, so far, a few problems with EasyHook.

1, In the documentation for the unmanaged API, the function LhSetGlobalInclusiveACL (section 1.1.8) is given a prototype using the name of LhSetGlobalExclusiveACL. Cut-and-paste error.

2. In caller.c, in LhBarrierCallStackTrace, the function captures 32 level of stack trace no matter what is passed in as the InMaxMethodCount. This wrong on two counts: first, if you pass less than 32, you get a buffer overflow. Two, the docs specifies that the maximum allowed is 64 but clearly it will never capture more than 32 as currently written. Just change the hard-coded 32 to the InMaxMethodCount.

That's all so far, but I'm a bit disappointed in a library that has so many ecstatic reviews.

Also, there is little description of how to use the library in unmanaged code. One huge oversight is that nowhere is explained how to call the original function. Turns out that the first call within the hook to the original function using the original name is routed to the original function magically. There two downside to this: one is the lack of explanation of this behavior that made me search how to call the old function for a while. The second is that it's impossible to call the original function more than once from within the hook. I was hooking HeapAlloc and needed to call the original function more than once to get memory to keep a trace if the calls and had to come up with a work-around.

Oct 19, 2010 at 3:40 PM

I think the demo-applications make pretty clear how the lib has to be used (copy/paste).

 

Re: original function magically

See here: http://easyhook.codeplex.com/Thread/View.aspx?ThreadId=227633  Just disable the TLB thread lock barrier and live happily ever after.

Oct 19, 2010 at 6:05 PM

A few paragraph in the unmanaged docs would fulfill the need to guide first time users of the library. I don't want to parse the source code of demo application, especially since they will contain a lot of fluff I don't need to understand the library, like UI code or specific usage patterns.

 

As for the code, thanks, it seems to be exactly what I originally needed. The problem for me, is that I don't know if the code I'm calling (C++ STL) will end-up allocating memory or not. What I've done is write a special STL allocator that takes memory from a well-known private heap that doesn't get traced.

Oct 19, 2010 at 9:03 PM

Another small glitch, in caller.c, in LhUpdateModuleInformation(). If the module path is not found or doesn't contain a backslash, then the module name is not set and may contains a garbage pointer, which will be returned when LhBarrierPointerToModule() is called. I added

List[ModIndex].ModuleName = 0;

Before the loop that tries each path to make sure the pointer is initialized to NULL if the loop faisl to find anything.

May 29, 2012 at 11:03 PM

Interesting, i too have looked all over to find out "how to call the original function".  It seems that this is of.... ummm... high importance haha.  In about 99% of cases, I'm sure you'd be interested in calling the original  code so you don't break the application you are injecting into.  I did not know that the "first call within the hook to the original function using the original name is routed to the original function".  I have not tested this yet, but if this is true, that's a relief.  I found some posts talking about using the trampoline pointer to get back to the original method call, but it seemed overly complicated and i wasn't looking forward to messing with it.  Using the actual hijacked function name is simpler for sure, but i agree 100% that this is complete "magic".  Should have been documented in several places.  I'm in my 15th hour of plowing through this thing and I just barely found that out.  The silly MessageBeepHook() example where it steals the MessageBeep() method doesn't even demonstrate how to call the original function.  Just has some non-nonsensical method calls into the EasyHook API without any explanation of what its doing.  Kind of defeats the purpose of an example.

 

BOOL WINAPI MessageBeepHook(__in UINT uType)
{
    /*
        Test barrier methods...
    */
	PVOID					CallStack[64];
	MODULE_INFORMATION		Mod;
	ULONG					MethodCount;

	LhUpdateModuleInformation();

	LhEnumModules((HMODULE*)CallStack, 64, &MethodCount);

	for(int i = 0; i < MethodCount; i++)
	{
		LhBarrierPointerToModule(CallStack[i], &Mod);
	}

	LhBarrierCallStackTrace(CallStack, 64, &MethodCount);

	LhBarrierGetCallingModule(&Mod);

    return TRUE;
}

 

See what I mean.  Spent a lot of time trying to figure things out from this "simple" example and didn't turn up with much until encountering this post.

Coordinator
May 30, 2012 at 11:56 PM

EasyHook restores the original pointer to the function immediately before calling your hook function, so calling the original API method anywhere within your replacement will call the original.

EasyHook then replaces the original pointer again when your hooked function has completed. I.e. EasyHook is taking care of all the mechanics of the trampoline for you.

There is a task for updating documentation for the 2.7 release - anyone is welcome to assist with this. Any help with the project whatsoever is always appreciated (including posts like yours above).

Cheers,

J

May 31, 2012 at 8:59 PM

I'd just like to point out some conflicting notions in this discussion thread and clarify with results I've just tested.

 

pierrebai:

Turns out that the first call within the hook to the original function using the original name is routed to the original function magically.

spazzarama:

EasyHook restores the original pointer to the function immediately before calling your hook function, so calling the original API method anywhere within your replacement will call the original.

EasyHook then replaces the original pointer again when your hooked function has completed. I.e. EasyHook is taking care of all the mechanics of the trampoline for you.

 

Pierrebai's comments suggests that only "the first call" to the original function gets routed to its original implementation of the function.  Spazzarama's suggest that "all" calls made to the original function within the hook get routed to the functions original implementation.  The latter explanation makes more sense to me, but I wanted to test it out, at least in unmanaged code.  I found that spazzarama's explanation is correct.  All calls made to the original function within the hook replacement function get routed to the original function implementation, not just the first call.

Just wanted to clear up any confusion.