来自WNetAddConnection2W
WNet 函数在与 登录会话 关联的 MS-DOS 设备名称空间中创建和删除网络驱动器号,因为
MS-DOS 设备由 AuthenticationID(本地唯一的
标识符或 LUID,与登录会话相关联。)
还有
如果作为 LocalSystem 运行的代码调用 WNetAddConnection2 函数,则映射的驱动器对所有人可见
用户登录会话。
从技术上讲,这意味着如果代码在\GLOBAL??\ 文件夹中以LocalSystem 的身份运行,则会创建指向网络磁盘的符号链接。否则将在
下创建链接
\Sessions\0\DosDevices\<token LogonId>\
并且仅对令牌中具有相同 LogonId 的线程(进程)可见
如果您的代码具有管理员权限 - 它通常(几乎总是)具有调试权限。有了这个,我们可以使用 LocalSystem 令牌打开进程并在调用 WNetAddConnection2 之前模拟它。
还可能获得 TCB 特权,在此调用 WTSQueryUserToken 之后,通过 DuplicateToken 和模拟 - SetThreadToken 将主令牌转换为模拟令牌。最后打电话给WNetAddConnection2。
好的。我首先尝试简单地模拟 LocalSystem
让我们有函数
NTSTATUS ImpersonateSystemOrTcbToken(bool bTcb);
将 LocalSystem 或具有 Tcb 权限的令牌设置为当前线程(据我所知,所有 LocalSystem 令牌都具有 TCB 权限,但无论如何写 2使用 TCB 或使用 LocalSystem 获取确切令牌的不同代码)
和
HRESULT AdjustDebugPrivilegesToThread();
在当前线程令牌中启用调试权限(它必须存在于管理员令牌中)
在这种情况下,代码可以是下一个:
inline HRESULT BOOL_TO_HRESULT(BOOL f)
{
return f ? NOERROR : HRESULT_FROM_WIN32(GetLastError());
}
HRESULT MapRemoteDrive(PCWSTR local, PCWSTR remote, PCWSTR username, PCWSTR password)
{
NETRESOURCEW nr = {
0, RESOURCETYPE_DISK, 0, 0, const_cast<PWSTR>(local), const_cast<PWSTR>(remote)
};
return HRESULT_FROM_WIN32(WNetAddConnection2W(&nr, password, username, 0));
}
HRESULT MapRemoteDriveEx1(PCWSTR local, PCWSTR remote, PCWSTR username, PCWSTR password)
{
HRESULT hr = BOOL_TO_HRESULT(ImpersonateSelf(::SecurityImpersonation));
if (SUCCEEDED(hr))
{
if (SUCCEEDED(hr = AdjustDebugPrivilegesToThread()) &&
SUCCEEDED(hr = HRESULT_FROM_NT(ImpersonateSystemOrTcbToken(false))))
{
hr = MapRemoteDrive(local, remote, username, password);
// WNetCancelConnection2W(local, 0, TRUE);
}
SetThreadToken(0, 0);
}
return hr;
}
代码工作正常,确实创建了网络位置,但有下一个视图:
尽管如此 - 驱动器在点击时浏览正确。我不研究为什么描述中的 Disconected 词。但这里的权限可能存在一些问题
如果尝试为具体的 LUID 创建驱动器,代码会更复杂
HRESULT MapRemoteDriveEx2(PCWSTR local, PCWSTR remote, PCWSTR username, PCWSTR password)
{
HRESULT hr = BOOL_TO_HRESULT(ImpersonateSelf(::SecurityImpersonation));
if (SUCCEEDED(hr))
{
HANDLE hToken, hImpToken;
if (SUCCEEDED(hr = AdjustDebugPrivilegesToThread()) &&
SUCCEEDED(hr = HRESULT_FROM_NT(ImpersonateSystemOrTcbToken(true))) &&
SUCCEEDED(hr = BOOL_TO_HRESULT(WTSQueryUserToken(WTSGetActiveConsoleSessionId(), &hToken))))
{
hr = BOOL_TO_HRESULT(DuplicateToken(hToken, ::SecurityImpersonation, &hImpToken));
CloseHandle(hToken);
if (SUCCEEDED(hr))
{
hr = BOOL_TO_HRESULT(SetThreadToken(0, hImpToken));
CloseHandle(hImpToken);
if (SUCCEEDED(hr))
{
hr = MapRemoteDrive(local, remote, username, password);
// WNetCancelConnection2W(local, 0, TRUE);
}
}
}
SetThreadToken(0, 0);
}
return hr;
}
这个结果完全没问题
现在为 util 函数编写代码:
HRESULT AdjustDebugPrivilegesToThread()
{
ULONG dwError;
HANDLE hToken;
if (OpenThreadToken(NtCurrentThread(), TOKEN_ADJUST_PRIVILEGES, TRUE, &hToken))
{
static const ::TOKEN_PRIVILEGES tp = { 1, { { { SE_DEBUG_PRIVILEGE } } } };
AdjustTokenPrivileges(hToken, FALSE, const_cast<::PTOKEN_PRIVILEGES>(&tp), 0, 0, 0);
dwError = GetLastError();
CloseHandle(hToken);
}
else
{
dwError = GetLastError();
}
return HRESULT_FROM_WIN32(dwError);
}
和..
NTSTATUS GetSystemToken(PVOID buf)
{
NTSTATUS status;
union {
PVOID pv;
PBYTE pb;
PSYSTEM_PROCESS_INFORMATION pspi;
};
pv = buf;
ULONG NextEntryOffset = 0;
do
{
pb += NextEntryOffset;
HANDLE hProcess, hToken, hNewToken;
CLIENT_ID ClientId = { pspi->UniqueProcessId };
if (ClientId.UniqueProcess)
{
static SECURITY_QUALITY_OF_SERVICE sqos = {
sizeof sqos, SecurityImpersonation, SECURITY_DYNAMIC_TRACKING, FALSE
};
static OBJECT_ATTRIBUTES soa = { sizeof(soa), 0, 0, 0, 0, &sqos };
if (0 <= NtOpenProcess(&hProcess, PROCESS_QUERY_LIMITED_INFORMATION, &zoa, &ClientId))
{
status = NtOpenProcessToken(hProcess, TOKEN_QUERY|TOKEN_DUPLICATE, &hToken);
NtClose(hProcess);
if (0 <= status)
{
ULONG rcb;
TOKEN_STATISTICS ts;
static const LUID SystemLuid = SYSTEM_LUID;
status = -1;
if (0 <= NtQueryInformationToken(hToken, TokenStatistics, &ts, sizeof(ts), &rcb) &&
ts.AuthenticationId.LowPart == SystemLuid.LowPart &&
ts.AuthenticationId.HighPart == SystemLuid.HighPart)
{
status = NtDuplicateToken(hToken, TOKEN_IMPERSONATE,
&soa, FALSE, TokenImpersonation, &hNewToken);
}
NtClose(hToken);
if (0 <= status)
{
status = NtSetInformationThread(NtCurrentThread(), ThreadImpersonationToken, &hNewToken, sizeof(hNewToken));
NtClose(hNewToken);
return status;
}
}
}
}
} while (NextEntryOffset = pspi->NextEntryOffset);
return STATUS_UNSUCCESSFUL;
}
NTSTATUS GetTcbToken(PVOID buf)
{
NTSTATUS status;
union {
PVOID pv;
PBYTE pb;
PSYSTEM_PROCESS_INFORMATION pspi;
};
pv = buf;
ULONG NextEntryOffset = 0;
do
{
pb += NextEntryOffset;
HANDLE hProcess, hToken, hNewToken;
if (pspi->InheritedFromUniqueProcessId && pspi->UniqueProcessId)
{
static SECURITY_QUALITY_OF_SERVICE sqos = {
sizeof sqos, SecurityImpersonation, SECURITY_DYNAMIC_TRACKING, FALSE
};
static OBJECT_ATTRIBUTES soa = { sizeof(soa), 0, 0, 0, 0, &sqos };
CLIENT_ID ClientId = { pspi->UniqueProcessId };
if (0 <= NtOpenProcess(&hProcess, PROCESS_QUERY_LIMITED_INFORMATION, &zoa, &ClientId))
{
status = NtOpenProcessToken(hProcess, TOKEN_DUPLICATE, &hToken);
NtClose(hProcess);
if (0 <= status)
{
status = NtDuplicateToken(hToken, TOKEN_ADJUST_PRIVILEGES|TOKEN_IMPERSONATE,
&soa, FALSE, TokenImpersonation, &hNewToken);
NtClose(hToken);
if (0 <= status)
{
static const TOKEN_PRIVILEGES tp = { 1, { { { SE_DEBUG_PRIVILEGE } } } };
status = NtAdjustPrivilegesToken(hNewToken, FALSE, const_cast<PTOKEN_PRIVILEGES>(&tp), 0, 0, 0);
if (STATUS_SUCCESS == status)
{
status = NtSetInformationThread(NtCurrentThread(), ThreadImpersonationToken, &hNewToken, sizeof(hNewToken));
}
NtClose(hNewToken);
if (STATUS_SUCCESS == status)
{
return STATUS_SUCCESS;
}
}
}
}
}
} while (NextEntryOffset = pspi->NextEntryOffset);
return STATUS_UNSUCCESSFUL;
}
NTSTATUS ImpersonateSystemOrTcbToken(bool bTcb)
{
NTSTATUS status;
ULONG cb = 0x10000;
do
{
status = STATUS_INSUFFICIENT_RESOURCES;
if (PBYTE buf = new BYTE[cb += 0x1000])
{
if (0 <= (status = NtQuerySystemInformation(SystemProcessInformation, buf, cb, &cb)))
{
status = (bTcb ? GetTcbToken : GetSystemToken)(buf);
if (status == STATUS_INFO_LENGTH_MISMATCH)
{
status = STATUS_UNSUCCESSFUL;
}
}
delete [] buf;
}
} while(status == STATUS_INFO_LENGTH_MISMATCH);
return status;
}