我有这个代码:
#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;
}
我创建一个服务器管道并侦听传入连接。就我而言,我运行:
cmd.exe /c echo hello > //./pipe/TestSV
我应该能够模拟
cmd.exe
的线程令牌并以 notepad.exe
用户而不是本地用户身份启动 SYSTEM
,但是有些东西无法正常工作。
我检查了 Microsoft 文档,它指出成功调用
CreateProcessWithTokenW()
和 ImpersonateNamedPipeClient()
应返回非零值。在这两种情况下,我都得到了大量的回报,所以我有点困惑为什么它不起作用。我是不是错过了什么?
更新:我按照建议更新了代码,但它仍然无法正常工作,现在我收到了不同的错误:
#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;
}
首先,您根本不检查
ImpersonateNamedPipeClient()
和 CreateProcessWithTokenW()
的返回值(就像对 ConnectNamedPipe()
所做的那样)。您正在检查 GetLastError()
的返回值。不是同一件事。如果这些函数在失败时不返回 GetLastError()
,那么 FALSE
对于这些函数来说毫无意义。所以,用这个代替:
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;
}
话虽这么说,一旦解决了这个问题,您就会注意到
ImpersonateNamedPipeClient()
失败并出现错误 1368
(ERROR_CANNOT_IMPERSONATE
):
在从该管道读取数据之前,无法使用命名管道进行模拟。
ImpersonateNamedPipeClient()
文档指出:
ImpersonateNamedPipeClient 函数允许命名管道的服务器端模拟客户端。调用此函数时,命名管道文件系统会更改调用进程的线程,以开始模拟从管道读取的最后一条消息的安全上下文。只有管道的服务器端才能调用此函数。
通常,在客户端首次向您发送访问需要客户端安全上下文的内容的请求之前,您不会模拟客户端。读取请求,然后根据需要进行模拟,然后访问所需的任何内容,然后恢复模拟,然后重复,直到客户端断开连接。
因此,就您的情况而言,请先读取
hello
输入,然后模拟,然后启动
cmd.exe
。 的说法,调用进程必须具有 SE_IMPERSONATE_NAME
权限。这就是您收到错误
ERROR_PRIVILEGE_NOT_HELD
(1314) 的原因,因此请尝试使用管理员运行您的代码(服务器管道)。再次,根据 MSDN 的 ImpersonateNamedPipeClient,调用进程的线程开始模拟从管道读取的最后一条消息的安全上下文。应使用 READ
访问权限打开客户端管道才能进行模拟,否则您将收到错误
ERROR_CANNOT_IMPERSONATE
(1368)。例如,CreateFile(pwsPipeName, GENERIC_READ, 0, NULL, OPEN_EXISTING, dwFlags, NULL);