Click here to Skip to main content
15,881,882 members
Articles / Programming Languages / C++
Article

Win32 API hooking: Another reason why it might not work

Rate me:
Please Sign up or sign in to vote.
4.94/5 (31 votes)
22 Dec 20056 min read 136.1K   106   17
This article illustrates an undocumented feature of the Portable Executable format, which makes Win32-hooking based upon IAT-patching fail.

Introduction

Like (many) other people, I've been quite fascinated by the possibilities given by Win32 hooking. After reading API hooking revealed, I decided the best way to do it was to rewrite the Import Address Table (IAT) in the application I targeted. This technique is called IAT-patching. It worked quite well for some time, but then, my program failed to hook properly on a number of target applications, such as the MSN Messenger or the latest versions of Emule. I first suspected some kind of protection within these applications, but since Emule is open source, I could download the code and see there was no such thing that should have made IAT-patching fail. Then, an in-depth look at how the function I targeted was called from MSN and Emule allowed me to understand where the problem came from: the targeted function, imported from a standard DLL, was not loaded into these executables in the standard way, based upon the IAT (and which is described by Matt Pietrek in MSDN). I'll describe here how the loader deals with these functions, and why this undocumented feature of the Portable Executable format makes IAT-patching powerless.

Background

Since my goal here is to show why IAT-patching does not always work, it would be quite useful to know how IAT is supposed to work. The two articles I mentioned in the introduction are the best places for a starter.

The standard way of calling an imported function

When a function is imported into an executable, the loader stores the actual address of this function into the IAT before any call to this function is made. In an application that calls its imported functions in a standard way, here is what you get when you make such a call after the loader has done its job:

ASM
00412CB7   CALL DWORD PTR DS:[0042C544]

The four bytes at DS:[0042C544] are inside the IAT, they store the actual address (that is 0x77D5C411) of the function I intend to hook (in this case: SetMenuItemInfoW from User32.dll). Rewriting the IAT basically means I'll change the address written at DS:[0042C544] and write the address of my replacement function instead.

How SetMenuItemInfoW is called in Emule

The problem is: SetMenuItemInfoW is not called this way in Emule. The first thing to say about SetMenuItemInfoW is that you won't even find it in the IAT if you use programs such as PEView or DUMPBIN. And still, it is used by emule.exe. Here's the assembler code for Emule:

ASM
00568A65   CALL DWORD PTR DS:[0074BE08]

This assembler line is the line that corresponds to the C++ line that calls SetMenuItemInfoW, but the address stored at DS:[0074BE08] is not the address of SetMenuItemInfoW, and the four bytes at 0074BE08 are not inside the IAT: there's no way you could change the value there. The address pointed to by PTR DS:[0074BE08] is 005D4F70, and it is inside emule.exe itself. Here is what we have at 005D4F70:

Image 1

As you can see, instead of calling SetMenuItemInfoW, the code calls a function located at 005D5529. I'll call it the "loader function" in this article. This function takes five arguments, three of them are interesting to describe:

  • Arg3=0074BE08 is the address used just before (at 00568A65: CALL DWORD PTR DS:[0074BE08]). (It is cast here as the address of an ASCII string, this is due to a little problem with my assembler debugger.)
  • Arg2 is the name of the function originally called: "SetMenuItemInfoW".
  • Arg1 is the name of the DLL that exports SetMenuItemInfoW, "user32.dll".

(You can see that this function is also called for other imported functions such as EnumFontsW or GetDateFormatW...).

Immediately after the call to the loader function, there is a line of code (at 005D4F8F) that calls the same address as the line at 00568A65 : JMP DWORD PTR DS:[0074BE08]. Remember the address stored at DS:[0074BE08] is the address of the loader function. That is, once the loader function has returned, this JMP instruction will cause the loader function to be called again, in an infinite cycle.

I won't give too much useless information as to what the loader function does. Here is the most important: it first calls LoadLibrary to get a handle to the required DLL, and then it looks up the name of the function originally called inside the Export Address Table of that DLL in order to calculate the actual address of this function (note: the loader function does not use GetProcAddress).

Once the loader function has returned, the line that follows it is modified: it is still JMP DWORD PTR DS:[0074BE08], but the location pointed to by DS:[0074BE08] is no longer 005D4F70 but the actual address of SetMenuItemInfoW (= 77D5C411). So, the JMP will not call the loader function again, but SetMenuItemInfoW instead.

Image 2

Now, we can jump to SetMenuItemInfoW for good.

As you noticed, the first time, at 00568A65, we "called" the address DS:[0074BE08] (CALL DWORD PTR ...), and the second time, at 005D4F8F, we "jumped" (JMP DWORD PTR ...). This way, when SetMenuItemInfoW returns, it will not return to the line where it was actually called (with a JMP) but to the original CALL line.

Of course, since the address in the JMP and in the CALL are the same, the next time we call SetMenuItemInfoW, at 00568A65, we will call the actual function not the protocol we had to go through for the first call.

The next schemas will illustrate this discussion:

  • before the first call:

    Image 3

  • right after the loader function has returned:

    Image 4

  • all the subsequent calls:

    Image 5

What are the consequences for Win32 hooking?

Since this mechanism does not use IAT, there is no way you can intercept calls to SetMenuItemInfoW by altering the IAT. And there is no way to say where to find the four bytes that will be altered by the loader function: those four bytes are inside the executable, exactly where the imported function is called.

There are still two possibilities that could allow you to hook such a function.

The first one is similar to IAT altering: but instead of rewriting entries in the IAT, you have to rewrite entries in the Export Address Table (EAT) of the DLL that hosts the targeted function. But this has to be done before any call to the targeted function is made; once the first call is made, the loader function has already looked up the EAT and written the actual address. So the trick is to alter the EAT so that the loader function will read the address of your own function.

The second possibility consists in changing the first few bytes at the beginning of the function you want to hook, the new bytes will be a JMP to your own function.

Image 6

This solution is in fact the most efficient since it enables you to bypass not only the protocol described in this article but also other forms of protection against Win32-hooking (use of GetProcAddress, encrypted executables ...). But it goes beyond the scope of this article. Anyway, you'll find all the necessary information here.

Conclusion

Going through this protocol is a lengthy process, so it is implemented only if the targeted function is called from a single location in the executable. Calling the loader function only once is similar to processing the IAT: both protocols must load the DLL and calculate the actual address of the function. But, while the IAT must be processed when the executable is launched, the process described here happens only when the function is called for the first time, causing the executable to launch faster. It is thus similar to the delay-import function mechanism.

License

This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here


Written By
Web Developer
France France
This member has not yet provided a Biography. Assume it's interesting and varied, and probably something to do with programming.

Comments and Discussions

 
GeneralGreat! Pin
Murat DOGANCAY9-Jan-10 4:54
Murat DOGANCAY9-Jan-10 4:54 
Questiondisplay a splash screen as soon as app startup? Pin
yph200401079-Dec-07 20:55
yph200401079-Dec-07 20:55 
GeneralThx Pin
Kharfax27-Jun-06 3:33
Kharfax27-Jun-06 3:33 
QuestionWhat triggers this mechanism Pin
yafan15-Jan-06 5:56
yafan15-Jan-06 5:56 
GeneralDll for Hook the API OpenProcess Pin
Vitoto4-Jan-06 5:56
Vitoto4-Jan-06 5:56 
Hi man, is posible using you code fix my problem ?

Global Model Image.
http://www.legion-of-terror.cl/download/temp/model.jpg

Detail :
This is the API OpenProcess or Class in .Net GetProcessID
http://msdn.microsoft.com/library/default.asp?url=/library/en-us/wcekernl/html/_wcesdk_win32_OpenProcess.asp

Cheat program use the API and use in parameter PID the Game in Memory.
Game is not .Net program, i not have source, only launch from .Net code.

Example : Game Pid is : 5675

When cheat program "program01.exe(pid 1234)" use the API then in parameters use the value 5675.

Example :

IDProcess = 5675
HandleWindow = OpenProcess(PROCESS_ALL_ACCESS,False,IDProcess);

For get value 5675 use others APIs :

IDProcess = FindWindow -> Get using Window Name -> "Game Window Name"
IDProcess = GetHProcExe -> Get using game.exe
IDProcess = GetWindowProcessID -> Get using Directly IDProcess -> PID the Game.exe

I not want stop this 3 methods, i need Stop OpenProcess where values will be passed.

After OpenProcess get Access to Game Process using parameter : PROCESS_ALL_ACCESS
Return HandleWindow and use after in API WriteProcessMemory
WriteProcessMemory(HandleWindow, Offset, Value, 1, 0&)

From my ".Net Program" i use PID 1234 "program01.exe" for Spy, Hook or Intercept if Use API OpenProcess.

When program01.exe was detected using the API OpenProcess then Log, Hook or Intercept the value in parameter used.

If value is IDProcess = 5675, then assume "program01.exe" is Cheat Program trying attack GAME Process.

After check program02.exe and detect not using API OpenProcess, skip for monitor others process.

GeneralContact Pin
choose file29-Dec-05 22:12
choose file29-Dec-05 22:12 
GeneralAm I Missing Something... Pin
qwerty666@codeproject.com28-Dec-05 5:03
qwerty666@codeproject.com28-Dec-05 5:03 
GeneralRe: Am I Missing Something... Pin
meggash m28-Dec-05 12:35
meggash m28-Dec-05 12:35 
GeneralRe: Am I Missing Something... Pin
qwerty666@codeproject.com29-Dec-05 3:03
qwerty666@codeproject.com29-Dec-05 3:03 
GeneralInvaluable ! Pin
WREY26-Dec-05 3:44
WREY26-Dec-05 3:44 
GeneralInteresting stuff! Pin
Nish Nishant23-Dec-05 0:18
sitebuilderNish Nishant23-Dec-05 0:18 
QuestionDelayed loading? Pin
dighn22-Dec-05 13:55
dighn22-Dec-05 13:55 
AnswerRe: Delayed loading? Pin
meggash m22-Dec-05 23:23
meggash m22-Dec-05 23:23 
GeneralRe: Delayed loading? Pin
J0ker23-Dec-05 2:40
J0ker23-Dec-05 2:40 
GeneralRe: Delayed loading? Pin
dighn24-Dec-05 17:23
dighn24-Dec-05 17:23 
GeneralRe: Delayed loading? Pin
c2j226-Dec-05 23:31
c2j226-Dec-05 23:31 
GeneralRe: Delayed loading? Pin
meggash m28-Dec-05 12:10
meggash m28-Dec-05 12:10 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.