I have this code:
#include <Windows.h>
#include <namedpipeapi.h>
#include <processthreadsapi.h>
#include <iostream>
int main() {
LPCWSTR pipeName = L"\\\\.\\pipe\\TestSV";
HANDLE serverPipe;
int err = 0;
BOOL isPipeConnected;
BOOL isPipeOpen;
DWORD bytesWritten = 0;
std::wcout << "Creating named pipe " << pipeName << std::endl;
serverPipe = CreateNamedPipe(pipeName, PIPE_ACCESS_DUPLEX, PIPE_TYPE_MESSAGE, 1, 2048, 2048, 0, NULL);
isPipeConnected = ConnectNamedPipe(serverPipe, NULL);
if (isPipeConnected) {
std::wcout << "Incoming connection to " << pipeName << std::endl;
}
std::wcout << "Impersonating the client..." << std::endl;
ImpersonateNamedPipeClient(serverPipe);
err = GetLastError();
std::wcout << "Impersonating status..." << err << std::endl;
STARTUPINFO si = {};
wchar_t command[] = L"C:\\Windows\\System32\\notepad.exe";
std::wcout << command << std::endl;
PROCESS_INFORMATION pi = {};
HANDLE threadToken = GetCurrentThreadToken();
std::wcout << "Thread token " << threadToken << std::endl;
CreateProcessWithTokenW(threadToken, LOGON_WITH_PROFILE, command, NULL, CREATE_NEW_CONSOLE, NULL, NULL, &si, &pi);
err = GetLastError();
std::wcout << "Elv status..." << err << std::endl;
return 0;
}
I create a server pipe and listen for an incoming connection. In my case, I run:
cmd.exe /c echo hello > //./pipe/TestSV
I am supposed to be able to impersonate the thread token of cmd.exe and launch notepad.exe as the SYSTEM user instead of the local user, however something is not working correctly.
I checked Microsoft documentation, and it states that a successful call to CreateProcessWithTokenW() and ImpersonateNamedPipeClient() should return a non-zero value. I got large numbers as my return in both cases, so I am kind of confused why it's not working. Am I missing something?
UPDATE: I updated the code as suggested, but it is still not working correctly, and I get a different error now:
#include <Windows.h>
#include <namedpipeapi.h>
#include <processthreadsapi.h>
#include <iostream>
int main() {
LPCWSTR pipeName = L"\\\\.\\pipe\\TestSV";
LPVOID pipeBuffer = NULL;
HANDLE serverPipe;
HANDLE threadToken = NULL;
DWORD readBytes = 0;
DWORD readBuffer = 0;
int err = 0;
BOOL isPipeConnected;
BOOL isPipeOpen;
BYTE bMessage[128] = { 0 };
DWORD bytesWritten = 0;
std::wcout << "Creating named pipe " << pipeName << std::endl;
serverPipe = CreateNamedPipe(pipeName, PIPE_ACCESS_DUPLEX, PIPE_TYPE_MESSAGE, 1, 2048, 2048, 0, NULL);
isPipeConnected = ConnectNamedPipe(serverPipe, NULL);
if (isPipeConnected) {
std::wcout << "Incoming connection to " << pipeName << std::endl;
}
if (!ReadFile(serverPipe, &bMessage, 1, &bytesWritten, NULL)) {
std::wcout << "Failed to READ" << std::endl;
}
std::wcout << "Impersonating the client..." << std::endl;
if (!ImpersonateNamedPipeClient(serverPipe)) {
err = GetLastError();
std::wcout << "Impersonating error..." << err << std::endl;
}
// get a handle to this threads token
if (!OpenThreadToken(GetCurrentThread(), TOKEN_ALL_ACCESS, FALSE, &threadToken)) {
err = GetLastError();
std::wcout << "Token error..." << err << std::endl;
}
STARTUPINFO si = {};
PROCESS_INFORMATION pi = {};
wchar_t command[] = L"C:\\Windows\\System32\\notepad.exe";
int process_status = CreateProcessWithTokenW(&threadToken, LOGON_WITH_PROFILE, command, NULL, CREATE_NEW_CONSOLE, NULL, NULL, &si, &pi);
err = GetLastError();
std::wcout << "Elv error..." << process_status << std::endl;
std::wcout << "Elv error..." << err << std::endl;
return 0;
}
First off, you are NOT checking the return values of ImpersonateNamedPipeClient() and CreateProcessWithTokenW() at all (like you do with ConnectNamedPipe()). You are checking the return value of GetLastError() instead. Not the same thing. GetLastError() is meaningless for these functions if they do not return FALSE on failure. So, use this instead:
if (!ImpersonateNamedPipeClient(serverPipe))
{
err = GetLastError();
std::wcout << "Impersonating failed..." << err << std::endl;
}
if (!CreateProcessWithTokenW(threadToken, LOGON_WITH_PROFILE, command, NULL, CREATE_NEW_CONSOLE, NULL, NULL, &si, &pi))
{
err = GetLastError();
std::wcout << "Elv failed..." << err << std::endl;
}
That being said, once you have fixed that, you'll notice that ImpersonateNamedPipeClient() fails with error 1368 (ERROR_CANNOT_IMPERSONATE):
Unable to impersonate using a named pipe until data has been read from that pipe.
The ImpersonateNamedPipeClient() documentation states:
The ImpersonateNamedPipeClient function allows the server end of a named pipe to impersonate the client end. When this function is called, the named-pipe file system changes the thread of the calling process to start impersonating the security context of the last message read from the pipe. Only the server end of the pipe can call this function.
Typically, you would not impersonate a client until after the client has first sent you a request to access something that needs the client's security context. Read a request, then impersonate if needed, then access whatever is needed, then revert impersonation, and repeat, until the client is disconnected.
So, in your case, read the hello input first, then impersonate, then launch cmd.exe.
Related
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;
}
Trying to retrieve the running directory of a process using the PID. I am obtaining the PID using FindWindow() and GetWindowThreadProcessId() which results in the same process ID as shown in task manager, so I assume it to be correct.
When using GetModuleFileNameEx(), instead of resulting in a path, I get what seems to be a memory address.
auto wnd = FindWindow(nullptr, L"prog");
while (wnd == nullptr)
{
wnd = FindWindow(nullptr, L"prog");
}
TCHAR fBuf[MAX_PATH]; // buffer for path
DWORD procId; // process id
GetWindowThreadProcessId(wnd, &procId); // get process id
std::cout << procId << std::endl; // results in correct pid
auto procHdl = OpenProcess(PROCESS_QUERY_INFORMATION | PROCESS_VM_READ, false, procId); // create handle for process
if (procHdl != NULL) {
if (GetModuleFileNameEx(procHdl, 0, fBuf, MAX_PATH) == 0) {
std::cerr << "Failed to get module filename." << std::endl;
}
else {
std::cout << "Module filename is: " << fBuf << std::endl;
}
CloseHandle(procHdl);
}
Sample output is:
10488
Module filename is: 008CF93C
I have had the same result using GetProcessImageFileNname() too.
To get the directory of a program, first use GetModuleFileNameEx to get the program path, and your directory will start from the first character to the last backslash found.
Example:
Program is: "C:\Windows\notepad.exe";
Directory is: "C:\Windows";
In code:
DWORD pid = 104;
CHAR ProgramFile[MAX_PATH];
std::string DirectoryPath;
HANDLE hProcess = OpenProcess(PROCESS_VM_READ|PROCESS_QUERY_INFORMATION, FALSE, pid);
GetModuleFileNameExA(hProcess, NULL, ProgramFile, MAX_PATH);
DirectoryPath = ProgramFile;
DirectoryPath = DirectoryPath.substr(0, DirectoryPath.find_last_of('\\'));
std::cout << "ProgramFile: " << ProgramFile << endl;
std::cout << "Directory: " << DirectoryPath.c_str();
I am trying to learn how named pipes work, and created 2 consoles to test the connectivity between server and client. Client will send a message to the server and the server will display the message, but instead of the message, it returns a value of "nullptr" as shown in the error exception break from VS.
below are my codes, do enlighten me if you found any problem with my code, and I am still learning..
Server.cpp
#include "cust_ostream.hpp"
#include <Windows.h>
#include <iostream>
#include <conio.h>
using namespace std;
int main()
{
LPVOID buffer = NULL;
DWORD readbyte;
cout << "---Named Pipe Server Test---" << endl << endl;
cout << "Creating named pipe: \\\\.\\pipe\\mypipe" << endl;
HANDLE hPipe = CreateNamedPipeA("\\\\.\\pipe\\mypipe", PIPE_ACCESS_DUPLEX, PIPE_TYPE_MESSAGE | PIPE_READMODE_MESSAGE | PIPE_WAIT,
PIPE_UNLIMITED_INSTANCES, 1024, 1024, 0, NULL);
if (!hPipe || hPipe == INVALID_HANDLE_VALUE)
{
cout << "Pipe creation failed." << endl;
return 0;
}
cout << "Connecting pipe to client..." << endl;
BOOL connect = ConnectNamedPipe(hPipe, NULL);
if (!connect)
{
cout << "Connect named pipe failed" << endl;
}
cout << "Success! Reading pipe message from client..." << endl;
ReadFile(hPipe, buffer, sizeof(buffer), &readbyte, NULL);
c_cout << "Pipe message = " << *(int *)buffer << endl;
_getch();
return 0;
}
cust_ostream.hpp
#include <Windows.h>
#include <iostream>
#include <sstream>
using namespace std;
#define endl "\n"
class cust_ostream
{
public:
~cust_ostream()
{
cout << m_buffer.str();
}
template <typename T>
cust_ostream &operator<<(T const &value)
{
m_buffer << value;
return *this;
}
private:
ostringstream m_buffer;
};
#define c_cout cust_ostream()
and my client
#include <Windows.h>
#include <iostream>
#include <conio.h>
using namespace std;
int main()
{
LPVOID data;
DWORD writebyte;
int i = 2;
cout << "---Named Pipe Client---" << endl << endl;
cout << "Creating pipe file: \\\\.\\pipe\\mypipe" << endl;
HANDLE pipe = CreateFileA("\\\\.\\pipe\\mypipe", GENERIC_READ | GENERIC_WRITE, 0, NULL, OPEN_EXISTING, 0, NULL);
if (!pipe || pipe == INVALID_HANDLE_VALUE)
{
cout << "Pipe client failed." << endl;
return 0;
}
cout << "Pipe connected to server, sending data..." << endl;
WriteFile(pipe, &i, sizeof(i), &writebyte, NULL);
_getch();
return 0;
}
You need to wait for the NamedPipe to have a ConnectPipeReady event on it. As it stands, you are trying to create the pipe without actually seeing if it was succesfull. See the MSDN documentation for Named Pipes here: https://msdn.microsoft.com/en-ca/library/windows/desktop/aa365592(v=vs.85).aspx
Specifically, this block:
while (1)
{
hPipe = CreateFile(
lpszPipename, // pipe name
GENERIC_READ | // read and write access
GENERIC_WRITE,
0, // no sharing
NULL, // default security attributes
OPEN_EXISTING, // opens existing pipe
0, // default attributes
NULL); // no template file
// Break if the pipe handle is valid.
if (hPipe != INVALID_HANDLE_VALUE)
break;
// Exit if an error other than ERROR_PIPE_BUSY occurs.
if (GetLastError() != ERROR_PIPE_BUSY)
{
_tprintf( TEXT("Could not open pipe. GLE=%d\n"), GetLastError() );
return -1;
}
// All pipe instances are busy, so wait for 20 seconds.
if ( ! WaitNamedPipe(lpszPipename, 20000))
{
printf("Could not open pipe: 20 second wait timed out.");
return -1;
}
}
Also you shouldn't use #define endl "\n", use std::endl
You have initialized your buffer as NULL which means that by default its length is zero. Now when you use the sizeof operator in your read function in server (to retrieve the message received by server from client), what happens is that you are asking the sizeof operator in Read function to read 0 bytes! which means that nothing will be read.
To solve this, you can declare a char array of size 100 or a size of a message which you are sure that won't be exceeded by client. Like if you are assuming that message by client is not going to be longer than lets say 60 characters, then you can create your char buffer to be of size 100 just to make sure that you do accommodate all the message by client.
And one more thing, if problem still persists, instead of using sizeof in read, use 100 or whatever the size of of your char buffer array. This should solve your problem.
I have written DLL injector, to inject i use NtCreateThread to create remote thread into remote process
I work on 64 bit OS - Windows 7
To comiple i use
32 bit - g++ (tdm-2) 4.8.1
64 bit - g++ (tdm64-1) 5.1.0
Problem concerns 64 bit verion of my injector
I have 2 problems
1)
OK. My injector sometimes work and sometimes not work.
I dont know what is reason.
This is problematic line
funcNtCreateThreadEx( &hRemoteThread,GENERIC_EXECUTE, NULL, hProcess, (LPTHREAD_START_ROUTINE)lpBaseAddress, lpSpace, FALSE, NULL, NULL, NULL, (LPVOID)ntbuffer );
When i call function NtCreateThreadEx and in last argument is pointer to NtCreateThreadExBuffer, it not work.
funcNtCreateThreadEx( &hRemoteThread,GENERIC_EXECUTE /*GENERIC_ALL 0x1FFFFF/*Może tu jest błąd*/, NULL, hProcess, (LPTHREAD_START_ROUTINE)lpBaseAddress, lpSpace, FALSE, NULL, NULL, NULL, **ntbuffer** );
When i give in last parametr NULL it works and I DONT KNOW WHY.
funcNtCreateThreadEx( &hRemoteThread,GENERIC_EXECUTE /*GENERIC_ALL 0x1FFFFF/*Może tu jest błąd*/, NULL, hProcess, (LPTHREAD_START_ROUTINE)lpBaseAddress, lpSpace, FALSE, NULL, NULL, NULL, **NULL** );
Any idea?
2)
I use NtCreateThreadEx because i want inject code to SYSTEM PROCESS (like csrss.exe) because i like BSoD :).
Unfortunately, my injector can inject code ONLY to process in this same session.
Reason is ?Session Separation? (implemented on Vista and every later OS)
It is rise why i cannot use CreateRemoteThread
I have thought when i use NtCreateThreadEx separation of sessions is not a problem.
I try inject DLL to process in sesion 0.
I tried reduce the privileges
I replaced:
HProcess = OpenProcess(PROCESS_ALL_ACCESS, FALSE, PID);
on
HProcess = OpenProcess(PROCESS_VM_WRITE | PROCESS_VM_OPERATION | PROCESS_CREATE_THREAD, FALSE, PID);
etc. but it not work.
Can you tell me why NtCreateThreadEx() works in admin mode, but not in standard mode ?
Maby i should use other undocumented functions (like NtCreateThreadEx) for example NtOpenProcess to get handle to process with all access?
I need any idea what i should doing. Please help me.
#include <iostream>
#include <string>
#include <windows.h>
struct NtCreateThreadExBuffer;
HANDLE NtCreateThreadEx(HANDLE hProcess,LPVOID lpBaseAddress, LPVOID lpSpace);
BOOL EnableDebugPrivilege();
int main()
{
int PIDOfProcess = 0;
std::string pathToDLL = "dll64.dll\0"; ///find DLL in local directory
DWORD PID = (DWORD)PIDOfProcess; ///PID
HANDLE HProcess = NULL; ///Handle to process
LPVOID LibAddr = NULL; ///Address of procedure 'LoadLibraryA'
LPVOID DllAdr = NULL; ///Address of memory in other process
HANDLE hThread = NULL; ///Handle to remote thread
int WirteStatus = 0; ///Status of writing to memory of other process
std::cout << "Get PID of process" << std::endl;
std::cin >> PIDOfProcess;
PID = (DWORD)PIDOfProcess;
if(EnableDebugPrivilege() == true)
{
std::cout << "Debug privilege was enabled with status: "<< GetLastError() << " [OK]" << std::endl;
}else std::cout << "Debug privilege was not enabled: "<< GetLastError() << " [FAILED]" << std::endl;
/*
NtOpenProcess(
OUT PHANDLE ProcessHandle,
IN ACCESS_MASK AccessMask,
IN POBJECT_ATTRIBUTES ObjectAttributes,
IN PCLIENT_ID ClientId );*/
/// NtOpenProcess();
HProcess = OpenProcess(PROCESS_VM_WRITE | PROCESS_VM_OPERATION | PROCESS_CREATE_THREAD, FALSE, PID);
if(HProcess == NULL)
{
std::cout << "Could not find process: "<< GetLastError() << " [FAILED]" << std::endl;
system("pause");
return GetLastError();
} std::cout << "Process opened: [OK]" << std::endl;
DllAdr = (LPVOID)VirtualAllocEx(HProcess, NULL, pathToDLL.size() +1, MEM_RESERVE|MEM_COMMIT, PAGE_EXECUTE_READWRITE);
if(DllAdr == NULL)
{
std::cout << "Cannot allocate memory in remote process: "<< GetLastError() << " [FAILED]" << std::endl;
system("pause");
return GetLastError();
} std::cout << "Region of memory in remote process allocated: [OK]" << std::endl;
WirteStatus = WriteProcessMemory(HProcess, (LPVOID)DllAdr, pathToDLL.c_str() ,pathToDLL.size()+1, NULL);
if(WirteStatus == 0)
{
std::cout << "Could not write to process's address space: "<< GetLastError() << " [FAILED]" << std::endl;
system("pause");
return GetLastError();
} std::cout << "A memory has written name of DLL: [OK]" << std::endl;
LibAddr = (LPVOID)GetProcAddress(GetModuleHandle("kernel32.dll"), "LoadLibraryA");
if(LibAddr == NULL)
{
std::cout << "Unable to locate LoadLibraryA: "<< GetLastError() << " [FAILED]" << std::endl;
system("pause");
return GetLastError();
} std::cout << "A address of procedure LoadLibraryA has found [OK]" << std::endl;
hThread = NtCreateThreadEx(HProcess,LibAddr,DllAdr);
if(hThread == NULL)
{
std::cout << "Could not create remote thread on process: "<< GetLastError() << " [FAILED]" << std::endl;
system("pause");
return GetLastError();
}
system("pause");
}
struct NtCreateThreadExBuffer///This information is derived based on reverse engineering work.
{
ULONG Size;
ULONG Unknown1;
ULONG Unknown2;
PULONG Unknown3;
ULONG Unknown4;
ULONG Unknown5;
ULONG Unknown6;
PULONG Unknown7;
ULONG Unknown8;
};
HANDLE NtCreateThreadEx(HANDLE hProcess,LPVOID lpBaseAddress,LPVOID lpSpace)
{
///The prototype of NtCreateThreadEx from undocumented.ntinternals.com
///typ_zwracanej_wartości (__konwencja_wywolania*nazwa_wskaźnika)(typ1 argument1, typ2 argument2);
/* 2OOO, 11 November http://undocumented.ntinternals.net/
NTSYSAPI NTSTATUS NTAPI NtCreateThread(
OUT PHANDLE ThreadHandle,
IN ACCESS_MASK DesiredAccess,
IN POBJECT_ATTRIBUTES ObjectAttributes OPTIONAL,
IN HANDLE ProcessHandle,
OUT PCLIENT_ID ClientId,
IN PCONTEXT ThreadContext,
IN PINITIAL_TEB InitialTeb,
IN BOOLEAN CreateSuspended );*/
typedef LONG64 (WINAPI * functypeNtCreateThreadEx)(
PHANDLE ThreadHandle, ///Handle to thread which will be created /// OUT
ACCESS_MASK DesiredAccess, ///possible: GENERIC_WRITE GENERIC_READ GENERIC_EXECUTE GENERIC_ALL//// IN
LPVOID ObjectAttributes, /// IN,OPTIONAL
HANDLE ProcessHandle, ///Handle to our process /// IN
LPTHREAD_START_ROUTINE lpStartAddress,
LPVOID lpParameter,
BOOL CreateSuspended,
/*DWORD*/LONG64 dwStackSize,
LONG64 SizeOfStackCommit,
LONG64 SizeOfStackReserve,
LPVOID lpBytesBuffer ///pointer to initialized object of NtCreateThreadExBuffer
);
NtCreateThreadExBuffer * ntbuffer = NULL;
HANDLE hRemoteThread = NULL;
HMODULE hNtDllModule = NULL;
functypeNtCreateThreadEx funcNtCreateThreadEx = NULL;
ntbuffer = new NtCreateThreadExBuffer;
memset (ntbuffer,0,sizeof(NtCreateThreadExBuffer));///fill buffer zeros
DWORD temp1 = 0;
DWORD temp2 = 0;
ntbuffer->Size = sizeof(NtCreateThreadExBuffer);
ntbuffer->Unknown1 = 0x10003;
ntbuffer->Unknown2 = 0x8;
ntbuffer->Unknown3 = &temp2;
ntbuffer->Unknown4 = 0;
ntbuffer->Unknown5 = 0x10004;
ntbuffer->Unknown6 = 4;
ntbuffer->Unknown7 = &temp1;
ntbuffer->Unknown8 = 0;
//Get handle for ntdll which contains NtCreateThreadEx
hNtDllModule = GetModuleHandle( "ntdll.dll" );
if ( hNtDllModule == NULL )
{
std::cout << "Cannot get module ntdll.dll error: " << GetLastError() << std::endl;
return NULL;
} std::cout << "A 'ntdll.dll' module has got: "<< GetLastError() << " [OK]" << std::endl;
funcNtCreateThreadEx = (functypeNtCreateThreadEx)GetProcAddress( hNtDllModule, "NtCreateThreadEx" );
if ( !funcNtCreateThreadEx )
{
std::cout << "Cannot get procedure address error: " << GetLastError() << std::endl;
return NULL;
} std::cout << "'NtCreateThreadEx' has executed: "<< GetLastError() << " [OK]" << std::endl;
///Here is problem - when in last argument i replace NULL
funcNtCreateThreadEx( &hRemoteThread,GENERIC_EXECUTE /*GENERIC_ALL 0x1FFFFF/*Może tu jest błąd*/, NULL, hProcess, (LPTHREAD_START_ROUTINE)lpBaseAddress, lpSpace, FALSE, NULL, NULL, NULL, ntbuffer );
std::cout << "_______________________________________ " << std::endl;
std::cout << "NtCreateThreadEx' has status: " << GetLastError() << std::endl;
std::cout << "hRemoteThread: " << hRemoteThread << std::endl;
std::cout << "State of handle to DLL: " << hNtDllModule << std::endl;
std::cout << "Addres of prcoedure NtCreateThreadEx: " << funcNtCreateThreadEx << std::endl;
return hRemoteThread;
}
BOOL EnableDebugPrivilege()
{
HANDLE hToken;
LUID luid;
TOKEN_PRIVILEGES tkp;
if(!OpenProcessToken( GetCurrentProcess(), TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY, &hToken ))
{
return FALSE;
}
if(!LookupPrivilegeValue( NULL, SE_DEBUG_NAME, &luid ))
{
return FALSE;
}
tkp.PrivilegeCount = 1;
tkp.Privileges[0].Luid = luid;
tkp.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED;
if(!AdjustTokenPrivileges( hToken, false, &tkp, sizeof( tkp ), NULL, NULL ))
{
return FALSE;
}
if(!CloseHandle( hToken ))
{
return FALSE;
}
return TRUE;
}
try: zwcreatethreatex function.
typedef_ZwCreateThreadEx ZwCreateThreadEx = (typedef_ZwCreateThreadEx)::GetProcAddress(hNtdllDll, "ZwCreateThreadEx");
if (NULL == ZwCreateThreadEx)
{
ShowError("GetProcAddress_ZwCreateThread");
return FALSE;
}
I have written DLL injector. I used CreateRemoteThread to inject my DLL to process and all was good.
Now i am trying inject DLL to process by undocumented function - NtCreateThreadEx. I have written injector but he is not working.
When i use 32 bit injector to inject 32 bit DLL to 32 bit process
all working good.
Problem is when i use 64 bit injector to inject 64 bit DLL to 64 bit process.
My DLL code:
#include <windows.h>
///Compilation with option -m64
extern "C" BOOL __stdcall DllMain(HMODULE hDLL, DWORD Reason, LPVOID Reserved)
{
MessageBox( NULL, "MESSAGE FROM 64 BIT DLL", "Lorem ipsum", MB_ICONINFORMATION | MB_OKCANCEL );
return 0;
}
My TestApp code
#include <iostream>
#include <windows.h>
int main()
{
std::cout << " Lorem IPSUM" << std::endl;
//HMODULE HDLL = LoadLibraryA("dll64.dll");
//std::cout << "Error: " << GetLastError() << std::endl;
while(1)
{
std::cout << "petla" << std::endl;
Sleep(5000);
}
return 0;
}
My injector code:
#include <iostream>
#include <string>
#include <windows.h>
/// 64 bit OS - Windows 7
///=====================
///* In this same user context ("User")
///TYPE OF(32/64 bits)
///INJECTOR===DLL===PROCESS===RESULT
/// 32 32 32 -SUCESS
/// 64 64 64 -FALIED (error: 1300)
//Handle to process,Address of'LoadLibraryA',see DllAdr
///TO DO
///* Inject DLL to process from normal user context ("User") to higher user context (Zarzadca)
///* Inject DLL to process from normal user context ("User") to other normal user context (User1)
HANDLE NtCreateThreadEx(HANDLE hProcess,LPVOID lpBaseAddress, LPVOID lpSpace);
int privileges();
int main()
{
int PIDOfProcess = 0;
std::string pathToDLL = "dll64.dll\0"; ///find DLL in local directory
DWORD PID = (DWORD)PIDOfProcess; ///PID
HANDLE HProcess = NULL; ///Handle to process
LPVOID LibAddr = NULL; ///Address of procedure 'LoadLibraryA'
LPVOID DllAdr = NULL; ///Address of memory in other process
HANDLE hThread = NULL; ///Handle to remote thread
int WirteStatus = 0; ///Status of writing to memory of other process
std::cout << "ptr size = " << sizeof(void *) << std::endl;
std::cout << "Get PID of process" << std::endl;
std::cin >> PIDOfProcess;
PID = (DWORD)PIDOfProcess;
///std::cout << "Get path to DLL" << std::endl;
///std::cin >> pathToDLL;
if( privileges() != 0 )
{
std::cout << "Cannot get the right privileges" << std::endl;
}
HProcess = OpenProcess(PROCESS_ALL_ACCESS, FALSE, PID);
if(HProcess == NULL)
{
std::cout << "Could not find process" << std::endl;
std::cout << GetLastError() << std::endl;
system("pause");
return GetLastError();
}
DllAdr = (LPVOID)VirtualAllocEx(HProcess, NULL, pathToDLL.size() +1, MEM_RESERVE|MEM_COMMIT, PAGE_EXECUTE_READWRITE);
if(DllAdr == NULL)
{
std::cout <<"Can not allocate memory." << std::endl;
std::cout << GetLastError() << std::endl;
system("pause");
return GetLastError();
}
WirteStatus = WriteProcessMemory(HProcess, (LPVOID)DllAdr, pathToDLL.c_str() ,pathToDLL.size()+1, NULL);
if(WirteStatus == 0)
{
std::cout << "Could not write to process's address space" << std::endl;
std::cout << GetLastError() << std::endl;
system("pause");
return GetLastError();
}
LibAddr = (LPVOID)GetProcAddress(GetModuleHandle("kernel32.dll"), "LoadLibraryA");
if(LibAddr == NULL)
{
std::cout << "Unable to locate LoadLibraryA" << std::endl;
std::cout << GetLastError() << std::endl;
system("pause");
return GetLastError();
}
hThread = NtCreateThreadEx(HProcess,LibAddr,DllAdr);
///DWORD threadId = 0;
///hThread = CreateRemoteThread(HProcess, NULL, 0, (LPTHREAD_START_ROUTINE)LibAddr, DllAdr, 0, &threadId);
if(hThread == NULL)
{
std::cout << "Error: ";
std::cout << GetLastError() << std::endl;
system("pause");
return GetLastError();
}
system("pause");
}
HANDLE NtCreateThreadEx(HANDLE hProcess,LPVOID lpBaseAddress,LPVOID lpSpace)
{
///The prototype of NtCreateThreadEx from undocumented.ntinternals.com
typedef DWORD (WINAPI * functypeNtCreateThreadEx)(
PHANDLE ThreadHandle,
ACCESS_MASK DesiredAccess,
LPVOID ObjectAttributes,
HANDLE ProcessHandle,
LPTHREAD_START_ROUTINE lpStartAddress,
LPVOID lpParameter,
BOOL CreateSuspended,
DWORD dwStackSize,
DWORD Unknown1,
DWORD Unknown2,
LPVOID Unknown3
);
HANDLE hRemoteThread = NULL;
HMODULE hNtDllModule = NULL;
functypeNtCreateThreadEx funcNtCreateThreadEx = NULL;
//Get handle for ntdll which contains NtCreateThreadEx
hNtDllModule = GetModuleHandle( "ntdll.dll" );
if ( hNtDllModule == NULL )
{
std::cout << "Cannot get module ntdll.dll error: " << GetLastError() << std::endl;
return NULL;
}
funcNtCreateThreadEx = (functypeNtCreateThreadEx)GetProcAddress( hNtDllModule, "NtCreateThreadEx" );
if ( !funcNtCreateThreadEx )
{
std::cout << "Cannot get procedure address error: " << GetLastError() << std::endl;
return NULL;
}
funcNtCreateThreadEx( &hRemoteThread, /*GENERIC_ALL*/0x1FFFFF, NULL, hProcess, (LPTHREAD_START_ROUTINE)lpBaseAddress, lpSpace, FALSE, NULL, NULL, NULL, NULL );
std::cout << "Status NtCreateThreadEx " << GetLastError() << std::endl;
std::cout << "hRemoteThread: " << hRemoteThread << std::endl;
std::cout << "hNtDllModule: " << hNtDllModule << std::endl;
std::cout << "funcNtCreateThreadEx: " << funcNtCreateThreadEx << std::endl;
return hRemoteThread;
}
int privileges()
{
HANDLE Token;
TOKEN_PRIVILEGES tp;
if(OpenProcessToken( GetCurrentProcess(), TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY,&Token)) ///It opens the access token associated with a process.
{
LookupPrivilegeValue(NULL, SE_DEBUG_NAME, &tp.Privileges[0].Luid);///Function retrieves the locally unique identifier (LUID)
tp.PrivilegeCount = 1;
tp.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED;
if (AdjustTokenPrivileges(Token, false, &tp, sizeof(tp), NULL, NULL) != 0)///Function enables or disables privileges in the specified access token.
{
return 0; //OK
}
}
return 1;
}
When i use 64 bit injector to inject 64 bit DLL to 64 bit process, function NtCreateThreadEx return code of error 1300 and my DLL doesn't execute.
I use to compile on 64 bit architecture: g++ (tdm64-1) 5.1.0
I am working on Virus Windows 7 64 bit as normal user. Run as administrator doesn't help.
I dont know why it doesn't work, what i doing wrong.
PS: When i use 32 bit injector to inject 32 bit DLL to 32 bit process, function NtCreateThreadEx return code of error 1300 but my DLL execute.
In 32 bit version TestApp GetLastError return code 1114.
I use to compile on 32 bit architecture: g++ (tdm-2) 4.8.1
I include image
I based on:
http://www.p-programowanie.pl/cpp/dll-injection/ - Dll Injection (polish)
====
http://www.codeproject.com/Questions/369890/Ask-about-NtCreateThreadEx-in-Window-x
- Ask about NtCreateThreadEx in Window 7 x64!
=====
http://www.rohitab.com/discuss/topic/39535-code-injections-beginner-and-advanced/ Code Injections [beginner and advanced]
=====
http://securityxploded.com/ntcreatethreadex.php Remote Thread Execution in System Process using NtCreateThreadEx for Vista & Windows7
=====
http://cpp0x.pl/dokumentacja/WinAPI/Systemowe-kody-bledow-1300-1699/1470 Systemowe kody błędów (1300-1699) (polish)
Link to my topic on other forum (polish): http://forum.4programmers.net/C_i_C++/267735-dll_injection_w_windows_za_pomoca_nieudokumentowanej_funkcji_w_winapi?p=1234215#id1234215
My injector is working when i use him to inject DLL to other process in my user space (i am working as normal user)
but it is not working when
i am injecting to csrss.exe (or other system's process).
I get code of error 5 - Access Denied, when i run injector as Administrator i get code of error 0 (SUCCESS?) but my DLL not aborting process
( abort() - i try do BSoD).
I read about Session Separation and i think it is reason of my problem so i have one question: How i can hacking Windows :)
If it is imposibble can i inject DLL as normal user to proces in Administrator context (or other normal user's process) ?