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

Intercepting WinAPI calls

Rate me:
Please Sign up or sign in to vote.
4.83/5 (29 votes)
31 May 20063 min read 150.8K   3.7K   80   42
An article about intercepting WinAPI calls.

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:
    ASM
    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


Written By
Chief Technology Officer
Ukraine Ukraine
This member has not yet provided a Biography. Assume it's interesting and varied, and probably something to do with programming.

Comments and Discussions

 
QuestionProblem in Release Build Pin
noumantariq28-Oct-09 23:49
noumantariq28-Oct-09 23:49 
GeneralCorrection in assembly code [modified] Pin
MattsUserName27-Jul-09 15:04
MattsUserName27-Jul-09 15:04 
QuestionVista compatibility? Pin
Amit Vilas Shinde7-Mar-08 1:36
Amit Vilas Shinde7-Mar-08 1:36 
GeneralHi, tezm. I ran into the same question as yours. Pin
mybayern19742-Aug-07 22:02
mybayern19742-Aug-07 22:02 
GeneralRe: Hi, tezm. I ran into the same question as yours. Pin
tezm4-Aug-07 6:55
tezm4-Aug-07 6:55 
GeneralRe: Hi, tezm. I ran into the same question as yours. Pin
mjmim10-Oct-08 12:34
mjmim10-Oct-08 12:34 
QuestionError installing service, how to solve it? Pin
tezm27-Jul-07 8:29
tezm27-Jul-07 8:29 
AnswerRe: Error installing service, how to solve it? Pin
tezm28-Jul-07 1:28
tezm28-Jul-07 1:28 
GeneralI want to support vista O.S., please help me Pin
Hsiao, Tsu Tair25-Jul-07 16:46
Hsiao, Tsu Tair25-Jul-07 16:46 
GeneralInjection works fine, but impossible to subclass using this method? [modified] Pin
ehaerim5-Jan-07 11:05
ehaerim5-Jan-07 11:05 
QuestionHow to inject dll into specific processes, not all the processes? [modified] Pin
ehaerim30-Dec-06 21:28
ehaerim30-Dec-06 21:28 
AnswerRe: How to inject dll into specific processes, not all the processes? Pin
ehaerim5-Jan-07 10:35
ehaerim5-Jan-07 10:35 
The code below works for WinXP or later to find out the name of full process image name.

typedef NTSTATUS (*QUERY_INFO_PROCESS) (<br />
    __in HANDLE ProcessHandle,<br />
    __in PROCESSINFOCLASS ProcessInformationClass,<br />
    __out_bcount(ProcessInformationLength) PVOID ProcessInformation,<br />
    __in ULONG ProcessInformationLength,<br />
    __out_opt PULONG ReturnLength<br />
    );<br />
<br />
QUERY_INFO_PROCESS ZwQueryInformationProcess;<br />
<br />
NTSTATUS GetProcessImageName(PUNICODE_STRING ProcessImageName)<br />
{<br />
    NTSTATUS status;<br />
    ULONG returnedLength;<br />
    ULONG bufferLength;<br />
    PVOID buffer;<br />
    PUNICODE_STRING imageName;<br />
    <br />
    PAGED_CODE(); // this eliminates the possibility of the IDLE Thread/Process<br />
<br />
    if (NULL == ZwQueryInformationProcess) {<br />
<br />
        UNICODE_STRING routineName;<br />
<br />
        RtlInitUnicodeString(&routineName, L"ZwQueryInformationProcess");<br />
<br />
        ZwQueryInformationProcess = <br />
               (QUERY_INFO_PROCESS) MmGetSystemRoutineAddress(&routineName);<br />
<br />
        if (NULL == ZwQueryInformationProcess) {<br />
            DbgPrint("Cannot resolve ZwQueryInformationProcess\n");<br />
        }<br />
    }<br />
    //<br />
    // Step one - get the size we need<br />
    //<br />
    status = ZwQueryInformationProcess( NtCurrentProcess(), <br />
                                        ProcessImageFileName,<br />
                                        NULL, // buffer<br />
                                        0, // buffer size<br />
                                        &returnedLength);<br />
<br />
    if (STATUS_INFO_LENGTH_MISMATCH != status) {<br />
<br />
        return status;<br />
<br />
    }<br />
<br />
    //<br />
    // Is the passed-in buffer going to be big enough for us?  <br />
    // This function returns a single contguous buffer model...<br />
    //<br />
    bufferLength = returnedLength - sizeof(UNICODE_STRING);<br />
    <br />
    if (ProcessImageName->MaximumLength < bufferLength) {<br />
<br />
        ProcessImageName->Length = (USHORT) bufferLength;<br />
<br />
        return STATUS_BUFFER_OVERFLOW;<br />
        <br />
    }<br />
<br />
    //<br />
    // If we get here, the buffer IS going to be big enough for us, so <br />
    // let's allocate some storage.<br />
    //<br />
    buffer = ExAllocatePoolWithTag(PagedPool, returnedLength, 'ipgD');<br />
<br />
    if (NULL == buffer) {<br />
<br />
        return STATUS_INSUFFICIENT_RESOURCES;<br />
        <br />
    }<br />
<br />
    //<br />
    // Now lets go get the data<br />
    //<br />
    status = ZwQueryInformationProcess( NtCurrentProcess(), <br />
                                        ProcessImageFileName,<br />
                                        buffer,<br />
                                        returnedLength,<br />
                                        &returnedLength);<br />
<br />
    if (NT_SUCCESS(status)) {<br />
        //<br />
        // Ah, we got what we needed<br />
        //<br />
        imageName = (PUNICODE_STRING) buffer;<br />
<br />
        RtlCopyUnicodeString(ProcessImageName, imageName);<br />
        <br />
    }<br />
<br />
    //<br />
    // free our buffer<br />
    //<br />
    ExFreePool(buffer);<br />
<br />
    //<br />
    // And tell the caller what happened.<br />
    //    <br />
    return status;<br />
    <br />
}

QuestionNewNtTerminateProcess called with NULL as the value of ProcessHandle. Why? Pin
ehaerim30-Dec-06 20:59
ehaerim30-Dec-06 20:59 
GeneralSecond MessageBox does not show up when process termination but only sound. Pin
ehaerim2-Dec-06 14:56
ehaerim2-Dec-06 14:56 
GeneralRe: Second MessageBox does not show up when process termination but only sound. Pin
tezm28-Jul-07 1:25
tezm28-Jul-07 1:25 
QuestionVC++ 6.0 Version available? Pin
ehaerim2-Dec-06 10:52
ehaerim2-Dec-06 10:52 
QuestionHow to hook multiple instances of Notepad? [modified] Pin
ehaerim30-Nov-06 15:10
ehaerim30-Nov-06 15:10 
GeneralWhere can I get info about SetCreateProcessNotificationRoutine? [modified] Pin
ehaerim30-Nov-06 13:42
ehaerim30-Nov-06 13:42 
Generalstarting programs by clicking on document icons Pin
richardmoss15-Nov-06 5:43
richardmoss15-Nov-06 5:43 
GeneralRe: starting programs by clicking on document icons Pin
Andriy Oriekhov23-Nov-06 0:25
Andriy Oriekhov23-Nov-06 0:25 
QuestionHow to get the executable name from the created process?? Pin
girm13-Sep-06 1:41
girm13-Sep-06 1:41 
AnswerRe: How to get the executable name from the created process?? Pin
Andriy Oriekhov14-Sep-06 2:53
Andriy Oriekhov14-Sep-06 2:53 
GeneralRe: How to get the executable name from the created process?? Pin
girm24-Oct-06 20:40
girm24-Oct-06 20:40 
GeneralRe: How to get the executable name from the created process?? Pin
Andriy Oriekhov25-Oct-06 11:32
Andriy Oriekhov25-Oct-06 11:32 
GeneralRe: How to get the executable name from the created process?? Pin
ehaerim5-Jan-07 10:32
ehaerim5-Jan-07 10:32 

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.