I am making a tester program that opens a console application and reads it's standard input writes to it's standard output, but am having issues with the pipes. I am using named pipes since I might have to run this threaded or even open multiple executables to communicate with at once. These will need to remain running and continuously take input and give out put, as in a console calculator that asks if you if you want another calculation or to exit after each calculation.
Using error checking I have found that The pipes are created successfully, I apply them to the startupInfo struct and successfully open the executable. A note here, if I set a break point in visual studio just after the call to createProcess, the child process does show up in my task manager, the check STILL_ACTIVE is true and peaking at the pipe reveals an empty pipe. If no break point is set then I don't see it, and the check STILL_ACTIVE is false.
To simplify the problem I went back to basics, a simple hello world executable in c++. The calculator will be the next test. This prints hello world to the console and via a cin:get() waits for the enter key to be pressed. I ran this with the tester and tried to read the "Hello World" from the child process. I get nothing.
The end project will be open source, I don't want the user to have to download any other libraries to compile the project, and Boost::Process actually requires 2 install since process is not standard yet.
I know that I am close, here is my simple tester as one file with the process class extracted to be inline in the main. Note: I have enabled c++20 in my compiler.
// Tester.cpp
#include <string>
#include <string_view>
#include <vector>
#include <iostream>
#include <fstream>
#include <filesystem>
#include <io.h>
#include <fcntl.h>
#include <windows.h>
int main()
{
std::string data = "";
int id = 1;
std::string executable = "HelloWorld.exe";
if (_access((executable).c_str(), 0) != -1)
{
std::cerr << "Error: Executable file not found: " << executable << std::endl;
exit(0);
}
SECURITY_ATTRIBUTES saAttr{};
saAttr.nLength = sizeof(SECURITY_ATTRIBUTES);
saAttr.bInheritHandle = TRUE;
saAttr.lpSecurityDescriptor = NULL;
//Pipe names
std::wstring pipeErr = L"\\\\.\\pipe\\err_" + std::to_wstring(id);
std::wstring pipeOut = L"\\\\.\\pipe\\out_" + std::to_wstring(id);
std::wstring pipeIn = L"\\\\.\\pipe\\in_" + std::to_wstring(id);
// The Child error pipe for reading
CreateNamedPipeW(pipeErr.c_str(), PIPE_ACCESS_DUPLEX | FILE_FLAG_OVERLAPPED, PIPE_TYPE_MESSAGE | PIPE_READMODE_MESSAGE | PIPE_WAIT, 1, 1024, 1024, 0, NULL);
HANDLE err_pipe = CreateFileW(pipeErr.c_str(), GENERIC_READ | GENERIC_WRITE, 0, &saAttr, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL | FILE_FLAG_OVERLAPPED, NULL);
// The Child out pipe for reading
CreateNamedPipeW(pipeOut.c_str(), PIPE_ACCESS_DUPLEX | FILE_FLAG_OVERLAPPED, PIPE_TYPE_MESSAGE | PIPE_READMODE_MESSAGE | PIPE_WAIT, 1, 1024, 1024, 0, NULL);
HANDLE out_pipe = CreateFileW(pipeOut.c_str(), GENERIC_READ | GENERIC_WRITE, 0, &saAttr, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL | FILE_FLAG_OVERLAPPED, NULL);
// The Child in pipe for writing
CreateNamedPipeW(pipeIn.c_str(), PIPE_ACCESS_DUPLEX | FILE_FLAG_OVERLAPPED, PIPE_TYPE_BYTE | PIPE_READMODE_BYTE | PIPE_WAIT, 1, 1024, 1024, 0, NULL);
HANDLE in_pipe = CreateFileW(pipeIn.c_str(), GENERIC_READ | GENERIC_WRITE, 0, &saAttr, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL | FILE_FLAG_OVERLAPPED, NULL);
if (in_pipe == INVALID_HANDLE_VALUE || out_pipe == INVALID_HANDLE_VALUE || err_pipe == INVALID_HANDLE_VALUE)
{
std::cout << "Error Creating Handles, Code: " << GetLastError() << std::endl;
return 0;
}
// Make sure the handles' inheritance is set correctly
if (!SetHandleInformation(in_pipe, HANDLE_FLAG_INHERIT, HANDLE_FLAG_INHERIT) ||
!SetHandleInformation(out_pipe, HANDLE_FLAG_INHERIT, HANDLE_FLAG_INHERIT) ||
!SetHandleInformation(err_pipe, HANDLE_FLAG_INHERIT, HANDLE_FLAG_INHERIT))
{
std::cerr << "Error: Failed to set handle information for the child process" << std::endl;
return 0;
}
// Set up the startup info struct
STARTUPINFOA startupInfo;
memset(&startupInfo, 0, sizeof(startupInfo));
startupInfo.cb = sizeof(STARTUPINFOA);
startupInfo.hStdInput = in_pipe;
startupInfo.hStdOutput = out_pipe;
startupInfo.hStdError = err_pipe;
startupInfo.dwFlags |= STARTF_USESTDHANDLES;
// Set up the process info struct
PROCESS_INFORMATION processInfo;
memset(&processInfo, 0, sizeof(processInfo));
// Create the child process
if (CreateProcessA(NULL, executable.data(), NULL, NULL, TRUE, 0, NULL, NULL, &startupInfo, &processInfo) == 0)
{
std::cerr << "Error: Failed to create the child process" << std::endl;
return 0;
}
// Set the pipes to non-blocking mode
DWORD mode = PIPE_NOWAIT;
SetNamedPipeHandleState(out_pipe, &mode, NULL, NULL);
SetNamedPipeHandleState(err_pipe, &mode, NULL, NULL);
SetNamedPipeHandleState(in_pipe, &mode, NULL, NULL);
Sleep(500); //wait for child to start, may not be neccesary
// Get the exit code of the child process
DWORD exitCode;
GetExitCodeProcess(processInfo.hProcess, &exitCode);
if (exitCode == STILL_ACTIVE) {
// Set up the read buffer
char buffer[1024];
memset(buffer, 0, sizeof(buffer));
DWORD bytesRead = 0;
DWORD bytesAvail = 0;
// Check if there is data available to read from the pipe
if (!PeekNamedPipe(out_pipe, buffer, sizeof(buffer), &bytesRead, &bytesAvail, NULL)) {
std::cerr << "PeekNamedPipe failed (" << GetLastError() << ").\n";
return 0;
}
if (bytesAvail == 0)
{
std::cerr << "Pipe is empty" << std::endl;
}
if (!ReadFile(out_pipe, buffer, sizeof(buffer) - 1, &bytesRead, NULL))
{
std::cerr << "Failed to read from pipe. Error code: " << GetLastError() << std::endl;
return 0;
}
data = buffer;
}
if (data == "") {
std::cout << "Something went wrong. Code: " << GetLastError() << std::endl;
}
else {
std::cout << data << std::endl;
}
std::cout << "Press any key." << std::endl;
std::cin.get();
return 0;
}
and, for reference, here is helloworld.exe:
// HelloWorld.cpp
#include <iostream>
int main()
{
std::cout << "Hello World!" << std::endl;
std::cin.get();
}
Thanks to #Igor Tandetnik!
Here is the working Tester.cpp:
// Tester.cpp
#include <string>
#include <string_view>
#include <vector>
#include <iostream>
#include <fstream>
#include <filesystem>
#include <io.h>
#include <fcntl.h>
#include <windows.h>
#include <aclapi.h>
constexpr auto BUFSIZE = 4096;
int main()
{
std::string data = "";
int id = 1;
std::wstring executable = L"HelloWorld.exe";
std::wstring argv = L"";
std::string name_c = "";
std::string path_c = "";
HANDLE hChildStd_IN_Rd = NULL;
HANDLE hChildStd_IN_Wr = NULL;
HANDLE hChildStd_OUT_Rd = NULL;
HANDLE hChildStd_OUT_Wr = NULL;
HANDLE hChildStd_ERR_Rd = NULL;
HANDLE hChildStd_ERR_Wr = NULL;
size_t size;
wcstombs_s(&size, nullptr, 0, executable.c_str(), executable.length());
name_c.resize(size);
wcstombs_s(&size, name_c.data(), name_c.size(), executable.c_str(), executable.length());
wchar_t current_dir[FILENAME_MAX];
if (_wgetcwd(current_dir, FILENAME_MAX) == nullptr) {
std::cerr << "Error getting current working directory. Code:" << GetLastError() << std::endl;
exit(0);
}
wchar_t path_exe[MAX_PATH];
GetModuleFileName(NULL, path_exe, MAX_PATH);
std::wstring path = path_exe;
path = std::filesystem::path(path).parent_path();
path += L"\\";
path += executable;
wcstombs_s(&size, nullptr, 0, path.c_str(), path.length());
path_c.resize(size);
wcstombs_s(&size, path_c.data(), path_c.size(), path.c_str(), path.length());
int found = _waccess_s(path.c_str(), 0);
if (found != 0)
{
std::cerr << "Error: Executable file not found: " << name_c << std::endl;
exit(0);
}
SECURITY_ATTRIBUTES sa_attr{};
sa_attr.nLength = sizeof(SECURITY_ATTRIBUTES);
sa_attr.bInheritHandle = TRUE;
sa_attr.lpSecurityDescriptor = NULL;
// Create the pipes
if (!CreatePipe(&hChildStd_OUT_Rd, &hChildStd_OUT_Wr, &sa_attr, 0)
|| !CreatePipe(&hChildStd_IN_Rd, &hChildStd_IN_Wr, &sa_attr, 0)
|| !CreatePipe(&hChildStd_ERR_Rd, &hChildStd_ERR_Wr, &sa_attr, 0)) {
std::cout << "Error Creating Pipes, Code: " << GetLastError() << std::endl;
return 1;
}
if (hChildStd_OUT_Rd == INVALID_HANDLE_VALUE || hChildStd_OUT_Wr == INVALID_HANDLE_VALUE
|| hChildStd_IN_Rd == INVALID_HANDLE_VALUE || hChildStd_IN_Wr == INVALID_HANDLE_VALUE
|| hChildStd_ERR_Rd == INVALID_HANDLE_VALUE || hChildStd_ERR_Wr == INVALID_HANDLE_VALUE)
{
std::cout << "Error Creating Handles, Code: " << GetLastError() << std::endl;
return 1;
}
// Set up the startup info struct
STARTUPINFOW startup_info;
ZeroMemory(&startup_info, sizeof(STARTUPINFOW));
startup_info.cb = sizeof(STARTUPINFOW);
startup_info.hStdOutput = hChildStd_OUT_Wr;
startup_info.hStdError = hChildStd_ERR_Wr;
startup_info.hStdInput = hChildStd_IN_Rd;
startup_info.dwFlags |= STARTF_USESTDHANDLES;
// Set up the process info struct
PROCESS_INFORMATION process_info;
memset(&process_info, 0, sizeof(process_info));
// Create the child process
if (!CreateProcess(path.data(), NULL, &sa_attr, NULL, TRUE, 0, NULL, NULL, &startup_info, &process_info))
{
std::cerr << "Error: Failed to create the child process. Code: " << GetLastError() << std::endl;
return 1;
}
// Get the exit code of the child process
DWORD exitCode;
GetExitCodeProcess(process_info.hProcess, &exitCode);
if (exitCode != STILL_ACTIVE) {
std::wcout << "Unable to Start Process: " << executable.c_str() << std::endl;
return 1;
}
std::wcout << "Started Process: " << executable.c_str() << std::endl;
Sleep(500); //wait for child to start, may not be neccesary
// Get the exit code of the child process
GetExitCodeProcess(process_info.hProcess, &exitCode);
if (exitCode == STILL_ACTIVE) {
// Set up the read buffer
DWORD bytesRead{}, dwWritten{};
CHAR buffer[BUFSIZE]{};
BOOL bSuccess = FALSE;
HANDLE hParentStdOut = GetStdHandle(STD_OUTPUT_HANDLE);
bSuccess = ReadFile(hChildStd_OUT_Rd, buffer, BUFSIZE, &bytesRead, NULL);
if (!bSuccess || bytesRead == 0)
{
std::cerr << "Failed to read from pipe. Error code: " << GetLastError() << std::endl;
return 1;
}
std::vector<char> v_data(buffer, buffer + bytesRead);
data = std::string(v_data.data(), v_data.size());
}
std::cout << "Recieved from Child: " << data << std::endl;
if (data == "") {
std::cout << "Something went wrong. Code: " << GetLastError() << std::endl;
return 1;
}
else {
std::cout << data << std::endl;
}
CloseHandle(process_info.hThread);
std::cout << "Press any key." << std::endl;
std::cin.get();
return 0;
}
I'm trying to get the process dump. But when I use flag MiniDumpWithFullMemory the function crashes.
Maybe I use DuplicateHandle wrong... I don't know.
Is MiniDumpWithFullMemory is necessary flag to obtain general information or I can use only other flags (like in code listing below) ??
I need to get the dump in order to send it to VirusTotal...
Here is the code:
#include <iostream>
#include <windows.h>
using namespace std;
#pragma comment(lib, "dbghelp.lib")
#include <dbghelp.h>
#include <stdio.h>
void WriteFullDump(wchar_t* path_to_dump, HANDLE hProc)
{
const DWORD Flags = MiniDumpWithHandleData|
MiniDumpWithHandleData|
MiniDumpWithDataSegs|
MiniDumpScanMemory |
MiniDumpFilterMemory|
MiniDumpNormal|
MiniDumpWithUnloadedModules|
MiniDumpWithThreadInfo;
HANDLE hFile = CreateFile(path_to_dump, GENERIC_WRITE, 0, nullptr,CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, nullptr);
BOOL Result = MiniDumpWriteDump(hProc,
GetProcessId(hProc),
hFile,
(MINIDUMP_TYPE)Flags,
nullptr,
nullptr,
nullptr);
CloseHandle(hFile);
if (!Result)
{
cout << Result << endl;
std::cerr << "Looks like an error: MiniDumpWriteDump failed" << endl;
cout << GetLastError() << endl;
}
CloseHandle(hFile);
return;
}
int main()
{
wchar_t* path = L"C:\\C++\\122332.txt";
int pid;
cin >> pid;
HANDLE proc = OpenProcess(PROCESS_DUP_HANDLE, 1, pid);
HANDLE pseudoproc;
HANDLE my = GetCurrentProcess();
DuplicateHandle(proc, proc, my, &pseudoproc, PROCESS_QUERY_INFORMATION, 1, DUPLICATE_SAME_ACCESS);
WriteFullDump(path,proc);
CloseHandle(proc);
cout << "Finished!" << endl;
char g;
cin >> g;
return 0;
}
I'm trying to recast a char* to a LPWSTR to use in the CreateProcess API-function. However, I can't get it to work properly. (Compiler is not complaining in the end, however CreateProcess keeps telling me it can't find the path specified (error-code)). This makes me assume that recasting the char* into a LPWSTR fails and therefore, the path is not accessible. (I also tried it with functions such as mbstowcs, but without any luck so far.
My sample-code:
int StartCommand(char* Command) {
STARTUPINFO si;
PROCESS_INFORMATION pi;
ZeroMemory(&si, sizeof(si));
si.cb = sizeof(si);
ZeroMemory(&pi, sizeof(pi));
cout << Command << endl; //Shows the right command
wchar_t wtext[20];
mbstowcs(wtext, Command, strlen(Command) + 1);//Plus null
LPWSTR CommandLPWSTR = wtext;
if (!CreateProcess(NULL, CommandLPWSTR, NULL, NULL, FALSE, CREATE_NO_WINDOW | CREATE_SUSPENDED | CREATE_BREAKAWAY_FROM_JOB, NULL, NULL, &si, &pi)) {
cout << GetLastError() << endl;
cout << GetLastErrorAsString() << endl; //TELLS ME: Can't find the path specified
Message = "Couldn't start command!";
Exit(Message);
}
AssignProcessToJobObject(Handle, pi.hProcess); // Does not work if without CREATE_BREAKAWAY_FROM_JOB
ResumeThread(pi.hThread);
CloseHandle(pi.hProcess);
CloseHandle(pi.hThread);
return pi.dwProcessId;
}
Pid = StartCommand((char*)("C:/notepad.exe").c_str());
for (int i = 0; i < n; i++)
{
const char* cstr = strings[i].c_str();
swprintf_s(fullCommandLine, L"\"%s\" \"%s\" %S", pathToModule, pathToFile, cstr);
if(CreateProcess(NULL,
(LPWSTR)fullCommandLine,
NULL,
NULL,
FALSE,
0,
NULL,
NULL,
&si,
&pi))
{
cout << "succes";
}
else cout << "fail";
}
I'm creating n procesess to find string in given file like this, and In my module(wchich looks for given string in file) I want to send messages to other n-1 processes to quit
while (file >> readout)
{
if (readout == search)
{
cout << "I found string";
SendMessage(/*what should be here*/);
}
}
From where I could get handles to those other processes?
Please see my PostThreadMessage to Console Application.
I created that because it certainly is possible to send a message to a console program, we just must make a message loop, just as it is possible to show a window from a console program.
Note that PostThreadMessage needs a thread id, not a process id. Every process also has a thread id and a process's thread id is in the PROCESS_INFORMATION from CreateProcess.
The following is a larger example but easier to use for demonstrating that PostThreadMessage works in console programs. This program will call itself (passing its thread id) if there is no argument for it then it will wait for the new process to send messages. If there is an argument then it will assume the argument is a thread id and send a message to that thread followed by a WM_QUIT.
#include "stdafx.h"
int _tmain(int argc, _TCHAR* argv[])
{
TCHAR szCmdline[300];
PROCESS_INFORMATION piProcInfo;
STARTUPINFO siStartInfo;
BOOL bSuccess = FALSE;
ZeroMemory(&piProcInfo, sizeof(PROCESS_INFORMATION));
ZeroMemory(&siStartInfo, sizeof(STARTUPINFO));
siStartInfo.cb = sizeof(STARTUPINFO);
siStartInfo.hStdError = NULL;
siStartInfo.hStdOutput = NULL;
siStartInfo.hStdInput = NULL;
DWORD dwThread;
MSG Msg;
TCHAR ThreadIdBuffer[40];
// if no argument then execute ourself then wait for a message from that thread
if (argc == 1) {
_itot_s(GetCurrentThreadId(), ThreadIdBuffer, 40, 10);
szCmdline[0] = '"';
szCmdline[1] = 0;
_tcscat_s(szCmdline, 300, argv[0]); // ourself
int n = _tcslen(szCmdline);
szCmdline[n++] = '"';
szCmdline[n++] = ' ';
szCmdline[n++] = 0;
_tcscat_s(szCmdline, 300, ThreadIdBuffer); // our thread id
bSuccess = CreateProcess(argv[0], // execute ourself
szCmdline, // command line
NULL, // process security attributes
NULL, // primary thread security attributes
TRUE, // handles are inherited
0, // creation flags
NULL, // use parent's environment
NULL, // use parent's current directory
&siStartInfo, // STARTUPINFO pointer
&piProcInfo); // receives PROCESS_INFORMATION
if (!bSuccess) {
std::cout << "Process not started\n";
return 0;
}
std::cout << "Waiting\n";
// Now wait for the other process to send us a message
while (GetMessage(&Msg, NULL, 0, WM_USER)) {
if (Msg.message == WM_COMMAND)
std::cout << "WM_COMMAND\n";
else
std::cout << "Message: " << Msg.message << '\n';
}
std::cout << "End of message loop\n";
return 0;
}
// if there is an argument then assume it is a threadid of another one of us
std::cout << "Press Enter to send the message\n";
if (std::wcin.get() != '\n')
return 0;
dwThread = _wtoi(argv[1]);
if (!PostThreadMessage(dwThread, WM_COMMAND, (WPARAM)0, (LPARAM)0))
std::cout << GetLastError() << " PostThreadMessage error\n";
if (!PostThreadMessage(dwThread, WM_QUIT, (WPARAM)0, (LPARAM)0))
std::cout << GetLastError() << " PostThreadMessage error\n";
return 0;
}
I have recently been learning C++ and I am at the stage where I can build sufficient enough programs.
At the moment I am just trying to inject ONE DLL into a process of my choice, I am having trouble because when it injects it works fine, but when I don't have the DLL it still acts as if it injected.
Help please.
My Source:
#include <iostream>
#include <Windows.h>
#include <TlHelp32.h>
using namespace std;
bool InjectDLL(DWORD ProcessID);
char FileToInject[] = "DLL.dll";
char ProcessName[] = "ac_client.exe";
typedef HINSTANCE (*fpLoadLibrary)(char*);
int main()
{
DWORD processId = NULL;
PROCESSENTRY32 pe32 = {sizeof(PROCESSENTRY32)};
HANDLE hProcSnap;
while(!processId)
{
system("CLS");
cout << "|Coded by Proton|" << endl;
cout << "Waiting for AssaultCube ..." << endl;
hProcSnap = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
if(Process32First(hProcSnap, &pe32))
{
do
{
if(!strcmp(pe32.szExeFile, ProcessName))
{
processId = pe32.th32ProcessID;
break;
}
}
while(Process32Next(hProcSnap, &pe32));
}
Sleep(1000);
}
while(!InjectDLL(processId))
{
system("CLS");
cout << "DLL failed to inject." << endl;
Sleep(1000);
}
cout << "DLL Injected." << endl << endl;
cout << "Closing Injector in 5 seconds ..." << endl;
CloseHandle(hProcSnap);
Sleep(5000);
return 0;
}
bool InjectDLL(DWORD ProcessID)
{
HANDLE hProc;
LPVOID paramAddr;
HINSTANCE hDll = LoadLibrary("KERNEL32");
fpLoadLibrary LoadLibraryAddr = (fpLoadLibrary)GetProcAddress(hDll, "LoadLibraryA");
hProc = OpenProcess (PROCESS_ALL_ACCESS, false, ProcessID);
char dllPath[250] = "C:\\";
strcat(dllPath, FileToInject);
paramAddr = VirtualAllocEx(hProc, 0, strlen(dllPath)+1, MEM_COMMIT, PAGE_READWRITE);
bool memoryWritten = WriteProcessMemory(hProc, paramAddr, dllPath, strlen(dllPath)+1, NULL);
CreateRemoteThread(hProc, 0, 0, (LPTHREAD_START_ROUTINE)LoadLibraryAddr, paramAddr, 0, 0);
CloseHandle(hProc);
return memoryWritten;
}
Thanks so much!
Don't ignore the return value from CreateRemoteThread.
Use the thread handle to check if it stays running (WaitForSingleObject with a timeout), and if it exited, check its return code.