[C++] Using IPC via Named Pipes - An Example Client/Server Setup menu

User Tag List

Results 1 to 3 of 3
  1. #1
    GlittPrizes's Avatar Active Member CoreCoins Purchaser
    Reputation
    35
    Join Date
    Nov 2019
    Posts
    88
    Thanks G/R
    46/24
    Trade Feedback
    0 (0%)
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)

    [C++] Using IPC via Named Pipes - An Example Client/Server Setup

    Credits to this blog (Introduction to Win32 Named Pipes (C++))

    Named Pipes has been mentioned here before - this is mostly just a little touch-up on the above code and an explanation of what that type of IPC can be used for. With DLL injection, it can be an involved process to communicate to an application outside the DLL. IPC via Windows Named Pipes can create a talking bridge between the DLL and its injector or host app. With the code below in a real world scenario, you would likely create a thread from DllMain to create the client after the host or injector app has created the server. You can then safely communicate between the processes without access violations. Run the server first, and it should work nicely.

    I decided to go this route despite the extra parsing involved because communicating to my Direct X hook was getting overly complicated when trying to call in-game functions remotely. This isn't the best of code manners, it's to demonstrate the concept.

    Server.cpp
    Code:
    #include  <iostream>
    #include  <string>
    #include  <windows.h>
    #include  <tchar.h>
    
    using namespace std;
    
    HANDLE CreatePipe()
    {
    	cout << "Creating an instance of a named pipe..." << endl;
    
    	return CreateNamedPipe(
    		L"\\\\.\\pipe\\myPipeName", // name of the pipe
    		PIPE_ACCESS_OUTBOUND, // 1-way pipe -- send only
    		PIPE_TYPE_BYTE, // send data as a byte stream
    		1, // only allow 1 instance of this pipe
    		0, // no outbound buffer
    		0, // no inbound buffer
    		0, // use default wait time
    		nullptr // use default security attributes
    	);
    }
    
    void WaitForClient(HANDLE pipe)
    {
    	cout << "Waiting for a client to connect to the pipe..." << endl;
    
    	const bool result = ConnectNamedPipe(pipe, nullptr);
    	if (!result) {
    		cout << "Failed to make connection on named pipe." << endl;
    		// look up error code here using GetLastError()
    		CloseHandle(pipe); // close the pipe
    		system("pause");
    	}
    	cout << "Connected to client. Awaiting input..." << endl;
    }
    
    bool SendData(HANDLE pipe, const char* message)
    {
    	cout << "Sending data to pipe..." << endl;
    
    	// This call blocks until a client process reads all the data
    	const auto* data = message;
    
    	DWORD numBytesWritten = 0;
    
    	const auto result = WriteFile(
    		pipe, // handle to our outbound pipe
    		data, // data to send
    		strlen(data) * sizeof(char*), // length of data to send (bytes)
    		&numBytesWritten, // will store actual amount of data sent
    		nullptr // not using overlapped IO
    	);
    
    	if (result) 
    		cout << "Number of bytes sent: " << numBytesWritten << endl;
    	else
    		cout << "Failed to send data." << endl;
    	
    	return result;
    }
    
    void GetInput(HANDLE pipe)
    {
    	string input;
    	do
    	{
    		getline(cin, input);
    		if (!input.empty() && input != "exit")
    			SendData(pipe, input.c_str());
    
    	} while (input != "exit");
    }
    
    int main()
    {
    	auto* const pipe = CreatePipe();
    
    	WaitForClient(pipe);
    
    	GetInput(pipe);
    
    	CloseHandle(pipe);
    
    	return 0;
    }
    Client.cpp
    Code:
    #include  <iostream>
    #include  <tchar.h>
    #include  <windows.h>
    
    using namespace std;
    
    HANDLE CreatePipe()
    {
        cout << "Connecting to pipe..." << endl;
    
        // Open the named pipe
        // Most of these parameters aren't very relevant for pipes.
        return CreateFile(
            L"\\\\.\\pipe\\myPipeName",
            GENERIC_READ, // only need read access
            FILE_SHARE_READ | FILE_SHARE_WRITE,
            nullptr,
            OPEN_EXISTING,
            FILE_ATTRIBUTE_NORMAL,
            nullptr
        );
    }
    
    const char* ReadData(HANDLE pipe)
    {
    	cout << "Reading data from pipe..." << endl;
    
    	// The read operation will block until there is data to read
    	char buffer[1024];
    	
    	DWORD numBytesRead = 0;
    	
    	const auto result = ReadFile(
    		pipe,
    		buffer, // the data from the pipe will be put here
    		127 * sizeof(char*), // number of bytes allocated
    		&numBytesRead, // this will store number of bytes actually read
    		nullptr // not using overlapped IO
    	);
    
    	if (result) {
    		buffer[numBytesRead / sizeof(char*)] = '\0'; // null terminate the string
    		cout << "Number of bytes read: " << numBytesRead << endl;
    		cout << "Message: " << buffer << endl;
            return buffer;
    	}
    	else {
    		cout << "Communication terminated." << endl;
    	}
        return "=+Failed+=";
    }
    
    void ReadInput(void* const pipe)
    {
    	while(true)
    	{
    		const auto* message = ReadData(pipe);
    		if (strcmp(message, "=+Failed+=") == 0)
    			break;
    	}
    }
    
    int main()
    {
        auto* const pipe = CreatePipe();
    
        ReadInput(pipe);
    
        CloseHandle(pipe);
    
        system("pause");
        return 0;
    }
    Pipes.PNG
    Last edited by GlittPrizes; 06-18-2020 at 10:51 AM. Reason: wording

    These ads disappear when you log in.

  2. Thanks Corthezz, Willy (2 members gave Thanks to GlittPrizes for this useful post)
  3. #2
    GlittPrizes's Avatar Active Member CoreCoins Purchaser
    Reputation
    35
    Join Date
    Nov 2019
    Posts
    88
    Thanks G/R
    46/24
    Trade Feedback
    0 (0%)
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    To give an idea of the implementation, what I did was turn the above into a class as part of a static library. You can then create a thread to spawn the server and do the same thing with a thread created in DllMain for making a client to connect to it.

    namedPipes.png

  4. #3
    GlittPrizes's Avatar Active Member CoreCoins Purchaser
    Reputation
    35
    Join Date
    Nov 2019
    Posts
    88
    Thanks G/R
    46/24
    Trade Feedback
    0 (0%)
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)

    [Release] Pipes standalone - Server/Client Library

    From the code above, I ended up doing the reverse to make the server spawn from my injected dll, then the client connects from an exe. This is a static library for inter-process communication (GitHub - glubdos/Pipes-standalone: Named Pipes Server/Client).

    How to use:

    Initialize (dll payload)
    Code:
    // thread created on attach
    DWORD Init(LPVOID hInstance)
    {
    	auto* const hPipe = Pipes::Server::Init();
    
    	Window::Init();    // SetWindowLongPtr or initialize your WndProc subclass here
    
    	while (!_safeToExit)
    	{
    		RunPipes();
    
    		Sleep(10);    // sleep interval in between ticks
    	}
    
    	WaitForSingleObject(hPipe, INFINITE);    // wait for signal state or check handle exit code here
    
    	Window::Close();    // restore window pointer
    
    	FreeLibraryAndExitThread(HMODULE(hInstance), 0);    // exit
    }
    Run on Tick
    Code:
    void RunPipes()
    {
    	if (!Pipes::Server::Request.empty())
    	{
    		if (Pipes::Server::Request != "@close")
    		{
    			// _luaCommand = Pipes::Server::Request;    // set communication request here such as a console lua call
    
    			const auto command = Pipes::Server::Request;
    			Pipes::Server::Request = "";    // after extraction don't let request linger
    
    			// LuaBase::State = ShouldExecute;    // here you could trigger lua execution based on global _luaCommand
    		}
    		else
    		{
    			Pipes::Server::Response = "#closing";    // program exit call received
    			Pipes::Server::Cv.notify_one();
    		}
    	}
    
    	if (!Pipes::Server::Connected)
    		_safeToExit = true;
    	else
    		Window::Send(WM_USER_TICK);    // push user message to WndProc callback
    }
    Handle Client (from exe)
    Code:
    // run client thread to connect and feed input (manually in this case)
    void RunClient()
    {
    	Pipes::Client::Init();
    
    	string input;
    	do 
    	{
    		getline(cin, input);
    		if (!input.empty()) 
    		{
    			std::tuple<string, string> response (input.substr(0, input.find('=')), input);
    			
    			// Convert input string to wchar_t*
    			auto* const wInput = new wchar_t[input.length() + 1];
    			std::copy(input.begin(), input.end(), wInput);
    			wInput[input.length()] = 0;
    			
    			std::get<1>(response) = Pipes::Client::SendReceive(wInput);
    
    			std::cout << std::get<0>(response) << ": " << std::get<1>(response) << std::endl;
    		}
    		
    	} while (input != "@close");
    
    	exit(0);
    }
    How to use with FrameScriptExecute:
    Code:
    // LuaBase.cpp
    LuaState LuaBase::State = LuaBusy;
    
    void LuaBase::Execute(string command)
    {
    	string varName;
    	
    	switch (State)
    	{
    	case ShouldExecute:
    		State = LuaBusy;
    		
    		GameMethods::Execute(command);
    		
    		varName = command.substr(0, command.find('='));    // this only allows for single returns and not myVar1,myVar2=...
    		
    		if (varName == command)
    			varName = "";
    	
    		if (!varName.empty())
    			Pipes::Server::Response = GameMethods::GetText(varName.c_str());
    		if (Pipes::Server::Response.empty())
    			Pipes::Server::Response = "nil";
    		
    		Pipes::Server::Cv.notify_one();
    		
    	case LuaBusy:
    	default:
    		break;
    	}
    }
    
    // Window.cpp (WndProc callback)
    LRESULT WINAPI Window::Hook(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
    {
    	switch (uMsg)
    	{
    	case WM_USER_TICK:
    	
    		GameMethods::EnumVisible(EnumObjectsCallback, 0);    // should probably check in game flag before calling this here
    
    		if (LuaBase::State != LuaBusy)
    			LuaBase::Execute(_luaCommand);
    		
    		return false;
    	default:
    		break;
    	}
    
    	// return original WndProc. 
    	return CallWindowProc(WndProc, hWnd, uMsg, wParam, lParam);
    }
    Last edited by GlittPrizes; 07-23-2020 at 07:09 AM. Reason: FSExecute

  5. Thanks 34D (1 members gave Thanks to GlittPrizes for this useful post)

Similar Threads

  1. Finding last name on an account?
    By Clovian in forum World of Warcraft General
    Replies: 2
    Last Post: 03-09-2008, 10:39 PM
  2. Find the Last Name on an account Super Easy!
    By tripleblade3 in forum WoW Scam Prevention
    Replies: 8
    Last Post: 02-29-2008, 09:59 PM
  3. Get the name of an account, the easy way
    By Cun in forum WoW Scam Prevention
    Replies: 22
    Last Post: 10-17-2007, 03:49 AM
  4. How to find out the full name on an account you scam
    By shadowfox47 in forum WoW Scam Prevention
    Replies: 8
    Last Post: 09-06-2007, 12:42 PM
All times are GMT -5. The time now is 09:18 AM. Powered by vBulletin® Version 4.2.3
Copyright © 2021 vBulletin Solutions, Inc. All rights reserved. User Alert System provided by Advanced User Tagging (Pro) - vBulletin Mods & Addons Copyright © 2021 DragonByte Technologies Ltd.
Digital Point modules: Sphinx-based search