I'm writing a very simple debugger and I defined a class called BREAKPOINT_INFO that contains information about breakpoints set.
class BREAKPOINT_INFO
{
public:
HANDLE hProcess;
PCHAR lpBreakPoint;
CHAR instr;
BOOL justCalled;
//Set default values
BREAKPOINT_INFO()
{
hProcess = NULL;
lpBreakPoint = NULL;
instr = 0x00;
justCalled = FALSE;
}
//Destructor
~BREAKPOINT_INFO()
{
//Let me know the destructor is being called
MessageBox(NULL, "Destructor called", NULL, MB_OK);
DWORD dwError = 0;
LPCSTR szErrorRest = (LPCSTR)"Error restoring original instruction: ";
LPCSTR szErrorHanlde = (LPCSTR)"Error closing process handle: ";
std::ostringstream oss;
if(hProcess != NULL && lpBreakPoint != NULL)
{
//write back the original instruction stored in instr
if(!WriteProcessMemory(hProcess, lpBreakPoint, &instr, sizeof(CHAR), NULL))
{
dwError = GetLastError();
oss << szErrorRest << dwError;
MessageBox(NULL, oss.str().c_str(), "ERROR", MB_OK|MB_ICONERROR);
}
}
}
};
I need the destructor to clean up any breakpoints set however the deconstructor is never called and I'm not quite sure why that is in my particular case.
Here's main.cpp:
BREAKPOINT_INFO instrMov;
//GetProcModuleHandle is a function I made to get the handle of a
//of a module in a remote process
LPVOID lpServerDll = (LPVOID)GetProcModuleHandle(dwPid, szServerDll);
//the instructions address is relative to the starting address of the server dll. Hence the offset.
PCHAR lpInstr = (PCHAR)((DWORD)lpServerDll+instr_offset);
hProcess = OpenProcess(PROCESS_ALL_ACCESS, TRUE, dwPid);
//sets the breakpoint
instrMov.InitializeBreakPoint(hProcess, lpInstr);
while(1)
{
if(!instrMov.justCalled)
{
instrMov.SetBreakPoint();
}
if(instrMov.justCalled)
{
instrMov.justCalled = FALSE;
}
if(WaitForDebugEvent(&dbgEvent, 0))
{
ProcessDebugEvent(&dbgEvent, lpBreakPoints, 3);
ContinueDebugEvent(dbgEvent.dwProcessId, dbgEvent.dwThreadId, DBG_CONTINUE);
}
}
return 0; //<---never reaches return
It's a never ending loop so the program, at the moment, never actually reaches the return. It has to be terminated with either Ctrl+C or by closing the terminal. Not sure if this could be causing the destructor to not be called or not.
Any information, solutions, etc would be greatly appreciated. Thank you for your time.
you have to handle SIGINT signal, otherwise program is terminated abnormally and dtors are not called.
Related
How can I use NotifyServiceStatusChange properly so I can get notified when the service specified is deleted? My current code successfully stops the service and marks it for deletion. However, I want to be notified when the service is fully deleted.
Here are the main points of my code:
SC_HANDLE SCManager = OpenSCManagerW(NULL, SERVICES_ACTIVE_DATABASE,
SC_MANAGER_CONNECT | SC_MANAGER_ENUMERATE_SERVICE);
HANDLE EventHandle = CreateEventW(NULL, TRUE, FALSE, NULL);
SERVICE_NOTIFY ServiceNotify;
ServiceNotify.dwVersion = SERVICE_NOTIFY_STATUS_CHANGE;
ServiceNotify.pszServiceNames = ServiceName;
ServiceNotify.pContext = &EventHandle;
ServiceNotify.pfnNotifyCallback = (PFN_SC_NOTIFY_CALLBACK)CallbackFunction;
DWORD status = NotifyServiceStatusChangeW(SCManager, SERVICE_NOTIFY_DELETED, &ServiceNotify);
WaitForSingleObject(EventHandle, INFINITE);
CloseServiceHandle(SCManager);
CloseHandle(EventHandle);
(ServiceName is WCHAR*)
CallbackFunction code:
VOID CALLBACK CallbackFunction(IN PVOID pParameter) {
SERVICE_NOTIFY* ServiceNotify = pParameter;
HANDLE EventHandle = *(HANDLE*)ServiceNotify->pContext;
SetEvent(EventHandle);
}
NotifyServiceStatusChange is returning ERROR_SUCCESS (0). However, my callback function is not being called at all. How can I fix this?
Edit:
Here is minimal reproducible code:
void ErrorExit(char* FunctionName, unsigned long ErrorCode) {
char* ErrorMessage;
FormatMessageA(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS,
NULL, ErrorCode, LANG_USER_DEFAULT, (LPTSTR)&ErrorMessage, 0, NULL);
int MessageSize = (strlen(ErrorMessage) + strlen(FunctionName) + 50) * sizeof(char);
char* FullMessage = malloc(MessageSize);
sprintf_s(FullMessage, MessageSize, "%s failed with error %d: %s", FunctionName, ErrorCode, ErrorMessage);
MessageBoxA(NULL, FullMessage, "Error", MB_OK);
ExitProcess(ErrorCode);
}
PFN_SC_NOTIFY_CALLBACK CallbackFunction(PVOID pParameter) {
printf("CallbackFunction has been called.\r\n");
SERVICE_NOTIFY* ServiceNotify = pParameter;
HANDLE EventHandle = ServiceNotify->pContext;
if (!SetEvent(EventHandle)) {
ErrorExit("SetEvent", GetLastError());
}
}
int main()
{
WCHAR* ServiceName = L"SERVICE NAME"; // Input service name here
SC_HANDLE SCManager = OpenSCManagerW(NULL, SERVICES_ACTIVE_DATABASE, SC_MANAGER_ALL_ACCESS);
if (!SCManager) {
ErrorExit("OpenSCManagerW", GetLastError());
}
SC_HANDLE ServiceHandle = OpenServiceW(SCManager, ServiceName,
SERVICE_ENUMERATE_DEPENDENTS | SERVICE_STOP | DELETE);
if (!ServiceHandle) {
ErrorExit("ServiceHandle", GetLastError());
}
if (!DeleteService(ServiceHandle)) {
ErrorExit("DeleteService", GetLastError());
}
if (!CloseServiceHandle(ServiceHandle)) {
ErrorExit("CloseServiceHandle", GetLastError());
}
HANDLE EventHandle = CreateEventW(NULL, TRUE, FALSE, NULL);
if (!EventHandle) {
ErrorExit("CreateEventW", GetLastError());
}
SERVICE_NOTIFY ServiceNotify;
ServiceNotify.dwVersion = SERVICE_NOTIFY_STATUS_CHANGE;
ServiceNotify.pszServiceNames = ServiceName;
ServiceNotify.pContext = EventHandle;
ServiceNotify.pfnNotifyCallback = CallbackFunction;
DWORD status = NotifyServiceStatusChangeW(SCManager, SERVICE_NOTIFY_DELETED, &ServiceNotify);
if (status != ERROR_SUCCESS) {
ErrorExit("NotifyServiceStatusChangeW", GetLastError());
}
status = WaitForSingleObjectEx(EventHandle, INFINITE, TRUE);
if (status == WAIT_FAILED) {
ErrorExit("WaitForSingleObjectEx", GetLastError());
}
printf("WaitForSingleObjectEx Result: %lu\r\n", status);
system("pause");
return 0;
}
When I run this, no other service depends on the service being deleted, and the service being deleted is already stopped. My error handling function "ErrorExit" is never called. Nothing is printed on the screen. My program simply pauses, which I assume is from WaitForSingleObjectEx.
I know the service is being deleted because I have ProcessHacker open, and it is giving me notifications that the service is being deleted.
NotifyServiceStatusChange is returning ERROR_SUCCESS (0). However, my callback function is not being called at all.
The NotifyServiceStatusChangeW documentation says:
When the service status changes, the system invokes the specified callback function as an asynchronous procedure call (APC) queued to the calling thread. The calling thread must enter an alertable wait (for example, by calling the SleepEx function) to receive notification. For more information, see Asynchronous Procedure Calls.
So, make sure you are actually processing APC notifications while you wait. WaitForSingleObject() will not do that for you.
Use WaitForSingleObjectEx() instead. It has a bAlertable parameter you can set to TRUE. You will have to call it in a loop, since it will return when any APC call is processed by the calling thread, which may not be the one you are expecting.
You also need to call NotifyServiceStatusChangeW() in a loop, too. The documentation does not mention this, but the callback will be called only 1 time per use. Once the callback is called, you need to call NotifyServiceStatusChangeW() again to receive another notification if the current one is not the event you are expecting.
With that said, try something more like this:
struct MyCallbackInfo {
HANDLE EventHandle;
LPCWSTR pszServiceName;
bool bDeleted;
};
...
VOID CALLBACK CallbackFunction(PVOID pParameter) {
SERVICE_NOTIFYW* ServiceNotify = (SERVICE_NOTIFYW*) pParameter;
MyCallbackInfo *ci = (MyCallbackInfo*) ServiceNotify->pContext;
if (ServiceNotify->dwNotificationStatus == ERROR_SUCCESS) {
LPWSTR pServiceName = ServiceNotify->pszServiceNames;
while (*pServiceName != L'\0') {
if (lstrcmpW(pServiceName, ci->pszServiceName) == 0) {
ci.bDeleted = true;
break;
}
pServiceName += (lstrlenW(pServiceName) + 1);
}
LocalFree(ServiceNotify->pszServiceNames);
}
SetEvent(ci->EventHandle);
}
...
MyCallbackInfo ci;
ci.EventHandle = CreateEventW(NULL, TRUE, FALSE, NULL);
ci.pszServiceName = ServiceName;
ci.bDeleted = false;
if (!ci.EventHandle) {
// error handling...
}
SC_HANDLE SCManager = OpenSCManagerW(NULL, SERVICES_ACTIVE_DATABASE,
SC_MANAGER_CONNECT | SC_MANAGER_ENUMERATE_SERVICE);
if (!SCManager) {
// error handling...
}
SERVICE_NOTIFYW ServiceNotify = {};
ServiceNotify.dwVersion = SERVICE_NOTIFY_STATUS_CHANGE;
ServiceNotify.pContext = &ci;
ServiceNotify.pfnNotifyCallback = CallbackFunction;
DWORD status;
do {
status = NotifyServiceStatusChangeW(SCManager, SERVICE_NOTIFY_DELETED, &ServiceNotify);
if (status != ERROR_SUCCESS) {
// error handling...
}
while ((status = WaitForSingleObjectEx(ci.EventHandle, INFINITE, TRUE)) == WAIT_IO_COMPLETION);
if (status == WAIT_FAILED) {
// error handling...
}
if (ci.bDeleted) {
// service has been deleted ...
break;
}
ResetEvent(ci.EventHandle);
}
while (true);
CloseServiceHandle(SCManager);
CloseHandle(ci.EventHandle);
As discussed in WTSQueryUserToken throws error 1008, even when running under LocalSystem, I'm having trouble getting my Windows service to launch an interactive process on a particular user's desktop as soon as they log in.
The proposed solution there was to handle the SERVICE_CONTROL_SESSIONCHANGE control code and use the passed dwSessionId. Here's all of the code (apologies that it's quite lengthy, but I was told to post it here anyway):
// These headers just contain system header #include's function prototypes
// and global variable declarations. If a variable below seems like it is
// undefined, rest assured that it *is* defined in one of these headers.
#include "events.h"
#include "main.h"
int __cdecl _tmain(int argc, LPTSTR argv[]) {
sysStart = system_clock::now();
LogInit();
// If command-line parameter is "install", install the service.
// Otherwise, the service is probably being started by the SCM
if (lstrcmpi(argv[1], L"install") == 0) {
return SvcInstall();
}
SERVICE_TABLE_ENTRY dispatchTable[] = {
{ &svcName[0], (LPSERVICE_MAIN_FUNCTION)SvcMain },
{ nullptr, nullptr }
};
// This call returns when the service has stopped. The
// process should simply terminate when the call returns
if (!StartServiceCtrlDispatcher(dispatchTable)) {
ReportSvcEvent("StartServiceCtrlDispatcher");
}
return ERROR_SUCCESS;
}
char* WINAPI GetTimestamp(string& buf) {
int ms = (high_resolution_clock::now().
time_since_epoch().count() / 1000000) % 1000;
auto tt = system_clock::to_time_t(
system_clock::now());
tm time;
localtime_s(&time, &tt);
strftime(&buf[0], 21, "[%d-%m-%Y %T", &time);
snprintf(&buf[0], 26, "%s.%03d] ", &buf[0], ms);
buf[25] = ' ';
return &buf[0];
}
bool WINAPI LaunchDebugger(void) {
// Get System directory, typically C:\Windows\System32
wstring systemDir(MAX_PATH + 1, '\0');
UINT nChars = GetSystemDirectory(&systemDir[0], systemDir.length());
if (nChars == 0) {
return false; // failed to get system directory
}
systemDir.resize(nChars);
// Get process ID and create the command line
// wostringstream ss;
// ss << systemDir << L"\\vsjitdebugger.exe -p " << GetCurrentProcessId();
wstring cmdLine = L"";
// Start debugger process
STARTUPINFOW si;
ZeroMemory(&si, sizeof(si));
si.cb = sizeof(si);
PROCESS_INFORMATION pi;
ZeroMemory(&pi, sizeof(pi));
if (!CreateProcess(nullptr, &cmdLine[0], nullptr,
nullptr, false, 0, nullptr, nullptr, &si, &pi)) {
return false;
}
// Close debugger process handles to eliminate resource leaks
CloseHandle(pi.hThread);
CloseHandle(pi.hProcess);
// Wait for the debugger to attach
while (!IsDebuggerPresent()) {
Sleep(100);
}
// Stop execution so the debugger can take over
DebugBreak();
return true;
}
VOID WINAPI LogActiveTime(void) {
// The computer is shutting down - write an entry to logFile to reflect
// this, prefixed with a null byte to mark the current file position
// (used for parsing in the timestamp on the next system boot)
logFile << '\0';
LogMessage("User action", "System shutting down after being "
"active for " + DurationString(system_clock::now() - sysStart));
logFile.close();
// If the log file contains > 40 lines (10 boot/shutdown cycles),
// remove the first 4 lines (the earliest boot/shutdown cycle).
// This stops the file from getting too long to read easily
ifstream inFile(logFilePath);
string line;
auto count = 0;
while (getline(inFile, line)) {
count++;
}
}
DWORD WINAPI LogError(const string& funcName) {
auto err = 0;
LogMessage(funcName, system_category(
).message(err = GetLastError()), true);
return err;
}
DWORD WINAPI LogInactiveTime(void) {
// Create a new log file to be used as the input on the next run
LogInit("temp");
// Open the existing log file for reading and find the last shutdown
// log entry by copying its contents to the new file until a null byte
// or EOF is found (see LogActiveTime() for more info)
ifstream inFile(logFilePath);
if (!inFile) {
return LogError("LogInactiveTime");
}
char ch = inFile.get();
while (ch != '\0' && !inFile.eof()) {
logFile << ch;
ch = inFile.get();
}
if (inFile.eof()) {
// No shutdown log entry was found, i.e. this is probably the first
// time the service has run on the current instance of our log file.
// Close the temp file and re-open the original log file before
// returning, otherwise future messages won't make it to the file!
LogInit();
return ERROR_SUCCESS;
}
// At this point we can be sure that a valid shutdown log entry
// exists, so we now need to parse it into a chrono::time_point.
// Also save the entry's starting position in pos for later use
auto pos = inFile.tellg();
auto tt = system_clock::to_time_t(sysStart);
tm start, end = { 0 };
localtime_s(&start, &tt);
inFile >> get_time(&end, "[%d-%m-%Y %T");
if (inFile.fail() || inFile.bad()) {
return LogError("LogInactiveTime");
}
// Ensure that both time_points refer to
// the correct time, regardless of DST
end.tm_isdst = start.tm_isdst;
sysEnd = system_clock::from_time_t(mktime(&end));
// Go back to the *actual* start of the shutdown
// log entry so we can copy it into the new file
inFile.seekg(pos);
// Finish copying over the rest of our existing log
// file, then close it and replace it with the new one
ch = inFile.get();
while (!inFile.eof()) {
logFile << ch;
ch = inFile.get();
}
inFile.close();
remove(logFilePath.c_str());
logFile.close();
rename("temp", logFilePath.c_str());
// Finally, do what we *actually* came here to do!
LogMessage("User action", "System booting after being "
"inactive for " + DurationString(sysStart - sysEnd));
return ERROR_SUCCESS;
}
VOID WINAPI LogInit(const string& filePath) {
setlocale(LC_ALL, "en_US.UTF8");
if (logFile.is_open()) {
logFile.close();
}
logFile.open(filePath == "" ?
logFilePath : filePath, ios::app);
if (!logFile) {
exit(GetLastError());
}
}
VOID WINAPI LogMessage(const string& funcName,
const string& msg, bool isError) {
if (!logFile.is_open()) {
LogInit();
}
string buf(52, '\0');
snprintf(&buf[0], 52, "%s%-6s %-18s ", GetTimestamp(buf),
isError ? "ERROR:" : "INFO:", &(funcName + ':')[0]);
buf[51] = ' ';
logFile << buf << msg << endl;
}
VOID WINAPI ReportSvcEvent(const string& funcName) {
HANDLE eventSrc = RegisterEventSource(nullptr, &svcName[0]);
if (eventSrc != nullptr) {
LPCSTR errParams[2] = { "WinUtilities" };
char buf[MAX_PATH];
StringCchPrintfA(buf, MAX_PATH, "Function '%s' failed: %s",
funcName.c_str(), system_category().message(GetLastError(
)).c_str());
errParams[1] = buf;
ReportEventA(eventSrc, // event log handle
EVENTLOG_ERROR_TYPE, // event type
0, // event category
SVC_ERROR, // event identifier
nullptr, // no security identifier
2, // size of lpszStrings array
0, // no binary data
errParams, // array of strings
nullptr); // no binary data
DeregisterEventSource(eventSrc);
}
}
VOID WINAPI ReportSvcStatus(DWORD newState,
DWORD exitCode, DWORD waitHint) {
static DWORD dwCheckPoint = 1;
static unordered_map<int, string> svcStates;
if (svcStates.empty()) {
// Initialise mapping from service state codes to readable strings
svcStates.insert({ SERVICE_STOPPED, "Stopped" });
svcStates.insert({ SERVICE_START_PENDING, "Start Pending" });
svcStates.insert({ SERVICE_STOP_PENDING, "Stop Pending" });
svcStates.insert({ SERVICE_RUNNING, "Running" });
svcStates.insert({ SERVICE_CONTINUE_PENDING, "Continue Pending" });
svcStates.insert({ SERVICE_PAUSE_PENDING, "Pause Pending" });
svcStates.insert({ SERVICE_PAUSED, "Paused" });
}
// Update the SERVICE_STATUS structure with the new passed-in values
svcStatus.dwCurrentState = newState;
svcStatus.dwWin32ExitCode = exitCode;
svcStatus.dwWaitHint = waitHint;
if (newState == SERVICE_START_PENDING) {
svcStatus.dwControlsAccepted = 0;
} else {
svcStatus.dwControlsAccepted =
SERVICE_ACCEPT_SESSIONCHANGE |
SERVICE_ACCEPT_STOP |
SERVICE_ACCEPT_PRESHUTDOWN;
}
if (newState == SERVICE_RUNNING ||
newState == SERVICE_STOPPED) {
svcStatus.dwCheckPoint = 0;
} else {
svcStatus.dwCheckPoint = dwCheckPoint++;
}
// Report the status of the service to the SCM and our log file
if (!SetServiceStatus(statusHandle, &svcStatus)) {
LogError("SetServiceStatus");
} else {
LogMessage("SetServiceStatus", "Service status " \
"updated to '" + svcStates[newState] + "'.");
}
}
DWORD WINAPI SvcCtrlHandler(DWORD ctrlCode, DWORD
eventType, LPVOID eventData, LPVOID context) {
switch (ctrlCode) {
case SERVICE_CONTROL_SESSIONCHANGE: {
auto sessionId = ((WTSSESSION_NOTIFICATION*
)eventData)->dwSessionId;
switch (eventType) {
case WTS_SESSION_LOGON: {
string userName;
DWORD size;
WTSQuerySessionInformationA(WTS_CURRENT_SERVER_HANDLE, sessionId,
WTS_INFO_CLASS::WTSUserName, (LPSTR*)&userName[0], &size);
ReportSvcEvent("log on");
// A user has successfully logged on to the PC. Now we can start
// an interactive worker process under that user's account which
// will perform the actual work that we want to do
STARTUPINFO si = { 0 };
si.cb = sizeof(si);
si.wShowWindow = true;
HANDLE hToken;
if (!WTSQueryUserToken(sessionId, &hToken)) {
LogError("WTSQueryUserToken");
return ERROR_CALL_NOT_IMPLEMENTED;
}
wstring cmdLine = L"C:\\Path\\to\\my\\app.exe";
if (!CreateProcessAsUser(hToken, &cmdLine[0], nullptr, nullptr, nullptr,
false, CREATE_NO_WINDOW, nullptr, nullptr, &si, &workerProc)) {
LogError("CreateProcessAsUser");
return ERROR_CALL_NOT_IMPLEMENTED;
}
CloseHandle(hToken);
break;
} default: {
break;
}
}
break;
} case SERVICE_CONTROL_STOP: {
// Signal the service to stop
ReportSvcStatus(SERVICE_STOP_PENDING, NO_ERROR, 0);
SetEvent(svcStopEvent);
break;
} case SERVICE_CONTROL_PRESHUTDOWN: {
LogActiveTime();
break;
} default: {
return ERROR_CALL_NOT_IMPLEMENTED;
}
}
return NO_ERROR;
}
VOID WINAPI SvcInit(DWORD argc, LPTSTR argv[]) {
// Get the time at which the last shutdown occurred, and
// log the duration for which the system was inactive
if (LogInactiveTime() > 0) {
return;
}
// Create an event. The control handler function (SvcCtrlHandler)
// signals this event when it receives the stop control code
svcStopEvent = CreateEvent(
nullptr, // default security attributes
TRUE, // manual reset event
FALSE, // not signaled
nullptr); // no name
if (svcStopEvent == nullptr) {
LogError("CreateEvent");
ReportSvcStatus(SERVICE_STOPPED, NO_ERROR, 0);
return;
}
// Report running status when initialisation is complete
ReportSvcStatus(SERVICE_RUNNING, NO_ERROR, 0);
// Wait until our stop event has been signalled
WaitForSingleObject(svcStopEvent, INFINITE);
// Code execution won't reach here until the service has been
// fully stopped. Report this to the SCM when it happens, then
// terminate the worker process and clean up its handles
ReportSvcStatus(SERVICE_STOPPED, NO_ERROR, 0);
if (workerProc.hProcess) {
TerminateProcess(workerProc.hProcess, 0);
CloseHandle(workerProc.hProcess);
CloseHandle(workerProc.hThread);
}
}
DWORD WINAPI SvcInstall(void) {
TCHAR path[MAX_PATH];
if (!GetModuleFileName(nullptr, path, MAX_PATH)) {
return LogError("GetModuleFileName");
}
// Get a handle to the SCM database
auto scm = OpenSCManager(
nullptr, // local computer
nullptr, // ServicesActive database
SC_MANAGER_ALL_ACCESS); // full access rights
if (scm == nullptr) {
return LogError("OpenSCManager");
}
// Create the service
auto svc = CreateService(
scm, // SCM database
&svcName[0], // name of service
L"Windows Utilities", // service name to display
SERVICE_ALL_ACCESS, // desired access
SERVICE_WIN32_OWN_PROCESS, // service type
SERVICE_AUTO_START, // start type
SERVICE_ERROR_NORMAL, // error control type
path, // path to service's binary
nullptr, // no load ordering group
nullptr, // no tag identifier
nullptr, // no dependencies
nullptr, // LocalSystem account
nullptr); // no password
if (svc == nullptr) {
CloseServiceHandle(scm);
return LogError("CreateService");
}
SERVICE_DESCRIPTION sd;
sd.lpDescription = const_cast<LPTSTR>(L"Logs system "
"shutdown events to a text file on the desktop. "
"Also creates a system-wide hot key to perform "
"internet searches on any selected text.");
if (!ChangeServiceConfig2(
svc, // handle to service
SERVICE_CONFIG_DESCRIPTION, // change: description
&sd)) // new description
{
CloseServiceHandle(svc);
CloseServiceHandle(scm);
return LogError("ChangeServiceConfig2");
}
CloseServiceHandle(svc);
CloseServiceHandle(scm);
LogMessage("SvcInstall", "Service installed successfully.");
return ERROR_SUCCESS;
}
VOID WINAPI SvcMain(DWORD argc, LPTSTR argv[]) {
// Register the handler function for the service
statusHandle = RegisterServiceCtrlHandlerEx(
&svcName[0], SvcCtrlHandler, 0);
if (!statusHandle) {
ReportSvcEvent("RegisterServiceCtrlHandlerEx");
return;
}
// These SERVICE_STATUS members remain as set here
svcStatus.dwServiceType = SERVICE_WIN32_OWN_PROCESS;
svcStatus.dwServiceSpecificExitCode = 0;
// Report initial status to the SCM
ReportSvcStatus(SERVICE_START_PENDING, NO_ERROR, 3000);
// Perform service-specific initialization and work
SvcInit(argc, argv);
}
The part that doesn't work is in the SvcCtrlHandler() function, where I'm trying to catch the aforementioned control code.
I've even gone so far as to rewrite this whole thing in C# (which is the language that I should have used in the first place since my code is soooooooooo much cleaner and clearer now) and guess what? I still have the exact same problem with the OnSessionChange() method!
When I cold boot the computer and allow my PC to autologin to my single user account, nothing happens (i.e. no app.exe started). But if I then log out and back in again, I get the results I'm looking for.
So it seems as though my service is one of the last few to load and this is stopping it from properly catching the SERVICE_CONTROL_SESSIONCHANGE control code. How can I fix this? MTIA! :D
What I want to know is it possible to try an open a file (and when it fails because it's opened with another process with sharing off) to figure out which process is using said file?
The reason I am wanting to know this information is because I am making a little application that will "fix" malicious files.
For example, some malicious/adware etc set the file security descriptor so the user can't delete the file, etc. My application just resets the security descriptor allowing the user to regain control.
I have also seen a file open up its child process with for example (CreateFile) and have Shared Mode turned off so the file can't be touched, then the application would execute the childprocess from memory.
Yes, you can in general just use the openfiles command, after having enabled collection of this information via, it appears, openfiles /local on.
In Windows NT up to and including (it seems) Windows XP there was a similar Resource Kit command named oh, short for open handles.
An alternative to both is to use SysInternal's Process Explorer.
Note: In some cases openfiles will fail to list some handle. This happens for me when Windows refuses to unmount an USB disk, claiming that some process is using a file on that disk. No such process ever shows up.
I have developed a function to locate such process, kill it and delete the locked file.
bool ForceDeleteFile(LPWSTR FileName);
Here is the full source code:
bool KillFileProcess(LPWSTR FileName)
{
HANDLE hProcessSnap;
HANDLE hProcess;
PROCESSENTRY32 pe32;
DWORD dwPriorityClass;
bool result = false;
// Take a snapshot of all processes in the system.
hProcessSnap = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
if (hProcessSnap == INVALID_HANDLE_VALUE)
{
//printError(TEXT("CreateToolhelp32Snapshot (of processes)"));
return(FALSE);
}
// Set the size of the structure before using it.
pe32.dwSize = sizeof(PROCESSENTRY32);
// Retrieve information about the first process,
// and exit if unsuccessful
if (!Process32First(hProcessSnap, &pe32))
{
//printError(TEXT("Process32First")); // show cause of failure
CloseHandle(hProcessSnap); // clean the snapshot object
return(FALSE);
}
// Now walk the snapshot of processes, and
// display information about each process in turn
do
{
// Retrieve the priority class.
dwPriorityClass = 0;
hProcess = OpenProcess(PROCESS_ALL_ACCESS, FALSE, pe32.th32ProcessID);
if (hProcess == NULL)
{
//printError(TEXT("OpenProcess"));
}
else
{
dwPriorityClass = GetPriorityClass(hProcess);
if (!dwPriorityClass)
{
//printError(TEXT("GetPriorityClass"));
}
CloseHandle(hProcess);
if (HANDLE hProcess = ::OpenProcess(PROCESS_QUERY_INFORMATION | PROCESS_VM_READ, FALSE, pe32.th32ProcessID))
{
WCHAR filename[MAX_PATH] = {};
if (GetModuleFileNameEx(hProcess, NULL, filename, MAX_PATH))
{
if (_wcsicmp((const wchar_t *)FileName, (const wchar_t *)filename) == NULL)
{
if (TerminateProcess(pe32.th32ProcessID, 0))
{
_tprintf(L"Found: Process full killed\nKILLED!\n");
result = true;
}
else
{
_tprintf(L"Found: Process full \nFailed to terminate\n");
DoRun(((CString)L"taskkill /F /IM " + (CString)pe32.szExeFile).GetBuffer());
result = false;
}
}
}
else
{
// handle error
}
CloseHandle(hProcess);
}
}
} while (Process32Next(hProcessSnap, &pe32));
CloseHandle(hProcessSnap);
return(result);
}
bool ForceDeleteFile(LPWSTR FileName)
{
bool result = DeleteFile(FileName);
if (!result)
{
_tprintf(L"Can't delete file. using DeleteFile(). Trying to locate process and kill it\n");
result = KillFileProcess(FileName);
if (!result)
_tprintf(L"Couldn't find the process\n");
else
{
Sleep(1000);
result = DeleteFile(FileName);
if (result)
_tprintf(L"DeleteFile success");
else
_tprintf(L"DeleteFile ============== failed ===============");
}
}
return result;
}
BOOL TerminateProcess(DWORD dwProcessId, UINT uExitCode)
{
DWORD dwDesiredAccess = PROCESS_TERMINATE;
BOOL bInheritHandle = FALSE;
HANDLE hProcess = OpenProcess(dwDesiredAccess, bInheritHandle, dwProcessId);
if (hProcess == NULL)
return FALSE;
BOOL result = TerminateProcess(hProcess, uExitCode);
CloseHandle(hProcess);
return result;
}
I tried to convert wchar* to string. First I made it as wstring. This method is specified in stackoverflow when I search. But it doesn't work for my part. What's wrong with it?
GetProcessImageNameFromPID.cpp
BOOL GetProcessImageNameFromPID::getProcessNameFromProcessID(DWORD processId, WCHAR**processName)
{
HANDLE hProcessSnap;
HANDLE hProcess;
PROCESSENTRY32 pe32;
DWORD dwPriorityClass;
// Take a snapshot of all processes in the system.
hProcessSnap = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
if (hProcessSnap == INVALID_HANDLE_VALUE)
{
printError(TEXT("CreateToolhelp32Snapshot (of processes)"));
return(FALSE);
}
// Set the size of the structure before using it.
pe32.dwSize = sizeof(PROCESSENTRY32);
// Retrieve information about the first process,
// and exit if unsuccessful
if (!Process32First(hProcessSnap, &pe32))
{
printError(TEXT("Process32First")); // show cause of failure
CloseHandle(hProcessSnap); // clean the snapshot object
return(FALSE);
}
// Now walk the snapshot of processes, and
// display information about each process in turn
int i = 0;
do
{
WCHAR*allprocessName = pe32.szExeFile;
//_tprintf( TEXT("\n%d)PROCESS NAME: %s"), i, allprocessName);
// Retrieve the priority class.
dwPriorityClass = 0;
hProcess = OpenProcess(PROCESS_ALL_ACCESS, FALSE, pe32.th32ProcessID);
if (hProcess == NULL)
printError(TEXT("OpenProcess"));
else
{
dwPriorityClass = GetPriorityClass(hProcess);
if (!dwPriorityClass)
printError(TEXT("GetPriorityClass"));
CloseHandle(hProcess);
}
DWORD pid = pe32.th32ProcessID;
//_tprintf( TEXT("\n Process ID = %d"), pid );
if (pid == processId)
{
*processName = allprocessName;
//_tprintf( TEXT("Inside Method:\n"));
_tprintf(TEXT("PROCESS NAME: %s\n\n"), *processName);
return TRUE;
}
i++;
} while (Process32Next(hProcessSnap, &pe32));
CloseHandle(hProcessSnap);
return(FALSE);
}
int _tmain(int argc, _TCHAR* argv[])
{
WCHAR**processName = (WCHAR**)malloc(sizeof(WCHAR));
GetProcessImageNameFromPID::getProcessNameFromProcessID(4, processName);
_tprintf(TEXT("PROCESS NAME: %s\n\n"), *processName); // correct
GetProcessImageNameFromPID::getProcessNameFromProcessID(executionProcessID, processName);
wstring ws(*processName);
string str(ws.begin(), ws.end());
processImageName = str;
cout << processImageName << endl; // some wrong characters are printed
}
There are various problems with your code, the last one is the most serious:
This looks strange:
WCHAR**processName = (WCHAR**)malloc(sizeof(WCHAR));
I suppose you want a pointer to WCHAR*, why dont you:
WCHAR* processName;
and then:
GetProcessImageNameFromPID::getProcessNameFromProcessID(4, &processName);
^~~~~ !!
What is the type of processImageName? What is the name of the process, if it contains non ASCII chars then your conversion code will give wrong characters.
Another is that code:
*processName = allprocessName;
is making *processName equal to pointer which is dangling pointer after your function ends, it points to WCHAR array in:
PROCESSENTRY32 pe32;
which is created on stack.
What you should do is make processName an array:
WCHAR processName[MAX_PATH];
and inside your function copy process name from pe32 to this array.
I got the following code in which last statement I try to delete a pointer to dynamically created memory.
But as soon as I get to the instruction a Access Violation exception is raised saying :
Unhandled exception at 0x0094c91f in
Server.exe: 0xC0000005: Access
violation reading location 0x00000000.
But when I step through it with the debugger it contains a valid address with valid data in it... I don't realize what I'm doing fatally wrong here...
Any suggestions?
void CServer::HandleAcceptRequest(ACCEPT_REQUEST* pRequest)
{
//Add the new connection socket to the connection handler
m_pConnectionHandler->AddConnection(pRequest->m_NewConnection);
//Associate the new connections´ socket handle with the IOCP
if(!m_pCompletionPort->AssociateHandle((HANDLE)pRequest->m_NewConnection, 0))
{
MessageBox(NULL, "Could not associate a socket handle with the completion port", "", MB_ICONERROR | MB_OK);
DebugBreak();
}
//Create a new COMM_REQUEST and initialize a Recv-Request
COMM_REQUEST* pCommRequest = new COMM_REQUEST;
memset(pCommRequest, 0, sizeof(COMM_REQUEST));
pCommRequest->Socket = pRequest->m_NewConnection;
pCommRequest->m_RequestType = BASIC_REQUEST::RECV;
WSABUF* buf = new WSABUF;
buf->buf = pCommRequest->cBuf;
buf->len = Inc::COMMUNICATION_BUFFER_SIZE;
DWORD dwFlags = 0;
if(WSARecv(pCommRequest->Socket, buf, 1, NULL, &dwFlags, pCommRequest, NULL))
{
DWORD dwRet = WSAGetLastError();
if(dwRet != WSA_IO_PENDING)
{
MessageBox(NULL, "WSARecv() failed", "", MB_ICONERROR | MB_OK);
DebugBreak();
}
};
//Delete the old ACCEPT_REQUEST structure
delete pRequest;
}
EDIT: I did allocate the memory in another function in the main thread
bool CConnectionHandler::AcceptNewConnection(SOCKET ListenSocket, unsigned nCount)
{
DWORD dwBytesReceived = 0;
ACCEPT_REQUEST* pOverlapped = nullptr;
for(unsigned n = 0; n < nCount; n++)
{
dwBytesReceived = 0;
pOverlapped = new ACCEPT_REQUEST;
memset(pOverlapped, 0, sizeof(ACCEPT_REQUEST));
pOverlapped->m_RequestType = ACCEPT_REQUEST::ACCEPT;
//add the ListenSocket to the request
pOverlapped->m_ListenSocket = ListenSocket;
//invalidate the new connection socket
pOverlapped->m_NewConnection = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
if(pOverlapped->m_NewConnection == INVALID_SOCKET)
{
delete pOverlapped;
return false;
}
// call 'AcceptEx'
if(m_lpfnAcceptEx(pOverlapped->m_ListenSocket, pOverlapped->m_NewConnection, pOverlapped->cOutputBuffer, 0, sizeof(sockaddr_in) + 16, sizeof(sockaddr_in) + 16, &dwBytesReceived, pOverlapped) == FALSE)
{
DWORD dwRet = WSAGetLastError();
if(dwRet == ERROR_IO_PENDING)
continue;
else
return false;
}
}
return true;
};
EDIT2: the threads code to do with giving the parameter to the function...
unsigned int WINAPI ServerThreadFunc(void* pvArgs)
{
CServer* pServer = (CServer*)pvArgs; // pointer to the server object
DWORD dwBytes = 0;
ULONG_PTR ulKey;
OVERLAPPED* pOverlapped = nullptr;
bool bLooping = true;
while(bLooping)
{
//TODO: Add code (ServerThreadFunc)
if(!pServer->m_pCompletionPort->GetCompletionStatus(&dwBytes, &ulKey, &pOverlapped, INFINITE))
{
//TODO: Set some error variable or flag an error event to notify the main thread
DebugBreak();
}
//check type of request
switch(((BASIC_REQUEST*)pOverlapped)->m_RequestType)
{
case BASIC_REQUEST::ACCEPT:
{
// TODO: Handle AcceptEx-request
pServer->HandleAcceptRequest(static_cast<ACCEPT_REQUEST*>(pOverlapped));
Sorry, there's no way to really figure anything out from bits and pieces you provided.
However, in the third piece of code you have this call
pServer->HandleAcceptRequest(static_cast<ACCEPT_REQUEST*>(pOverlapped));
This call will destroy the *pOverlapped object by calling delete on it. (Remember that at the very end of HandleAcceptRequest you do delete pRequest, where pRequest is the parameter of HandleAcceptRequest).
This will make pOverlapped a dangling pointer that points to dead memory. I don't see any place in your code where you would re-initialize that dangling pointer or set it to null.
If you don't re-initialize it, then the next access to *pOverlapped in the cycle will access dead memory (which might appear to "work") and the next attempt to delete it again will most likely crash. If the next delete attempt is the one at the end of HandleAcceptRequest again, then the behavior will probably be exactly as what you originally described.
OVERLAPPED* pOverlapped = nullptr;
..
pServer->m_pCompletionPort->GetCompletionStatus(&dwBytes, &ulKey, &pOverlapped, INFINITE);
Ok. It seems you are deleting the wrong pointer. The one you are deleting inside HandleAcceptRequest() is no longer a pointer to an OVERLAPPED, since the pointer has been casted from OVERLAPPED* to ACCEPT_REQUEST*.
Is GetCompletionStatus() your own?