Click here to Skip to main content
6,632,253 members and growing! (17,915 online)
Email Password   helpLost your password?
General Reading » Hardware & System » General     Intermediate

Intercepting WinAPI calls

By Andriy Oriekhov

An article about intercepting WinAPI calls.
VC6, VC7Win2K, WinXP, Win2003, Dev
Posted:6 Mar 2006
Updated:31 May 2006
Views:59,121
Bookmarked:69 times
Announcements
Loading...
 
Search    
Advanced Search
Add to IE Search
printPrint   add Share
      Discuss Discuss   Broken Article?Report  
27 votes for this article.
Popularity: 6.68 Rating: 4.67 out of 5

1
1 vote, 3.7%
2

3
4 votes, 14.8%
4
22 votes, 81.5%
5

Introduction

API calls interception is the task that allows to get access to some parts of other's programs. Lots of programmers spend time on developing and describing various methods which allow that access. Such methods are used in many anti-viruses and anti-spyware. Besides, sometimes, intercepting can help you to find errors in your application. However, it is not a secret that some viruses use it too. I spent much time to find and understand the technique of interception. I would like to describe here the results of my research.

Method description

First of all, you need to read the following article to understand the basics of the interception mechanism: HookSys (written by Ivo Ivanov). It was very helpful for me, and I used the sample code from it. However, it does not solve all my problems because Ivo's samples sometimes miss very important API calls. It happens when the application starts up too fast and the intercepting service has no time to inject the DLL. After some research, I found the actual problem, and it was related to using the kernel mode function, SetCreateProcessNotificationRoutine. This function is used to receive notification events about any new process creation. Such a notification is often fired when the process has already been started. Therefore, I needed to find a way to improve Ivo's code.

As far as I know, the execution of all Windows processes consists of the following steps:

  • initial process loading;
  • creating the main thread for the process in the suspended state;
  • mapping of the NT.DLL into the address space of the process;
  • mapping all needed DLLs, and calling their DllMain with the DLL_PROCESS_ATTACH reason;
  • resuming the main process' thread.

The step right before the main thread resuming looks like the most comfortable for injection because the process is in suspended state and none of its instructions have been executed yet.

Most of the work on the process creation is done in the kernel mode, so to change this algorithm, you need to intercept the kernel mode functions NtCreateProcess() and NtCreateThread(). The CONTEXT structure, the pointer to which is passed to the function NtCreateThread(), contains a member called EAX. I found that it equals to the process' start address in user mode, so if you can change it, then you can get the control right after process creation and before starting. To solve this task, I wrote a kernel mode driver. It starts while the system starts up.

There are some initialization steps:

  1. starting;
  2. receiving configuration from the user mode;
  3. intercepting kernel mode functions such as: NtCreateProcess(), NtCreateThread(), NtTerminateProcess(), NewNtCreateProcessEx() - for Windows 2003 Server.

A handler to the NtCreateThread() function contains code that will do most of the interesting jobs. Here is a brief description of its algorithm:

  1. allow access to the creating process by calling ObReferenceObjectByHandle();
  2. remember the main thread start address (ThreadContext->EAX);
  3. "jump" to the context of the creating process by calling KeAttachProcess();
  4. allocate memory for my code by calling ZwAllocateVirtualMemory(), similar to the well known technique for CreateRemoteThread() in user mode;
  5. copy the small code to the allocated memory that will load my DLL. This code looks like:
    push pszDllName
    mov  ebx, LoadLibraryAddr
    call [ebx]
    mov  eax, Win32StartAddr
    push eax
    ret
    pszDllName: db 'example.dll';
  6. "jump" to the initial process;
  7. change the thread start address (ThreadContext->EAX) so it will point to the allocated memory.

That is all. You can download and compile the complete source code for this article. Note: the sample is fully functional and quite enough for basic understanding, but for real usage, it might be rewritten.

Compiling the code

You need the NTDDK to be installed on your computer. I'm using MSVS 6.0 for compiling NtProcDrv, and MSVS 7.1 for the rest of the projects.

History

  • 2006-03-06 - Submitted.
  • 2006-05-31 - Source codes and binaries are updated.

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

About the Author

Andriy Oriekhov


Member

Occupation: Chief Technology Officer
Location: Ukraine Ukraine

Other popular Hardware & System articles:

Article Top
You must Sign In to use this message board.
FAQ FAQ 
 
Noise Tolerance  Layout  Per page   
 Msgs 1 to 25 of 42 (Total in Forum: 42) (Refresh)FirstPrevNext
QuestionProblem in Release Build Pinmembernoumantariq0:49 29 Oct '09  
GeneralCorrection in assembly code [modified] PinmemberMattsUserName16:04 27 Jul '09  
The instruction:
call [ebx]
should actually be
call ebx

I didn't think about it at first, and just assumed this was correct to do dereference ebx (which are what the the brackets mean "[]")
But when doing some testing in a VB6 app using the code bytes, it kept crashing.

BTW, if you are curious... I am using the code bytes in VB6 because I am testing out a hook Dll I am creating by hooking NtCreateThread, and then inject my Dll by changing this ThreadContext->Eax register)

After my VB6 app kept crashing, I finally realized that when doing: mov ebx, pLoadLib ... that is like saying ebx = pLoadLib. So really ebx is not a pointer to LoadLibrary's address, but is its actual address, so therefore we do NOT want to dereference it with the "[]"









Note: The follow are just some assembly tips, if you are new to it (like me :P)

If you are an assembly noob like me, then Visual Studio is your friend (I used VS2005)
You can use its debugger window (Hotkey = Alt+6, when in Debug mode) to translate C++ assembly instructions to its equivalent in assembly, and also its code bytes.

Example of using VS to translate these instructions to Asm code and Bytes:

_asm{
push 0x10000000
mov ebx, 0x2000000
call ebx
mov eax, 0x3000000
push eax
ret
}

1. Set a break point on the first instruction (or before it starts executing, to prevent any crashing)
2. Run app in debug mode (F5)
3. Open up the Disassembly window tab using the hotkey: Alt+8
4. Right click --> Check: Show Code Bytes
Note: The underscore "_" is required on the "asm" keyword. A double underscore also works "__" but it seems to do the same thing.

Now it shows the current addresses there this data currently has been placed in memory, the original source code, the assembly equivalent, and the hex byte values(AKA opcodes) that those instructions translate to





Visual studio can also do the reverse and take byte codes and translate them to assembly instructions (This trick was a little harder to find)
To do so you have to use the _emit (Technically the keyword is called "_asm _emit")
Ex:

_asm{
_emit 0xFF
_emit 0xD3
}

Notice how that translates to "call ebx"...



Unfortunately it isn't easy to place these on the same line (well it wasn't easy to figure out at least)... and no semicolons dont work at all.
Here is a mov eax, 10000000 instruction:

_asm _emit 0xB8 _asm _emit 0x00 _asm _emit 0x00 _asm _emit 0x00 _asm _emit 0x10

Yikes!



But here is a way to make it less long for each instruction:

#define bt _asm _emit
bt 0xB8 bt 0x00 bt 0x00 bt 0x00 bt 0x10

Now you can just use the custom defined "bt" keyword.
Yay!





Last Tip...
To see the bytes in a linear format: (Exactly how they appear in memory)
1. From the Disassembly, under each instruction there is an address to the far left side. - Copy it
2. Open up the memory window: Hotkey = Alt+8 (When in debug mode)
3. Type in 0x (for hex) and then paste in the address. Ex: 0x00411A1E







Hopefully this is helpful to someone out there, and also it shows the author and author that that instruction should be:
call ebx, not call [ebx]

modified on Monday, October 5, 2009 9:28 PM

Sign In·View Thread·PermaLink
GeneralVista compatibility? PinmemberAmit Vilas Shinde2:36 7 Mar '08  
GeneralHi, tezm. I ran into the same question as yours. Pinmembermybayern197423:02 2 Aug '07  
GeneralRe: Hi, tezm. I ran into the same question as yours. Pinmembertezm7:55 4 Aug '07  
GeneralRe: Hi, tezm. I ran into the same question as yours. Pinmembermjmim13:34 10 Oct '08  
QuestionError installing service, how to solve it? Pinmembertezm9:29 27 Jul '07  
AnswerRe: Error installing service, how to solve it? Pinmembertezm2:28 28 Jul '07  
GeneralI want to support vista O.S., please help me PinmemberHsiao, Tsu Tair17:46 25 Jul '07  
GeneralInjection works fine, but impossible to subclass using this method? [modified] Pinmemberehaerim12:05 5 Jan '07  
GeneralHow to inject dll into specific processes, not all the processes? [modified] Pinmemberehaerim22:28 30 Dec '06  
GeneralRe: How to inject dll into specific processes, not all the processes? Pinmemberehaerim11:35 5 Jan '07  
GeneralNewNtTerminateProcess called with NULL as the value of ProcessHandle. Why? Pinmemberehaerim21:59 30 Dec '06  
GeneralSecond MessageBox does not show up when process termination but only sound. Pinmemberehaerim15:56 2 Dec '06  
GeneralRe: Second MessageBox does not show up when process termination but only sound. Pinmembertezm2:25 28 Jul '07  
GeneralVC++ 6.0 Version available? Pinmemberehaerim11:52 2 Dec '06  
GeneralHow to hook multiple instances of Notepad? [modified] Pinmemberehaerim16:10 30 Nov '06  
GeneralWhere can I get info about SetCreateProcessNotificationRoutine? [modified] Pinmemberehaerim14:42 30 Nov '06  
Generalstarting programs by clicking on document icons Pinmemberrichardmoss6:43 15 Nov '06  
GeneralRe: starting programs by clicking on document icons PinmemberAndriy Oriekhov1:25 23 Nov '06  
GeneralHow to get the executable name from the created process?? Pinmembergirm2:41 13 Sep '06  
GeneralRe: How to get the executable name from the created process?? PinmemberAndriy Oriekhov3:53 14 Sep '06  
GeneralRe: How to get the executable name from the created process?? Pinmembergirm21:40 24 Oct '06  
GeneralRe: How to get the executable name from the created process?? PinmemberAndriy Oriekhov12:32 25 Oct '06  
GeneralRe: How to get the executable name from the created process?? Pinmemberehaerim11:32 5 Jan '07  

General General    News News    Question Question    Answer Answer    Joke Joke    Rant Rant    Admin Admin   

PermaLink | Privacy | Terms of Use
Last Updated: 31 May 2006
Editor: Smitha Vijayan
Copyright 2006 by Andriy Oriekhov
Everything else Copyright © CodeProject, 1999-2009
Web18 | Advertise on the Code Project