Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 3 additions & 1 deletion CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,8 @@ set(CMAKE_CXX_FLAGS_FUZZERDEBUG "${CMAKE_CXX_FLAGS_DEBUG} /fsanitize-coverage=in
# list(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/external/FindWDK/cmake")
# find_package(WDK REQUIRED)

set(NUGET_PACKAGE_VERSION "10.0.26100.4204")

set(NUGET_PACKAGES
"Microsoft.Windows.SDK.CPP"
"Microsoft.Windows.SDK.CPP.arm64"
Expand All @@ -39,7 +41,7 @@ if(NOT NUGET)
message("ERROR: You must first install nuget.exe from https://www.nuget.org/downloads")
else()
foreach(PACKAGE ${NUGET_PACKAGES})
execute_process(COMMAND ${NUGET} install ${PACKAGE} -ExcludeVersion -OutputDirectory ${PROJECT_BINARY_DIR}/packages)
execute_process(COMMAND ${NUGET} install ${PACKAGE} -Version ${NUGET_PACKAGE_VERSION} -ExcludeVersion -OutputDirectory ${PROJECT_BINARY_DIR}/packages)
endforeach()
endif()

Expand Down
14 changes: 14 additions & 0 deletions inc/usersim/ps.h
Original file line number Diff line number Diff line change
Expand Up @@ -104,4 +104,18 @@ void
usersime_invoke_process_creation_notify_routine(
_Inout_ PEPROCESS process, _In_ HANDLE process_id, _Inout_opt_ PPS_CREATE_NOTIFY_INFO create_info);

typedef PACCESS_TOKEN PEPROCESS_ACCESS_TOKEN;

USERSIM_API
PACCESS_TOKEN
PsReferencePrimaryToken(_In_ PEPROCESS process);

USERSIM_API
VOID
PsDereferencePrimaryToken(_In_ PACCESS_TOKEN token);

USERSIM_API
void
usersim_clean_up_ps();

CXPLAT_EXTERN_C_END
5 changes: 5 additions & 0 deletions inc/usersim/rtl.h
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,11 @@ USERSIM_API
BOOLEAN
RtlValidSid(_In_ PSID sid);

USERSIM_API
NTSTATUS
RtlCopySid(
_In_ ULONG DestinationSidLength, _Out_writes_bytes_(DestinationSidLength) PSID DestinationSid, _In_ PSID SourceSid);

USERSIM_API
NTSTATUS
NTAPI
Expand Down
16 changes: 16 additions & 0 deletions inc/usersim/se.h
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@

#include "..\src\platform.h"
#include "ke.h"
#include "rtl.h"

CXPLAT_EXTERN_C_BEGIN

Expand Down Expand Up @@ -180,6 +181,21 @@ USERSIM_API
NTSTATUS
SeQueryAuthenticationIdToken(_In_ PACCESS_TOKEN token, _Out_ PLUID authentication_id);

USERSIM_API
NTSTATUS
SeQueryInformationToken(
_In_ PACCESS_TOKEN token, _In_ TOKEN_INFORMATION_CLASS token_information_class, _Out_ PVOID* token_information);

USERSIM_API
NTSTATUS
SecLookupAccountSid(
_In_ PSID Sid,
_Out_ PULONG NameSize,
_Inout_ PUNICODE_STRING NameBuffer,
_Out_ PULONG DomainSize,
_Out_opt_ PUNICODE_STRING DomainBuffer,
_Out_ PSID_NAME_USE NameUse);

void
usersim_initialize_se();

Expand Down
2 changes: 2 additions & 0 deletions src/platform_user.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
#include "usersim/ex.h"
#include "usersim/ke.h"
#include "usersim/mm.h"
#include "usersim/ps.h"
#include "usersim/se.h"
#include "usersim/wdf.h"
#include "utilities.h"
Expand Down Expand Up @@ -104,6 +105,7 @@ usersim_platform_terminate()

usersim_free_semaphores();
usersim_free_threadpool_timers();
usersim_clean_up_ps();
usersim_clean_up_dpcs();
usersim_clean_up_irql();
if (_cxplat_initialized) {
Expand Down
30 changes: 29 additions & 1 deletion src/ps.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,12 @@
#include "platform.h"
#include "usersim/ps.h"

#include <assert.h>

// Ps* functions.

static ULONG _primary_token_ref_count = 0;

HANDLE
PsGetCurrentProcessId() { return (HANDLE)(uintptr_t)GetCurrentProcessId(); }

Expand Down Expand Up @@ -212,4 +216,28 @@ usersime_invoke_process_creation_notify_routine(
if (_usersim_process_creation_notify_routine != NULL) {
_usersim_process_creation_notify_routine(process, process_id, create_info);
}
}
}

PACCESS_TOKEN
PsReferencePrimaryToken(_In_ PEPROCESS process)
Comment thread
LakshK98 marked this conversation as resolved.
{
UNREFERENCED_PARAMETER(process);
_primary_token_ref_count++;
// In user mode, return the current process token handle as a pseudo-token.
return (PACCESS_TOKEN)GetCurrentProcessToken();
}

VOID
PsDereferencePrimaryToken(_In_ PACCESS_TOKEN token)
{
UNREFERENCED_PARAMETER(token);
assert(_primary_token_ref_count > 0);
_primary_token_ref_count--;
}

void
usersim_clean_up_ps()
{
assert(_primary_token_ref_count == 0);
_primary_token_ref_count = 0;
}
13 changes: 13 additions & 0 deletions src/rtl.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,19 @@ RtlValidSid(_In_ PSID sid)
return (sid != nullptr);
}

NTSTATUS
RtlCopySid(
_In_ ULONG DestinationSidLength, _Out_writes_bytes_(DestinationSidLength) PSID DestinationSid, _In_ PSID SourceSid)
{
ULONG source_sid_length = RtlLengthSid(SourceSid);
if (DestinationSidLength < source_sid_length) {
return STATUS_BUFFER_TOO_SMALL;
}

memcpy(DestinationSid, SourceSid, source_sid_length);
return STATUS_SUCCESS;
}

NTSTATUS
RtlAddAccessAllowedAce(_Inout_ PACL Acl, _In_ unsigned long AceRevision, _In_ ACCESS_MASK AccessMask, _In_ PSID Sid)
{
Expand Down
148 changes: 148 additions & 0 deletions src/se.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -250,3 +250,151 @@ SeQueryAuthenticationIdToken(_In_ PACCESS_TOKEN token, _Out_ PLUID authenticatio
usersim_convert_sid_to_luid(((PTOKEN_OWNER)token_owner_buffer)->Owner, authentication_id);
return STATUS_SUCCESS;
}

NTSTATUS
SeQueryInformationToken(
_In_ PACCESS_TOKEN token, _In_ TOKEN_INFORMATION_CLASS token_information_class, _Out_ PVOID* token_information)
{
*token_information = nullptr;

if (cxplat_fault_injection_inject_fault()) {
return STATUS_UNSUCCESSFUL;
}

HANDLE token_handle = (HANDLE)token;
DWORD needed = 0;

// Get required buffer size.
(void)GetTokenInformation(token_handle, token_information_class, nullptr, 0, &needed);
DWORD error = GetLastError();
if (error != ERROR_INSUFFICIENT_BUFFER || needed == 0) {
return win32_error_to_usersim_error(error);
}

void* buffer = ExAllocatePoolUninitialized(NonPagedPoolNx, needed, USERSIM_TAG_TOKEN_ACCESS_INFORMATION);
if (buffer == nullptr) {
return STATUS_NO_MEMORY;
}

if (!GetTokenInformation(token_handle, token_information_class, buffer, needed, &needed)) {
ExFreePool(buffer);
return win32_error_to_usersim_error(GetLastError());
}

*token_information = buffer;
return STATUS_SUCCESS;
}

NTSTATUS
SecLookupAccountSid(
_In_ PSID Sid,
_Out_ PULONG NameSize,
_Inout_ PUNICODE_STRING NameBuffer,
_Out_ PULONG DomainSize,
_Out_opt_ PUNICODE_STRING DomainBuffer,
_Out_ PSID_NAME_USE NameUse)
{
if (cxplat_fault_injection_inject_fault()) {
return STATUS_UNSUCCESSFUL;
}

if (Sid == nullptr || NameSize == nullptr || DomainSize == nullptr || NameUse == nullptr) {
return STATUS_INVALID_PARAMETER;
}

// Use LookupAccountSidW to resolve the SID in user mode.
DWORD name_chars = 0;
DWORD domain_chars = 0;
SID_NAME_USE name_use;

// First call to get required buffer sizes (in characters including null terminator).
(void)LookupAccountSidW(nullptr, Sid, nullptr, &name_chars, nullptr, &domain_chars, &name_use);
DWORD error = GetLastError();
if (error != ERROR_INSUFFICIENT_BUFFER) {
return win32_error_to_usersim_error(error);
}

// If caller just wants sizes (NameBuffer and DomainBuffer are NULL), return sizes in bytes.
if (NameBuffer == nullptr && DomainBuffer == nullptr) {
// Return sizes as byte counts (without null terminator) to match kernel SecLookupAccountSid behavior.
*NameSize = (name_chars > 0) ? (ULONG)((name_chars - 1) * sizeof(WCHAR)) : 0;
*DomainSize = (domain_chars > 0) ? (ULONG)((domain_chars - 1) * sizeof(WCHAR)) : 0;
*NameUse = name_use;
return STATUS_BUFFER_TOO_SMALL;
}

// Allocate temporary buffers for the LookupAccountSidW call.
WCHAR* name_buf = nullptr;
WCHAR* domain_buf = nullptr;

if (name_chars > 0) {
name_buf =
(WCHAR*)ExAllocatePoolUninitialized(NonPagedPoolNx, name_chars * sizeof(WCHAR), USERSIM_TAG_ACCOUNT_NAME);
if (name_buf == nullptr) {
return STATUS_NO_MEMORY;
}
}
if (domain_chars > 0) {
domain_buf =
(WCHAR*)ExAllocatePoolUninitialized(NonPagedPoolNx, domain_chars * sizeof(WCHAR), USERSIM_TAG_ACCOUNT_NAME);
if (domain_buf == nullptr) {
ExFreePool(name_buf);
return STATUS_NO_MEMORY;
}
}

if (!LookupAccountSidW(nullptr, Sid, name_buf, &name_chars, domain_buf, &domain_chars, &name_use)) {
if (name_buf != nullptr) {
ExFreePool(name_buf);
}
if (domain_buf != nullptr) {
ExFreePool(domain_buf);
}
return win32_error_to_usersim_error(GetLastError());
}

// Copy results into caller-provided UNICODE_STRING buffers.
// *NameSize and *DomainSize are output-only:
// - On success: set to the actual number of bytes copied.
// - On truncation (buffer too small): set to the required buffer size.
BOOLEAN truncated = FALSE;
ULONG required_name_bytes = (name_chars > 0) ? (ULONG)(name_chars * sizeof(WCHAR)) : 0;
ULONG required_domain_bytes = (domain_chars > 0) ? (ULONG)(domain_chars * sizeof(WCHAR)) : 0;

if (NameBuffer != nullptr && NameBuffer->Buffer != nullptr && name_chars > 0) {
USHORT byte_len = (USHORT)required_name_bytes;
if (byte_len > NameBuffer->MaximumLength) {
byte_len = NameBuffer->MaximumLength;
truncated = TRUE;
}
memcpy(NameBuffer->Buffer, name_buf, byte_len);
NameBuffer->Length = byte_len;
*NameSize = required_name_bytes;
} else {
*NameSize = 0;
}

if (DomainBuffer != nullptr && DomainBuffer->Buffer != nullptr && domain_chars > 0) {
USHORT byte_len = (USHORT)required_domain_bytes;
if (byte_len > DomainBuffer->MaximumLength) {
byte_len = DomainBuffer->MaximumLength;
truncated = TRUE;
}
memcpy(DomainBuffer->Buffer, domain_buf, byte_len);
DomainBuffer->Length = byte_len;
*DomainSize = required_domain_bytes;
} else {
*DomainSize = 0;
}

*NameUse = name_use;

if (name_buf != nullptr) {
ExFreePool(name_buf);
}
if (domain_buf != nullptr) {
ExFreePool(domain_buf);
}

return truncated ? STATUS_BUFFER_TOO_SMALL : STATUS_SUCCESS;
}
1 change: 1 addition & 0 deletions src/tags.h
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
#pragma once

// Pool tags used by the usersim library.
#define USERSIM_TAG_ACCOUNT_NAME 'ansu'
#define USERSIM_TAG_ETW_PROVIDER 'pesu'
#define USERSIM_TAG_FWPS_CONNECT_REQUEST0 'cfsu'
#define USERSIM_TAG_HANDLE 'ahsu'
Expand Down
10 changes: 10 additions & 0 deletions tests/ps_test.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -139,3 +139,13 @@ TEST_CASE("PsGetProcessStartKey", "[ps]")
key = PsGetProcessStartKey(nullptr);
REQUIRE(key == 0);
}

TEST_CASE("PsReferencePrimaryToken", "[ps]")
{
// PsReferencePrimaryToken should return a non-null pseudo-token.
PACCESS_TOKEN token = PsReferencePrimaryToken(nullptr);
REQUIRE(token != nullptr);

// PsDereferencePrimaryToken is a no-op but should not crash.
PsDereferencePrimaryToken(token);
}
26 changes: 26 additions & 0 deletions tests/rtl_test.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -438,4 +438,30 @@ TEST_CASE("RtlNumberGenericTableElementsAvl", "[rtl]")
for (int i = 0; i < 100; ++i) {
REQUIRE(RtlDeleteElementGenericTableAvl(&table, &i));
}
}

TEST_CASE("RtlCopySid", "[rtl]")
{
// Build a SID with 1 sub-authority (S-1-5-18, i.e. Local System).
SID source_sid = {0};
source_sid.Revision = SID_REVISION;
source_sid.SubAuthorityCount = 1;
source_sid.IdentifierAuthority = SECURITY_NT_AUTHORITY;
source_sid.SubAuthority[0] = SECURITY_LOCAL_SYSTEM_RID;

ULONG sid_length = RtlLengthSid(&source_sid);
REQUIRE(sid_length > 0);

// Successful copy.
BYTE destination_buffer[SECURITY_MAX_SID_SIZE] = {0};
REQUIRE(NT_SUCCESS(RtlCopySid(sizeof(destination_buffer), (PSID)destination_buffer, &source_sid)));
REQUIRE(memcmp(destination_buffer, &source_sid, sid_length) == 0);

// Buffer too small should fail.
REQUIRE(RtlCopySid(sid_length - 1, (PSID)destination_buffer, &source_sid) == STATUS_BUFFER_TOO_SMALL);

// Exact size should succeed.
memset(destination_buffer, 0, sizeof(destination_buffer));
REQUIRE(NT_SUCCESS(RtlCopySid(sid_length, (PSID)destination_buffer, &source_sid)));
REQUIRE(memcmp(destination_buffer, &source_sid, sid_length) == 0);
}
Loading