Troubles when using named pipes in Windows service - c++

I'm creating service for Windows 10. I've followed this tutorial. https://www.codeproject.com/Articles/499465/Simple-Windows-Service-in-Cplusplus
But when I've added my code to this example, something happened and I can't stop service from Services list. I can stop it only from task manager. But if I comment my code, service stopping properly.
I would be grateful for any advice. Code listed below.
#include <Windows.h>
#include <tchar.h>
#include <string>
#include <fstream>
SERVICE_STATUS g_ServiceStatus = { 0 };
SERVICE_STATUS_HANDLE g_StatusHandle = NULL;
HANDLE g_ServiceStopEvent = INVALID_HANDLE_VALUE;
std::wofstream output(L"C:\\Users\\0x0\\source\\Service\\output.txt");
VOID WINAPI ServiceMain(DWORD argc, LPTSTR *argv);
VOID WINAPI ServiceCtrlHandler(DWORD);
DWORD WINAPI ServiceWorkerThread(LPVOID lpParam);
#define SERVICE_NAME _T("TestService")
DWORD WINAPI ServiceWorkerThread(LPVOID lpParam)
{
SECURITY_ATTRIBUTES sa;
sa.nLength = sizeof(SECURITY_ATTRIBUTES);
sa.lpSecurityDescriptor = NULL;
sa.bInheritHandle = TRUE;
auto dwFlags = FILE_ATTRIBUTE_NORMAL;
STARTUPINFOW si;
GetStartupInfoW(&si);
PROCESS_INFORMATION pi;
ZeroMemory(&si, sizeof(si));
si.cb = sizeof(si);
si.dwFlags = STARTF_USESHOWWINDOW;
si.wShowWindow = SW_HIDE;
ZeroMemory(&pi, sizeof(pi));
HANDLE service_pipe = CreateNamedPipe(TEXT("\\\\.\\pipe\\ServicePipe"),
PIPE_ACCESS_DUPLEX,
PIPE_TYPE_BYTE | PIPE_READMODE_BYTE | PIPE_WAIT,
128,
16 * 1024,
16 * 1024,
0,
nullptr);
if (service_pipe == INVALID_HANDLE_VALUE)
{
return 1;
}
TCHAR inbox_buffer[1024];
DWORD read, write;
while (1)
{
if (WaitForSingleObject(g_ServiceStopEvent, 0) != WAIT_OBJECT_0)
{
//If I comment this 'if block', service stopping properly. But I don't see any errors in this part of code.
if (ConnectNamedPipe(service_pipe, nullptr))
{
if (ReadFile(service_pipe, inbox_buffer, 1024 * sizeof(TCHAR), &read, nullptr))
{
std::wstring args = inbox_buffer;
std::wstring command = L"\"C:\\Program Files (x86)\\Unility\\utility.exe\" " + args;
if (!CreateProcessW(NULL, (LPWSTR)command.c_str(), NULL, NULL, TRUE, CREATE_NO_WINDOW, NULL, NULL, &si, &pi))
{
output << "CreateProcessW Error = " << GetLastError() << std::endl;
}
WaitForSingleObject(pi.hProcess, INFINITE);
CloseHandle(pi.hProcess);
CloseHandle(pi.hThread);
bool success = false;
do
{
success = WriteFile(service_pipe, L"executed", sizeof(L"executed"), &write, nullptr);
} while (!success);
}
DisconnectNamedPipe(service_pipe);
}
}
else
{
output << "WaitForSingleObject(g_ServiceStopEvent, 0)" << std::endl;
break;
}
}
output << "CloseHandle(service_pipe)_1" << std::endl;
CloseHandle(service_pipe);
output << "CloseHandle(service_pipe)_2" << std::endl;
return ERROR_SUCCESS;
}
VOID WINAPI ServiceCtrlHandler(DWORD CtrlCode)
{
switch (CtrlCode)
{
case SERVICE_CONTROL_STOP:
if (g_ServiceStatus.dwCurrentState != SERVICE_RUNNING)
break;
g_ServiceStatus.dwControlsAccepted = 0;
g_ServiceStatus.dwCurrentState = SERVICE_STOP_PENDING;
g_ServiceStatus.dwWin32ExitCode = 0;
g_ServiceStatus.dwCheckPoint = 4;
if (SetServiceStatus(g_StatusHandle, &g_ServiceStatus) == FALSE)
{
output << "TestService: ServiceCtrlHandler: SetServiceStatus returned error" << std::endl;
}
SetEvent(g_ServiceStopEvent);
output << "SetEvent(g_ServiceStopEvent)_1" << std::endl;
break;
default:
break;
}
output << "SetEvent(g_ServiceStopEvent)_2" << std::endl;
}
VOID WINAPI ServiceMain(DWORD argc, LPTSTR *argv)
{
DWORD Status = E_FAIL;
g_StatusHandle = RegisterServiceCtrlHandler(SERVICE_NAME, ServiceCtrlHandler);
if (g_StatusHandle == NULL)
{
return;
}
ZeroMemory(&g_ServiceStatus, sizeof(g_ServiceStatus));
g_ServiceStatus.dwServiceType = SERVICE_WIN32_OWN_PROCESS;
g_ServiceStatus.dwControlsAccepted = 0;
g_ServiceStatus.dwCurrentState = SERVICE_START_PENDING;
g_ServiceStatus.dwWin32ExitCode = 0;
g_ServiceStatus.dwServiceSpecificExitCode = 0;
g_ServiceStatus.dwCheckPoint = 0;
if (SetServiceStatus(g_StatusHandle, &g_ServiceStatus) == FALSE)
{
output << "TestService: ServiceMain: SetServiceStatus returned error" << std::endl;
}
g_ServiceStopEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
if (g_ServiceStopEvent == NULL)
{
g_ServiceStatus.dwControlsAccepted = 0;
g_ServiceStatus.dwCurrentState = SERVICE_STOPPED;
g_ServiceStatus.dwWin32ExitCode = GetLastError();
g_ServiceStatus.dwCheckPoint = 1;
if (SetServiceStatus(g_StatusHandle, &g_ServiceStatus) == FALSE)
{
output << "TestService: ServiceMain: SetServiceStatus returned error" << std::endl;
}
return;
}
g_ServiceStatus.dwControlsAccepted = SERVICE_ACCEPT_STOP;
g_ServiceStatus.dwCurrentState = SERVICE_RUNNING;
g_ServiceStatus.dwWin32ExitCode = 0;
g_ServiceStatus.dwCheckPoint = 0;
if (SetServiceStatus(g_StatusHandle, &g_ServiceStatus) == FALSE)
{
output << "TestService: ServiceMain: SetServiceStatus returned error" << std::endl;
}
HANDLE hThread = CreateThread(NULL, 0, ServiceWorkerThread, NULL, 0, NULL);
WaitForSingleObject(hThread, INFINITE);
output << "CloseHandle(g_ServiceStopEvent)_1" << std::endl;
CloseHandle(g_ServiceStopEvent);
output << "CloseHandle(g_ServiceStopEvent)_2" << std::endl;
g_ServiceStatus.dwControlsAccepted = 0;
g_ServiceStatus.dwCurrentState = SERVICE_STOPPED;
g_ServiceStatus.dwWin32ExitCode = 0;
g_ServiceStatus.dwCheckPoint = 3;
if (SetServiceStatus(g_StatusHandle, &g_ServiceStatus) == FALSE)
{
output << "TestService: ServiceMain: SetServiceStatus returned error" << std::endl;
}
return;
}
int _tmain(int argc, TCHAR *argv[])
{
SERVICE_TABLE_ENTRY ServiceTable[] =
{
{(LPWSTR)"TestService", (LPSERVICE_MAIN_FUNCTION)ServiceMain},
{NULL, NULL}
};
if (StartServiceCtrlDispatcher(ServiceTable) == FALSE)
{
return GetLastError();
}
return 0;
}

Dirty way, but it's working. I've created function which handles all job related to pipes. I've runned this function in new thread. When the service receives a stop signal, I send a stop message to the pipe and it stops the loop.
#include <Windows.h>
#include <tchar.h>
#include <string>
#include <thread>
#include <fstream>
SERVICE_STATUS g_ServiceStatus = { 0 };
SERVICE_STATUS_HANDLE g_StatusHandle = NULL;
HANDLE g_ServiceStopEvent = INVALID_HANDLE_VALUE;
VOID WINAPI ServiceMain(DWORD argc, LPTSTR *argv);
VOID WINAPI ServiceCtrlHandler(DWORD);
DWORD WINAPI ServiceWorkerThread(LPVOID lpParam);
#define SERVICE_NAME _T("TestService")
void pipe_server_function() {
SECURITY_ATTRIBUTES sa;
sa.nLength = sizeof(SECURITY_ATTRIBUTES);
sa.lpSecurityDescriptor = NULL;
sa.bInheritHandle = TRUE;
auto dwFlags = FILE_ATTRIBUTE_NORMAL;
STARTUPINFOW si;
GetStartupInfoW(&si);
PROCESS_INFORMATION pi;
ZeroMemory(&si, sizeof(si));
si.cb = sizeof(si);
si.dwFlags = STARTF_USESHOWWINDOW;
si.wShowWindow = SW_HIDE;
ZeroMemory(&pi, sizeof(pi));
HANDLE service_pipe = CreateNamedPipe(TEXT("\\\\.\\pipe\\ServicePipe"),
PIPE_ACCESS_DUPLEX,
PIPE_TYPE_BYTE | PIPE_READMODE_BYTE | PIPE_WAIT,
128,
16 * 1024,
16 * 1024,
0,
nullptr);
if (service_pipe == INVALID_HANDLE_VALUE)
{
return;
}
TCHAR inbox_buffer[1024];
std::fill(inbox_buffer, inbox_buffer + 1024, '\0');
DWORD read, write;
while (true)
{
if (ConnectNamedPipe(service_pipe, nullptr))
{
if (ReadFile(service_pipe, inbox_buffer, 1024 * sizeof(TCHAR), &read, nullptr))
{
std::wstring args = inbox_buffer;
if (args.find("stop_signal") != std::wstring::npos)
{
DisconnectNamedPipe(service_pipe);
break;
}
std::wstring command = L"\"C:\\Program Files (x86)\\Unility\\utility.exe\" " + args;
if (!CreateProcessW(NULL, (LPWSTR)command.c_str(), NULL, NULL, TRUE, CREATE_NO_WINDOW, NULL, NULL, &si, &pi))
{
//CreateProcessW failed. You should log this!
}
WaitForSingleObject(pi.hProcess, INFINITE);
CloseHandle(pi.hProcess);
CloseHandle(pi.hThread);
bool success = false;
do
{
success = WriteFile(service_pipe, L"executed", sizeof(L"executed"), &write, nullptr);
} while (!success);
}
DisconnectNamedPipe(service_pipe);
std::fill(inbox_buffer, inbox_buffer + 1024, '\0');
}
}
CloseHandle(service_pipe);
}
DWORD WINAPI ServiceWorkerThread(LPVOID lpParam)
{
if (WaitForSingleObject(g_ServiceStopEvent, 0) != WAIT_OBJECT_0) {
Sleep(1000);
}
service::handle gsprint_pipe = CreateFile(TEXT("\\\\.\\pipe\\ServicePipe"),
GENERIC_READ | GENERIC_WRITE,
0,
nullptr,
OPEN_EXISTING,
0,
nullptr);
bool succeess = false;
DWORD read;
do
{
succeess = WriteFile(gsprint_pipe, L"stop_signal", sizeof(L"stop_signal"), &read, nullptr);
} while (!succeess);
return ERROR_SUCCESS;
}
VOID WINAPI ServiceCtrlHandler(DWORD CtrlCode)
{
switch (CtrlCode)
{
case SERVICE_CONTROL_STOP:
if (g_ServiceStatus.dwCurrentState != SERVICE_RUNNING)
break;
g_ServiceStatus.dwControlsAccepted = 0;
g_ServiceStatus.dwCurrentState = SERVICE_STOP_PENDING;
g_ServiceStatus.dwWin32ExitCode = 0;
g_ServiceStatus.dwCheckPoint = 4;
if (SetServiceStatus(g_StatusHandle, &g_ServiceStatus) == FALSE)
{
//SetServiceStatus failed. You should log this!
}
SetEvent(g_ServiceStopEvent);
break;
default:
break;
}
}
VOID WINAPI ServiceMain(DWORD argc, LPTSTR *argv)
{
DWORD Status = E_FAIL;
g_StatusHandle = RegisterServiceCtrlHandler(SERVICE_NAME, ServiceCtrlHandler);
if (g_StatusHandle == NULL)
{
return;
}
ZeroMemory(&g_ServiceStatus, sizeof(g_ServiceStatus));
g_ServiceStatus.dwServiceType = SERVICE_WIN32_OWN_PROCESS;
g_ServiceStatus.dwControlsAccepted = 0;
g_ServiceStatus.dwCurrentState = SERVICE_START_PENDING;
g_ServiceStatus.dwWin32ExitCode = 0;
g_ServiceStatus.dwServiceSpecificExitCode = 0;
g_ServiceStatus.dwCheckPoint = 0;
if (SetServiceStatus(g_StatusHandle, &g_ServiceStatus) == FALSE)
{
//SetServiceStatus failed. You should log this!
}
g_ServiceStopEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
if (g_ServiceStopEvent == NULL)
{
g_ServiceStatus.dwControlsAccepted = 0;
g_ServiceStatus.dwCurrentState = SERVICE_STOPPED;
g_ServiceStatus.dwWin32ExitCode = GetLastError();
g_ServiceStatus.dwCheckPoint = 1;
if (SetServiceStatus(g_StatusHandle, &g_ServiceStatus) == FALSE)
{
//SetServiceStatus failed. You should log this!
}
return;
}
g_ServiceStatus.dwControlsAccepted = SERVICE_ACCEPT_STOP;
g_ServiceStatus.dwCurrentState = SERVICE_RUNNING;
g_ServiceStatus.dwWin32ExitCode = 0;
g_ServiceStatus.dwCheckPoint = 0;
if (SetServiceStatus(g_StatusHandle, &g_ServiceStatus) == FALSE)
{
//SetServiceStatus failed. You should log this!
}
std::thread pipe_server(pipe_server_function);
HANDLE hThread = CreateThread(NULL, 0, ServiceWorkerThread, NULL, 0, NULL);
WaitForSingleObject(hThread, INFINITE);
pipe_server.join();
CloseHandle(g_ServiceStopEvent);
g_ServiceStatus.dwControlsAccepted = 0;
g_ServiceStatus.dwCurrentState = SERVICE_STOPPED;
g_ServiceStatus.dwWin32ExitCode = 0;
g_ServiceStatus.dwCheckPoint = 3;
if (SetServiceStatus(g_StatusHandle, &g_ServiceStatus) == FALSE)
{
//SetServiceStatus failed. You should log this!
}
return;
}
int _tmain(int argc, TCHAR *argv[])
{
SERVICE_TABLE_ENTRY ServiceTable[] =
{
{(LPWSTR)"TestService", (LPSERVICE_MAIN_FUNCTION)ServiceMain},
{NULL, NULL}
};
if (StartServiceCtrlDispatcher(ServiceTable) == FALSE)
{
return GetLastError();
}
return 0;
}

Related

C++ on Windows: Pipe to stdin not properly transferring data + ReadFile stucks when reading from pipe

I tried to create a pwntools-like program for windows. I implemented a send and recv to send data to stdin of a and to receive data from stdout. I did that using pipes.
#include <iostream>
#include <cstdio>
#include <windows.h>
void spawn();
void pwn_send(CONST CHAR chBuf[]);
CHAR* pwn_recv(SIZE_T sz);
CHAR* pwn_recv();
HANDLE stdout_write = NULL;
HANDLE stdout_read = NULL;
HANDLE stdin_write = NULL;
HANDLE stdin_read = NULL;
int main() {
SECURITY_ATTRIBUTES saAttr;
saAttr.nLength = sizeof(SECURITY_ATTRIBUTES);
saAttr.bInheritHandle = TRUE;
saAttr.lpSecurityDescriptor = NULL;
if(!CreatePipe(&stdout_read, &stdout_write, &saAttr, 0)) return -1;
if(!SetHandleInformation(stdout_read, HANDLE_FLAG_INHERIT, 0)) return -1;
if(!CreatePipe(&stdin_read, &stdin_write, &saAttr, 0)) return -1;
if(!SetHandleInformation(stdin_write, HANDLE_FLAG_INHERIT, 0)) return -1;
spawn();
CHAR chBuf[] = "mkdir C:\\Users\\comma\\Desktop\\x\r\n";
pwn_send(chBuf);
std::cout << pwn_recv(20) << std::endl;
return 0;
}
void spawn() {
TCHAR cmd[] = TEXT("cmd.exe");
STARTUPINFO si;
PROCESS_INFORMATION pi;
ZeroMemory(&si, sizeof(si));
si.cb = sizeof(si);
si.hStdError = stdout_write;
si.hStdOutput = stdout_write;
si.hStdInput = stdin_read;
si.dwFlags |= STARTF_USESTDHANDLES;
ZeroMemory(&pi, sizeof(pi));
if(!CreateProcess(NULL, cmd, NULL, NULL, FALSE, 0, NULL, NULL, &si, &pi)) exit(-1);
return;
}
void pwn_send(CONST CHAR chBuf[]) {
DWORD dwRead = 0, dwWritten = 0;
BOOL bSuccess = FALSE;
while(1) {
bSuccess = WriteFile(stdin_write, chBuf, strlen(chBuf), &dwWritten, NULL);
if (bSuccess) break;
}
CloseHandle(stdin_write);
return;
}
CHAR* pwn_recv(SIZE_T sz) {
HANDLE hHeap = GetProcessHeap();
DWORD dwRead = 0, dwWritten = 0;
BOOL bSuccess = FALSE;
CHAR* chBuf = (CHAR*)HeapAlloc(hHeap, 0, sz+1);
while (1) {
std::cout << "XXX" << std::endl;
bSuccess = ReadFile(stdout_read, chBuf, sz, &dwRead, NULL);
std::cout << "YYY" << std::endl;
if (bSuccess) break;
}
return chBuf;
}
CHAR* pwn_recv() {
HANDLE hHeap = GetProcessHeap();
DWORD dwRead = 0, dwWritten = 0;
BOOL bSuccess = FALSE;
CHAR* chBuf = (CHAR*)HeapAlloc(hHeap, 0, 0x1000);
while (1) {
bSuccess = ReadFile(stdout_read, chBuf, 0x1000, &dwWritten, NULL);
if (bSuccess) break;
}
return chBuf;
}
So the first issue here is that the program I started with spawn(cmd.exe) doesn't properly receive the command I send to stdin. No folder is created anywhere. CreateProcess succeeds. Receiving output doesn't work either, because ReadFile seems to stuck. XXX is displayed in the console, butYYY never. Any ideas?

Win32 communicating with MariaDb

Greetings StackOverflow comrades. Last time I inquired about environment variables. Thanks to Remy for informing me.
Thanks to him I completed my Process class. Now the real problem was connecting to and communicating with MariaDb. I successfully launched MariaDb; but for some reason, reading from MariaDb deadlocks my program. I know before hand that, once connected to MariaDb using, mysql --user=root, MariaDb writes MariaDb[NONE]> to the console. And expects an SQL query input. But I my application deadlocks when trying to read.
I am wondering if MariaDb is using the handles I passed it in CreateProcess StartUpInfo. I did some search on google and found a library on MariaDb website which allows C/C++ programs to connect to MariaDb. So probably they are coercing us to use there library to connect to MariaDb.
Edit:
#Secumen I am trying to communicate with MariaDb via win32 CreateProcess; you know that popular database program? I am using the one shipped with Xampp software.
I want to be able to automate the tasks of adding tables, data, users, etc.
I created the pipes with CreatePipe(...). Then I launched MariaDb using CreateProcess(...). The second argument to CreateProcess was the command line, mysql --user=root. Note that Xampp calls MariaDb MySql. Now I am connected to MariaDb and expect it to write MariaDb[NONE]> to the console. Which means that I should have data to read via ReadFile(...). However ReadFile deadlocks and PeekNamedFile shows that there was zero bytes available to be read.
How the heck then would I communicate with MariaDb if it is not writing to the handles I passed it in CreateProcess?
Edit - Minimal Example
SECURITY_ATTRIBUTES sa = {};
sa.bInheritHandle = true;
sa.lpSecurityDescriptor =NULL;
sa.nLength = sizeof(sa);
HANDLE r,w;
HANDLE r1,w1;
if(!CreatePipe(&r,&w,&sa,0)) throw "Failed to create pipe\n";
if(!CreatePipe(&r1,&w1,&sa,0)) throw "Failed to create pipe\n";
auto cmd = "MYSQL --user=root";
auto current_dir = R"(C:\Program Files\xampp\mysql\bin)";
SetCurrentDirectoryA(current_dir);
STARTUPINFOA si = {sizeof(si)};
PROCESS_INFORMATION pi;
si.dwFlags |= STARTF_USESTDHANDLES;
si.hStdError = w;
si.hStdOutput = w;
si.hStdInput = r1;
if(!CreateProcessA(NULL,cmd,NULL,NULL,true,0,NULL,NULL,&si,&pi))
throw "Failed to create process";
CloseHandle(w);
CloseHandle(r1);
{
DWORD sz, avail;
char *buf = new char[1024];
PeekNamedPipe(r,NULL,0,NULL,&avail,NULL);
printf("available %i",avail);
ReadFile(r,buf,1023,&sz,NULL);
buf[sz] = 0;
printf("%s",buf);
delete[] buf;
}
CloseHandle(pi.hProcess);
CloseHandle(pi.hThread);
I have written the following code by referring to MSDN. I am using visual studio 2017 and test with win32 application.
I have passed several SQL statements through PIPE for testing, and confirmed that the results were exactly obtained through PIPE.
#include <string>
#include <iostream>
#include <windows.h>
using namespace std;
HANDLE hChildOutRd = NULL;
HANDLE hChildOutWr = NULL;
HANDLE hChildInRd = NULL;
HANDLE hChildInWr = NULL;
//. Internal functions.
int CreatePipes();
int CreateChildProcess();
int PipeIO(string & request, string & response);
int main()
{
if (CreatePipes() != ERROR_SUCCESS)
{
cout << "Failed to create pipe. error: " << GetLastError() << endl;
return -1;
}
//. Create the child process.
if (CreateChildProcess() != ERROR_SUCCESS)
{
cout << "Failed to create child process. error: " << GetLastError() << endl;
return -2;
}
//. Write and Read.
string request, response;
request = "use test_db; select count(*) from test_table;";
PipeIO(request, response);
cout << "[Request]: " << request << "\n[Response]: \n" << response << endl << endl;
return 0;
}
int CreatePipes()
{
SECURITY_ATTRIBUTES sa{ sizeof(SECURITY_ATTRIBUTES), NULL, TRUE };
//. Create a pipe for the child process's output.
if (!CreatePipe(&hChildOutRd, &hChildOutWr, &sa, 0))
{
return -1;
}
if (!SetHandleInformation(hChildOutRd, HANDLE_FLAG_INHERIT, 0))
{
return -2;
}
//. Create a pipe for the child process's input.
if (!CreatePipe(&hChildInRd, &hChildInWr, &sa, 0))
{
return -3;
}
if (!SetHandleInformation(hChildInWr, HANDLE_FLAG_INHERIT, 0))
{
return -4;
}
return ERROR_SUCCESS;
}
int CreateChildProcess()
{
STARTUPINFO si;
PROCESS_INFORMATION pi;
ZeroMemory(&si, sizeof(STARTUPINFO));
ZeroMemory(&pi, sizeof(PROCESS_INFORMATION));
si.cb = sizeof(STARTUPINFO);
si.hStdError = hChildOutWr;
si.hStdOutput = hChildOutWr;
si.hStdInput = hChildInRd;
si.dwFlags |= STARTF_USESTDHANDLES;
wchar_t cmd[] = L" -uroot -ppassword";
BOOL bRet = CreateProcess(L"C:\\xampp\\mysql\\bin\\mysql.exe", cmd, NULL, NULL, TRUE, 0, NULL, NULL, &si, &pi);
if (!bRet)
{
return -5;
}
else
{
CloseHandle(pi.hProcess);
CloseHandle(pi.hThread);
CloseHandle(hChildInRd);
CloseHandle(hChildOutWr);
}
return ERROR_SUCCESS;
}
int PipeIO(string & request, string & response)
{
int nRet = ERROR_SUCCESS;
DWORD dwRead = 0, dwWrite = 0;
response.clear();
if (!WriteFile(hChildInWr, request.c_str(), request.length(), &dwWrite, NULL))
{
cout << "ERROR: failed to write pipe. error: " << GetLastError() << endl;
return -1;
}
CloseHandle(hChildInWr);
while (true)
{
char buffer[1024] = { 0 };
if (!ReadFile(hChildOutRd, buffer, 1024, &dwRead, NULL) || dwRead == 0)
{
break;
}
response += buffer;
}
CloseHandle(hChildOutRd);
return ERROR_SUCCESS;
}
Then, you can do this asynchronously.
I referred to RbMm's answer at this article.
#include <malloc.h>
#include <windows.h>
#include <winternl.h>
#include <array>
#include <string>
#include <iostream>
typedef ULONG(__stdcall *RTLNTSTATUSTODOSERROR)(NTSTATUS);
RTLNTSTATUSTODOSERROR pRtlNtStatusToDosError = NULL;
struct IO_COUNT
{
HANDLE _hFile;
HANDLE _hEvent;
LONG _dwIoCount;
IO_COUNT()
{
_dwIoCount = 1;
_hEvent = 0;
}
~IO_COUNT()
{
if (_hEvent)
{
CloseHandle(_hEvent);
}
}
void BeginIo()
{
InterlockedIncrement(&_dwIoCount);
}
void EndIo()
{
if (!InterlockedDecrement(&_dwIoCount))
{
SetEvent(_hEvent);
}
}
void Wait()
{
WaitForSingleObject(_hEvent, INFINITE);
}
ULONG Create(HANDLE hFile);
};
struct U_IRP : OVERLAPPED
{
enum { read, write };
IO_COUNT* _pIoObject;
ULONG _code;
LONG _dwRef;
char _buffer[256];
void AddRef()
{
InterlockedIncrement(&_dwRef);
}
void Release()
{
if (!InterlockedDecrement(&_dwRef)) delete this;
}
U_IRP(IO_COUNT* pIoObject) : _pIoObject(pIoObject)
{
_dwRef = 1;
pIoObject->BeginIo();
RtlZeroMemory(static_cast<OVERLAPPED*>(this), sizeof(OVERLAPPED));
}
~U_IRP()
{
_pIoObject->EndIo();
}
ULONG CheckIoResult(BOOL is_ok)
{
if (is_ok)
{
OnIoComplete(NOERROR, InternalHigh);
return NOERROR;
}
ULONG dwErrorCode = GetLastError();
if (dwErrorCode != ERROR_IO_PENDING)
{
OnIoComplete(dwErrorCode, 0);
}
return dwErrorCode;
}
ULONG Read()
{
_code = read;
AddRef();
return CheckIoResult(ReadFile(_pIoObject->_hFile, _buffer, sizeof(_buffer) - 1, 0, this));
}
ULONG Write(const void* pvBuffer, ULONG cbBuffer)
{
_code = write;
AddRef();
return CheckIoResult(WriteFile(_pIoObject->_hFile, pvBuffer, cbBuffer, 0, this));
}
VOID OnIoComplete(DWORD dwErrorCode, DWORD_PTR dwNumberOfBytesTransfered)
{
switch (_code)
{
case read:
if (dwErrorCode == NOERROR)
{
if (dwNumberOfBytesTransfered)
{
_buffer[dwNumberOfBytesTransfered] = 0;
std::cout << _buffer;
}
Read();
}
break;
case write:
break;
}
Release();
}
static VOID WINAPI _OnIoComplete(DWORD dwErrorCode, DWORD_PTR dwNumberOfBytesTransfered, LPOVERLAPPED lpOverlapped)
{
static_cast<U_IRP*>(lpOverlapped)->OnIoComplete(pRtlNtStatusToDosError(dwErrorCode), dwNumberOfBytesTransfered);
}
};
ULONG IO_COUNT::Create(HANDLE hFile)
{
_hFile = hFile;
return BindIoCompletionCallback(hFile, (LPOVERLAPPED_COMPLETION_ROUTINE)U_IRP::_OnIoComplete, 0) &&
SetFileCompletionNotificationModes(hFile, FILE_SKIP_COMPLETION_PORT_ON_SUCCESS) &&
(_hEvent = CreateEvent(0, TRUE, FALSE, 0)) ? NOERROR : GetLastError();
}
int main()
{
static const WCHAR name[] = L"\\\\?\\pipe\\somename";
pRtlNtStatusToDosError = (RTLNTSTATUSTODOSERROR)GetProcAddress(GetModuleHandle(L"ntdll.dll"), "RtlNtStatusToDosError");
HANDLE hFile = CreateNamedPipeW(name, PIPE_ACCESS_DUPLEX | FILE_READ_DATA | FILE_WRITE_DATA | FILE_FLAG_OVERLAPPED, PIPE_TYPE_BYTE | PIPE_READMODE_BYTE, 1, 0, 0, 0, 0);
if (hFile == INVALID_HANDLE_VALUE)
{
return -1;
}
IO_COUNT obj;
if (obj.Create(hFile) != NOERROR)
{
CloseHandle(hFile);
return -2;
}
PROCESS_INFORMATION pi;
STARTUPINFOW si = { sizeof(si) };
SECURITY_ATTRIBUTES sa = { sizeof(sa), 0, TRUE };
si.dwFlags = STARTF_USESTDHANDLES;
si.hStdError = CreateFile(name, FILE_GENERIC_READ | FILE_GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE, &sa, OPEN_EXISTING, 0, 0);
if (si.hStdError == INVALID_HANDLE_VALUE)
{
CloseHandle(hFile);
return -3;
}
si.hStdInput = si.hStdOutput = si.hStdError;
WCHAR param[] = L" -uroot -ppassword";
if (!CreateProcess(L"C:\\xampp\\mysql\\bin\\mysql.exe", param, 0, 0, TRUE, 0, 0, 0, &si, &pi))
{
CloseHandle(hFile);
return -4;
}
//. Close unneeded handles.
CloseHandle(pi.hThread);
CloseHandle(pi.hProcess);
CloseHandle(si.hStdError);
U_IRP* p;
if (p = new U_IRP(&obj))
{
p->Read();
p->Release();
}
obj.EndIo();
std::array<std::string, 5> commands = {
"show databases;\n",
"use test_db;\n",
"select count(*) from test_table;\n",
"select * from test_table;\n",
"exit\n"
};
for (auto & iter : commands)
{
if (p = new U_IRP(&obj))
{
p->Write(iter.c_str(), iter.length());
p->Release();
}
}
obj.Wait();
CloseHandle(hFile);
DisconnectNamedPipe(hFile);
return 0;
}

Windows service does not capture shutdown event C/C++

I created a small windows service to capture some events, including the system shutdown. Every time I turn off the computer, the service event is not called. But when I restart the computer the event works normally.
I already tried to use: SERVICE_ACCEPT_PRESHUTDOWN + SERVICE_CONTROL_PRESHUTDOWN and was not successful.
How could I catch the computer shutdown event?
Ps: After compiling I register the service with the following command: sc.exe create "ZSimpleService" binPath = "C:\path\ZSimpleService.exe"
Edit 1: I'm using windows 10
Code:
SERVICE_STATUS g_ServiceStatus = { 0 };
SERVICE_STATUS_HANDLE g_StatusHandle = NULL;
HANDLE g_ServiceStopEvent = INVALID_HANDLE_VALUE;
VOID WINAPI ServiceMain(DWORD argc, LPTSTR *argv);
DWORD WINAPI ServiceWorkerThread(LPVOID lpParam);
DWORD WINAPI CtrlHandlerEx(DWORD CtrlCode, DWORD eventType, LPVOID eventData, LPVOID context);
#define SERVICE_NAME _T("ZSimpleService")
VOID WINAPI ServiceMain(DWORD argc, LPTSTR *argv) {
g_StatusHandle = RegisterServiceCtrlHandlerEx(SERVICE_NAME, CtrlHandlerEx,
NULL);
if (g_StatusHandle == NULL) {
goto EXIT;
}
// Tell the service controller we are starting
ZeroMemory( & g_ServiceStatus, sizeof(g_ServiceStatus));
g_ServiceStatus.dwServiceType = SERVICE_WIN32_OWN_PROCESS;
g_ServiceStatus.dwControlsAccepted = SERVICE_ACCEPT_STOP
| SERVICE_ACCEPT_POWEREVENT | SERVICE_ACCEPT_SESSIONCHANGE
| SERVICE_ACCEPT_SHUTDOWN;
g_ServiceStatus.dwCurrentState = SERVICE_START_PENDING;
g_ServiceStatus.dwWin32ExitCode = 0;
g_ServiceStatus.dwServiceSpecificExitCode = 0;
g_ServiceStatus.dwCheckPoint = 0;
if (SetServiceStatus(g_StatusHandle, &g_ServiceStatus) == FALSE) {
//...
}
g_ServiceStopEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
if (g_ServiceStopEvent == NULL) {
//...
goto EXIT;
}
g_ServiceStatus.dwCurrentState = SERVICE_RUNNING;
g_ServiceStatus.dwWin32ExitCode = 0;
g_ServiceStatus.dwCheckPoint = 0;
if (SetServiceStatus(g_StatusHandle, &g_ServiceStatus) == FALSE) {
//...
}
HANDLE hThread = CreateThread(NULL, 0, ServiceWorkerThread, NULL, 0, NULL);
WaitForSingleObject(hThread, INFINITE);
CloseHandle(g_ServiceStopEvent);
g_ServiceStatus.dwCurrentState = SERVICE_STOPPED;
g_ServiceStatus.dwWin32ExitCode = 0;
g_ServiceStatus.dwCheckPoint = 3;
if (SetServiceStatus(g_StatusHandle, &g_ServiceStatus) == FALSE) {
}
EXIT: return;
}
void suspending() {
FILE *file = fopen("C:\\test\\suspending.txt", "w+");
if (file)
fprintf(file, "...");
}
void logoff() {
FILE *file = fopen("C:\\test\\logoff.txt", "w+");
if (file)
fprintf(file, "...");
}
void lock_session() {
FILE *file = fopen("C:\\test\\lock_session.txt", "w+");
if (file)
fprintf(file, "...");
}
void poweroff() {
FILE *file = fopen("C:\\test\\poweroff.txt", "w+");
if (file)
fprintf(file, "...");
}
DWORD WINAPI CtrlHandlerEx(DWORD CtrlCode, DWORD eventType, LPVOID eventData,
LPVOID context) {
switch (CtrlCode) {
case SERVICE_CONTROL_STOP: {
g_ServiceStatus.dwCurrentState = SERVICE_STOP_PENDING;
g_ServiceStatus.dwWin32ExitCode = 0;
g_ServiceStatus.dwCheckPoint = 0;
g_ServiceStatus.dwWaitHint = 0;
if (SetServiceStatus(g_StatusHandle, &g_ServiceStatus) == FALSE) {
//...
}
SetEvent(g_ServiceStopEvent);
return NO_ERROR;
}
case SERVICE_CONTROL_POWEREVENT: {
if (eventType == PBT_APMQUERYSUSPEND) {
//Computer is suspending
suspending();
}
return NO_ERROR;
}
case SERVICE_CONTROL_SESSIONCHANGE: {
switch (eventType) {
case WTS_SESSION_LOGOFF:
//User is logging off
logoff();
break;
case WTS_SESSION_LOCK:
//User locks the session
lock_session();
break;
}
return NO_ERROR;
}
case SERVICE_CONTROL_SHUTDOWN: {
//Computer is shutting down
poweroff();
return NO_ERROR;
}
default:
return ERROR_CALL_NOT_IMPLEMENTED;
}
}
DWORD WINAPI ServiceWorkerThread(LPVOID lpParam) {
while (WaitForSingleObject(g_ServiceStopEvent, 0) != WAIT_OBJECT_0) {
//many cool code....
Sleep(3000);
}
return 0;
}
DWORD RunService() {
SERVICE_TABLE_ENTRY serviceTable[] = { { SERVICE_NAME, ServiceMain },
{ 0, 0 } };
if (StartServiceCtrlDispatcher(serviceTable)) {
return 0;
} else {
DWORD erro = GetLastError();
//handler erro...
return 1;
}
}
int _tmain(int argc, TCHAR *argv[]) {
RunService();
return 0;
}
When the fast startup option enabled, click start menu -> shutdown will put the machine into sleep mode/hibernation instead of shutdown. But restart menu isn't affected. (And shuting down from command line isn't affected too as we already found.)
So you can disable fast startup to solve this issue.

How to inject DLL into suspended x64 process?

I'm trying to inject my DLL into a 64-bit process that I just created. I initially create it suspended so that I can apply WinAPI patch trampolines in that process (from my injected DLL.) But if I understand it correctly, I cannot inject my DLL into a suspended process.
So I came up with the following code, following the guy's suggestions, but it doesn't go far. The VirtualProtectEx fails and I get an error code ERROR_INVALID_ADDRESS. I marked it in the source below.
Any idea where am I messing up?
PROCESS_INFORMATION pi = {0};
STARTUPINFO si = {0};
si.cb = sizeof(si);
WCHAR buffer[MAX_PATH];
::StringCchCopy(buffer, _countof(buffer), L"injected-process.exe");
if(CreateProcessW(0, buffer, 0, 0, 0, CREATE_SUSPENDED, 0, 0, &si, &pi))
{
inject_dll_into_suspended_x64_proc(pi.hProcess, pi.hThread, "injected-process.exe");
//... continue on
}
and then the code to prep process for injection:
bool inject_dll_into_suspended_x64_proc(HANDLE hProc, HANDLE hMainThread, const char* pstrProcFileName)
{
bool bRes = false;
int nOSError = NO_ERROR;
DWORD dwEntryOffset = 0;
LOADED_IMAGE li = { 0 };
if (::MapAndLoad(pstrProcFileName, NULL, &li, FALSE, TRUE))
{
dwEntryOffset = li.FileHeader->OptionalHeader.AddressOfEntryPoint;
::UnMapAndLoad(&li);
}
if(dwEntryOffset)
{
// 90 nop
// EB FE jmp self
static BYTE inject_asm_x64[] = {
0x90,
0xEB, 0xFE,
};
BYTE buffBackup[sizeof(inject_asm_x64)] = { 0 };
//Get process base addr
BYTE* pBaseAddr = (BYTE*)::VirtualAllocEx(hProc, NULL, sizeof(buffBackup), MEM_COMMIT, PAGE_EXECUTE_READWRITE);
if (pBaseAddr)
{
BYTE* pAddr = pBaseAddr + dwEntryOffset;
DWORD dwOldProtect = 0;
if (::VirtualProtectEx(hProc, pAddr, sizeof(buffBackup), PAGE_EXECUTE_READWRITE, &dwOldProtect)) //** FAILS: With error code: 487, or ERROR_INVALID_ADDRESS
{
__try
{
//Backup what we have there now
size_t szcbRead = 0;
if (::ReadProcessMemory(hProc, pAddr, buffBackup, sizeof(buffBackup), &szcbRead) &&
szcbRead == sizeof(buffBackup))
{
//Now write our code into entry point
size_t dwcbSzWrtn = 0;
if (WriteProcessMemory(hProc, pAddr, inject_asm_x64, sizeof(inject_asm_x64), &dwcbSzWrtn) &&
dwcbSzWrtn == sizeof(inject_asm_x64))
{
bool bIntermediateSuccess = false;
bool bThreadIsSuspended = true;
//Resume thread
if (ResumeThread(hMainThread) == 1)
{
bThreadIsSuspended = false;
CONTEXT context;
bool bReached = false;
//Wait for it to reach our JMP self opcode
for(;; ::Sleep(1))
{
if(!::GetThreadContext(hMainThread, &context))
{
//Failed
nOSError = ::GetLastError();
break;
}
if(context.Rip == (DWORD64)(pAddr + 1)) //First is nop, so skip it
{
//Got it
bReached = true;
break;
}
}
if(bReached)
{
//Do our DLL injection now
if(inject_dll_here(hProc))
{
//Injected OK
bIntermediateSuccess = true;
}
else
nOSError = ::GetLastError();
//Suspend main thread
if(::SuspendThread(hMainThread) == 0)
{
//Thread is again suspended
bThreadIsSuspended = true;
}
else
{
//Failed
nOSError = ::GetLastError();
bIntermediateSuccess = false;
}
}
}
else
nOSError = ::GetLastError();
if(bThreadIsSuspended)
{
//Revert process memory back
if (WriteProcessMemory(hProc, pAddr, buffBackup, sizeof(buffBackup), &dwcbSzWrtn) &&
dwcbSzWrtn == sizeof(buffBackup))
{
//Now restore the main thread
if (ResumeThread(hMainThread) == 1)
{
//Done
bRes = bIntermediateSuccess;
}
else
nOSError = ::GetLastError();
}
else
nOSError = ::GetLastError();
}
}
else
nOSError = ::GetLastError();
}
else
nOSError = ::GetLastError();
}
__finally
{
//Reset protection flags
::VirtualProtectEx(hProc, pAddr, sizeof(buffBackup), dwOldProtect, NULL);
}
}
else
nOSError = ::GetLastError();
//Free mem
::VirtualFreeEx(hProc, pBaseAddr, 0, MEM_RELEASE);
}
else
nOSError = ::GetLastError();
}
else
nOSError = ::GetLastError();
return bRes;
}

C++ Make service do something

I made a service using a tutorial. How can I make it actually do something? Here's the code:
#include <windows.h>
SERVICE_STATUS serviceStatus = { 0 };
SERVICE_STATUS_HANDLE statusHandle;
HANDLE serviceStopEvent = INVALID_HANDLE_VALUE;
void WINAPI ServiceMain(DWORD argc, LPTSTR *argv);
void WINAPI ServiceCtrlHandler(DWORD);
DWORD WINAPI ServiceWorkerThread(LPVOID lParam);
#define SERVICE_NAME "Cool Service"
int main(int argc, char **argv)
{
SERVICE_TABLE_ENTRY serviceTable[] =
{
{SERVICE_NAME, (LPSERVICE_MAIN_FUNCTION)ServiceMain},
{NULL, NULL}
};
if (!StartServiceCtrlDispatcher(serviceTable))
return GetLastError();
return 0;
}
void WINAPI ServiceMain(DWORD argc, LPTSTR *argv)
{
DWORD status = E_FAIL;
statusHandle = RegisterServiceCtrlHandler(SERVICE_NAME, ServiceCtrlHandler);
if (statusHandle == NULL)
goto EXIT;
ZeroMemory(&serviceStatus, sizeof(serviceStatus));
serviceStatus.dwServiceType = SERVICE_WIN32_OWN_PROCESS;
serviceStatus.dwControlsAccepted = 0;
serviceStatus.dwCurrentState = SERVICE_START_PENDING;
serviceStatus.dwWin32ExitCode = 0;
serviceStatus.dwServiceSpecificExitCode = 0;
serviceStatus.dwCheckPoint = 0;
if (!SetServiceStatus(statusHandle, &serviceStatus))
OutputDebugString("Cool Service: Service Main: SetServiceStatus returned error!");
serviceStopEvent = CreateEvent(NULL, true, false, NULL);
if (serviceStopEvent == NULL)
{
serviceStatus.dwControlsAccepted = 0;
serviceStatus.dwCurrentState = SERVICE_STOPPED;
serviceStatus.dwWin32ExitCode = GetLastError();
serviceStatus.dwCheckPoint = 1;
if (!SetServiceStatus(statusHandle, &serviceStatus))
OutputDebugString("Cool Service: Service Main: SetServiceStatus returned error!");
goto EXIT;
}
serviceStatus.dwControlsAccepted = SERVICE_ACCEPT_STOP;
serviceStatus.dwCurrentState = SERVICE_RUNNING;
serviceStatus.dwWin32ExitCode = 0;
serviceStatus.dwCheckPoint = 0;
if (!SetServiceStatus(statusHandle, &serviceStatus))
OutputDebugString("Cool Service: Service Main: SetServiceStatus returned error!");
HANDLE thread = CreateThread(NULL, 0, ServiceWorkerThread, NULL, 0, NULL);
WaitForSingleObject(thread, INFINITE);
CloseHandle(serviceStopEvent);
serviceStatus.dwControlsAccepted = 0;
serviceStatus.dwCurrentState = SERVICE_STOPPED;
serviceStatus.dwWin32ExitCode = 0;
serviceStatus.dwCheckPoint = 3;
if (!SetServiceStatus(statusHandle, &serviceStatus))
OutputDebugString("Cool Service: Service Main: SetServiceStatus returned error!");
EXIT:
return;
}
void WINAPI ServiceCtrlHandler(DWORD ctrlCode)
{
switch (ctrlCode)
{
case SERVICE_CONTROL_STOP:
{
if (serviceStatus.dwCurrentState != SERVICE_RUNNING)
break;
serviceStatus.dwControlsAccepted = 0;
serviceStatus.dwCurrentState = SERVICE_STOP_PENDING;
serviceStatus.dwWin32ExitCode = 0;
serviceStatus.dwCheckPoint = 4;
if (!SetServiceStatus(statusHandle, &serviceStatus))
OutputDebugString("Cool Service: Service Handler: SetServiceStatus returned error!");
SetEvent(serviceStopEvent);
break;
}
default: break;
}
}
void Function()
{
Beep(7000, 1000);
}
DWORD WINAPI ServiceWorkerThread(LPVOID lParam)
{
while (WaitForSingleObject(serviceStopEvent, 0) != WAIT_OBJECT_0)
{
Function();
Sleep(3000);
}
return ERROR_SUCCESS;
}
The service does nothing after I install and start it in "services.msc", it should beep, right? Or my thinking is off the way here. Thanks for help :)