Click here to Skip to main content
13,800,402 members
Click here to Skip to main content
Add your own
alternative version


138 bookmarked
Posted 9 Oct 2003

DLL Injection and function interception tutorial

, 23 Oct 2003
Rate this:
Please Sign up or sign in to vote.
How to inject a DLL into a running process and then intercept function calls in statically linked DLLs.
Revision 4


The source files depend a lot on function pointers. Overview is recommended.

DLL Injection is similar to 'Injecting' code into an already running process. Many things have been taken from Matt Pietrek's book 'Secrets of Windows 95'.

Function interception means 'intercepting' an API function that was loaded by a statically linked DLL by modifying the address of the beginning of the function's code, resulting in the application to call your 'intercepting' function instead of the original 'intercepted' function. This is similar to the idea in "APIHijack - A Library for easy DLL function hooking" article posted by Wade Brainerd.

DLL Injection

"DLL Injection" is not an accurate name for what my content will actually be. My code will 'inject' a series of assembled assembly language instructions [Code] into some available space in the running process and alters the registers to point at the offset of the 'injected' [code]. The process will of course execute the instructions which will load a certain DLL, which is the DLL that is being injected. Note that this code 'Injects' [code]. The code can be anything, it doesn't necessarily have to load a DLL. Hence, the inaccurate title.

There are two ways to 'Inject' a series of bytes into an already running process. VirtualAllocEx() - which isn't supported in Win9x/ME - will allow a process to reserve or commit a region of memory within the virtual address space of a separate specified process. Use WriteProcessMemory() to write the data in the reserved/committed area of the target process' memory. The other way is to directly use ReadProcessMemory() and WriteProcessMemory() - which is supported in all versions of Windows - to search for some accessible area of the target process' memory and replace the bytes within the area size equal to the size of the code. Of course, you will be saving a backup of the replaced bytes in order to put them all back later on.

(Of course, you can use CreateRemoteThread() instead of all this, but it's not supported in all versions of Windows.)

One good yet slow method of injecting the code is using Windows' debugging functions. Suspend the threads of the running process (using the debugging functions) and use GetThreadContext() and SetThreadContext() to save a backup of all the registers and then modify the EIP register, which is the register that contains the offset of the current to-be-executed code, to point it to the 'Injected' code. The injected code block will have a breakpoint set at the end of it (Interrupt 3h -int 3h-). Again, use the debugging functions to resume the threads, which will then continue executing till the first breakpoint is reached. Once your application receives the notification, all you have to do is restore the modified bytes and/or un-allocate any allocated space in memory, and finally restore the registers (SetThreadContext()). That's all there is to it. The application has no idea of what has happened! The code was executed, and probably loaded a DLL. As you know, loaded DLLs are in an application's address space, therefore, the DLLs can access all memory and control the whole application. Very interesting.

Lookup the MSDN library for more information (MSDN).

Useful points to lookup:

  1. Memory need to know how Windows manages its memory.
  2. How DLLs tick - MSDN - I suggest you read it. Might help in inspirations. Revising isn't bad.
  3. PE/COFF Headers specifications... The most important thing if you're doing this in Win9x/ME - MSDN.
  4. Basic debugging APIs...those are some APIs that allow you to debug certain applications. Lookup the section "Debugging and Error Handling" in MSDN.
  5. Enough knowledge of ASM is required of course...and OPCODES of instructions.

Have a look at the accompanied files:


Google Groups - A Message board thread on CreateRemoteThread()'s method.

Function Interception

Notice the DLL project in the zip file. This function is in HookApi.h.

// Macro for adding pointers/DWORDs together without C arithmetic interfering 
// -- Taken from Matt Pietrek's book
// Thought it'd be great to use..
#define MakePtr( cast, ptr, addValue ) (cast)( (DWORD)(ptr)+(DWORD)(addValue))
//This code is very similar to Matt Pietrek's, except that it is written 
//according to my understanding...
//And Matt Pietrek's also handles Win32s 
//--(Because they it has some sort of a problem)
PROC WINAPI HookImportedFunction(HMODULE hModule,
			         //Module to intercept calls from
     PSTR FunctionModule, //The dll file that contains the function you want to 
			  //hook(ex: "USER32.dll")
     PSTR FunctionName,   //The function that you want to hook 
			  //(ex: "MessageBoxA")
     PROC pfnNewProc)     //New function, this gets called instead
    PROC pfnOriginalProc; //The intercepted function's original location
    IMAGE_DOS_HEADER *pDosHeader; 
// Verify that a valid pfn was passed

if ( IsBadCodePtr(pfnNewProc) ) return 0;

pfnOriginalProc = GetProcAddress(GetModuleHandle(FunctionModule),
if(!pfnOriginalProc) return 0;
pDosHeader = (PIMAGE_DOS_HEADER)hModule;
//kindly read the ImgHelp function reference
//in the Image Help Library section in MSDN
//hModule is the Process's Base address  (GetModuleHandle(0))
//even if called in the dll, it still gets the hModule of the calling process
//---That's you should save the hInstance of the DLL as a global variable,
//in DllMain(), because it's the only way to get it(I think)
// Tests to make sure we're looking at a module image (the 'MZ' header)
if ( IsBadReadPtr(pDosHeader, sizeof(IMAGE_DOS_HEADER)) )
    return 0;
if ( pDosHeader->e_magic != IMAGE_DOS_SIGNATURE )
//Image_DOS_SIGNATURE is a WORD (2bytes, 'M', 'Z' 's values)
    return 0;
// The MZ header has a pointer to the PE header
pNTHeader = MakePtr(PIMAGE_NT_HEADERS, pDosHeader, pDosHeader->e_lfanew);
//it's like doing pDosHeader + pDosHeader->e_lfanew
// e_lfanew contains a RVA to the 'PE\0\0' Header...An rva means, offset,
// relative to the BaseAddress of module
// -pDosHeader is the base address..and e_lfanew is the RVA,
// so summing them, will give you the Virtual Address..
// More tests to make sure we're looking at a "PE" image
if ( IsBadReadPtr(pNTHeader, sizeof(IMAGE_NT_HEADERS)) )
    return 0;
if ( pNTHeader->Signature != IMAGE_NT_SIGNATURE )
//IMAGE_NT_SIGNATURE is a DWORD (4bytes, 'P', 'E', '\0', '\0' 's values)
    return 0;
// We now have a valid pointer to the module's PE header.
// Now get a pointer to its imports section
  pDosHeader, //IMAGE_IMPORT_DESCRIPTOR *pImportDesc;

//What i just did was get the imports section by getting the RVA of it
//(like i did above), then adding the base addr to it.
//// pNTHeader->OptionalHeader.DataDirectory
//// IMAGE_DIRECTORY_ENTRY_IMPORT==1 -- Look at that PE documentation.
//// Pietrek's articles in MSJ and MSDN Magazine will be real helpful!
//Go out if imports table doesn't exist
if ( pImportDesc == (PIMAGE_IMPORT_DESCRIPTOR)pNTHeader )
    return 0; //pImportDesc will ==pNTHeader.
//if the RVA==0, cause pNTHeader+0==pNTHeader -> stored in pImportDesc
//Therefore, pImportDesc==pNTHeader
// Iterate through the array of imported module descriptors, looking
// for the module whose name matches the FunctionModule parameter
while ( pImportDesc->Name ) //Name is a DWORD (RVA, to a DLL name)
    PSTR pszModName = MakePtr(PSTR, pDosHeader, pImportDesc->Name);

    if ( stricmp(pszModName, FunctionModule) == 0 )
    //str"i"cmp,,, suggest you to ignore cases when comparing,
        break; //or strcmpi() in some compilers
    pImportDesc++;  // Advance to next imported module descriptor
// Get out if we didn't find the Dll name.
// pImportDesc->Name will be non-zero if we found it.
if ( pImportDesc->Name == 0 )
    return 0;
// Get a pointer to the found module's import address table (IAT)
//           =====IMAGE_THUNK_DATA *pThunk;
   pThunk = MakePtr(PIMAGE_THUNK_DATA, pDosHeader, pImportDesc->FirstThunk);
//This is what i was talkin about earlier...
//In pThunk, if it was image loaded in memory, you'll get the address to
//entry point of functions
//but in a disk file, It's a function name

// Look through the table of import addresses, of the found
// DLL, looking for the function's entry point that matches
// the address we got back from GetProcAddress above.
while ( pThunk->u1.Function )
   if ( (DWORD)pThunk->u1.Function == (DWORD)pfnOriginalProc )
   // We found it!  Overwrite the original address with the
   // address of the interception function.  Return the original
   // address to the caller so that they can chain on to it.
        pThunk->u1.Function = (PDWORD)pfnNewProc;
    // pfnNewProc is in the parameters of the function
    //pfnOriginalProc = (PROC)(DWORD)pdw1;
        return pfnOriginalProc;

    pThunk++;   // Advance to next imported function address
    return 0; //function not found!!!!!

Also notice:

  • HANDLE OpenLog(char *Filename)
  • BOOL CloseLog(HANDLE h)
  • DWORD AppendLog(char *str, DWORD uSize, HANDLE h)

They are the functions to write to the LOG file. What the whole project does is inject a DLL into an already running process (mIRC.exe - mIRC chatting program (mIRC)) [in my case]. It then creates a Log file of all the intercepted Winsock functions. Have a look at Successlog.txt. It is highly recommended that you apply the program on mIRC only, since it has been created for it. Have fun coding your own :)

I think you got the idea. I hope this is useful.



Nasser R. Rowhani -


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

Qatar Qatar
Nasser R. Rowhani
Programming simply pumps my adrenaline..

Okay... I like people critisizing me...
Let me fix this article...

You may also be interested in...

Comments and Discussions

GeneralA very interesting Link collection Pin
Elmue29-Sep-08 18:04
memberElmue29-Sep-08 18:04 
Questionwhy do you call your method 'good yet slow'? Pin
code_discuss4-Dec-07 21:07
membercode_discuss4-Dec-07 21:07 
AnswerRe: why do you call your method 'good yet slow'? Pin
AndrewWood7-Jan-08 23:43
memberAndrewWood7-Jan-08 23:43 
QuestionIntercept functions from kernel32.dll Pin
Higginz6-Sep-06 9:13
memberHigginz6-Sep-06 9:13 
GeneralW32SKRNL.DLL Pin
vmelkon7-Jun-05 21:03
membervmelkon7-Jun-05 21:03 
GeneralWinsock functions hooking doesn't work !! Pin
Wessam Fathi28-Apr-05 13:04
memberWessam Fathi28-Apr-05 13:04 
GeneralCreateWindowEx & CreateWindow Pin
dj013-Feb-05 11:14
memberdj013-Feb-05 11:14 
GeneralProtect NET Pin
Anonymous8-Nov-04 3:44
memberAnonymous8-Nov-04 3:44 
GeneralRe: Protect NET Pin
Anonymous9-Nov-04 13:29
memberAnonymous9-Nov-04 13:29 
GeneralHooked function address is replaced by Original one Pin
samren23-Sep-04 21:03
membersamren23-Sep-04 21:03 
GeneralRe: Hooked function address is replaced by Original one Pin
Tarmo Kalda26-Jan-05 2:24
memberTarmo Kalda26-Jan-05 2:24 
QuestionHas anyone solved the NT/XP Issue? Pin
dj018-Dec-03 20:38
memberdj018-Dec-03 20:38 
AnswerRe: Has anyone solved the NT/XP Issue? Pin
CrankHank18-Dec-03 23:02
memberCrankHank18-Dec-03 23:02 
GeneralRe: Has anyone solved the NT/XP Issue? Pin
dj019-Dec-03 9:48
memberdj019-Dec-03 9:48 
Generalquestion Pin
Brandon Sneed24-Oct-03 6:37
memberBrandon Sneed24-Oct-03 6:37 
GeneralRe: question Pin
CrankHank28-Dec-03 5:23
memberCrankHank28-Dec-03 5:23 
GeneralRe: question Pin
Brandon Sneed28-Dec-03 11:07
memberBrandon Sneed28-Dec-03 11:07 
GeneralRe: question Pin
CrankHank29-Dec-03 3:08
memberCrankHank29-Dec-03 3:08 
GeneralFinally! Problem solved.. Pin
CrankHank23-Oct-03 21:33
memberCrankHank23-Oct-03 21:33 
GeneralRe: Finally! Problem solved.. Pin
yuxc25-Oct-03 20:26
memberyuxc25-Oct-03 20:26 
GeneralWin98 is successed but win2000 is faliure! Pin
yuxc19-Oct-03 17:56
memberyuxc19-Oct-03 17:56 
GeneralRe: Win98 is successed but win2000 is faliure! Pin
CrankHank20-Oct-03 4:26
memberCrankHank20-Oct-03 4:26 
QuestionDllMain never got called? Pin
Peter Leng17-Oct-03 10:13
memberPeter Leng17-Oct-03 10:13 
AnswerRe: DllMain never got called? Pin
CrankHank18-Oct-03 6:17
memberCrankHank18-Oct-03 6:17 
AnswerRe: DllMain never got called? Pin
yuxc18-Oct-03 22:47
memberyuxc18-Oct-03 22:47 

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.

Permalink | Advertise | Privacy | Cookies | Terms of Use | Mobile
Web01 | 2.8.181214.1 | Last Updated 24 Oct 2003
Article Copyright 2003 by CrankHank
Everything else Copyright © CodeProject, 1999-2018
Layout: fixed | fluid