You are on page 1of 21

DLL Injection atom0s {RES} 2008 Team Resurrection

What Is DLL Injection?


DLL injection is a method that allows remote code execution. It is a method of loading your, or another, module into another processes memory space that would not normally load the given module. By doing this you gain direct access to the code, memory, and functions of the injected process which gives the DLL full control, in theory, over the process. DLL injection can be used for various different things, the more common method that uses DLL injection is used for game hacking. However, loaders for programs can be non-intrusive to rewriting the actual code inside a file by injection by rewriting code during runtime. This can be a way to avoid copyright infringement and such other useless laws. As mentioned above, when you inject your module into another process, you have direct access to the processes memory, functionality, etc. You can completely take over a process via injection. However, not all injection is intended to be harmful to a process, but instead helpful, extending, or something on a better line.

Tools Used Throughout This Tutorial


This is a small cover section to the tools I will be using in this tutorial. You do not have to use the same tools as me, as everyone enjoys their own set of things, but my tutorial will be based around the use of the ones listed here.

Analysis: PeiD (www.peid.has.it) Debugging: OllyDBG (www.ollydbg.de) Programming: Visual Studio 6, Visual Studio 2008 (C/C++) (www.microsoft.com) Windows XP Professional Windows 98 Sun Virtual Box VM Software (www.virtualbox.org) (Used to run Windows 98.)

Again, you are not limited to the use of these specific tools, I personally use these when doing most of my game hacking, hooking, etc. as they are, in my opinion, the best of the best. :) Along with that, almost every program listed above is free. (Excluding the operating systems and VS6.) If you wish to use all free things, you can download, BUT I DO NOT RECOMMEND IT, DevC++.

DLL Injection atom0s {RES} 2008 Team Resurrection

Setup And Preparation


Before we dive into the main purpose of this tutorial, lets setup some example code for a module to test our injection with. This is simply going to be a blank DLL that calls MessageBox to tell us that we got loaded. This is just for testing purposes. So we will create a DLL with the following code:
#include <windows.h> BOOL __stdcall DllMain( HMODULE, DWORD dwReason, LPVOID ) { switch( dwReason ) { case DLL_PROCESS_ATTACH: MessageBoxA( 0, "Injected!", "Caption", MB_OK ); break; } return TRUE; }

You can compile that and leave it where its at for now. The next step is to create a console application that we will be doing the injection code in. It just needs to be a standard console application. So the bare bones code will look like this:
#include <windows.h> #include <tchar.h> #include <iostream> int main( int argc, TCHAR* argv[] ) { return 0; }

These will be the bare bone parts of each project we will create throughout this tutorial. At the start of each method, I will expect you to create a new project for that specific method so you can just create a new project with this above code and work from there.

DLL Injection atom0s {RES} 2008 Team Resurrection

Method 1. - API Injection Method (1)


There are various methods you can use to inject your module into another process. I will cover how to do a few of these methods that I have personally done throughout my coding and hacking experiences. I do not plan to cover every single method known as there are a lot of methods. After reading this you should look for more ways, use what you need to suit your needs based on your target process, and work from there. API injection method 1 consists of injection by using the following API:

VirtualAllocEx / VirtualFreeEx WriteProcessMemory CreateRemoteThread

Combined with other API, these are the main used to inject our hook via creating a remote thread in the target process and calling LoadLibrary inside the target to tell it to load our module. This method works by allocating a block of memory and writing the path to our module into the remote process using WriteProcessMemory. Once that is finished, we call CreateRemoteThread with LoadLibrary as the main function of the thread and pass it the address that our path was written to, so in essence this creates a standard LoadLibrary call inside the process. Once the thread finishes, we cleanup by deallocating our block of memory for the process path. The first part we will need to do is code the starting part of the loader to locate the path of our module. I personally suggest always going with 'same-path' methods meaning that the module to be injected should be in the same folder as the injector. This is how I will be coding but you can extend your loader later on if you wish. To start, we will want to get the path our loader is inside of. To do that, we can use the 'GetCurrentDirectory' API. So we will want to start our code with:
TCHAR tszHookPath[ MAX_PATH ]; GetCurrentDirectory( MAX_PATH, tszHookPath );

This will get the current directory for us and store it in tszHookPath. Next, we will want to append the hook name to the end of this path to create a full working path to the module we plan to inject. We can use the 'strcat / wcscat / _tcscat' functions to do this. I personally choose to use the TCHAR functions as they compile to both Unicode and Multi-byte character sets based on compiler settings. I also use the secure functions in the new versions of the CRT. You are free to use which ever you want though.

DLL Injection atom0s {RES} 2008 Team Resurrection


So the next step in our code is to append the module name, we can do it like this:
_tcscat_s( tszHookPath, MAX_PATH, _T(\\hook.dll) );

The next part is up to you how you wish to inject. You can create the process yourself with your loader, or you can pause the currently running process that you want to inject into. I find it easier to load the process myself instead of injecting into the already running process as it is less code. But, I will show you both in this method alone, and the rest will use a loading method from here out. The first method will be injecting into an already running process. The first thing to do when injecting into a running process is to locate the process and it's main thread. You can use the CreateToolhelp32Snapshot API to accomplish this. We will need to use Process32First / Process32Next as well as Thread32First / Thread32Next to loop the snapshots. I am not going into detail about these API as they are not really part of this tutorial. You can find all the info you need about them on the MSDN website. To locate the process we can do the following:
DWORD _ProcIdByName( TCHAR* tszProcess ) { PROCESSENTRY32 pe32; HANDLE hSnapshot; pe32.dwSize = sizeof( PROCESSENTRY32 ); hSnapshot = CreateToolhelp32Snapshot( TH32CS_SNAPPROCESS, 0 ); if( hSnapshot == INVALID_HANDLE_VALUE ) return 0; if( Process32First( hSnapshot, &pe32 ) ) { do { if( _tcsicmp( pe32.szExeFile, tszProcess ) == 0 ) { CloseHandle( hSnapshot ); return pe32.th32ProcessID; } } while( Process32Next( hSnapshot, &pe32 ) ); } CloseHandle( hSnapshot ); return 0; }

This function will allow us to obtain the process id of a process by its name that is shown in task manager. This is the name of the executable file when it is loaded into memory. A summary of the code above would be we create a snapshot of the process list and iterate through it with the Process32First / Process32Next API and compare the current process in the iteration to the given process name in the parameter of the function. If it matches, we close the open snapshot handle and return the handle. If the process is not found, we return 0.

DLL Injection atom0s {RES} 2008 Team Resurrection


The next step would be to obtain the processes main thread id to be able to pause the process while we load our module to prevent errors from occurring. Just like the above, we create a snapshot of a specific processes threads and iterate through them to find the main thread id. We do this by comparing to the parent process id.
DWORD _ThreadIdByProcId( DWORD dwProcId ) { THREADENTRY32 te32; HANDLE hSnapshot; te32.dwSize = sizeof( THREADENTRY32 ); hSnapshot = CreateToolhelp32Snapshot( TH32CS_SNAPTHREAD, dwProcId ); if( hSnapshot == INVALID_HANDLE_VALUE ) return 0; if( Thread32First( hSnapshot, &te32 ) ) { do { if( te32.th32OwnerProcessID == dwProcId ) { CloseHandle( hSnapshot ); return te32.th32ThreadID; } } while( Thread32Next( hSnapshot, &te32 ) ); } CloseHandle( hSnapshot ); return 0;

In order to use these we need to add another include to our project. You can add this under the windows.h include:
#include <tlhelp32.h>

Now that we have the ability to obtain the process and thread id's, we can move onto the next step of code. Next we will want to obtain the process id, check if we got a valid return, and then obtain the main thread id and make sure we got a valid id from that. We do this by doing:
DWORD dwProcId = 0; DWORD dwThreadId = 0; dwProcId = _ProcIdByName( _T("winmine.exe") ); if( dwProcId == 0 ) return 0; dwThreadId = _ThreadIdByProcId( dwProcId ); if( dwThreadId == 0 ) return 0;

As you can see, I am using Minesweeper as my test subject. This is a process every system usually

DLL Injection atom0s {RES} 2008 Team Resurrection


has as long as the user installed Windows with normal programs and such. If you don't have it, just use something else thats common such as Calculator, or another game or something small and fast loading. The next step we will do is to open the handles to the process to obtain proper access needed to pause the threads, write to the memory, and execute a thread. I personally take advantage of the PROCESS_INFORMATION structure when doing this as it holds the 4 key elementals to doing this. So we can setup the next part of code like this:
PROCESS_INFORMATION pi; pi.dwProcessId = dwProcId; pi.dwThreadId = dwThreadId; pi.hProcess = OpenProcess( PROCESS_ALL_ACCESS, FALSE, pi.dwProcessId ); pi.hThread = OpenThread( THREAD_ALL_ACCESS, FALSE, pi.dwThreadId ); if( pi.hProcess == 0 || pi.hThread == 0 ) { if( pi.hProcess ) CloseHandle( pi.hProcess ); if( pi.hThread ) CloseHandle( pi.hThread ); return 0; }

By doing this, we are opening the process and thread of our process for full access. Keep in mind this will fail if you do not set the proper priviledges to your process before calling OpenProcess/OpenThread with PROCESS_ALL_ACCESS/THREAD_ALL_ACCESS. I will cover that in the ending part of this tutorial and it will also be in the examples include with this tutorial. If either handle fails to open we cannot continue, so we close anything that was opened and return. If all went well, we should be able to continue with what we are doing. The next part for this is to suspend the thread execution in the process by suspending the thread handle we have just obtained. So we next need to do:
SuspendThread( pi.hThread ); Sleep( 100 );

This will suspend the main thread of the process causing it to completely be suspended. Then we add a small sleep to give the CPU some time to catch up and allow the thread to suspend correctly. There are other methods you can do to determine the status of the thread to ensure it's suspended and such but this will suffice for most situations. Next, we will start the main injection code block. This will be first started with creating a block of memory in the remote process using VirtualAllocEx. This block of memory is used to hold the path to our module that will be loaded inside the remote process. It is important to ensure that you create

DLL Injection atom0s {RES} 2008 Team Resurrection


a big enough buffer to hold your path so you do not crash the application due to an overflow error or memory access violations. The easiest way to prevent this is just allocating a block big enough to hold any path. Windows has a max path size of 256 characters. So allocating a block that is 1000 bytes big will be plenty for this purpose. We can allocate the block of memory by doing:
LPVOID lpAlloc; lpAlloc = VirtualAllocEx( pi.hProcess, NULL, 1000, MEM_COMMIT, PAGE_EXECUTE_READWRITE );

What this does is tells the remote process to allocate a block of memory at least 1000 bytes long with the page access level of execute+read/write which is basically full access to the page that is allocated. Our variable lpAlloc holds the address the buffer was created at to be used with other API that require to use that block of memory. Our next step is to now write our path to the block of memory we just allocated. We can use WriteProcessMemory to write to the remote processes memory to do this.
WriteProcessMemory( pi.hProcess, lpAlloc, tszHookPath, _tcslen(tszHookPath), NULL );

This says that we want to write to the address of the allocated buffer we just created in the remote process. We are writing the buffer of our path variable, which should be the full path to the hook we are injecting. The size is the length of the path string. WriteProcessMemory returns a boolean value, true on success, false on failure. You can add error checking to your loader to check and make sure that the call to this API worked for debug output just incase. At this point we have created the buffer, written our hooks path to it, next it's time to create our remote thread. Threads in Windows have a param that is called LPTHREAD_START_ROUTINE. This is the address to the function that the thread executes. You can either pass an address inside the remote process to call, or, you can directly call an API. We will be calling LoadLibraryA to load our module. When we directly call LoadLibraryA, the next param in CreateRemoteThread is a param to be sent to the function you call. In this case, the LPVOID param in CreateRemoteThread is going to be the address to our path. This will create the effect of actually calling LoadLibrary normally inside the process. So we setup our CreateRemoteThread call like this:
HANDLE hRemoteThread; hRemoteThread = CreateRemoteThread( pi.hProcess, 0, 0, (LPTHREAD_START_ROUTINE)GetProcAddress(GetModuleHandle("kernel32"), "LoadLibraryA"), lpAlloc, 0, 0 );

Think of this as a 'push + call' in ASM. We would push the address to the buffer that holds the path, then call LoadLibraryA. Again, this is where the term 'Remote Code Execution' plays it's role in this. We are remotely executing this inside the process to force it to load our module. Once this is called this is when the module gets loaded inside the process.

DLL Injection atom0s {RES} 2008 Team Resurrection


Our next step is to wait for the thread to finish it's job, obtain the exit code of the thread to determine if it executed properly, then cleanup what we have done. To wait for the thread to finish and obtain its exit code, we use WaitForSingleObject and GetExitCodeThread. Understand thought, in this case, the exit code of the thread will be EAX's value, which is the return from LoadLibrary. In this case, it will the be address where our module was loaded into. The exit code of a thread does not always have to be an actual value and can be 0 based on how it is coded. The way this will determine if the thread worked is by checking if the exit code is not 0. But please keep in mind this will not always be the best method to do this with other things you can do with remote threads. So our call to WaitForSingleObject takes two parameters, the first being our threads handle, the second being the amount of time we want to wait before just saying 'continue' without waiting any longer. Then, GetExitCodeThread also takes two parameters, the first also being our threads handle, the second being a pointer to a DWORD buffer to hold the exit code. So we would do:
DWORD dwExitCode; WaitForSingleObject( hRemoteThread, INFINITE ); GetExitCodeThread( hRemoteThread, &dwExitCode ); CloseHandle( hRemoteThread );

Now that we have obtained the exit code, cleaned up the thread by closing the handle, we can finish cleaning up by deallocating our buffer inside the remote process that held our modules path to load. We do not need it in the process any longer so we can remove it. After that, we need to cleanup the open handles, resume the processes main thread, and then determine the status of our threads return. To cleanup the buffer, we make a call to VirtualFreeEx. This uses our allocated buffer pointer to determine where to deallocate the memory at. When you call this, be sure to use the same size as you did in VirtualAllocEx or you can deallocate memory that the process really needs! So for cleanup and deallocation, we would use the following:
VirtualFreeEx( pi.hProcess, lpAlloc, 1000, MEM_DECOMMIT ); ResumeThread( pi.hThread ); CloseHandle( pi.hThread ); CloseHandle( pi.hProcess );

At this point, we now let the process resume from being paused. Then we have closed the open handles we created, so in turn the process you create with this code should be safe to close now. But, before we close, we want to make sure the module loaded, and if not to alert us that it didn't. Even though the example module we injected with this code has a message box when it loads, not all modules will have this so checking the return code is a good way to determine if it got loaded.

DLL Injection atom0s {RES} 2008 Team Resurrection


We can check the exit code doing the following:
if( dwExitCode == 0 ) MessageBox( 0, "Module did not load.", "Error", MB_OK );

After this we can safely return our program just by giving 'return 0;' at the end. And thats it! Now you can test your loader by compiling it, and launching it. Make sure to do the following: 1. Make sure to compile this code as Multi-byte. 2. Make sure to copy the hook.dll example module into the same folder as your loader.exe. 3. Make sure to launch the loader directly and not through your compiler. Some compilers do not use the proper launch path with you adjusting it and it will fail to see your hook. Visual Studio has this issue but you can change the working path while debugging in the debugging options if you wish. Once you have made sure to do the above, open the debug folder that your loader compiled to and launch it. Be sure that your target application is running. Or you will not be injecting into anything and the program will exit early based on other checks. Also, as I mentioned above, when we call PROCESS_ALL_ACCESS / THREAD_ALL_ACCESS in newer systems, we must set the debug token for our process to be given access to use these parameters. Without it the OpenProcess / OpenThread calls will fail. We can make a simple function as seen below to fix this. Simply call this function before you setup the PROCESS_INFORMATION block with the ids and handles.
BOOL _SetDebugPrivilege() { TOKEN_PRIVILEGES TokenPrivileges; LUID SeDebugNameValue; HANDLE hToken; if( !OpenProcessToken( GetCurrentProcess(), TOKEN_ADJUST_PRIVILEGES| TOKEN_QUERY, &hToken ) ) return FALSE; if( !LookupPrivilegeValue( NULL, SE_DEBUG_NAME, &SeDebugNameValue ) ) return FALSE; TokenPrivileges.PrivilegeCount = 1; TokenPrivileges.Privileges[0].Luid = SeDebugNameValue; TokenPrivileges.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED; if( !AdjustTokenPrivileges( hToken, FALSE, &TokenPrivileges, sizeof( TokenPrivileges ), NULL, NULL ) ) { CloseHandle( hToken ); return FALSE; } CloseHandle( hToken ); return TRUE; }

You can find this entire examples code in: /Examples/Method1

DLL Injection atom0s {RES} 2008 Team Resurrection

Method 2. - API Injection Method (2)


The next method, method 2 as I will refer to it as, is very similar to the above code, but instead of writing LoadLibrary in a thread, we will actually write function code to the process to call. Doing this method takes a bit more work to fully do. We will need to write code and debug the process for the bytes of the code first. In this method, we will actually write a function to the remote process to be executed when we call CreateRemoteThread. This method will allow us to fully mimic a function call inside a process. Like the first method, we will be using the first chunk of code that it used as well:
int main( int argc, TCHAR* argv[] ) { TCHAR tszHookPath[ MAX_PATH ]; GetCurrentDirectory( MAX_PATH, tszHookPath ); _tcscat_s( tszHookPath, MAX_PATH, _T("\\hook.dll") ); DWORD dwProcId = 0; DWORD dwThreadId = 0; dwProcId = _ProcIdByName( _T("winmine.exe") ); if( dwProcId == 0 ) return 0; dwThreadId = _ThreadIdByProcId( dwProcId ); if( dwThreadId == 0 ) return 0; if( !_SetPrivilege() ) return 0; PROCESS_INFORMATION pi; pi.dwProcessId = dwProcId; pi.dwThreadId = dwThreadId; pi.hProcess = OpenProcess( PROCESS_ALL_ACCESS, FALSE, pi.dwProcessId ); pi.hThread = OpenThread( THREAD_ALL_ACCESS, FALSE, pi.dwThreadId ); if( pi.hProcess == 0 || pi.hThread == 0 ) { if( pi.hProcess ) CloseHandle( pi.hProcess ); if( pi.hThread ) CloseHandle( pi.hThread ); return 0; } SuspendThread( pi.hThread ); Sleep( 100 );

Next we will need to debug for what we plan to write to the process memory. We will want to write a few things first before writing our function. Doing this method we will need to write the API addresses for a few API to ensure that the proper ones get called. You can obtain the API addresses

DLL Injection atom0s {RES} 2008 Team Resurrection


using GetProcAddress inside your loader as throughout the full system it will be the same so no need to worry about changing. The ASM of the code we will be injecting will look something like this:
7B 43 64 69 5C 6F 1D 3A 20 73 6C 6B 80 5C 53 74 6F 2E 7C 44 65 72 61 64 >Address To LoadLibraryA 6E >ASCII "C:\Documents an" 73 5C >ASCII "d Settings\Admin" 44 65 >ASCII "istrator\Desktop" 65 62 >ASCII "\loader\debug\ho" ASCII "ok.dll",0 PUSH EBP MOV EBP,ESP PUSH ECX MOV EAX,DWORD PTR SS:[EBP+8] ADD EAX,4 PUSH EAX MOV ECX,DWORD PTR SS:[EBP+8] CALL DWORD PTR DS:[ECX] MOV ESP,EBP POP EBP RET 4

6F 74 61 64 6C

63 74 74 65 6C

75 69 6F 72 00

6D 6E 72 5C

65 67 5C 64

55 8BEC 51 8B45 08 83C0 04 50 8B4D 08 FF11 8BE5 5D C2 0400

What we are doing is creating a buffer with VirtualAllocEx like the last method, but this time we are writing 3 things to the buffer. First, we write the procedure address to LoadLibraryA. After, we write the path to our hook that we want the process to load. Then, following that, we will write a small code block function to be called with CreateRemoteThread to execute the code. When writing this block, you need to be very careful with where things are written. If you do not correctly write the block of code and execute it properly, you will in most cases crash the remote target. The first thing we need to do is create a buffer with a proper size. To determine a proper size, you can take these factors into account. The first 4 bytes are an address, the last part of the block is a static block of code that will not change. The only sizable part of this is the path. However, Windows has a max path size of 260 characters. So we have a base to work with already. Address + Max Path Size + Function Size or 4 + 260 + 24 = 288 So a buffer with the size of 1000 will be plenty of memory for this. 1000 will not be the exact as Windows automatically rounds to the next page boundary but thats not a big deal.

DLL Injection atom0s {RES} 2008 Team Resurrection


So our allocation call will look just like the last one:
LPVOID lpAlloc; lpAlloc = VirtualAllocEx( pi.hProcess, NULL, 1000, MEM_COMMIT, PAGE_EXECUTE_READWRITE );

The next steps are the major changes to this method. To start, the first part we want to write into the buffer as said above was the procedure address to LoadLibraryA. We can use GetProcAddress in our program (loader) to obtain this address and write it to the buffer like this:
DWORD dwLoadLibraryA = (DWORD)GetProcAddress( GetModuleHandleA( "kernel32" ), "LoadLibraryA" ); WriteProcessMemory( pi.hProcess, lpAlloc, &dwLoadLibraryA, 4, NULL );

This will obtain the function address of LoadLibraryA, then write it to the start of the buffer. The next step is to write the path we have in our buffer variable tszHookPath. To begin with this, again we will be using WriteProcessMemory just like the first method write the path to the buffer. But instead of just passing lpAlloc as the address, we need to add +4 to the address so we do not overwrite the LoadLibraryA address we just wrote. So we can do that by doing:
WriteProcessMemory( pi.hProcess, (LPVOID)((DWORD)lpAlloc+4), tszHookPath, _tcslen(tszHookPath), NULL );

So now our buffer holds the address and the module to load. The last part is to write the function code to the buffer. We can't easily write the ASM directly to the process, but instead, we have to write the opcode bytes. As seen above in the function I mentioned, we can convert the bytes for the opcodes to a byte array and write that. This is a crucial part that must be done correctly. This is the code that will be executed, this needs to be correct as well as written to the proper location in the buffer or, as I said before, you can crash the remote target. We will convert and write the opcode bytes like this:
BYTE btFunction[] = {0x55, 0x8B, 0xEC, 0x51, 0x8B, 0x45, 0x08, 0x83, 0xC0, 0x04, 0x50, 0x8B, 0x4D, 0x08, 0xFF, 0x11, 0x8B, 0xE5, 0x5D, 0xC2, 0x04, 0x00}; WriteProcessMemory( pi.hProcess, (LPVOID) ((DWORD)lpAlloc+4+_tcslen(tszHookPath)+1), &btFunction, sizeof(btFunction), NULL );

For the address part of this, this says that want to write our function to the end of the string for the hook path +1. lpAlloc being the start of the buffer, +4 being for the first address, _tcslen(tszHookPath) to account for the size of the string, and +1 for safety. The next step is to create the thread to call our code. As I mentioned before the thread has a param

DLL Injection atom0s {RES} 2008 Team Resurrection


for the function address to be executed for the thread, as well as a param to pass any parameters to the function address. In our case we will be passing the pointer to our buffer as the functions paramter, and for the function, we will pass the address were our code block starts in our buffer. The first part would be the function address in the buffer where our code block starts. Just like the last WriteProcessMemory we did, we would use the same math to calculate the offset in the buffer. Then the actual parameter for our function will be the start of the buffer. So we could call CreateRemoteThread like this:
HANDLE hRemoteThread; hRemoteThread = CreateRemoteThread( pi.hProcess, 0, 0, (LPTHREAD_START_ROUTINE)((DWORD)lpAlloc+_tcslen(tszHookPath)+5), lpAlloc, 0, 0 );

Then the last part of the code would be the same as the other methods loader. We would wait for the thread, then cleanup and check our return. So we would use the same code as before:
DWORD dwExitCode; WaitForSingleObject( hRemoteThread, INFINITE ); GetExitCodeThread( hRemoteThread, &dwExitCode ); CloseHandle( hRemoteThread ); VirtualFreeEx( pi.hProcess, lpAlloc, 1000, MEM_DECOMMIT ); ResumeThread( pi.hThread ); CloseHandle( pi.hThread ); CloseHandle( pi.hProcess ); if( dwExitCode == 0 ) MessageBox( 0, "Module did not load.", "Error", MB_OK ); return 0; }

Again, be sure to compile in Multi-byte if you do not plan to adjust things that may need to be adjusted for Unicode. Also, you will not be able to debug this in Visual Studio without changing the paths for the working directory in debug mode like mentioned in method 1. Just like before, to test drop the hook.dll in the same folder as your loader.exe and then run Minesweeper, in this case that is my target. Then load loader.exe and it should inject.

DLL Injection atom0s {RES} 2008 Team Resurrection

Method 3. - API Injection Method (3)


API injection method 3 consists of using the API SetWindowsHookEx. I personally suggest not using this method as it can get out of hand. You are able to hook into every single process at once due to what SetWindowsHookEx was designed for doing. This method is done by hooking the WH_CTB message handler inside an application. CTB stands for Computer-based training, it is an application type that handles messages received from the system for several purposes. The more common of those would be for window placement, sizing, and visbility. You can find a very good amount of information about this procedure in the links section of this tutorial for this API. To start, we will have to adjust our hook DLL. We need to add a CBT callback function to it for it to inject properly. This is very simple to do and requires little effort. To do this, we need to create the message pump inside the module we create just to allow it to be loaded into the process. As I said, this is intrusive, so if you can do the other methods, I highly suggest it. So we want to start with the message loop function.. this will look like this:
LRESULT __stdcall CBTProc( int nCode, WPARAM wParam, LPARAM lParam ) { }

Next, for the inner actual code of this function, we simply need to allow this to pass through back to the original CBTProc of the process. As I mentioned, we are just getting this to load our module for us, and nothing else. We do not want to disturbed the normal flow of the process. When using SetWindowsHookEx on a message handler, we need to allow it to continue the normal flow by using CallNextHookEx. Like I said, we are just passing the message through without touching it so our inner code will look like this:
return CallNextHookEx( 0, nCode, wParam, lParam );

The last part for the hook is to export this function so we can obtain a procedure address inside the loader using GetProcAddress. We can export this easily by creating a new header file inside the hook project and adding the following code:
#ifndef _HOOK_HEADER_INCLUDE_ONCE_ #define _HOOK_HEADER_INCLUDE_ONCE_ #ifdef HOOK_EXPORTS #define HOOK_API __declspec( dllexport ) #else #define HOOK_API __declspec( dllimport ) #endif HOOK_API LRESULT __stdcall CBTProc(int,WPARAM,LPARAM); #endif

DLL Injection atom0s {RES} 2008 Team Resurrection


This header tells the compiler to export our function if it locates HOOK_EXPORTS in the preprocessor definitions. The next step for this is to add our header include to the top of the main.cpp under our windows.h include. Then, you need to add a .def file to the project. With newer versions of Visual Studio (I do not know of 6.0 does it automatically) it will automatically add HOOK_EXPORTS to the preprocessor definitions for you when you add it to the project. So add a .def file to the project as well and we will be adding the following into it:
LIBRARY "hook" EXPORTS CBTProc

This file tells the compiler how to export our functions, if we need to redirect functions, or if we want to cleanup the compiled name of the function so it can be used with Visual Basic applications and such. Now the hook itself is done. All we have left to do is the actual loading process to have the module injected. The nice part of this method is that just about all of the work is done for us by SetWindowsHookEx. All we have to do is obtain the address inside our module that our CBTProc is at, obtain the thread id for the process we want to inject into, and then simply call SetWindowsHookEx with the proper parameters. To get started, lets make a fully new console application. No need to use the same code as above, be cause it wont be using almost anything that we used before. First, for our includes, we will need the following:
#include <windows.h> #include <tlhelp32.h> #include <tchar.h>

Next, for basic code to get started, we will need to use two functions we wrote earlier: _ProcIdByName, and _ThreadIdByProcId. They are written above in method one and will be used for this method as well as we need the thread id from the process we want to inject into. We will start our main function off by building our hook path since we need to obtain the procedure address to our CBTHook function. Like before, I will be using same path things for this, you can extend it to other directories or allowing the user to pick where the module is at, I personally prefer expecting the module to be in the same folder as the loader. So we start again by using GetCurrentDirectory and append the hook name to the end of the path.
TCHAR tszHookPath[ MAX_PATH ]; GetCurrentDirectory( MAX_PATH, tszHookPath ); _tcscat_s( tszHookPath, MAX_PATH, _T("\\hook.dll") );

Next, we can obtain the process and thread ids to make sure our target is running. As I said, this can be used to hook into every single process, so we do not want to allow screw ups with this method.

DLL Injection atom0s {RES} 2008 Team Resurrection


While your hook might be fine to inject into every process, it is not something we want to allow happen with more intrusive hooks that plan to do process patching, hooking, and so on. So just like before in the other methods we can do this by doing:
DWORD dwProcId = 0; DWORD dwThreadId = 0; dwProcId = _ProcIdByName( _T("winmine.exe") ); if( dwProcId == 0 ) return 0; dwThreadId = _ThreadIdByProcId( dwProcId ); if( dwThreadId == 0 ) return 0;

Next, we need to load our hook into our loader to get a valid handle and procedure address for our CBTProc handler. SetWindowsHookEx requires both of these for parameters. So keep in mind your loader should be kept simple as it will have your hook be loaded into it so you do not want the loader to crash before it can fully finish it's objective. To obtain the two things we need here, we can use LoadLibrary and GetProcAddress. LoadLibrary will give us the handle we need while GetProcAddress will give us the procedure address to the hook message.
HMODULE hModule; FARPROC fpProcAddr; hModule = LoadLibrary( tszHookPath ); fpProcAddr = GetProcAddress( hModule, "CBTProc" );

Like I mentioned above, when we call LoadLibrary to get the handle of the module, it will load the hook into our process first. With our example hook, this will make the message box appear even though we are not injected yet. Keep this in mind. The last part of this method is to simply call SetWindowsHookEx now with the information we have obtained above. Lets go over the parameters real quick before we actually call the API though. SetWindowsHookEx is setup like this (taken from MSDN:)
HHOOK SetWindowsHookEx(int idHook, HOOKPROC lpfn, HINSTANCE hMod, DWORD dwThreadId);

The first paramter idHook is the type of hook we wish to create. In our case this will be WH_CBT to hook the CBTProc message prodecure. The second parameter lpfn is the pointer to the prodecure we plan to replace the idHook with. This is what we just obtained with GetProcAddress. Next is hMod, which is a module instance handle, which we just obtained with LoadLibrary. Last, we have dwThreadId which is the processes main thread id that we are wanting to hook. Please keep in mind if you pass 0 as a thread id you will hook into every single thread on the system that is loaded under the same desktop instance. This is what we want to avoid so be sure to check the thread id before calling this API if you modified the code to your own needs!

DLL Injection atom0s {RES} 2008 Team Resurrection


So lastly, our call to SetWindowsHookExA will look like this..
SetWindowsHookEx( WH_CBT, (HOOKPROC)fpProcAddr, hModule, dwThreadId );

Now something else to keep in mind. SetWindowsHookEx DOES NOT work right off the bat. Instead, when you inject a hook like this, the window must perform something to call what ever it is you are hooking with SetWindowsHookEx. In this case, a window needs to obtain a window message. This can be done by moving the mouse over the window, resizing it, putting it into focus, minimizing it, and so on. So to ensure the hook gets injected, we need to tell our loader to wait. The easier method to do is pausing the loader which can be done using the iostream by doing:
std::cin.sync(); std::cin.ignore();

Something else you will notice, if you close the loader while the process is still running, you will unload your hook. This occurs because the actual hook is mapped inside the loader and not inside the actual target process. Ways to fix this? You can use two DLLs in this method. Have the CBTProc hook DLL to be used to inject the initial message to be able to get inside the process. Then inside the CBTProc DLL you can have the 2nd module loaded. Another alternative is to use WH_CALLWNDPROC but keep in mind, not all processes have a WNDPROC so this will not work for every process. I again, suggest not using this method unless you absolutely need to use it.

DLL Injection atom0s {RES} 2008 Team Resurrection

Other Methods (Overview)


While I am not going to get into full detail about other methods that you can use, I will give a small oversight to some other methods I have seen done. I personally feel that the methods that will be explained here are a bit over the top or more intrusive and painstaking then need be for this process. However, in same cases, taking extreme measures is needed to be done due to protection systems and such that are developed today. - Drivers One method that I feel is a bit much is using a driver to inject your code into another process. You can do this inside a driver by hooking kernel layer API such as NtCreateProcess (not suggested because a process does not have to call this to be created) or NtCreateFile and NtCreateSection (which both get called during all process creations). This way you can monitor for specifics of a process, and when found you can tell the driver to load your module into the process as well. - Proxy DLL Another method would be to hand edit a DLL that the process uses. There are two methods to this, you can edit create a proxy DLL to proxy a system DLL that the process uses, or, you can edit one that the process comes with specifically designed for the process itself. By using the proxy method, we allow our proxy DLL to be used instead of the original system DLL. By default, a Windows application will look inside the same folder when loading DLLs before looking into the PATH directories of the system. This means you can proxy just about any system DLL. By proxy, I mean export all the functions that the normal DLL would, and not edit them, but simply pass the functions to the real system DLL to be normally called and executed. Inside your proxy, you can then edit the entrypoint, load other code, and do what you wish normally. - Entry Point Change The other method with editing a DLL that comes with the process is to edit the entry point of the DLL. Instead of letting it flow normally, you can create a code cave inside the DLL that is statically added to the module. Your cave would call LoadLibrary on your module, then jump back to the original entry point of the DLL. So at the entry point you would now have a jump to the cave you create inside the DLL to which will load your module then jump back to the real entry point to continue normal execution. This way is a bit messy and can be easily detected by MD5/CrC32 checksums and such as you will be editing the module directly. - TLS Creation Another method that isn't too commonly seen is editing a process. This is also another very intrusive and messy method as you will need to do a bit of work for this to work correctly. TLS stands for Thread Local Storage, which can be used to initialize anything before the actual entry point of a process is called. This is commonly seen in Delphi applications as the Delphi runtime is

DLL Injection atom0s {RES} 2008 Team Resurrection


initialized inside a TLS block. If a process does not have a TLS you can create on yourself by hand with a bit of work. The TLS information is stored inside the PE header and can be edited by a hex editor. You will need to also create a block of code to do the actual LoadLibrary call and point the TLS block to call that specific location. This method is very intrusive and requires a bit of knowledge of how to create the TLS block, where you can place code, etc. Which is where you will be doing a lot of research, trial and error, and work to the original process.

There are many possible ways to inject into a process, it all depends on the needs of the user to get the module loaded. Some processes will prevent you from executing code remotely, they will prevent other DLLs being loaded in the process, etc. So it is up to you to find and pick a method that will work best for your needs. Anything that you can do to get the job done.

DLL Injection atom0s {RES} 2008 Team Resurrection

Windows 98 Injection (Wait what..? Win98!?)


Well this section is here due to request. Someone had requested how to inject on Windows 98 machines, I personally do not know why this would be needed as Windows 98 is no longer supported nor used much anymore other then by older machines that people probably don't use anymore. But, I will do what I can do cover this thoroughly. At this time, this section of the tutorial cannot be completed to due my system not wanting to run Windows 98 correctly. When I have the time, and another system, to fully install the operating system onto, I will attempt to find the time to finish this section. Sorry. :(

DLL Injection atom0s {RES} 2008 Team Resurrection

Credits, Greetz, Shouts, Etcs...


Credits:

Microsoft

For their online MSDN. For their C++ IDE and compiler that doesn't suck. For Minesweeper being my bitch for just about everything I test stuff on.

PEiD, OllyDbg, Cheat Engine, and all the other tools I use daily. Google.com for various searches for help with explanations that I couldn't word greatly. OpenOffice for not being elitists and being open source.

Greetz / Shouts:

SunBeam, STN, Lab, attilathedud, Renko Team {RES}, Appznet Community, Appznet Friends and Family Anyone that reads this, finds this useful, or uses this content. Stay true to your word, keep it close, and never let go.

You might also like