hookAPI() return issue

c++ / delphi package - dll injection and api hooking
Post Reply
wineggdrop
Posts: 19
Joined: Mon Nov 18, 2019 6:18 am

hookAPI() return issue

Post by wineggdrop »

lpModule = "User32.dll";
lpAPIToHook = "SetWindowsHookExA";
if (HookAPI(lpModule,lpAPIToHook,Detour_SetWindowsHookExA,(PVOID*)&Real_SetWindowsHookExA))
{
OutputDebugPrintf("Mad Hook %s Successful",lpAPIToHook);
}
else
{
OutputDebugPrintf("Mad Hook %s Failure",lpAPIToHook);
}

about code works fine.if I change the code to:

lpModule = "ntdll.dll";
lpAPIToHook = "SetWindowsHookExA";
if (HookAPI(lpModule,lpAPIToHook,Detour_SetWindowsHookExA,(PVOID*)&Real_SetWindowsHookExA))
{
OutputDebugPrintf("Mad Hook %s Successful",lpAPIToHook);
}
else
{
OutputDebugPrintf("Mad Hook %s Failure",lpAPIToHook);
}

it still returns TRUE even SetWindowsHookExA isn't exported from ntdll.dll
iconic
Site Admin
Posts: 1065
Joined: Wed Jun 08, 2005 5:08 am

Re: hookAPI() return issue

Post by iconic »

This can happen because down the line the process may or may not load that module containing that API, if it happens to, it will be hooked and added to the internal hook chain. In other words, other traditional hook libraries will return FALSE if the pointer to the API is not present at the time of the Hook() call, but in madCodeHook the information is stored and new calls to loadlibrary are monitored so hooks may be applied later on so you don't miss them, especially useful if a targeted DLL is dynamically loaded/unloaded frequently.

--Iconic
wineggdrop
Posts: 19
Joined: Mon Nov 18, 2019 6:18 am

Re: hookAPI() return issue

Post by wineggdrop »

I do suspect that would happen,but Detour_SetWindowsHookExA never gets triggered if using the ntdll.dll module to hook.Detour_SetWindowsHookExA gets triggered if using users32.dll to hook.even weird thing is I using any randon dll such as "abc1234.dll"(this module doesn't exist on my system) to hook SetWindowsHookExA,hookAPI() return TRUE as well.Moreover,code like
HookAPI("Users32.dll","SetWindowsHookExA_testtest",Detour_SetWindowsHookExA,(PVOID*)&Real_SetWindowsHookExA) return TRUE as well,API SetWindowsHookExA_testtest doesn't exist and it return TRUE,so I wonder if HookAPI even return FALSE.
iconic
Site Admin
Posts: 1065
Joined: Wed Jun 08, 2005 5:08 am

Re: hookAPI() return issue

Post by iconic »

HookAPI() requires the name of the module that the API exists in, in this case SetWindowsHookExA() does *not* exist in the export table of ntdll.dll at all. It only exists inside user32.dll so the behavior is completely normal. It will never trigger on ntdll.dll, only when SetWindowsHookExA() is called from user32.dll because it only exists inside this DLL.

--Iconic
wineggdrop
Posts: 19
Joined: Mon Nov 18, 2019 6:18 am

Re: hookAPI() return issue

Post by wineggdrop »

iconic wrote:HookAPI() requires the name of the module that the API exists in, in this case SetWindowsHookExA() does *not* exist in the export table of ntdll.dll at all. It only exists inside user32.dll so the behavior is completely normal. It will never trigger on ntdll.dll, only when SetWindowsHookExA() is called from user32.dll because it only exists inside this DLL.

--Iconic
I am aware of that,but HookAPI() should return false when SetWindowsHookExA() does "not" exist in the export table of ntdll.dll,shouldn't it?
madshi
Site Admin
Posts: 10753
Joined: Sun Mar 21, 2004 5:25 pm

Re: hookAPI() return issue

Post by madshi »

Maybe it should. But there could be situations where you want a different behaviour. For example, let's say you do "HookAPI(weird.dll, weirdApi)", and let's say that there are different versions of "weird.dll" around, some of which export the "weirdApi" and some don't. Now if a process loads an old version of "weird.dll" which doesn't contain the API, then obviously you would expect HookAPI() to fail. But what happens if the process unloads "weird.dll" and loads a newer version of it which *does* export "weirdApi"? In that situation madCodeHook would actually automatically successfully hook the "weirdApi" for you in the moment when the process loads the new version of the dll.

Now I admit, the above scenario will be rare. But it *can* happen.

I'd like to make the following argument: If your code isn't buggy, then you will have a good reason to ask madCodeHook to hook a specific API in a specific DLL, won't you? So madCodeHook tends to trust that your code isn't buggy, and that the API will actually exist in some version of the dll somewhere. If it didn't, you wouldn't ask madCodeHook to hook it, would you? So based on that madCodeHook just thinks to itself: "Well, the API isn't available now, but clearly wineggdrop thinks that some version of the dll does export this API, so I'd better prepare for that situation, in case a new version of the dll gets loaded later".

Makes sense?
wineggdrop
Posts: 19
Joined: Mon Nov 18, 2019 6:18 am

Re: hookAPI() return issue

Post by wineggdrop »

madshi wrote:Maybe it should. But there could be situations where you want a different behaviour. For example, let's say you do "HookAPI(weird.dll, weirdApi)", and let's say that there are different versions of "weird.dll" around, some of which export the "weirdApi" and some don't. Now if a process loads an old version of "weird.dll" which doesn't contain the API, then obviously you would expect HookAPI() to fail. But what happens if the process unloads "weird.dll" and loads a newer version of it which *does* export "weirdApi"? In that situation madCodeHook would actually automatically successfully hook the "weirdApi" for you in the moment when the process loads the new version of the dll.

Now I admit, the above scenario will be rare. But it *can* happen.

I'd like to make the following argument: If your code isn't buggy, then you will have a good reason to ask madCodeHook to hook a specific API in a specific DLL, won't you? So madCodeHook tends to trust that your code isn't buggy, and that the API will actually exist in some version of the dll somewhere. If it didn't, you wouldn't ask madCodeHook to hook it, would you? So based on that madCodeHook just thinks to itself: "Well, the API isn't available now, but clearly wineggdrop thinks that some version of the dll does export this API, so I'd better prepare for that situation, in case a new version of the dll gets loaded later".

Makes sense?
It does make sense,however,adding one more parameters to HookAPI would be nice
#define HOOK_SUCCESS 0x00
#define API_NOT_EXPORT 0x01
#define MODULE_LOAD_ERROR 0x02
#define ...........
HookAPI(LPCSTR pszModule, LPCSTR pszFuncName,PVOID pCallbackFunc, PVOID *pNextHook,DWORD &dwFlags)
the dwFlags can return one of the above define value,so can check what's going on about the hook such as if returning API_NOT_EXPORT,I would know the API isn't exported from that module or anything else.would it be even better?
madshi
Site Admin
Posts: 10753
Joined: Sun Mar 21, 2004 5:25 pm

Re: hookAPI() return issue

Post by madshi »

I guess I could do that, but it would really only help during development, but it would not improve anything on the PC of the end user, or would it?
wineggdrop
Posts: 19
Joined: Mon Nov 18, 2019 6:18 am

Re: hookAPI() return issue

Post by wineggdrop »

madshi wrote:I guess I could do that, but it would really only help during development, but it would not improve anything on the PC of the end user, or would it?
it would be very helpful since windows have so many versions now and even MSDN can't provide the correct info about which API exported from which system module.some API exported from 64 bit DLL but not from 32 bit dll,quite messy
iconic
Site Admin
Posts: 1065
Joined: Wed Jun 08, 2005 5:08 am

Re: hookAPI() return issue

Post by iconic »

Hello,

madCodeHook was built with one purpose in mind, powerful underlying code encapsulated into an "easy" to use API, hence the "dumb" untyped datatypes and not needing to case whether a function call succeeded or not based solely upon numeric return values (positive or negative signed int values (such as Microsoft's NTSTATUS type etc.). The more params a function API grows, or get/set flags, switch casing return codes to judge failure and success etc. the more confusing to the end user. I'm sure you can understand this and it entirely defeats the original purpose of the first sentence and goal of the package. Now, in saying this, if you care about rigidity, you don't have to call HookAPI(), which can and often return TRUE for unknown modules and APIs that may or may not be loaded/hooked later on.

You can call HookCode() and in the moment you call it, have a much more precise BOOL result. For example, HookCode((PVOID)GetProcAddress(GetModuleHandle("Wrong.dll"), "WrongAPI"), Callback, (PVOID*)&NextFunc) will surely fail for you, while HookAPI() will attempt to *follow* this in the process lifetime, whether it ever gets loaded or exists or not while returning an optimistic TRUE value. Again though, this can and will happen, so HookAPI() is more advanced but less in a sense when it comes to real-time informative return values to the programmer. Both functions exist for different reasons, HookAPI() for exported APIs (by name string and ordinal value), if it’s ever to be, and HookCode() for unexported code, which can be any address in the process' address space.

Adding new params would force headers to be changed and documented, even if optional. So, it creates a lot more work
for developers to attempt to figure out what we have potentially already hoped the caller would have, since code hooking
and injection is not a novice undertaking, we can somewhat assume a dev will create his/her own wrappers or modifications tailored to their own liking that suits their specific use case needs.

--Iconic
madshi
Site Admin
Posts: 10753
Joined: Sun Mar 21, 2004 5:25 pm

Re: hookAPI() return issue

Post by madshi »

@iconic,

well, HookAPI() already has a "flags" parameter, so I would not have to change the API definition, I would just have to add a flag.

However, your HookCode() idea is good.

There's another very simple workaround for wineggdrop: He could simply call "GetProcAddress(GetModuleHandle("Wrong.dll"), "WrongAPI")" manually and check the return value. If it's NULL, and if "GetModuleHandle("Wrong.dll")" is non-NULL, then that's the situation he's talking about. So it's *very* easy for him to test himself. He could even make a very simple HookAPI2() wrapper to add this handling.
iconic
Site Admin
Posts: 1065
Joined: Wed Jun 08, 2005 5:08 am

Re: hookAPI() return issue

Post by iconic »

Ahh that's right, can't say I've needed to use them though much at all. Well, the new flag(s) would still need to be documented and implemented of course, which is something the user/dev can/should do is what I was getting at. And yes, HookCode dismisses a NULL pfn, otherwise it would be Houdini considering it can't hook a NULL pointer or store the module name and/or API for later like the HookAPI() counterpart. :D Most of what he had asked, if not all, can actually easily be implemented on the user end. If I had say 50 API hooks (in a CollectHooks() frame for example) I'd prefer not having to manually do if (pfn != NULL) HookAPI() because those 50 lines can easily become 100. But of course madCodeHook knows better and disregards the NULL pointer like it should. Another thing I meant to mention, instead of extending the Flags param, it might also be convenient that when returning True a meaningful success "error" could also be returned such as ERROR_STATUS_PENDING, indicating True for (if) that happens and at least letting the user know it hasn't necessarily been hooked yet at that particular time yet is returning True because there's nothing to potentially fail on yet. Anyhow, just an idea of course. I'm speaking more of the inverse than requested by him here, instead of kicking out a False return with a flag or whatever to indicate why, I'm talking about setting an error code when True instead, as per the PENDING example if it's to-be in the future etc.

--Iconic
Post Reply