Related
In the past, when not operating as a debugger, I have used this approach to inject a DLL and Create Thread In a Process which has worked well for me. Note: I need this to work on Windows XP 32-bit only (Although prefer methods that also work on latested OS):
#include <iostream>
#include <Windows.h>
#include <Psapi.h>
#include <pathcch.h>
#include "log.h"
#include <wchar.h>
#pragma comment(lib,"Pathcch.lib")
typedef void (WINAPI* PHookInit)();
HMODULE WINAPI GetRemoteModuleHandle(HANDLE hProcess, LPCWSTR lpModuleName)
{
HMODULE* ModuleArray = NULL;
DWORD ModuleArraySize = 100;
DWORD NumModules = 0;
WCHAR lpModuleNameCopy[MAX_PATH] = { 0 };
WCHAR ModuleNameBuffer[MAX_PATH] = { 0 };
if (lpModuleName == NULL) return NULL;
ModuleArray = new HMODULE[ModuleArraySize];
if (ModuleArray == NULL) return NULL;
if (!EnumProcessModulesEx(hProcess, ModuleArray,
ModuleArraySize * sizeof(HMODULE), &NumModules, LIST_MODULES_ALL))
{
DWORD dwResult = GetLastError();
LOG_E("Unable to get modules in process Error %i", dwResult);
}
else
{
NumModules /= sizeof(HMODULE);
if (NumModules > ModuleArraySize)
{
delete[] ModuleArray;
ModuleArray = NULL;
ModuleArray = new HMODULE[NumModules];
if (ModuleArray != NULL)
{
ModuleArraySize = NumModules;
if (EnumProcessModulesEx(
hProcess,
ModuleArray,
ModuleArraySize * sizeof(HMODULE),
&NumModules,
LIST_MODULES_ALL))
{
NumModules /= sizeof(HMODULE);
}
}
}
}
for (DWORD i = 0; i <= NumModules; ++i)
{
GetModuleBaseNameW(hProcess, ModuleArray[i],
ModuleNameBuffer, MAX_PATH);
LOG_I("Module = '%s'", ModuleNameBuffer);
if (_wcsicmp(ModuleNameBuffer, lpModuleName) == 0)
{
LOG_I("Target module found!");
HMODULE TempReturn = ModuleArray[i];
delete[] ModuleArray;
return TempReturn;
}
}
if (ModuleArray != NULL)
delete[] ModuleArray;
return NULL;
}
int wmain(HINSTANCE hInstance, HINSTANCE hPrevInstance,LPSTR lpCmdLine, INT nCmdShow)
{
LPWSTR* argv;
int argc;
argv = CommandLineToArgvW(GetCommandLineW(), &argc);
LOG_I(L"LaunchAndInject Started");
wchar_t CurrentProcessDirectory[MAX_PATH];
wchar_t TargetDllFilename[MAX_PATH];
#ifdef _WIN64
wchar_t TargetDllName[] = L"HookInit64.dll";
#else
wchar_t TargetDllName[] = L"HookInit32.dll";
#endif
char TargetFunctionName[] = "HookInit";
STARTUPINFO si;
PROCESS_INFORMATION pi;
DWORD dwTimeOut = 60000;
if (argc < 2)
{
LOG_E(L"No command line parameters specified.");
return 1;
}
wchar_t* cmd_pos = wcsstr(GetCommandLine(), argv[1]) - 1;
if (cmd_pos)
{
if (cmd_pos[0] != L'"')
{
cmd_pos += 1;
}
}
LOG_I(L"Command Line='%s'", cmd_pos);
DWORD dwResult = GetModuleFileNameW(NULL, CurrentProcessDirectory, MAX_PATH);
PathCchRemoveFileSpec(CurrentProcessDirectory, MAX_PATH);
PathCchCombine(TargetDllFilename, MAX_PATH, CurrentProcessDirectory, TargetDllName);
LOG_I(L"Current Directory='%s' Result='%i'", CurrentProcessDirectory, dwResult);
LOG_I(L"Target DLL='%s'", TargetDllFilename);
ZeroMemory(&si, sizeof(si));
si.cb = sizeof(si);
ZeroMemory(&pi, sizeof(pi));
// Start the child process.
if (!CreateProcess(NULL, // No module name (use command line)
cmd_pos, // Command line
NULL, // Process handle not inheritable
NULL, // Thread handle not inheritable
FALSE, // Set handle inheritance to FALSE
CREATE_SUSPENDED, // No creation flags
NULL, // Use parent's environment block
NULL, // Use parent's starting directory
&si,
&pi)
)
{
dwResult = GetLastError();
LOG_E(L"CreateProcess Failed with Error #%i", dwResult);
return 1;
}
LOG_I(L"Suspended Process created with PID '%i'", pi.dwProcessId);
LOG_I("Loading Target DLL");
// load DLL in this process first so we can calculate function offset
HMODULE hModuleTargetDll = LoadLibraryW(TargetDllFilename);
__int64 iTargetProcAddress = 0;
__int64 iTargetOffset = 0;
if (hModuleTargetDll != NULL)
{
iTargetProcAddress = (__int64)GetProcAddress(hModuleTargetDll, TargetFunctionName);
iTargetOffset = iTargetProcAddress - (__int64)hModuleTargetDll;
LOG_I("Function Target Offset = %i", iTargetOffset);
}
HMODULE hModuleKernel32 = GetModuleHandle(L"kernel32.dll");
LPVOID pLoadLibraryAddress = NULL;
if (hModuleKernel32 != NULL)
{
pLoadLibraryAddress = (LPVOID)GetProcAddress(hModuleKernel32, "LoadLibraryW");
}
else
{
LOG_E("Unable to get module handle for kernel32.dll");
}
if (pLoadLibraryAddress == NULL) {
dwResult = GetLastError();
LOG_E(L"ERROR: Unable to find LoadLibraryW in Kernel32.dll Error: %i", dwResult);
}
// allocate space for LoadLibrary arguments in target process
size_t iTargetDllSize = (wcslen(TargetDllFilename) + 1) * sizeof(wchar_t);
LPVOID pLoadLibraryArguments = (LPVOID)VirtualAllocEx(
pi.hProcess,
NULL,
iTargetDllSize,
MEM_RESERVE | MEM_COMMIT, PAGE_READWRITE);
if (pLoadLibraryArguments == NULL) {
dwResult = GetLastError();
LOG_E(L"ERROR: Unable to allocate %i bytes in target process Error: %i",
iTargetDllSize,
dwResult);
}
else
{
if (!WriteProcessMemory(
pi.hProcess,
pLoadLibraryArguments,
TargetDllFilename,
iTargetDllSize,
NULL))
{
dwResult = GetLastError();
LOG_E("Unable to write bytes into target process address space. Error %i", dwResult);
}
else
{
LOG_I("LoadLibrary Arguments Successfully written to target process address space.");
HANDLE hThread = NULL;
if (pLoadLibraryAddress != NULL)
{
hThread = CreateRemoteThread(
pi.hProcess,
NULL,
0,
(LPTHREAD_START_ROUTINE)pLoadLibraryAddress,
pLoadLibraryArguments,
NULL,
NULL);
}
if (hThread == NULL) {
dwResult = GetLastError();
LOG_E("The remote thread calling LoadLibrary could not be created. Error %i", dwResult);
}
else {
LOG_I("Remote Thread for LoadLibrary successfully created.");
dwResult = WaitForSingleObject(hThread, dwTimeOut);
if (dwResult == WAIT_FAILED)
{
dwResult = GetLastError();
LOG_I("Remote Thread for LoadLibrary Failed Error %i", dwResult);
}
if (dwResult == WAIT_TIMEOUT)
{
LOG_E("Remote Thread for LoadLibrary in hung state");
}
HMODULE hInjected = GetRemoteModuleHandle(pi.hProcess, TargetDllName);
PHookInit pHookInit = NULL;
if (hInjected == NULL)
{
LOG_E("Unable to get module handle in target process");
}
else
{
pHookInit = (PHookInit)((__int64)hInjected + iTargetOffset);
}
if (pHookInit != NULL)
{
LOG_I("Running HookInit function!");
hThread = CreateRemoteThread(pi.hProcess, NULL, 0, (LPTHREAD_START_ROUTINE)pHookInit, NULL, NULL, NULL);
if (hThread == NULL)
{
dwResult = GetLastError();
LOG_E("The remote thread calling HookInit could not be created. Error %i", dwResult);
}
else
{
LOG_I("HookInit function started!");
dwResult = WaitForSingleObject(hThread, dwTimeOut);
if (dwResult == WAIT_FAILED)
{
dwResult = GetLastError();
LOG_I("Remote Thread for HookInit Failed Error %i", dwResult);
}
if (dwResult == WAIT_TIMEOUT)
{
LOG_E("Remote Thread for HookInit in hung state");
}
}
}
}
}
}
LOG_I("Resuming threads in target process");
ResumeThread(pi.hThread);
LOG_I("Process Resumed. Waiting for process to exit");
dwResult = WaitForSingleObject(pi.hProcess, INFINITE);
DWORD exitCode = 0;
if (GetExitCodeProcess(pi.hProcess, &exitCode))
{
LOG_I("Process Terminated with exit code %i", exitCode);
}
else
{
LOG_W("Process terminated, unable to determine Exit Code");
}
CloseHandle(pi.hProcess);
CloseHandle(pi.hThread);
}
However in this case I need to capture various debug events of process, and hook the process via Image Execution Debugger registry key (as I don't have control of its launch), including for child processes. While the inject code works fine with previous approach, when launched as a debugger I'm trying to work out how to create the remote thread (and have it complete) before resuming main application execution. While I can create the remote thread fine, it hangs when trying to wait for its completion when using the approach below. I'm trying to work out what method to use to create my remote thread and wait for it to complete before resuming main application.
// Start the child process.
if (!CreateProcess(NULL, // No module name (use command line)
cmd_pos, // Command line
NULL, // Process handle not inheritable
NULL, // Thread handle not inheritable
FALSE, // Set handle inheritance to FALSE
DEBUG_PROCESS, // Debug
NULL, // Use parent's environment block
NULL, // Use parent's starting directory
&si,
&pi)
)
{
dwResult = GetLastError();
LOG_E(L"CreateProcess Failed with Error #%i", dwResult);
return 1;
}
DebugSetProcessKillOnExit(TRUE);
DebugActiveProcess(pi.dwProcessId);
DEBUG_EVENT DebugEv = { 0 };
DWORD dwContinueStatus = DBG_CONTINUE; // exception continuation
LOG_I(L"Debug Process created with PID '%i'", pi.dwProcessId);
LOG_I("Loading Target DLL");
/*
// load DLL in this process first so we can calculate function offset
*/
for (;;)
{
// Wait for a debugging event to occur. The second parameter indicates
// that the function does not return until a debugging event occurs.
WaitForDebugEvent(&DebugEv, INFINITE);
// Process the debugging event code.
switch (DebugEv.dwDebugEventCode)
{
case EXCEPTION_DEBUG_EVENT:
// Process the exception code. When handling
// exceptions, remember to set the continuation
// status parameter (dwContinueStatus). This value
// is used by the ContinueDebugEvent function.
OutputDebugString(L"EXCEPTION\r\n");
switch (DebugEv.u.Exception.ExceptionRecord.ExceptionCode)
{
case EXCEPTION_ACCESS_VIOLATION:
// First chance: Pass this on to the system.
// Last chance: Display an appropriate error.
break;
case EXCEPTION_BREAKPOINT:
// First chance: Display the current
// instruction and register values.
break;
case EXCEPTION_DATATYPE_MISALIGNMENT:
// First chance: Pass this on to the system.
// Last chance: Display an appropriate error.
break;
case EXCEPTION_SINGLE_STEP:
// First chance: Update the display of the
// current instruction and register values.
break;
case DBG_CONTROL_C:
// First chance: Pass this on to the system.
// Last chance: Display an appropriate error.
break;
default:
// Handle other exceptions.
break;
}
break;
case CREATE_THREAD_DEBUG_EVENT:
OutputDebugString(L"CREATETHREAD\r\n");
// dwContinueStatus = OnCreateThreadDebugEvent(&DebugEv);
break;
case CREATE_PROCESS_DEBUG_EVENT:
dwContinueStatus = OnCreateProcessDebugEvent(&DebugEv);
break;
case EXIT_THREAD_DEBUG_EVENT:
// Display the thread's exit code.
OutputDebugString(L"EXITTHREAD\r\n");
// dwContinueStatus = OnExitThreadDebugEvent(&DebugEv);
break;
case EXIT_PROCESS_DEBUG_EVENT:
// Display the process's exit code.
OutputDebugString(L"EXITPROCESS\r\n");
// dwContinueStatus = OnExitProcessDebugEvent(&DebugEv);
break;
case LOAD_DLL_DEBUG_EVENT:
// Read the debugging information included in the newly
// loaded DLL. Be sure to close the handle to the loaded DLL
// with CloseHandle.
OutputDebugString(L"LOADDLL\r\n");
// dwContinueStatus = OnLoadDllDebugEvent(&DebugEv);
break;
case UNLOAD_DLL_DEBUG_EVENT:
// Display a message that the DLL has been unloaded.
OutputDebugString(L"UNLOADDLL\r\n");
// dwContinueStatus = OnUnloadDllDebugEvent(&DebugEv);
break;
case OUTPUT_DEBUG_STRING_EVENT:
OutputDebugString(L"OUTPUTDEBUG\r\n");
// Display the output debugging string.
// dwContinueStatus = OnOutputDebugStringEvent(&DebugEv);
break;
case RIP_EVENT:
OutputDebugString(L"RIP\r\n");
// dwContinueStatus = OnRipEvent(&DebugEv);
break;
}
// Resume executing the thread that reported the debugging event.
OutputDebugString(L"CONTINUE\r\n");
ContinueDebugEvent(DebugEv.dwProcessId,
DebugEv.dwThreadId,
dwContinueStatus);
}
DWORD OnCreateProcessDebugEvent(const LPDEBUG_EVENT DebugEv)
{
DWORD dwResult;
HMODULE hModuleTargetDll = LoadLibraryW(TargetDllFilename);
__int64 iTargetProcAddress = 0;
__int64 iTargetOffset = 0;
if (hModuleTargetDll != NULL)
{
iTargetProcAddress = (__int64)GetProcAddress(hModuleTargetDll, TargetFunctionName);
iTargetOffset = iTargetProcAddress - (__int64)hModuleTargetDll;
LOG_I("Function Target Offset = %i", iTargetOffset);
}
HMODULE hModuleKernel32 = GetModuleHandle(L"kernel32.dll");
LPVOID pLoadLibraryAddress = NULL;
if (hModuleKernel32 != NULL)
{
pLoadLibraryAddress = (LPVOID)GetProcAddress(hModuleKernel32, "LoadLibraryW");
}
else
{
LOG_E("Unable to get module handle for kernel32.dll");
}
if (pLoadLibraryAddress == NULL) {
dwResult = GetLastError();
LOG_E(L"ERROR: Unable to find LoadLibraryW in Kernel32.dll Error: %i", dwResult);
}
// allocate space for LoadLibrary arguments in target process
size_t iTargetDllSize = (wcslen(TargetDllFilename) + 1) * sizeof(wchar_t);
HANDLE hProcess = OpenProcess(PROCESS_ALL_ACCESS, false, DebugEv->dwProcessId);
LPVOID pLoadLibraryArguments = (LPVOID)VirtualAllocEx(
hProcess,
NULL,
iTargetDllSize,
MEM_RESERVE | MEM_COMMIT, PAGE_READWRITE);
if (pLoadLibraryArguments == NULL) {
dwResult = GetLastError();
LOG_E(L"ERROR: Unable to allocate %i bytes in target process Error: %i",
iTargetDllSize,
dwResult);
}
else
{
if (!WriteProcessMemory(
hProcess,
pLoadLibraryArguments,
TargetDllFilename,
iTargetDllSize,
NULL))
{
dwResult = GetLastError();
LOG_E("Unable to write bytes into target process address space. Error %i", dwResult);
}
else
{
LOG_I("LoadLibrary Arguments Successfully written to target process address space.");
HANDLE hThread = NULL;
if (pLoadLibraryAddress != NULL)
{
hThread = CreateRemoteThread(
hProcess,
NULL,
0,
(LPTHREAD_START_ROUTINE)pLoadLibraryAddress,
pLoadLibraryArguments,
NULL,
NULL);
}
if (hThread == NULL) {
dwResult = GetLastError();
LOG_E("The remote thread calling LoadLibrary could not be created. Error %i", dwResult);
}
else {
LOG_I("Remote Thread for LoadLibrary successfully created.");
ResumeThread(hThread);
dwResult = WaitForSingleObject(hThread, dwTimeOut);
if (dwResult == WAIT_FAILED)
{
dwResult = GetLastError();
LOG_I("Remote Thread for LoadLibrary Failed Error %i", dwResult);
}
if (dwResult == WAIT_TIMEOUT)
{
LOG_E("Remote Thread for LoadLibrary in hung state");
}
HMODULE hInjected = GetRemoteModuleHandle(hProcess, TargetDllName);
PHookInit pHookInit = NULL;
if (hInjected == NULL)
{
LOG_E("Unable to get module handle in target process");
}
else
{
pHookInit = (PHookInit)((__int64)hInjected + iTargetOffset);
}
if (pHookInit != NULL)
{
LOG_I("Running HookInit function!");
hThread = CreateRemoteThread(hProcess, NULL, 0, (LPTHREAD_START_ROUTINE)pHookInit, NULL, NULL, NULL);
if (hThread == NULL)
{
dwResult = GetLastError();
LOG_E("The remote thread calling HookInit could not be created. Error %i", dwResult);
}
else
{
LOG_I("HookInit function started!");
dwResult = WaitForSingleObject(hThread, dwTimeOut);
if (dwResult == WAIT_FAILED)
{
dwResult = GetLastError();
LOG_I("Remote Thread for HookInit Failed Error %i", dwResult);
}
if (dwResult == WAIT_TIMEOUT)
{
LOG_E("Remote Thread for HookInit in hung state");
}
}
}
}
}
}
return DBG_CONTINUE;
}
I am trying to inject a dll I've made into note pad.
I've written the following code where I open a process of notepad and inject it with my dll.
Yet, for some reason, my virtualalloc fails.
Would really appreciate some help on the matter.
Code:
#include <Windows.h>
#include <stdio.h>
int main()
{
char dllPath[125];
DWORD pathLen = GetFullPathNameA("MagshimimFinalProject.dll" ,160 , dllPath, NULL);
PVOID addrLoadLibrary = (PVOID)GetProcAddress(GetModuleHandle("notepad.exe"), "LoadLibraryA"); // maybee we need text instead of the dllpath
HANDLE proc = OpenProcess("PROCESS_CREATE_PROCESS",FALSE, "6340");
PVOID memAddr = (PVOID)VirtualAllocEx(proc, NULL,pathLen,MEM_COMMIT, PAGE_EXECUTE_READWRITE);
if (NULL == memAddr)
{
printf("Couldn't allocate memory");
DWORD err = GetLastError();
return 0;
}
HANDLE hRemote = CreateRemoteThread(proc,NULL,0,memAddr, NULL,0,NULL );
if (NULL == hRemote)
{
printf("Couldn't create thread");
DWORD err = GetLastError();
return 0;
}
WaitForSingleObject(hRemote, INFINITE);
BOOL check = CloseHandle(hRemote);
return 0;
}
I've tried looking up all of the functions and making sure they are all getting the right variables, and looking up tutorials on the matter on youtube, but to no avail.
Edit - as the first comment suggested, I've used the debugger to look at each and every line, and when I checked addrLoadLibrary its value was: "identifier "addrLoadLibrary" is undefined" for some reason. any clue why that is?
Edit 2 - firstly, I was checking its value after the line has been completed (value of addrloadlibrary). Secondly, as someone has mentioned, proc is actually the problem for some reason, as its always NULL. Still trying to figure out why is it not working
edit 3 - fixed the problem with proc by removing the quotation marks like some guy in the comments was telling me (sorry for not remembering name, I'm very bad at that.) Now all thats left to figure out is why is addrLoadLibrary undefined even after line
edit 4 - re-read the documentation of the function "getModuleHandle" and relized it should get the dllpath variable. hopefully im not wrong because im not a native english speaker and documentations are especially tough for me to understand.
There are tons of things wrong with this code. Most of the API calls are just plain incorrect. You are not doing very much error checking. You are not copying the DLL path into the memory you allocate before creating the remote thread.
Try something more like this instead:
#include <Windows.h>
#include <stdio.h>
int main() {
char dllPath[MAX_PATH];
DWORD pathLen = GetFullPathNameA("MagshimimFinalProject.dll", MAX_PATH, dllPath, NULL);
if (!pathLen) {
DWORD err = GetLastError();
printf("Couldn't get DLL path. Error: %u", err);
return 0;
}
PVOID addrLoadLibrary = GetProcAddress(GetModuleHandle("kernel32.dll"), "LoadLibraryA");
// TODO: use CreateToolhelp32Snapshot() or EnumProcesses() to find the correct process ID...
DWORD procID = 6340;
HANDLE proc = OpenProcess(PROCESS_CREATE_THREAD | PROCESS_QUERY_INFORMATION | PROCESS_VM_OPERATION | PROCESS_VM_WRITE | PROCESS_VM_READ, FALSE, procID);
if (!proc) {
DWORD err = GetLastError();
printf("Couldn't open process. Error: %u", err);
return 0;
}
PVOID memAddr = VirtualAllocEx(proc, NULL, pathLen+1, MEM_COMMIT, PAGE_READWRITE);
if (!memAddr) {
DWORD err = GetLastError();
CloseHandle(proc);
printf("Couldn't allocate memory. Error: %u", err);
return 0;
}
if (!WriteProcessMemory(proc, memAddr, dllPath, pathLen+1, NULL)) {
DWORD err = GetLastError();
VirtualFreeEx(proc, memAddr, 0, MEM_RELEASE);
CloseHandle(proc);
printf("Couldn't write to memory. Error: %u", err);
return 0;
}
HANDLE hRemote = CreateRemoteThread(proc, NULL, 0, addrLoadLibrary, memAddr, 0, NULL);
if (!hRemote) {
DWORD err = GetLastError();
VirtualFreeEx(proc, memAddr, 0, MEM_RELEASE);
CloseHandle(proc);
printf("Couldn't create thread. Error: %u", err);
return 0;
}
WaitForSingleObject(hRemote, INFINITE);
CloseHandle(hRemote);
VirtualFreeEx(proc, memAddr, 0, MEM_RELEASE);
CloseHandle(proc);
return 0;
}
I'm facing an issue doing a select() call waiting on a socket + pipe.
I know there are already some topics on that but I have read lots of things and their opposite and I can't figure out what is the best solution for my problem.
The best for me would be to use WaitForMultipleObjects() listening on these two objects but when I try to call it only on the WSAEvent object, it fails and last error catch is code 6 (Invalid Handle).
WSAEVENT sockEvent = WSACreateEvent();
sockEvent = WSAEventSelect(fd, sockEvent, FD_WRITE);
HANDLE *pHandles = &sockEvent;
DWORD dwEvent = WaitForMultipleObjects(1, pHandles, FALSE, amqp_time_ms_until(deadline));
switch (dwEvent)
{
// ghEvents[0] was signaled
case WAIT_OBJECT_0 + 0:
// TODO: Perform tasks required by this event
return AMQP_STATUS_OK;
// ghEvents[1] was signaled
case WAIT_OBJECT_0 + 1:
// TODO: Perform tasks required by this event
return AMQP_STATUS_POLL_EXTERNAL_WAKE;
case WAIT_TIMEOUT:
return AMQP_STATUS_TIMEOUT;
// Return value is invalid.
default:
return AMQP_STATUS_SOCKET_ERROR;
}
So WaitForMultipleObjects doesn't seems to Work with WinSocks events, however I have already seen some examples on the net working with it.
And the of WSACreateEvent documentation (https://msdn.microsoft.com/en-us/library/windows/desktop/ms741561%28v=vs.85%29.aspx) says this :
Windows Sockets 2 event objects are system objects in Windows
environments. Therefore, if a Windows application wants to use an
auto-reset event rather than a manual-reset event, the application can
call the CreateEvent function directly.
This doesn't mean that WSAEvent are based on regular windows events ? If it's the case why it doesn't work with WaitForMultipleObjects ? The doc says it can handle regular events.
Thanks for helping.
This is your problem:
sockEvent = WSAEventSelect(fd, sockEvent, FD_WRITE);
You're overwriting the event handle! (As documented, the return value for WSAEventSelect is either 0 or SOCKET_ERROR. It is not a new event handle.)
Try something like
if (WSAEventSelect(fd, sockEvent, FD_WRITE) != 0) return SOCKET_ERROR;
Looking at the declaration of WSAEVENT revealed that WSAEVENT is simply an alias for HANDLE. This explains the note of the WSACreateEvent documentation you added to your post. So WSACreateEvent simply creates a manual reset event by calling CreateEvent(..., TRUE, FALSE, ...);.
Therefore an event returned by WSACreateEvent has to work along with WaitForMultipleObjects(..).
According to the code you've posted I cannot see any reason why WaitForMultipleObjects(..) should return "invalid handle" when supplied with an event returned by WSACreateEvent...
It may be though that pipes do not work with WaitForMultipleObjects(..). I remember having problems with that a long time ago but I cannot remember the details right now. But maybe it is another place to start digging...
Here is the code of my little test application which creates two threads (one event thread signalling a normal event and a simple TCP/IP server sending data). In the main loop a connection to the server is established and signalled events are processed.
#include <winsock2.h>
#include <windows.h>
#include <stdio.h>
#include <stdlib.h>
#include <conio.h>
#pragma comment(lib, "Ws2_32.lib");
#define SERVER_PORT 5000
HANDLE hSomeEvent;
HANDLE hSocketEvent;
DWORD WINAPI eventThread(LPVOID pData)
{
while (1)
{
SleepEx(2250, FALSE);
SetEvent(hSomeEvent);
}
return (0);
}
DWORD WINAPI serverThread(LPVOID pData)
{
SOCKET listener;
struct sockaddr_in sockaddr;
int size;
SOCKET client;
listener = socket(AF_INET, SOCK_STREAM, 0);
if (listener == INVALID_SOCKET)
{
printf("Could not create socket : %d" , WSAGetLastError());
}
sockaddr.sin_family = AF_INET;
sockaddr.sin_addr.s_addr = INADDR_ANY;
sockaddr.sin_port = htons(SERVER_PORT);
if (bind(listener, (struct sockaddr *)&sockaddr , sizeof(sockaddr)) == SOCKET_ERROR)
{
printf("Bind failed with error code : %d" , WSAGetLastError());
}
listen(listener, 1);
while (listener)
{
size = sizeof(struct sockaddr_in);
client = accept(listener, (struct sockaddr *)&sockaddr, &size);
printf("client connected\n");
while (client != INVALID_SOCKET)
{
SleepEx(5000, FALSE);
if (send(client, "hello\0", 6, 0) != 6)
{
closesocket(client);
shutdown(client, 2);
client = INVALID_SOCKET;
}
}
SetEvent(hSomeEvent);
}
return (0);
}
int main()
{
WSADATA wsaData;
HANDLE events[2];
DWORD result;
SOCKET s;
struct hostent *hp;
struct sockaddr_in sockaddr;
int len;
char buff[1024 * 16];
HANDLE *evtPtr;
WSAStartup(MAKEWORD(2, 2), &wsaData);
hSocketEvent = WSACreateEvent();
//hSocketEvent = CreateEvent(NULL, FALSE, FALSE, "socket_event");
hSomeEvent = CreateEvent(NULL, FALSE, FALSE, "some_event");
CreateThread(NULL, 0, eventThread, NULL, 0, &result);
CreateThread(NULL, 0, serverThread, NULL, 0, &result);
s = socket(AF_INET, SOCK_STREAM, 0);
if (s == INVALID_SOCKET)
{
printf("Could not create socket : %d" , WSAGetLastError());
}
hp = gethostbyname("127.0.0.1");
sockaddr.sin_addr.s_addr = *((unsigned long*)hp->h_addr);
sockaddr.sin_family = AF_INET;
sockaddr.sin_port = htons(SERVER_PORT);
if (connect(s, (struct sockaddr*)&sockaddr, sizeof(sockaddr)))
{
closesocket(s);
printf("Could not connect socket : %d" , WSAGetLastError());
}
WSAEventSelect(s, hSocketEvent, FD_READ);
do
{
//events[0] = hSocketEvent;
//events[1] = hSomeEvent;
//result = WaitForMultipleObjects(2, events, FALSE, 1000);
evtPtr = &hSocketEvent;
result = WaitForMultipleObjects(1, evtPtr, FALSE, 1000);
switch (result)
{
case WAIT_OBJECT_0 + 0:
printf("hSocketEvent is signalled!\n");
len = recv(s, buff, sizeof(buff), 0);
printf(" %d bytes received\n", len);
WSAResetEvent(hSocketEvent);
break;
case WAIT_OBJECT_0 + 1:
printf("hSomeEvent is signalled!\n");
break;
case WAIT_TIMEOUT:
printf("timeout\n");
break;
default:
printf("error = %d\n", GetLastError());
break;
}
}
while (1);
printf("\n\nend.");
getch();
return (0);
}
Note that if you use WSACreateEvent you have to manually reset the event after readinng the data (otherwise WaitForMultipleObjects(..) will go nuts).
there's a question:
Is there any way to pair Bluetooth device in Windows programmatically? (c++, c#)
thanks for replies
Yes, the reference documentation is available on MSDN.
32feet.NET is a C# wrapper, available here. Information on pairing is here.
Python is a tempting and overall easy solution, but PyBlueZ does not expose the windows Bluetooth authentication APIs here: https://msdn.microsoft.com/en-us/library/windows/desktop/cc766819(v=vs.85).aspx
One way to get around this is to create a command line tool and use this through Python. To create command line tools for Windows, use Visual Studio and add the necessary libraries to your project linker properties: Bthprops.lib and ws2_32.lib
Below is the code for a project to make a command line tool with 1 parameter, the MAC address, that pairs the specified device using "Just Works" pairing. See commented code for using passkey pairing.
#include "stdafx.h"
#include <initguid.h>
#include <winsock2.h>
#include <BluetoothAPIs.h>
#include <ws2bth.h>
BOOL WINAPI BluetoothAuthCallback(LPVOID pvParam, PBLUETOOTH_AUTHENTICATION_CALLBACK_PARAMS pAuthCallbackParams);
int _tmain(int argc, _TCHAR* argv[])
{
SOCKADDR_BTH sa = { 0 };
int sa_len = sizeof(sa);
DWORD dwRet;
BLUETOOTH_DEVICE_INFO btdi = { 0 };
HBLUETOOTH_AUTHENTICATION_REGISTRATION hRegHandle = 0;
// initialize windows sockets
WORD wVersionRequested;
WSADATA wsaData;
wVersionRequested = MAKEWORD(2, 0);
if (WSAStartup(wVersionRequested, &wsaData) != 0) {
ExitProcess(2);
}
// parse the specified Bluetooth address
if (argc < 2) {
fprintf(stderr, "usage: csbtpair <addr>\n"
"\n addr must be in the form (XX:XX:XX:XX:XX:XX)");
ExitProcess(2);
}
if (SOCKET_ERROR == WSAStringToAddress(argv[1], AF_BTH,
NULL, (LPSOCKADDR)&sa, &sa_len)) {
ExitProcess(2);
}
// setup device info
btdi.dwSize = sizeof(BLUETOOTH_DEVICE_INFO);
btdi.Address.ullLong = sa.btAddr;
btdi.ulClassofDevice = 0;
btdi.fConnected = false;
btdi.fRemembered = false;
btdi.fAuthenticated = false;
// register authentication callback. this prevents UI from showing up.
dwRet = BluetoothRegisterForAuthenticationEx(&btdi, &hRegHandle, &BluetoothAuthCallback, NULL);
if (dwRet != ERROR_SUCCESS)
{
fprintf(stderr, "BluetoothRegisterForAuthenticationEx ret %d\n", dwRet);
ExitProcess(2);
}
// authenticate device (will call authentication callback)
AUTHENTICATION_REQUIREMENTS authreqs = MITMProtectionNotRequired;
fprintf(stderr, "BluetoothAuthReqs = %d\n", authreqs);
dwRet = BluetoothAuthenticateDeviceEx(NULL, NULL, &btdi, NULL, authreqs);
if (dwRet != ERROR_SUCCESS)
{
fprintf(stderr, "BluetoothAuthenticateDevice ret %d\n", dwRet);
if (dwRet == ERROR_CANCELLED)
{
fprintf(stderr, "Cancelled");
}
else if (dwRet == ERROR_INVALID_PARAMETER)
{
fprintf(stderr, "Invalid Parameter");
}
else if (dwRet == ERROR_NO_MORE_ITEMS)
{
fprintf(stderr, "Already paired!");
}
}
fprintf(stderr, "pairing finish\n");
ExitProcess(0);
return 0;
}
// Authentication callback
BOOL WINAPI BluetoothAuthCallback(LPVOID pvParam, PBLUETOOTH_AUTHENTICATION_CALLBACK_PARAMS pAuthCallbackParams)
{
DWORD dwRet;
fprintf(stderr, "BluetoothAuthCallback 0x%x\n", pAuthCallbackParams->deviceInfo.Address.ullLong);
BLUETOOTH_AUTHENTICATE_RESPONSE AuthRes;
AuthRes.authMethod = pAuthCallbackParams->authenticationMethod;
fprintf(stderr, "Authmethod %d\n", AuthRes.authMethod);
// Check to make sure we are using numeric comparison (Just Works)
if (AuthRes.authMethod == BLUETOOTH_AUTHENTICATION_METHOD_NUMERIC_COMPARISON)
{
fprintf(stderr, "Numeric Comparison supported\n");
}
AuthRes.bthAddressRemote = pAuthCallbackParams->deviceInfo.Address;
AuthRes.negativeResponse = FALSE;
// Commented out code is used for pairing using the BLUETOOTH_AUTHENTICATION_METHOD_PASSKEY method
//memcpy_s(AuthRes.pinInfo.pin, sizeof(AuthRes.pinInfo.pin), L"1234", 0);
//AuthRes.pinInfo.pinLength = 0;
// Respond with numerical value for Just Works pairing
AuthRes.numericCompInfo.NumericValue = 1;
// Send authentication response to authenticate device
dwRet = BluetoothSendAuthenticationResponseEx(NULL, &AuthRes);
if (dwRet != ERROR_SUCCESS)
{
fprintf(stderr, "BluetoothSendAuthenticationResponseEx ret %d\n", dwRet);
if (dwRet == ERROR_CANCELLED)
{
fprintf(stderr, "Bluetooth device denied passkey response or communicatino problem.\n");
}
else if (dwRet == E_FAIL)
{
fprintf(stderr, "Device returned a failure code during authentication.\n");
}
else if (dwRet == 1244)
{
fprintf(stderr, "Not authenticated\n");
}
}
else
{
fprintf(stderr, "BluetoothAuthCallback finish\n");
}
return 1; // This value is ignored
}
In lieu of creating this yourself, you may want to try this pre-made solution:
http://bluetoothinstaller.com/bluetooth-command-line-tools/
It did not work for my particular solution.
Then, you will need to run your downloaded or custom command line tool from python as an administrator. To do this reliably, I recommend the stackoverflow question:
How to run python script with elevated privilege on windows
I meet the same problem,and I have resolved the problem, Maybe you can try it:
make a windows tool named pairtool.exe, it help you to pairing with command line. the key api is BluetoothAuthenticateDevice, please refering the functions document
dwRet = BluetoothAuthenticateDevice(NULL, NULL, &btdi, L"1234", 4);
if(dwRet != ERROR_SUCCESS)
{
fprintf(stderr, "BluetoothAuthenticateDevice ret %d\n", dwRet);
ExitProcess(2);
}
python code:
def connect2Btdev(devName):
#found the device addr
addr = inquiry(devName)
if addr == None:
return None
#pairing with pairtool.exe
cmd=r'%s %s' % ('pairtool.exe',addr)
ret = os.system(cmd)
if ret <> 0:
return None
here is all the code of pairtool.exe:
#include "stdafx.h"
#include <initguid.h>
#include <winsock2.h>
#include <BluetoothAPIs.h>
#include <ws2bth.h>
bool BluetoothAuthCallback(LPVOID pvParam, PBLUETOOTH_AUTHENTICATION_CALLBACK_PARAMS pAuthCallbackParams)
{
DWORD dwRet;
fprintf(stderr, "BluetoothAuthCallback 0x%x\n", pAuthCallbackParams->deviceInfo.Address.ullLong);
dwRet = BluetoothSendAuthenticationResponse(NULL, &(pAuthCallbackParams->deviceInfo), L"1234");
if(dwRet != ERROR_SUCCESS)
{
fprintf(stderr, "BluetoothSendAuthenticationResponse ret %d\n", dwRet);
ExitProcess(2);
return 1;
}
fprintf(stderr, "BluetoothAuthCallback finish\n");
ExitProcess(0);
return 1;
}
int _tmain(int argc, _TCHAR* argv[])
{
SOCKADDR_BTH sa = { 0 };
int sa_len = sizeof(sa);
DWORD dwRet;
BLUETOOTH_DEVICE_INFO btdi = {0};
HBLUETOOTH_AUTHENTICATION_REGISTRATION hRegHandle = 0;
// initialize windows sockets
WORD wVersionRequested;
WSADATA wsaData;
wVersionRequested = MAKEWORD( 2, 0 );
if( WSAStartup( wVersionRequested, &wsaData ) != 0 ) {
ExitProcess(2);
}
// parse the specified Bluetooth address
if( argc < 2 ) {
fprintf(stderr, "usage: rfcomm-client <addr>\n"
"\n addr must be in the form (XX:XX:XX:XX:XX:XX)");
ExitProcess(2);
}
if( SOCKET_ERROR == WSAStringToAddress( argv[1], AF_BTH,
NULL, (LPSOCKADDR) &sa, &sa_len ) ) {
ExitProcess(2);
}
//注册回调函数
btdi.dwSize = sizeof(BLUETOOTH_DEVICE_INFO);
btdi.Address.ullLong = sa.btAddr;
btdi.ulClassofDevice = 0;
btdi.fConnected = false;
btdi.fRemembered = false;
btdi.fAuthenticated = false;
dwRet = BluetoothRegisterForAuthenticationEx(&btdi, &hRegHandle, &BluetoothAuthCallback, NULL);
if(dwRet != ERROR_SUCCESS)
{
fprintf(stderr, "BluetoothRegisterForAuthenticationEx ret %d\n", dwRet);
ExitProcess(2);
}
dwRet = BluetoothAuthenticateDevice(NULL, NULL, &btdi, L"1234", 4);
if(dwRet != ERROR_SUCCESS)
{
fprintf(stderr, "BluetoothAuthenticateDevice ret %d\n", dwRet);
ExitProcess(2);
}
Sleep(1000);
fprintf(stderr, "pairing finish\n");
ExitProcess(0);
return 0;
}
Microsoft has introduced Windows.Devices.Enumeration API available for UWP and traditional applications, which makes pairing of bluetooth devices very easy (for details look at the official C# and C++ example). As far as I understand it is the API which is used by built-in "Bluetooth & other devices" UI dialog. As of an example of what console utility you can write using this API you can take a look at my console BluetoothDevicePairing utility.
You can do so by using the functions documented under MSDN Bluetooth Functions.
These enable searching and pairing of bluetooth devices programmatically.
I have an application that needs to monitor the primary drive for file changes via ReadDirectoryChangesW. However, when UAC is enabled, it doesn't work.
All of the Windows API calls succeed, but I'm not notified of any changes.
I can work around this by individually monitoring each directory in the root, but this is a problem, because it can potentially cause a blue screen if there are too many directories.
Is there an acceptable way to get around UAC and receive file change notifications on the entire primary drive?
The relevant CreateFile and ReadDirectoryChangesW is below. In the case where it doesn't work, directory is C:\. If I monitor any secondary drive (i.e. E:\, F:\, G:\) it works as expected. None of the calls return errors.
HANDLE fileHandle = CreateFileW(directory.c_str(), FILE_LIST_DIRECTORY,
FILE_SHARE_READ | FILE_SHARE_DELETE, NULL, OPEN_EXISTING,
FILE_FLAG_BACKUP_SEMANTICS | FILE_FLAG_OVERLAPPED, NULL);
BOOL success = ReadDirectoryChangesW(fileHandle, watched.buffer.data(),
watched.buffer.size(), TRUE,
FILE_NOTIFY_CHANGE_FILE_NAME | FILE_NOTIFY_CHANGE_LAST_WRITE,
NULL, &watched.overlapped, NULL);
Interestingly, the .NET System.IO.FileSystemWatcher does work correctly, and it uses the exact same functions and parameters as I'm using, but it behaves correctly.
First it is best for applications that use the ReadDirectoryChangesW API to run elevated make a manifest file for you app and set requireAdministrator as the requestedExecutionLevel level. Check here for reference.
Try removing FILE_SHARE_WRITE from the CreateFile call if you are using it.
Another option is to make your program run as a service, im not sure how applicable this is to your needs. You could post some code as to how you are getting the file handle and what are you passing to ReadDirectoryChangesW
Here's some working test code, for future reference.
#include <Windows.h>
#include <stdio.h>
int main(int argc, char ** argv)
{
HANDLE filehandle;
BYTE buffer[65536];
DWORD dw;
FILE_NOTIFY_INFORMATION * fni;
OVERLAPPED overlapped = {0};
overlapped.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
if (overlapped.hEvent == NULL)
{
printf("CreateEvent: %u\n", GetLastError());
return 1;
}
filehandle = CreateFile(L"C:\\",
FILE_LIST_DIRECTORY,
FILE_SHARE_READ | FILE_SHARE_DELETE,
NULL,
OPEN_EXISTING,
FILE_FLAG_BACKUP_SEMANTICS | FILE_FLAG_OVERLAPPED,
NULL);
if (filehandle == INVALID_HANDLE_VALUE)
{
printf("CreateFile: %u\n", GetLastError());
return 1;
}
for (;;)
{
if (!ReadDirectoryChangesW(filehandle, buffer, sizeof(buffer),
TRUE,
FILE_NOTIFY_CHANGE_FILE_NAME | FILE_NOTIFY_CHANGE_LAST_WRITE,
NULL, &overlapped, NULL))
{
printf("ReadDirectoryChangesW: %u\n", GetLastError());
return 1;
}
printf("Queued OK.\n");
if (!GetOverlappedResult(filehandle, &overlapped, &dw, TRUE))
{
printf("GetOverlappedResult: %u\n", GetLastError());
return 1;
}
printf("%u bytes read.\n", dw);
fni = (FILE_NOTIFY_INFORMATION *)buffer;
for (;;)
{
printf("Next entry offset = %u\n", fni->NextEntryOffset);
printf("Action = %u\n", fni->Action);
printf("File name = %.*ws\n",
fni->FileNameLength / 2,
fni->FileName);
if (fni->NextEntryOffset == 0) break;
fni = (FILE_NOTIFY_INFORMATION *)
(((BYTE *)fni) + fni->NextEntryOffset);
}
}
printf("All done\n");
return 0;
}
You can adjust the privileges of your process yourself like this:
// enable the required privileges for this process
LPCTSTR arPrivelegeNames[] = { SE_BACKUP_NAME,
SE_RESTORE_NAME,
SE_CHANGE_NOTIFY_NAME
};
for (int i=0; i<(sizeof(arPrivelegeNames)/sizeof(LPCTSTR)); ++i)
{
CAutoGeneralHandle hToken;
if (OpenProcessToken(GetCurrentProcess(), TOKEN_ADJUST_PRIVILEGES, hToken.GetPointer()))
{
TOKEN_PRIVILEGES tp = { 1 };
if (LookupPrivilegeValue(NULL, arPrivelegeNames[i], &tp.Privileges[0].Luid))
{
tp.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED;
AdjustTokenPrivileges(hToken, FALSE, &tp, sizeof(tp), NULL, NULL);
}
}
}
This works also for non-privileged processes (a.k.a. normal user processes).