For C#/.NET directly:
You can use classes from System.Diagnostics
, the property Process.Modules
will fetch you a list of modules that have been loaded by the associated process, you can list all processes by calling Processes.GetProcesses()
.
For C++ side or if you want to invoke platform APIs:
You are looking for Win32 function EnumProcessModules()
, which allows you to list all the modules (DLLs) loaded in a process referenced by a handle (which you can OpenProcess()
by PID or whatever).
There is even a full example called Enumerating All Modules For a Process.
Detecting also other opened files:
I noticed late you wanted to detect also any other opened files, so I suggest the following hack: Hook Win32 API CreateFileW()
(the Wide version is enough, Ascii one is just its wrapper) and log whatever is being opened (not only created, the function name can be misleading).
This is a working example code which shows what you'd need to do:
#include <windows.h>
#include <cstdio>
#include <cassert>
typedef HANDLE (WINAPI *CreateFileWType)(LPCWSTR, DWORD, DWORD,
LPSECURITY_ATTRIBUTES, DWORD, DWORD, HANDLE);
CreateFileWType origCreateFile;
HANDLE WINAPI myCreateFileW(LPCWSTR lpFileName, DWORD dwDesiredAccess,
DWORD dwShareMode, LPSECURITY_ATTRIBUTES lpSecurityAttributes,
DWORD dwCreationDisposition, DWORD dwFlagsAndAttributes,
HANDLE hTemplateFile)
{
// Log something about the file
printf("*** %S [%c%c]\n",
lpFileName,
(dwDesiredAccess & GENERIC_READ) ? 'R' : '-',
(dwDesiredAccess & GENERIC_WRITE) ? 'W' : '-');
// Call the original function
return origCreateFile(lpFileName, dwDesiredAccess, dwShareMode,
lpSecurityAttributes, dwCreationDisposition,
dwFlagsAndAttributes, hTemplateFile);
}
int main()
{
// Get pointer to the function and verify it can be hot-patched
HMODULE hKernelBase = GetModuleHandle(TEXT("KernelBase"));
BYTE* addr = reinterpret_cast<BYTE*>(GetProcAddress(hKernelBase, "CreateFileW"));
assert(addr != NULL);
assert(addr[0] == 0x8B && addr[1] == 0xFF); // `mov edi, edi` prologue
// Save the original function location (after the prologue)
origCreateFile = reinterpret_cast<CreateFileWType>(addr + 2);
// Hot-patch the function to call our hook
DWORD dwOldProtect;
VirtualProtect(addr - 5, 7, PAGE_EXECUTE_READWRITE, &dwOldProtect);
addr[-5] = 0xE9; // jmp ...
*reinterpret_cast<DWORD*>(&addr[-4]) = reinterpret_cast<BYTE*>(&myCreateFileW) - addr;
*reinterpret_cast< WORD*>(&addr[ 0]) = 0xF9EB; // jmps $-5
VirtualProtect(addr - 5, 7, dwOldProtect, &dwOldProtect);
// Test that it all works - original application would continue here
fopen("input.txt", "r");
fopen("output.txt", "w");
return 0;
}
Example output:
*** input.txt [R-]
*** output.txt [-W]
This won't log any low-level system operations like loading DLLs, because such things are using NT call NtCreateFile()
directly, but should work for most cases of file access fine. Also more error checking, converting into Managed C++/CLR or whatever is left as an exercise to the reader.