Injecting a DLL Using Remote Threads

[Previous] [Next]

The third method of injecting a DLL, using remote threads, offers the greatest flexibility. It requires that you understand several Windows features: processes, threads, thread synchronization, virtual memory management, DLLs, and Unicode. (If you're unclear about any of these features, please refer to their respective chapters in this book.) Most Windows functions allow a process to manipulate only itself. This is good because it prevents one process from corrupting another process. However, a handful of functions do allow one process to manipulate another. Most of these functions were originally designed for debuggers and other tools. However, any application can call these functions.

Basically, this DLL injection technique requires that a thread in the target process call LoadLibrary to load the desired DLL. Since we can't easily control the threads in a process other than our own, this solution requires that we create a new thread in the target process. Since we create this thread ourselves, we can control what code it executes. Fortunately, Windows offers a function called CreateRemoteThread that makes it easy to create a thread in another process:

 HANDLE CreateRemoteThread( HANDLE hProcess, PSECURITY_ATTRIBUTES psa, DWORD dwStackSize, PTHREAD_START_ROUTINE pfnStartAddr, PVOID pvParam, DWORD fdwCreate, PDWORD pdwThreadId); 

CreateRemoteThread is identical to CreateThread except that it has one additional parameter, hProcess. This parameter identifies the process that will own the newly created thread. The pfnStartAddr parameter identifies the memory address of the thread function. This memory address is, of course, relative to the remote process—the thread function's code cannot be in your own process's address space.

NOTE
In Windows 2000, the more commonly used CreateThread function is implemented internally as follows:

 HANDLE CreateThread(PSECURITY_ATTRIBUTES psa, DWORD dwStackSize, PTHREAD_START_ROUTINE pfnStartAddr, PVOID pvParam, DWORD fdwCreate, PDWORD pdwThreadID) { return(CreateRemoteThread(GetCurrentProcess(), psa, dwStackSize, pfnStartAddr, pvParam, fdwCreate, pdwThreadID)); } 

Windows 98
In Windows 98, the CreateRemoteThread function has no useful implementation and simply returns NULL; calling GetLastError returns ERROR_CALL_NOT_IMPLEMENTED. (The CreateThread function contains the complete implementation of the code that creates a thread in the calling process.) Because CreateRemoteThread is not implemented, you cannot use this technique to inject a DLL under Windows 98.

OK, so now you know how to create a thread in another process, but how do we get that thread to load our DLL? The answer is simple: we need the thread to call the LoadLibrary function:

 HINSTANCE LoadLibrary(PCTSTR pszLibFile); 

If you look up LoadLibrary in the WinBase.h header file, you'll find the following:

 HINSTANCE WINAPI LoadLibraryA(LPCSTR pszLibFileName); HINSTANCE WINAPI LoadLibraryW(LPCWSTR pszLibFileName); #ifdef UNICODE #define LoadLibrary LoadLibraryW #else #define LoadLibrary LoadLibraryA #endif // !UNICODE 

There are actually two LoadLibrary functions: LoadLibraryA and LoadLibraryW. The only difference between them is the type of parameter that you pass to the function. If you have the library's filename stored as an ANSI string, you must call LoadLibraryA. (The A stands for ANSI.) If the filename is stored as a Unicode string, you must call LoadLibraryW. (The W stands for wide characters.) No single LoadLibrary function exists—only LoadLibraryA and LoadLibraryW. For most applications, the LoadLibrary macro expands to LoadLibraryA.

Fortunately, the prototype for the LoadLibrary functions and the prototype for a thread function are identical. Here is a thread function's prototype:

 DWORD WINAPI ThreadFunc(PVOID pvParam); 

OK, the function prototypes are not exactly identical, but they are close enough. Both functions accept a single parameter and both return a value. Also, both use the same calling convention, WINAPI. This is extremely fortunate because all we have to do is create a new thread and have the thread function address be the address of the LoadLibraryA or LoadLibraryW function. Basically, all we need to do is execute a line of code that looks like this:

 HANDLE hThread = CreateRemoteThread(hProcessRemote, NULL, 0, LoadLibraryA, "C:\\MyLib.dll", 0, NULL); 

Or, if you prefer Unicode, the line looks like this:

 HANDLE hThread = CreateRemoteThread(hProcessRemote, NULL, 0, LoadLibraryW, L"C:\\MyLib.dll", 0, NULL); 

When the new thread is created in the remote process, the thread immediately calls the LoadLibraryA (or LoadLibraryW) function, passing to it the address of the DLL's pathname. This is easy. But there are two other problems.

The first problem is that you can't simply pass LoadLibraryA or LoadLibraryW as the fourth parameter to CreateRemoteThread, as I've shown above. The reason is quite subtle. When you compile and link a program, the resulting binary contains an import section (described in Chapter 19). This section consists of a series of thunks to imported functions. So when your code calls a function such as LoadLibraryA, the linker generates a call to a thunk in your module's import section. The thunk in turn jumps to the actual function.

If you use a direct reference to LoadLibraryA in the call to CreateRemoteThread, this resolves to the address of the LoadLibraryA thunk in your module's import section. Passing the address of the thunk as the starting address of the remote thread causes the remote thread to begin executing who-knows-what. The result is most likely an access violation. To force a direct call to the LoadLibraryA function, bypassing the thunk, you must get the exact memory location of LoadLibraryA by calling GetProcAddress.

The call to CreateRemoteThread assumes that Kernel32.dll is mapped to the same memory location in both the local and the remote processes' address spaces. Every application requires Kernel32.dll, and in my experience the system maps Kernel32.dll to the same address in every process. So we have to call CreateRemoteThread like this:

 // Get the real address of LoadLibraryA in Kernel32.dll. PTHREAD_START_ROUTINE pfnThreadRtn = (PTHREAD_START_ROUTINE) GetProcAddress(GetModuleHandle(TEXT("Kernel32")), "LoadLibraryA"); HANDLE hThread = CreateRemoteThread(hProcessRemote, NULL, 0, pfnThreadRtn, "C:\\MyLib.dll", 0, NULL); 

Or, again, if you prefer Unicode, do this:

 // Get the real address of LoadLibraryW in Kernel32.dll. PTHREAD_START_ROUTINE pfnThreadRtn = (PTHREAD_START_ROUTINE) GetProcAddress(GetModuleHandle(TEXT("Kernel32")), "LoadLibraryW"); HANDLE hThread = CreateRemoteThread(hProcessRemote, NULL, 0, pfnThreadRtn, L"C:\\MyLib.dll", 0, NULL); 

All right, this fixes one problem. But I said that there were two problems. The second problem has to do with the DLL pathname string. The string, "C:\\MyLib.dll", is in the calling process's address space. The address of this string is given to the newly created remote thread, which passes it to LoadLibraryA. But when LoadLibraryA dereferences the memory address, the DLL pathname string is not there and the remote process's thread will probably raise an access violation; the unhandled exception message box is presented to the user, and the remote process is terminated. That's right, the remote process is terminated—not your process. You will have successfully crashed another process while your process continues to execute just fine!

To fix this, we need to get the DLL's pathname string into the remote process's address space. Then, when CreateRemoteThread is called, we need to pass it the address (relative to the remote process) of where we placed the string. Again, Windows offers a function, VirtualAllocEx, that allows one process to allocate memory in another process's address space:

 PVOID VirtualAllocEx( HANDLE hProcess, PVOID pvAddress, SIZE_T dwSize, DWORD flAllocationType, DWORD flProtect); 

Another function allows us to free this memory:

 BOOL VirtualFreeEx( HANDLE hProcess, PVOID pvAddress, SIZE_T dwSize, DWORD dwFreeType); 

Both of these functions are similar to their non-Ex versions (which are discussed in Chapter 15). The only difference is that these two functions require a handle to a process as their first argument. This handle indicates the process where the operation is to be performed.

Once we allocate memory for the string, we also need a way to copy the string from our process's address space over to the remote process's address space. Windows offers functions that allow one process to read and write from/to another process's address space:

 BOOL ReadProcessMemory( HANDLE hProcess, PVOID pvAddressRemote, PVOID pvBufferLocal, DWORD dwSize, PDWORD pdwNumBytesRead); BOOL WriteProcessMemory( HANDLE hProcess, PVOID pvAddressRemote, PVOID pvBufferLocal, DWORD dwSize, PDWORD pdwNumBytesWritten); 

The remote process is identified by the hProcess parameter. The pvAddressRemote parameters indicate the address in the remote process, pvBufferLocal is the address of memory in the local process, dwSize is the requested number of bytes to transfer, and pdwNumBytesRead and pdwNumBytesWritten indicate the number of bytes actually transferred and can be examined when the function returns.

Now that you understand all that I'm trying to do, let me summarize the steps you must take:

  1. Use the VirtualAllocEx function to allocate memory in the remote process's address space.
  2. Use the WriteProcessMemory function to copy the DLL's pathname to the memory allocated in step 1.
  3. Use the GetProcAddress function to get the real address (inside Kernel32.dll) of the LoadLibraryA or LoadLibraryW function.
  4. Use the CreateRemoteThread function to create a thread in the remote process that calls the proper LoadLibrary function, passing it the address of the memory allocated in step 1.

At this point, the DLL has been injected into the remote process's address space, and the DLL's DllMain function receives a DLL_PROCESS_ATTACH notification and can execute the desired code. When DllMain returns, the remote thread returns from its call to LoadLibrary back to the BaseThreadStart function (discussed in Chapter 6). BaseThreadStart then calls ExitThread, causing the remote thread to die.

Now the remote process has the block of storage allocated in step 1 and the DLL still stuck in its address space. To clean this stuff up, we'll need to execute the following steps after the remote thread exists:

  1. Use the VirtualFreeEx function to free the memory allocated in step 1.
  2. Use the GetProcAddress function to get the real address (inside Kernel32.dll) of the FreeLibrary function.
  3. Use the CreateRemoteThread function to create a thread in the remote process that calls FreeLibrary function, passing the remote DLL's HINSTANCE.

That's basically it. The only downside to this DLL injection technique—the most versatile one we have discussed so far—is that Windows 98 doesn't support a lot of these functions. You can use this technique only on Windows 2000.

The Inject Library Sample Application

The InjLib.exe application, listed in Figure 22-3, injects a DLL using the CreateRemoteThread function. The source code and resource files for the application and DLL are in the 22-InjLib and 22-ImgWalk directories on the companion CD-ROM. The program uses the following dialog box to accept the process ID of a running process.

You can obtain a process's ID by using the Task Manager that ships with Windows 2000. Using the ID, the program attempts to open a handle to this running process by calling OpenProcess, requesting the appropriate access rights:

 hProcess = OpenProcess( PROCESS_CREATE_THREAD | // For CreateRemoteThread PROCESS_VM_OPERATION | // For VirtualAllocEx/VirtualFreeEx PROCESS_VM_WRITE, // For WriteProcessMemory FALSE, dwProcessId); 

If OpenProcess returns NULL, the application is not running under a security context that allows it to open a handle to the target process. Some processes—such as WinLogon, SvcHost, and Csrss—run under the local system account, which the logged-on user cannot alter. You might be able to open a handle to these processes if you are granted and enable the debug security privilege. The ProcessInfo sample in Chapter 4 demonstrates how to do this.

If OpenProcess is successful, a buffer is initialized with the full pathname of the DLL that is to be injected. Then InjectLib is called, passing it the handle of the desired remote process and the pathname of the DLL to inject into it. Finally, when InjectLib returns, the program displays a message box indicating whether the DLL successfully loaded in the remote process; it then closes the handle to the process. That's all there is to it.

You might notice in the code that I make a special check to see whether the process ID passed is 0. If so, I set the process ID to InjLib.exe's own process ID by calling GetCurrentProcessId. This way, when InjectLib is called, the DLL is injected into the process's own address space. This makes debugging easier. As you can imagine, when bugs popped up, it was sometimes difficult to determine whether the bugs were in the local process or in the remote process. Originally, I started debugging my code with two debuggers, one watching InjLib and the other watching the remote process. This turned out to be terribly inconvenient. It then dawned on me that InjLib can also inject a DLL into itself—that is, into the same address space as the caller. This made it much easier to debug my code.

As you can see at the top of the source code module, InjectLib is really a symbol that expands to either InjectLibA or InjectLibW depending on how you're compiling the source code. The InjectLibW function is where all the magic happens. The comments speak for themselves, and I can't add much here. However, you'll notice that the InjectLibA function is short. It simply converts the ANSI DLL pathname to its Unicode equivalent and then calls the InjectLibW function to actually do the work. This approach is exactly what I recommended in Chapter 2. It also means that I only had to get the injection code running once—a nice timesaver.

Figure 22-3. The InjLib sample application

InjLib.cpp

 /****************************************************************************** Module: InjLib.cpp Notices: Copyright (c) 2000 Jeffrey Richter ******************************************************************************/ #include "..\CmnHdr.h" /* See Appendix A. */ #include <windowsx.h> #include <stdio.h> #include <tchar.h> #include <malloc.h> // For alloca #include <TlHelp32.h> #include "Resource.h" /////////////////////////////////////////////////////////////////////////////// #ifdef UNICODE #define InjectLib InjectLibW #define EjectLib EjectLibW #else #define InjectLib InjectLibA #define EjectLib EjectLibA #endif // !UNICODE /////////////////////////////////////////////////////////////////////////////// BOOL WINAPI InjectLibW(DWORD dwProcessId, PCWSTR pszLibFile) { BOOL fOk = FALSE; // Assume that the function fails HANDLE hProcess = NULL, hThread = NULL; PWSTR pszLibFileRemote = NULL; _ _try { // Get a handle for the target process. hProcess = OpenProcess( PROCESS_CREATE_THREAD | // For CreateRemoteThread PROCESS_VM_OPERATION | // For VirtualAllocEx/VirtualFreeEx PROCESS_VM_WRITE, // For WriteProcessMemory FALSE, dwProcessId); if (hProcess == NULL) _ _leave; // Calculate the number of bytes needed for the DLL's pathname int cch = 1 + lstrlenW(pszLibFile); int cb = cch * sizeof(WCHAR); // Allocate space in the remote process for the pathname pszLibFileRemote = (PWSTR) VirtualAllocEx(hProcess, NULL, cb, MEM_COMMIT, PAGE_READWRITE); if (pszLibFileRemote == NULL) _ _leave; // Copy the DLL's pathname to the remote process's address space if (!WriteProcessMemory(hProcess, pszLibFileRemote, (PVOID) pszLibFile, cb, NULL)) _ _leave; // Get the real address of LoadLibraryW in Kernel32.dll PTHREAD_START_ROUTINE pfnThreadRtn = (PTHREAD_START_ROUTINE) GetProcAddress(GetModuleHandle(TEXT("Kernel32")), "LoadLibraryW"); if (pfnThreadRtn == NULL) _ _leave; // Create a remote thread that calls LoadLibraryW(DLLPathname) hThread = CreateRemoteThread(hProcess, NULL, 0, pfnThreadRtn, pszLibFileRemote, 0, NULL); if (hThread == NULL) _ _leave; // Wait for the remote thread to terminate WaitForSingleObject(hThread, INFINITE); fOk = TRUE; // Everything executed successfully } _ _finally { // Now, we can clean everthing up // Free the remote memory that contained the DLL's pathname if (pszLibFileRemote != NULL) VirtualFreeEx(hProcess, pszLibFileRemote, 0, MEM_RELEASE); if (hThread != NULL) CloseHandle(hThread); if (hProcess != NULL) CloseHandle(hProcess); } return(fOk); } /////////////////////////////////////////////////////////////////////////////// BOOL WINAPI InjectLibA(DWORD dwProcessId, PCSTR pszLibFile) { // Allocate a (stack) buffer for the Unicode version of the pathname PWSTR pszLibFileW = (PWSTR) _alloca((lstrlenA(pszLibFile) + 1) * sizeof(WCHAR)); // Convert the ANSI pathname to its Unicode equivalent wsprintfW(pszLibFileW, L"%S", pszLibFile); // Call the Unicode version of the function to actually do the work. return(InjectLibW(dwProcessId, pszLibFileW)); } /////////////////////////////////////////////////////////////////////////////// BOOL WINAPI EjectLibW(DWORD dwProcessId, PCWSTR pszLibFile) { BOOL fOk = FALSE; // Assume that the function fails HANDLE hthSnapshot = NULL; HANDLE hProcess = NULL, hThread = NULL; _ _try { // Grab a new snapshot of the process hthSnapshot = CreateToolhelp32Snapshot(TH32CS_SNAPMODULE, dwProcessId); if (hthSnapshot == NULL) _ _leave; // Get the HMODULE of the desired library MODULEENTRY32W me = { sizeof(me) }; BOOL fFound = FALSE; BOOL fMoreMods = Module32FirstW(hthSnapshot, &me); for (; fMoreMods; fMoreMods = Module32NextW(hthSnapshot, &me)) { fFound = (lstrcmpiW(me.szModule, pszLibFile) == 0) || (lstrcmpiW(me.szExePath, pszLibFile) == 0); if (fFound) break; } if (!fFound) _ _leave; // Get a handle for the target process. hProcess = OpenProcess( PROCESS_CREATE_THREAD | PROCESS_VM_OPERATION, // For CreateRemoteThread FALSE, dwProcessId); if (hProcess == NULL) _ _leave; // Get the real address of LoadLibraryW in Kernel32.dll PTHREAD_START_ROUTINE pfnThreadRtn = (PTHREAD_START_ROUTINE) GetProcAddress(GetModuleHandle(TEXT("Kernel32")), "FreeLibrary"); if (pfnThreadRtn == NULL) _ _leave; // Create a remote thread that calls LoadLibraryW(DLLPathname) hThread = CreateRemoteThread(hProcess, NULL, 0, pfnThreadRtn, me.modBaseAddr, 0, NULL); if (hThread == NULL) _ _leave; // Wait for the remote thread to terminate WaitForSingleObject(hThread, INFINITE); fOk = TRUE; // Everything executed successfully } _ _finally { // Now we can clean everything up if (hthSnapshot != NULL) CloseHandle(hthSnapshot); if (hThread != NULL) CloseHandle(hThread); if (hProcess != NULL) CloseHandle(hProcess); } return(fOk); } /////////////////////////////////////////////////////////////////////////////// BOOL WINAPI EjectLibA(DWORD dwProcessId, PCSTR pszLibFile) { // Allocate a (stack) buffer for the Unicode version of the pathname PWSTR pszLibFileW = (PWSTR) _alloca((lstrlenA(pszLibFile) + 1) * sizeof(WCHAR)); // Convert the ANSI pathname to its Unicode equivalent wsprintfW(pszLibFileW, L"%S", pszLibFile); // Call the Unicode version of the function to actually do the work. return(EjectLibW(dwProcessId, pszLibFileW)); } /////////////////////////////////////////////////////////////////////////////// BOOL Dlg_OnInitDialog(HWND hwnd, HWND hwndFocus, LPARAM lParam) { chSETDLGICONS(hwnd, IDI_INJLIB); return(TRUE); } /////////////////////////////////////////////////////////////////////////////// void Dlg_OnCommand(HWND hwnd, int id, HWND hwndCtl, UINT codeNotify) { switch (id) { case IDCANCEL: EndDialog(hwnd, id); break; case IDC_INJECT: DWORD dwProcessId = GetDlgItemInt(hwnd, IDC_PROCESSID, NULL, FALSE); if (dwProcessId == 0) { // A process ID of 0 causes everything to take place in the // local process; this makes things easier for debugging. dwProcessId = GetCurrentProcessId(); } TCHAR szLibFile[MAX_PATH]; GetModuleFileName(NULL, szLibFile, sizeof(szLibFile)); _tcscpy(_tcsrchr(szLibFile, TEXT('\\')) + 1, TEXT("22 ImgWalk.DLL")); if (InjectLib(dwProcessId, szLibFile)) { chVERIFY(EjectLib(dwProcessId, szLibFile)); chMB("DLL Injection/Ejection successful."); } else { chMB("DLL Injection/Ejection failed."); } break; } } /////////////////////////////////////////////////////////////////////////////// INT_PTR WINAPI Dlg_Proc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam) { switch (uMsg) { chHANDLE_DLGMSG(hwnd, WM_INITDIALOG, Dlg_OnInitDialog); chHANDLE_DLGMSG(hwnd, WM_COMMAND, Dlg_OnCommand); } return(FALSE); } /////////////////////////////////////////////////////////////////////////////// int WINAPI _tWinMain(HINSTANCE hinstExe, HINSTANCE, PTSTR pszCmdLine, int) { chWindows9xNotAllowed(); DialogBox(hinstExe, MAKEINTRESOURCE(IDD_INJLIB), NULL, Dlg_Proc); return(0); } //////////////////////////////// End of File ////////////////////////////////// 

InjLib.rc

 //Microsoft Developer Studio generated resource script. // #include "resource.h" #define APSTUDIO_READONLY_SYMBOLS ///////////////////////////////////////////////////////////////////////////// // // Generated from the TEXTINCLUDE 2 resource. // #include "afxres.h" ///////////////////////////////////////////////////////////////////////////// #undef APSTUDIO_READONLY_SYMBOLS ///////////////////////////////////////////////////////////////////////////// // English (U.S.) resources #if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_ENU) #ifdef _WIN32 LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US #pragma code_page(1252) #endif //_WIN32 ///////////////////////////////////////////////////////////////////////////// // // Icon // // Icon with lowest ID value placed first to ensure application icon // remains consistent on all systems. IDI_INJLIB ICON DISCARDABLE "InjLib.ico" #ifdef APSTUDIO_INVOKED ///////////////////////////////////////////////////////////////////////////// // // TEXTINCLUDE // 1 TEXTINCLUDE DISCARDABLE BEGIN "resource.h\0" END 2 TEXTINCLUDE DISCARDABLE BEGIN "#include ""afxres.h""\r\n" "\0" END 3 TEXTINCLUDE DISCARDABLE BEGIN "\r\n" "\0" END #endif // APSTUDIO_INVOKED ///////////////////////////////////////////////////////////////////////////// // // Dialog // IDD_INJLIB DIALOG DISCARDABLE 15, 24, 158, 24 STYLE DS_3DLOOK | DS_CENTER | WS_MINIMIZEBOX | WS_VISIBLE | WS_CAPTION | WS_SYSMENU CAPTION "Inject Library Tester" FONT 8, "MS Sans Serif" BEGIN LTEXT "&Process Id (decimal):",-1,4,6,69,8 EDITTEXT IDC_PROCESSID,78,4,36,12,ES_AUTOHSCROLL DEFPUSHBUTTON "&Inject",IDC_INJECT,120,4,36,12,WS_GROUP END ///////////////////////////////////////////////////////////////////////////// // // DESIGNINFO // #ifdef APSTUDIO_INVOKED GUIDELINES DESIGNINFO DISCARDABLE BEGIN IDD_INJLIB, DIALOG BEGIN RIGHTMARGIN, 134 BOTTOMMARGIN, 20 END END #endif // APSTUDIO_INVOKED #endif // English (U.S.) resources ///////////////////////////////////////////////////////////////////////////// #ifndef APSTUDIO_INVOKED ///////////////////////////////////////////////////////////////////////////// // // Generated from the TEXTINCLUDE 3 resource. // ///////////////////////////////////////////////////////////////////////////// #endif // not APSTUDIO_INVOKED 

The Image Walk DLL

ImgWalk.dll, listed in Figure 22-4, is a DLL that, once injected into a process's address space, can report on all the DLLs that the process is using. (The source code and resource files for the DLL are in the 22-ImgWalk directory on the companion CD-ROM.) For example, if I first run Notepad and then run InjLib, passing it Notepad's process ID, InjLib injects ImgWalk.dll into Notepad's address space. Once there, ImgWalk determines which file images (executables and DLLs) are being used by Notepad and displays the following message box, which shows the results.

ImgWalk walks through a process's address space looking for mapped file images by repeatedly calling VirtualQuery to fill a MEMORY_BASIC_INFORMATION structure. With each iteration of the loop, ImgWalk checks for a file pathname to concatenate with a string. This string appears in the message box.

 char szBuf[MAX_PATH * 100] = { 0 }; PBYTE pb = NULL; MEMORY_BASIC_INFORMATION mbi; while (VirtualQuery(pb, &mbi, sizeof(mbi)) == sizeof(mbi)) { int nLen; char szModName[MAX_PATH]; if (mbi.State == MEM_FREE) mbi.AllocationBase = mbi.BaseAddress; if ((mbi.AllocationBase == hinstDll) || (mbi.AllocationBase != mbi.BaseAddress) || (mbi.AllocationBase == NULL)) { // Do not add the module name to the list // if any of the following is true: // 1. This region contains this DLL. // 2. This block is NOT the beginning of a region. // 3. The address is NULL. nLen = 0; } else { nLen = GetModuleFileNameA((HINSTANCE) mbi.AllocationBase, szModName, chDIMOF(szModName)); } if (nLen > 0) { wsprintfA(strchr(szBuf, 0), "\n%08X-%s", mbi.AllocationBase, szModName); } pb += mbi.RegionSize; } chMB(&szBuf[1]); 

First, I check to see whether the region's base address matches the base address of the injected DLL. If it matches, I set nLen to 0 so that the injected library does not appear in the message box. If it doesn't match, I attempt to get the filename for the module loaded at the region's base address. If the nLen variable is greater than 0, the system recognizes that the address identifies a loaded module and the system fills the szModName buffer with the full pathname of the module. I then concatenate the module's HINSTANCE (base address) and its pathname with the szBuf string that will eventually be displayed in the message box. When the loop is finished, the DLL presents a message box with the final string as its contents.

Figure 22-4. Source code for ImgWalk.dll

ImgWalk.cpp

 /****************************************************************************** Module: ImgWalk.cpp Notices: Copyright (c) 2000 Jeffrey Richter ******************************************************************************/ #include "..\CmnHdr.h" /* See Appendix A. */ #include <tchar.h> /////////////////////////////////////////////////////////////////////////////// BOOL WINAPI DllMain(HINSTANCE hinstDll, DWORD fdwReason, PVOID fImpLoad) { if (fdwReason == DLL_PROCESS_ATTACH) { char szBuf[MAX_PATH * 100] = { 0 }; PBYTE pb = NULL; MEMORY_BASIC_INFORMATION mbi; while (VirtualQuery(pb, &mbi, sizeof(mbi)) == sizeof(mbi)) { int nLen; char szModName[MAX_PATH]; if (mbi.State == MEM_FREE) mbi.AllocationBase = mbi.BaseAddress; if ((mbi.AllocationBase == hinstDll) || (mbi.AllocationBase != mbi.BaseAddress) || (mbi.AllocationBase == NULL)) { // Do not add the module name to the list // if any of the following is true: // 1. If this region contains this DLL // 2. If this block is NOT the beginning of a region // 3. If the address is NULL nLen = 0; } else { nLen = GetModuleFileNameA((HINSTANCE) mbi.AllocationBase, szModName, chDIMOF(szModName)); } if (nLen > 0) { wsprintfA(strchr(szBuf, 0), "\n%p-%s", mbi.AllocationBase, szModName); } pb += mbi.RegionSize; } chMB(&szBuf[1]); } return(TRUE); } //////////////////////////////// End of File ////////////////////////////////// 



Programming Applications for Microsoft Windows
Programming Applications for Microsoft Windows (Microsoft Programming Series)
ISBN: 1572319968
EAN: 2147483647
Year: 1999
Pages: 193

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net