Let's say I have very simple little code sample which uses async WinInet:
#include "stdafx.h"
#include "WinInet.h"
#pragma comment(lib, "wininet.lib")
DWORD LatestResult = 0;
HANDLE MayContinue = 0;
VOID CALLBACK
CallBack(
__in HINTERNET hInternet,
__in DWORD_PTR dwContext,
__in DWORD dwInternetStatus,
__in_bcount(dwStatusInformationLength) LPVOID lpvStatusInformation,
__in DWORD dwStatusInformationLength
)
{
if (dwInternetStatus == INTERNET_STATUS_REQUEST_COMPLETE)
{
LatestResult = ((LPINTERNET_ASYNC_RESULT)lpvStatusInformation)->dwResult;
SetEvent (MayContinue);
}
}
int APIENTRY _tWinMain(_In_ HINSTANCE hInstance,
_In_opt_ HINSTANCE hPrevInstance,
_In_ LPTSTR lpCmdLine,
_In_ int nCmdShow)
{
MayContinue = ::CreateEvent (NULL, FALSE, FALSE, NULL);
HINTERNET Session = InternetOpen (NULL, INTERNET_OPEN_TYPE_PRECONFIG, NULL, NULL, INTERNET_FLAG_ASYNC);
INTERNET_STATUS_CALLBACK CallbackPointer = InternetSetStatusCallback (Session, (INTERNET_STATUS_CALLBACK) CallBack);
MayContinue = ::CreateEvent (NULL, FALSE, FALSE, NULL);
InternetConnect (Session, _T ("ftp.secureftp-test.com"), INTERNET_INVALID_PORT_NUMBER, _T ("test"), _T ("test"), INTERNET_SERVICE_FTP, 0, 1);
WaitForSingleObject (MayContinue, INFINITE);
HINTERNET Internet = (HINTERNET) LatestResult;
WIN32_FIND_DATA *FindData = new WIN32_FIND_DATA;
FtpFindFirstFileW (Internet, _T ("*.*"), FindData, 0, 1);
WaitForSingleObject (MayContinue, INFINITE);
delete FindData;
return 0;
}
What I've got after execution:
Unhandled exception at 0xBAADF00D in WinInetTest.exe:
0xC0000005: Access violation executing location 0xBAADF00D.
It happens somewhere around final WaitForSingleObject and callstack is rather confusing.
But if I change
WIN32_FIND_DATA *FindData = new WIN32_FIND_DATA;
FtpFindFirstFileW (Internet, _T ("*.*"), FindData, 0, 1);
to
WIN32_FIND_DATAA *FindData = new WIN32_FIND_DATAA;
FtpFindFirstFileA (Internet, "*.*", FindData, 0, 1);
It executes and works as it should.
So my question is - am I really not doing something correctly or it just failure on WinInet side?
I'm testing it on Windows 7 using Visual Studio 2012 btw.
I also had difficulty with FtpFindFirstFileW. When I converted my project from MBCS to Unicode FtpFindFirstFileW resulted in an access violation that appeared to be a pointer dereference of 0xbaadf00d somewhere after the call, possibly when the async result was being prepared. I worked around this by using FtpFindFirstFileA, InternetFindNextFileA and the WIN32_FIND_DATAA struct in both MBCS and Unicode builds. I then convert the output cFileName field to a TCHAR string.
I'd recommend changing compiler setting Character Set from Unicode to Multi-Byte Character Set. The same thing was happening to me.
Related
VS10, MBCS, Unicode preprocessor defs.
Having gone through Dr Google's casebook, the best help was here, but it doesn't quite address this particular problem. Consider this code where thisexePath is the path to the executable:
cmdLineArg = (wchar_t *)calloc(BIGGERTHANMAXPATH, sizeof(wchar_t));
wcscpy_s (cmdLineArg, maxPathFolder, L"\\\\??\\C:\\My directory\\My directory\\");
CreateProcessW (thisexePath, cmdLineArg, NULL, NULL, FALSE, NULL, NULL, NULL, &lpStartupInfo, &lpProcessInfo)
When the program is called
int WINAPI wWinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPWSTR lpCmdLine, int nCmdShow)
{
wchar_t hrtext[256];
swprintf_s(hrtext, sizeof(hrtext), L"%ls", lpCmdLine);
//deal with possible buffer overflow later
MessageBoxW(NULL, hrtext, L"Display", MB_ICONINFORMATION);
}
hrtext only displays "My directory\My directory\" Something obvious missed here?
This is not correct:
wchar_t hrtext[256];
swprintf_s(hrtext, sizeof(hrtext), L"%ls", lpCmdLine);
The second parameter should denote the number of characters, not bytes.
MSDN link to swprintf_s
It should be this:
wchar_t hrtext[256];
swprintf_s(hrtext, sizeof(hrtext) / sizeof(wchar_t), L"%ls", lpCmdLine);
or simply:
wchar_t hrtext[256];
swprintf_s(hrtext, 256, L"%ls", lpCmdLine);
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.
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.
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.
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?