CreateProcess windows API stores excel in Documents folder - c++

I am working on a legacy application which calls CreateProcess method
method1()
{
BOOL bResult = ::CreateProcess(
NULL, // LPCTSTR
wctAppPath, // LPTSTR lpszCommandLine
NULL, // LPSECURITY_ATTRIBUTES lpsaProc
NULL, // LPSECURITY_ATTRIBUTES lpsaThread
FALSE, // BOOL fInheritHandles
NULL, // DWORD fdwCreate
NULL, // LPVOID lpvEnvironment
NULL, // LPCTSTR lpszCurDir
&stStatusInfo, // LPSTARTUPINFO lpsiStartInfo
&stProcInfo // LPPROCESS_INFORMATION lppiProcInfo
);
}
wctAppPath value : (folderPath\test.bat ..\excel.xls)
this CreateProcess trigger bat file that will launch vbscript and passed excel file is modified.
Once this method1() ends, they are calling CreateProcess() again in method2() to open the same excel file.
method2()
{
BOOL bResult = ::CreateProcess(
NULL, // LPCTSTR
wctAppPath, // LPTSTR
NULL, // LPSECURITY_ATTRIBUTES lpsaProc
NULL, // LPSECURITY_ATTRIBUTES lpsaThread
FALSE, // BOOL fInheritHandles
NULL, // DWORD fdwCreate
NULL, // LPVOID lpvEnvironment
NULL, // LPCTSTR lpszCurDir
&stStatusInfo, // LPSTARTUPINFO lpsiStartInfo
&stProcInfo // LPPROCESS_INFORMATION lppiProcInfo
);
}
Here wctAppPath is (excelInstallationPath\EXCEL.exe folderPath\excelFile.xls)
Once all the main function ends, the required modified excel in stored in Documents folder not in the actual given path. Actual given excel is not modified at all.
I have commented the method2 and tried. Expected excel appears in given path.

Legacy application is CATIA.
In V5, CATScriptUtilities::ExecuteScript is used to call catvba.
In V6, we can use the same API to call catvba. It works fine.
There is no need to call .bat->.vba->.catvba

Related

Hooking CreateFile in notepad.exe does not catch API calls

My ultimate goal is to track file operations done by explorer.exe via hooking file api in kernel32.dll, however I have yet to get that working (either explorer.exe is not calling the APIs, or something is wrong on my end). In order to figure out what is going on, I've set a goal to track whenever notepad.exe creates a file, however this has failed for some reason as well!
I have 3 Visual Studio 2012 C++ projects: my DLL, a DLL injector that will force any executable to load my dll (although it may fail if Unicode/Multibyte and 32/64bit settings don't match up), and a test program that calls API. I have a batch file that will start my test program, and then use the injector program to load my DLL into the test program. The weird part is that the outputs do show that the APIs are being tracked on the test program (spawns the console and prints everything!) but nothing happens for notepad.exe (no console = no operations caught).
Here is the DLL that hooks the API (uses the mhook library). The format and concepts are taken from this guide. The important things to note are that I hook CreateFile(A/W), and spawn a console to print file I/O logs when the first operation occurs.
#include "stdafx.h"
#include "mhook/mhook-lib/mhook.h"
//////////////////////////////////////////////////////////////////////////
// Defines and typedefs
typedef HANDLE (WINAPI *CreateFileWFP)(
_In_ LPCWSTR lpFileName,
_In_ DWORD dwDesiredAccess,
_In_ DWORD dwShareMode,
_In_opt_ LPSECURITY_ATTRIBUTES lpSecurityAttributes,
_In_ DWORD dwCreationDisposition,
_In_ DWORD dwFlagsAndAttributes,
_In_opt_ HANDLE hTemplateFile
);
typedef HANDLE (WINAPI *CreateFileAFP)(
_In_ LPCSTR lpFileName,
_In_ DWORD dwDesiredAccess,
_In_ DWORD dwShareMode,
_In_opt_ LPSECURITY_ATTRIBUTES lpSecurityAttributes,
_In_ DWORD dwCreationDisposition,
_In_ DWORD dwFlagsAndAttributes,
_In_opt_ HANDLE hTemplateFile
);
//////////////////////////////////////////////////////////////////////////
// Original function
CreateFileWFP OriginalCreateFileW = (CreateFileWFP)::GetProcAddress(::GetModuleHandle(TEXT("kernel32")), "CreateFileW");
CreateFileAFP OriginalCreateFileA = (CreateFileAFP)::GetProcAddress(::GetModuleHandle(TEXT("kernel32")), "CreateFileA");
//////////////////////////////////////////////////////////////////////////
// Some Helper Stuff
struct Console{
HANDLE handle;
Console(){ handle = INVALID_HANDLE_VALUE; }
void write(LPCWSTR text){
if(handle==INVALID_HANDLE_VALUE){
AllocConsole();
handle = GetStdHandle(STD_OUTPUT_HANDLE);
}
DWORD numCharsWritten = 0;
WriteConsoleW(handle, text, (DWORD)wcslen(text), &numCharsWritten,NULL);
}
void write(LPCSTR text){
if(handle==INVALID_HANDLE_VALUE){
AllocConsole();
handle = GetStdHandle(STD_OUTPUT_HANDLE);
}
DWORD numCharsWritten = 0;
WriteConsoleA(handle, text, (DWORD)strlen(text), &numCharsWritten,NULL);
}
} console;
void operationPrint(LPCWSTR left, LPCWSTR middle, LPCWSTR right){
console.write(left);
console.write(middle);
console.write(right);
console.write(L"\n");
}
void operationPrint(LPCSTR left, LPCSTR middle, LPCSTR right){
console.write(left);
console.write(middle);
console.write(right);
console.write(L"\n");
}
//////////////////////////////////////////////////////////////////////////
// Hooked function
HANDLE HookedCreateFileW(
_In_ LPCWSTR lpFileName,
_In_ DWORD dwDesiredAccess,
_In_ DWORD dwShareMode,
_In_opt_ LPSECURITY_ATTRIBUTES lpSecurityAttributes,
_In_ DWORD dwCreationDisposition,
_In_ DWORD dwFlagsAndAttributes,
_In_opt_ HANDLE hTemplateFile
){
HANDLE out = OriginalCreateFileW(
lpFileName,
dwDesiredAccess,
dwShareMode,
lpSecurityAttributes,
dwCreationDisposition,
dwFlagsAndAttributes,
hTemplateFile);
if(out == INVALID_HANDLE_VALUE) return out; //ignore failiures
operationPrint(L"CreatedW file",L" at ",lpFileName);
return out;
}
HANDLE HookedCreateFileA(
_In_ LPCSTR lpFileName,
_In_ DWORD dwDesiredAccess,
_In_ DWORD dwShareMode,
_In_opt_ LPSECURITY_ATTRIBUTES lpSecurityAttributes,
_In_ DWORD dwCreationDisposition,
_In_ DWORD dwFlagsAndAttributes,
_In_opt_ HANDLE hTemplateFile
){
HANDLE out = OriginalCreateFileA(
lpFileName,
dwDesiredAccess,
dwShareMode,
lpSecurityAttributes,
dwCreationDisposition,
dwFlagsAndAttributes,
hTemplateFile);
if(out == INVALID_HANDLE_VALUE) return out; //ignore failiures
operationPrint("CreatedA file"," at ",lpFileName);
return out;
}
//////////////////////////////////////////////////////////////////////////
// Entry point
BOOL WINAPI DllMain(
__in HINSTANCE hInstance,
__in DWORD Reason,
__in LPVOID Reserved
)
{
switch (Reason)
{
case DLL_PROCESS_ATTACH:
Mhook_SetHook((PVOID*)&OriginalCreateFileW, HookedCreateFileW);
Mhook_SetHook((PVOID*)&OriginalCreateFileA, HookedCreateFileA);
break;
case DLL_PROCESS_DETACH:
FreeConsole();
Mhook_Unhook((PVOID*)&OriginalCreateFileW);
Mhook_Unhook((PVOID*)&OriginalCreateFileA);
break;
}
return TRUE;
}
I can't find exactly where I found the injector program, but it is nearly identical to this guide. Some of the comments are even the same, so I'm pretty sure one took from the other (not sure which from whom though). In any case, I just made minor alterations so that it works when compiled either under Unicode or Multibyte. I won't post the entire code here unless requested because I think it's a waste of space, but here is the important part.
#include "Injector.h"
#include <windows.h>
#include <tlhelp32.h>
#include <shlwapi.h>
#include <conio.h>
#include <stdio.h>
#include "DebugPrint.h"
#include <atlbase.h>
using namespace std;
Injector::Injector(void)
{
}
Injector::~Injector(void)
{
}
#define CREATE_THREAD_ACCESS (PROCESS_CREATE_THREAD | PROCESS_QUERY_INFORMATION | PROCESS_VM_OPERATION | PROCESS_VM_WRITE | PROCESS_VM_READ)
bool Injector::Inject(string procName, string dllName){
DWORD pID = GetTargetThreadIDFromProcName(procName.c_str());
return Inject(pID,dllName);
}
bool Injector::Inject(DWORD pID, string dllName){
const char* DLL_NAME = dllName.c_str();
HANDLE Proc = 0;
HMODULE hLib = 0;
LPVOID RemoteString, LoadLibAddy;
if(!pID)
return false;
Proc = OpenProcess(PROCESS_ALL_ACCESS, FALSE, pID);
if(!Proc)
{
DEBUG_PRINT("OpenProcess() failed: %d", GetLastError());
return false;
}
LoadLibAddy = (LPVOID)GetProcAddress(GetModuleHandle(TEXT("kernel32.dll")), "LoadLibraryA");
// Allocate space in the process for our <strong class="highlight">DLL</strong>
RemoteString = (LPVOID)VirtualAllocEx(Proc, NULL, strlen(DLL_NAME), MEM_RESERVE | MEM_COMMIT, PAGE_READWRITE);
// Write the string name of our <strong class="highlight">DLL</strong> in the memory allocated
WriteProcessMemory(Proc, (LPVOID)RemoteString, DLL_NAME, strlen(DLL_NAME), NULL);
// Load our <strong class="highlight">DLL</strong>
CreateRemoteThread(Proc, NULL, NULL, (LPTHREAD_START_ROUTINE)LoadLibAddy, (LPVOID)RemoteString, NULL, NULL);
CloseHandle(Proc);
return true;
}
DWORD Injector::GetTargetThreadIDFromProcName(const 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!", "2MLoader", MB_OK);
DEBUG_PRINT("Error: Unable to create toolhelp snapshot!");
return false;
}
pe.dwSize = sizeof(PROCESSENTRY32);
retval = Process32First(thSnapShot, &pe);
while(retval)
{
#ifdef _UNICODE
char peSzExeFile[MAX_PATH];
wcstombs_s(NULL,peSzExeFile,MAX_PATH,pe.szExeFile,MAX_PATH);
#else
const char* peSzExeFile = pe.szExeFile;
#endif
DEBUG_PRINT("\nSearching for process: %s ",peSzExeFile);
if(!strcmp(peSzExeFile, ProcName))
{
DEBUG_PRINT(" Found!\n\n");
return pe.th32ProcessID;
}
retval = Process32Next(thSnapShot, &pe);
}
return 0;
}
Finally, my test program just calls some file APIs to see if the injected DLL can catch them. As mentioned before, it does catch the calls quite successfully.
#include <windows.h>
#include <tchar.h>
using namespace std;
#ifdef _UNICODE
#define printf(X,...) wprintf(TEXT(X),__VA_ARGS__); //I don't want to have to keep replacing printf whenever I switch to Unicode or Multibyte
#endif
#ifdef _DEBUG
int _tmain(int argc, TCHAR* argv[]){ //makes a console. printf() will have a place to go in this case
#else
int APIENTRY WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow){ //no console
#endif
Sleep(2000); //let DLL finish loading
LPCTSTR fileSrc = TEXT("C:\\Users\\jajoach\\Desktop\\hi.txt");
LPCTSTR fileDst = TEXT("C:\\Users\\jajoach\\Desktop\\hi\\hi.txt");
printf("Moving file from %s to %s\n",fileSrc,fileDst);
MoveFile(fileSrc,fileDst);
Sleep(1000);
printf("Moving file from %s to %s\n",fileSrc,fileDst);
MoveFile(fileDst,fileSrc);
Sleep(1000);
printf("Copying file from %s to %s\n",fileSrc,fileDst);
CopyFile(fileSrc,fileDst,true);
Sleep(1000);
printf("Deleting file %s\n",fileDst);
DeleteFile(fileDst);
Sleep(1000);
printf("Creating file %s\n",fileDst);
HANDLE h=CreateFile(fileDst,0,0,NULL,CREATE_NEW,FILE_ATTRIBUTE_NORMAL,NULL);
Sleep(1000);
printf("Deleting file %s\n",fileDst);
CloseHandle(h);
DeleteFile(fileDst);
Sleep(5000);
return 0;
}
Here is the confirmed output for Unicode (as noted by the 'W's) and Release mode, and what I was expecting (but didn't get) from both notepad.exe and explorer.exe. For the record, the test also works for Multibyte, but instead gives 'A's as expected.
I had thought that maybe explorer.exe and notepad.exe don't use these API for their file I/O, but my research says otherwise. This post hooks CreateFile in notepad.exe using Detours and reports success for that application. Additionally, ProgramMonitor clearly shows notepad.exe calling CreateFile during a Saveas operation (after many failed requests with different parameters...):
Nevermind about explorer.exe for now; why isn't my hook working for notepad.exe when I do Saveas?
EDIT: I forgot to mention that I also hook MoveFile(A/W) and CopyFile(A/W) with the test program, but I removed that code from the DLL on this post for brevity.
As noted in the comments of the OP, it seems that that notepad.exe uses ASLR and your test program does not. With that the address of LoadLibraryA would be different in each process, and your injection code fails.
The situation is that you are getting the addres of LoadLibraryA in the injector address space and assume that it is the same that in the target process. That would be usually right, but ASLR is designed specifically to make that assumption fail. And so it does... the thread you create get a -most likely- invalid address, and fails.
A couple of reasons:
Bitness of your hook code must match the target.
CreateFileA/W is quite low, but not low enough to catch em all!
To solve 2. you must hook Zw/NtCreateFile from Ntdll.dll which is what you're seeing in procmon. There is nothing lower than these API's in user land.

RegQueryInfoKey not giving registry key name text

Using the RegQueryInfoKey as in the example available on http://msdn.microsoft.com/en-us/library/windows/desktop/ms724256(v=vs.85).aspx, doesn't give me the registry folder name in the output parameter achClass. I always receive an empty string.
My question is: Do I have to call any other function after to get the text of the key name? Or can I use this, but I am missing something?
Side note: machine is Windows 7, 64 bit.
Thanks in advance
UPDATE: code
//Calling site:
HKEY hKey;
LSTATUS status= RegOpenKeyEx(HKEY_CURRENT_USER, InstanceFullName, 0, KEY_ALL_ACCESS, &hKey);
if ( status != ERROR_SUCCESS)
{
LPVOID lpMsgBuf;
FormatMessage(
FORMAT_MESSAGE_ALLOCATE_BUFFER |
FORMAT_MESSAGE_FROM_SYSTEM |
FORMAT_MESSAGE_IGNORE_INSERTS,
NULL,
status,
MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), // Default language
(LPTSTR) &lpMsgBuf,
0,
NULL
);
this->MessageBox( (LPCTSTR)lpMsgBuf, GetProgramTitle(), MB_OK | MB_ICONERROR );
LocalFree( lpMsgBuf );
return;
}
status= MyRegSaveKey(hKey, sTempRegFilePath, NULL);
//Called function
LSTATUS MyRegSaveKey(_In_ HKEY hKey, _In_ LPCTSTR lpFile, _In_opt_ LPSECURITY_ATTRIBUTES lpSecurityAttributes)
{
CXmlWriter xmlWriter;
if(!xmlWriter.Begin(lpFile))
return ERROR_CANNOT_MAKE;
LSTATUS retCode= _MyRegSaveKey(hKey, lpFile, lpSecurityAttributes, xmlWriter);
xmlWriter.Finish();
return retCode;
}
//Inner called function
LSTATUS _MyRegSaveKey(_In_ HKEY hKey, _In_ LPCTSTR lpFile, _In_opt_ LPSECURITY_ATTRIBUTES lpSecurityAttributes, CXmlWriter& writer)
{
//http://msdn.microsoft.com/en-us/library/windows/desktop/ms724256(v=vs.85).aspx
TCHAR achKey[MAX_KEY_LENGTH]; // buffer for subkey name
DWORD cbName; // size of name string
TCHAR achClass[MAX_PATH] = TEXT(""); // buffer for class name
achClass[0] = '\0';
DWORD cchClassName = MAX_PATH; // size of class string
DWORD cSubKeys=0; // number of subkeys
DWORD cbMaxSubKey; // longest subkey size
DWORD cchMaxClass; // longest class string
DWORD cValues; // number of values for key
DWORD cchMaxValue; // longest value name
DWORD cbMaxValueData; // longest value data
DWORD cbSecurityDescriptor; // size of security descriptor
FILETIME ftLastWriteTime; // last write time
DWORD i, retCode;
TCHAR achValue[MAX_VALUE_NAME];
DWORD cchValue = MAX_VALUE_NAME;
DWORD type;
BYTE* pData= NULL;
DWORD size;
// Get the class name and the value count.
retCode = RegQueryInfoKey(
hKey, // key handle
achClass, // buffer for class name
&cchClassName, // size of class string
NULL, // reserved
&cSubKeys, // number of subkeys
&cbMaxSubKey, // longest subkey size
&cchMaxClass, // longest class string
&cValues, // number of values for this key
&cchMaxValue, // longest value name
&cbMaxValueData, // longest value data
&cbSecurityDescriptor, // security descriptor
&ftLastWriteTime // last write time
);
if(retCode != ERROR_SUCCESS)
return retCode;
//... the rest does not make any difference
return ERROR_SUCCESS;
}
Now that I can see the code, it looks like you expect to get the name of the opened key.
You cannot use the lpClass parameter of RegQueryInfoKey() for this -- see this SO answer; it looks like the example you mentioned uses only the cSubKeys and cValues from the RegQueryInfoKey() call.
From this SO answer [even though the question was for Perl], it doesn't look like the Win32 Registry API has a function will let you take the handle and return the key name.
From the same article, the only two practical solutions are
Maintain a list of returned objects from Open and the path to them yourself
extend the Win32::Registry API to call the NtQueryKey exported function in ntdll.dll and do what's shown in this stackoverflow answer

EnumResourceNames Function C++

So I try to read resource types and names from given file (in my case, .msstyle on my desktop) using C++
But somehow the resinfo result is sort of weird and not accurate. It doesnt write what actually was found. For example, the msstyle gives a result of: http://pastebin.com/ZhnkPmUe
#include <windows.h>
#include <strsafe.h>
#include <stdio.h>
HANDLE g_hFile;
BOOL EnumTypesFunc(HMODULE hModule, LPTSTR lpType, LONG lParam);
BOOL EnumNamesFunc(HMODULE hModule, LPCTSTR lpType, LPTSTR lpName, LONG lParam);
BOOL EnumLangsFunc(HMODULE hModule, LPCTSTR lpType, LPCTSTR lpName, WORD wLang, LONG lParam);
void main(void)
{
HMODULE hExe;
TCHAR szBuffer[80];
DWORD cbWritten;
size_t cbString;
HRESULT hResult;
// Load the .EXE whose resources you want to list.
hExe = LoadLibrary(TEXT("C:\\Users\\Kala\\Desktop\\776.msstyles"));
g_hFile = CreateFile(TEXT("C:\\Users\\Kala\\Desktop\\resinfo.txt"), GENERIC_READ | GENERIC_WRITE, 0, (LPSECURITY_ATTRIBUTES) NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, (HANDLE) NULL);
// Find all of the loaded file's resources.
hResult = StringCchPrintf(szBuffer, sizeof(szBuffer)/sizeof(TCHAR),TEXT("The file contains the following resources:\r\n\r\n"));
hResult = StringCchLength(szBuffer, sizeof(szBuffer)/sizeof(TCHAR), &cbString);
WriteFile(g_hFile, szBuffer, (DWORD) cbString, &cbWritten, NULL);
//Calls the function to find types
EnumResourceTypes(hExe, (ENUMRESTYPEPROC)EnumTypesFunc, 0);
// Unload the executable file whose resources were
FreeLibrary(hExe);
CloseHandle(g_hFile);
}
// FUNCTION: EnumTypesFunc(HANDLE, LPSTR, LONG)
//
// PURPOSE: Resource type callback
BOOL EnumTypesFunc(HMODULE hModule, LPTSTR lpType, LONG lParam)
{
TCHAR szBuffer[80]; // print buffer for info file
DWORD cbWritten; // number of bytes written to resource info file
size_t cbString;
HRESULT hResult;
// Write the resource type to a resource information file.
// The type may be a string or an unsigned decimal
// integer, so test before printing.
if (!IS_INTRESOURCE(lpType))
{
hResult = StringCchPrintf(szBuffer, sizeof(szBuffer)/sizeof(TCHAR), TEXT("Type: %s\r\n"), lpType);
}
else
{
hResult = StringCchPrintf(szBuffer, sizeof(szBuffer)/sizeof(TCHAR), TEXT("Type: %u\r\n"), (USHORT)lpType);
}
hResult = StringCchLength(szBuffer, sizeof(szBuffer)/sizeof(TCHAR), &cbString);
WriteFile(g_hFile, szBuffer, (DWORD) cbString, &cbWritten, NULL);
// Find the names of all resources of type lpType.
EnumResourceNames(hModule, lpType, (ENUMRESNAMEPROC)EnumNamesFunc, 0);
return TRUE;
}
// FUNCTION: EnumNamesFunc(HANDLE, LPSTR, LPSTR, LONG)
//
// PURPOSE: Resource name callback
BOOL EnumNamesFunc(HMODULE hModule, LPCTSTR lpType, LPTSTR lpName, LONG lParam)
{
TCHAR szBuffer[80]; // print buffer for info file
DWORD cbWritten; // number of bytes written to resource info file
size_t cbString;
HRESULT hResult;
// Write the resource name to a resource information file.
// The name may be a string or an unsigned decimal
// integer, so test before printing.
if (!IS_INTRESOURCE(lpName))
{
hResult = StringCchPrintf(szBuffer, sizeof(szBuffer)/sizeof(TCHAR), TEXT("\tName: %s\r\n"), lpName);
}
else
{
hResult = StringCchPrintf(szBuffer, sizeof(szBuffer)/sizeof(TCHAR), TEXT("\tName: %u\r\n"), (USHORT)lpName);
}
hResult = StringCchLength(szBuffer, sizeof(szBuffer)/sizeof(TCHAR), &cbString);
WriteFile(g_hFile, szBuffer, (DWORD) cbString, &cbWritten, NULL);
return TRUE;
}
I think I must be missing something because I dont seem to get a proper strings I wanted from it, so if anyone could point me to right direction I would be very thankful
Your file is UTF-16 encoded because you are using the Unicode version of the Win32 API. Your text editor is interpreting the file as being 8 bit encoded. So you simply need to get your text editor to interpret the file as UTF-16. Probably the easiest way to do that is to put the UTF-16LE BOM at the beginning of the file.
As an aside I would advise you to stop coding to support MBCS character sets, stop using TCHAR and so on. Just write your program assuming that you will be targeting the Unicode version of the Win32 API. It will make your code much easier to read if you write L"..." rather than TEXT("...") and so on. Of course, if you need to support Windows 9x then forget I said this and carry on writing code that will compile in both MBCS and Unicode modes.

Is it possible to hook the creation of windows globally so I can control where the windows are placed on the screen?

I can inject a hook into running processes to catch when they create, destroy, max/min. But I haven't come up with a way to catch the creation of a new process so that I can inject my hook into that one.
Does anyone know the best way to accomplish this?
SetWindowsHookEx is your easiest solution.
If you don't mind upsetting the anti-virus software, you can also inject a DLL into each process that will then hook CreateProcess (to inject the DLL into further processes) and CreateWindowEx (for your purposes).
EDIT: I just read your question completely. Yes, you'll want to just hook CreateProcessW and inject your hook into future processes.
EDIT #2: I was actually working on something like this yesterday, so some code which does what you want.
#include <windows.h>
// call GetModuleFileNameto get the full path of the module before installing the hook
static LPWSTR lpszDllName;
HMODULE LoadModuleEx(__in HANDLE hProcess, __in_z LPCTSTR lpcszDll)
{
DWORD cdwSize;
LPVOID lpvAllocation;
HANDLE hThread;
HMODULE hRet;
cdwSize = lstrlen(lpcszDll) + 1;
cdwSize *= sizeof(TCHAR);
lpvAllocation = VirtualAllocEx(hProcess, NULL, cdwSize, MEM_COMMIT, PAGE_EXECUTE_READWRITE);
if (lpvAllocation != NULL)
{
if (WriteProcessMemory(hProcess, lpvAllocation, lpcszDll, cdwSize, NULL))
{
hThread = CreateRemoteThread(hProcess, NULL, 0, (LPTHREAD_START_ROUTINE)LoadLibrary, lpvAllocation, 0, NULL);
if (hThread != NULL)
{
GetExitCodeThread(hThread, (LPDWORD)&hRet);
CloseHandle(hThread);
}
}
VirtualFreeEx(hProcess, lpvAllocation, cdwSize, MEM_DECOMMIT);
}
return hRet;
}
// hook future process creation - install this hook on top of CreateProcessW
// I'd suggest using Microsoft Detours [http://research.microsoft.com/en-us/projects/detours/]
BOOL WINAPI CreateProcessWHook(__in_opt LPCWSTR lpApplicationName, __inout_opt LPWSTR lpCommandLine, __in_opt LPSECURITY_ATTRIBUTES lpProcessAttributes, __in_opt LPSECURITY_ATTRIBUTES lpThreadAttributes, __in BOOL bInheritHandles, __in DWORD dwCreationFlags, __in_opt LPVOID lpEnvironment, __in_opt LPCWSTR lpCurrentDirectory, __in LPSTARTUPINFO lpStartupInfo, __out LPPROCESS_INFORMATION lpProcessInformation)
{
// create the process suspended
if (dwCreationFlags & CREATE_SUSPENDED != CREATE_SUSPENDED)
dwCreationFlags |= CREATE_SUSPENDED;
// call original CreateProcessW
BOOL bRet = _CreateProcessW(lpApplicationName, lpCommandLine, lpProcessAttributes, lpThreadAttributes, bInheritHandles, dwCreationFlags, lpEnvironment, lpCurrentDirectory, lpStartupInfo, lpProcessInformation);
if (bRet)
{
// inject DLL
LoadModuleEx(lpProcessInformation->hProcess, lpszDllName);
// resume thread
ResumeThread(lpProcessInformation->hThread);
}
return bRet;
}
You'll want to use a system hook using SetWindowsHookEx. That will then allow you to provide a callback when applications (for example) create a window.
As other people have mentioned, using SetWindowsHookEx to create a shell hook is probably the optimal solution.
If you're really serious about catching the creation of new processes and injecting your own code, you'll have to load a kernel driver and use PsSetLoadImageNotifyRoutine, but this is almost certainly the wrong approach.

How to ensure only one process is created by CreateProcess when calling concurrently in c++?

Quoted from here:
BOOL WINAPI CreateProcess(
__in_opt LPCTSTR lpApplicationName,
__inout_opt LPTSTR lpCommandLine,
__in_opt LPSECURITY_ATTRIBUTES lpProcessAttributes,
__in_opt LPSECURITY_ATTRIBUTES lpThreadAttributes,
__in BOOL bInheritHandles,
__in DWORD dwCreationFlags,
__in_opt LPVOID lpEnvironment,
__in_opt LPCTSTR lpCurrentDirectory,
__in LPSTARTUPINFO lpStartupInfo,
__out LPPROCESS_INFORMATION lpProcessInformation
);
I have two independant programe that creates exactly the same process, how can I ensure that if one of them has already created the process, the other won't create it twice?
The most simple way is if you create a named object after the start of the program. For example CreateEvent, CreateMutex and so on. To verify existance of the application you can just use OpenEvent, OpenMutex and so on before creating of the object. You can choose (if desired) the name of the object with the the "Global\" prefix (see http://msdn.microsoft.com/en-us/library/aa382954.aspx) to allow only one process for all terminal server session.
UPDATED: Because how I can see there are different opinions about my suggestion I try to explain it more exactly and add the corresponding test example.
The main idea is that the application which are started create any named object is the object with the same name not yet exist. This only reserve the name in the Kernel Object Namespaces. No real usage of the object are needed. The advantaged of this way compared with creating of a file on the disk is that named objects are temporary and are owned by a application. So if the application are ended, be killed or be terminated in any other way (because of unhanded exception for example) the named object will be automatically deleted by the operation system. In the following example I don't use CloseHandle at all. How you can test the application can successfully determine whether it runs as the first instance or not.
#include <windows.h>
//#include <Sddl.h>
LPCTSTR g_pszEventName = TEXT("MyTestEvent"); // TEXT("Global\\MyTestEvent")
void DisplayFirstInstanceStartedMessage()
{
TCHAR szText[1024];
wsprintf (szText,
TEXT("The first instance are started.\nThe event with the name \"%s\" is created."),
g_pszEventName);
MessageBox (NULL,
szText,
TEXT("CreateEventTest"), MB_OK);
}
void DisplayAlreadyRunningMessage ()
{
TCHAR szText[1024];
wsprintf (szText,
TEXT("The first instance of the aplication is already running.\nThe event with the name \"%s\" already exist."),
g_pszEventName);
MessageBox (NULL,
szText,
TEXT("CreateEventTest"), MB_ICONWARNING | MB_OK);
}
void DisplayErrorMessage (DWORD dwErrorCode)
{
if (dwErrorCode == ERROR_ALREADY_EXISTS)
DisplayAlreadyRunningMessage();
else {
LPTSTR pErrorString;
if (FormatMessage (FORMAT_MESSAGE_FROM_SYSTEM | // Always search in system message table !!!
FORMAT_MESSAGE_ALLOCATE_BUFFER |
FORMAT_MESSAGE_IGNORE_INSERTS |
0, NULL, // source of message definition
dwErrorCode, // message ID
// 0, // language ID
// GetUserDefaultLangID(), // language ID
// GetSystemDefaultLangID(),
MAKELANGID (LANG_NEUTRAL, SUBLANG_DEFAULT),
(LPTSTR)&pErrorString, // pointer for buffer to allocate
0, // min number of chars to allocate
NULL)) {
MessageBox (NULL, pErrorString, TEXT("CreateEventTest"), MB_OK);
LocalFree (pErrorString);
}
else {
TCHAR szText[1024];
wsprintf (szText, TEXT("Error %d in the CreateEvent(..., \"%s\")"), dwErrorCode, g_pszEventName);
MessageBox (NULL, szText, TEXT("CreateEventTest"), MB_OK);
}
}
}
int WINAPI WinMain (HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nShowCmd)
{
//SECURITY_ATTRIBUTES sa;
//BOOL bSuccess;
HANDLE hEvent = OpenEvent (EVENT_MODIFY_STATE, FALSE, g_pszEventName);// EVENT_ALL_ACCESS
if (hEvent == NULL) {
DWORD dwErrorCode = GetLastError();
if (dwErrorCode != ERROR_FILE_NOT_FOUND) {
DisplayErrorMessage(dwErrorCode);
return 1;
}
}
else {
DisplayAlreadyRunningMessage();
return 0;
}
//sa.bInheritHandle = FALSE;
//sa.nLength = sizeof(SECURITY_ATTRIBUTES);
//bSuccess = ConvertStringSecurityDescriptorToSecurityDescriptor (
// TEXT("D:(A;OICI;GA;;;WD)"), // Allow full control
// SDDL_REVISION_1,
// &sa.lpSecurityDescriptor,
// NULL);
hEvent = CreateEvent (NULL, // &sa
TRUE, FALSE, g_pszEventName);
//sa.lpSecurityDescriptor = LocalFree (sa.lpSecurityDescriptor);
if (hEvent == NULL) {
DWORD dwErrorCode = GetLastError();
DisplayErrorMessage(dwErrorCode);
return 1;
}
else
DisplayFirstInstanceStartedMessage();
return 0;
UNREFERENCED_PARAMETER (hInstance);
UNREFERENCED_PARAMETER (hPrevInstance);
UNREFERENCED_PARAMETER (lpCmdLine);
UNREFERENCED_PARAMETER (nShowCmd);
}
If one want support that different users from the same desktop or from the different desktops could start only one instance of the program, one can uncomment some parts of the commented code or replace the name MyTestEvent of the event to Global\MyTestEvent.
I hope after the example my position will be clear. In such kind of the event usage no call of WaitForSingleObject() are needed.
You cannot do this by letting the process you start creating a named object. That's an inherent race condition, it takes time for the process to get started. Both programs need to call CreateMutex at some point before trying to create the 3rd process with an agreed-upon name. Then they need to call WaitForSingleObject() with a zero wait time to try to acquire the mutex. Whomever gets it is the one that should call CreateProcess().
More work is needed after this to deal with this 3rd process terminating.
You can use this function
BOOL WINAPI EnumProcesses(
__out DWORD *pProcessIds,
__in DWORD cb,
__out DWORD *pBytesReturned
);
to get a list of all the pids of all currently running processes and check if the process is running?