#include "Helper.h"

#define HELPER_ALLOC_TAG 'rplH'

NTSTATUS QuerySystemInformation(SYSTEM_INFORMATION_CLASS Class, PVOID* InfoBuffer, PSIZE_T InfoSize)
{
	PVOID info = NULL;
	NTSTATUS status;
	ULONG size = 0, written = 0;

	
	status = ZwQuerySystemInformation(Class, 0, 0, &size);
	if (status != STATUS_INFO_LENGTH_MISMATCH)
		return status;

	while (status == STATUS_INFO_LENGTH_MISMATCH)
	{
		size += written; 

		if (info)
			ExFreePoolWithTag(info, HELPER_ALLOC_TAG);

		info = ExAllocatePoolWithTag(NonPagedPool, size, HELPER_ALLOC_TAG);
		if (!info)
			break;

		status = ZwQuerySystemInformation(Class, info, size, &written);
	}

	if (!info)
		return STATUS_ACCESS_DENIED;

	if (!NT_SUCCESS(status))
	{
		ExFreePoolWithTag(info, HELPER_ALLOC_TAG);
		return status;
	}

	*InfoBuffer = info;
	*InfoSize = size;
	
	return status;
}

NTSTATUS QueryProcessInformation(PROCESSINFOCLASS Class, HANDLE Process, PVOID* InfoBuffer, PSIZE_T InfoSize)
{
	PVOID info = NULL;
	NTSTATUS status;
	ULONG size = 0, written = 0;

	// Query required size
	status = ZwQueryInformationProcess(Process, Class, 0, 0, &size);
	if (status != STATUS_INFO_LENGTH_MISMATCH)
		return status;

	while (status == STATUS_INFO_LENGTH_MISMATCH)
	{
		size += written; 

		if (info)
			ExFreePoolWithTag(info, HELPER_ALLOC_TAG);

		info = ExAllocatePoolWithTag(NonPagedPool, size, HELPER_ALLOC_TAG);
		if (!info)
			break;

		status = ZwQueryInformationProcess(Process, Class, info, size, &written);
	}

	if (!info)
		return STATUS_ACCESS_DENIED;

	if (!NT_SUCCESS(status))
	{
		ExFreePoolWithTag(info, HELPER_ALLOC_TAG);
		return status;
	}

	*InfoBuffer = info;
	*InfoSize = size;

	return status;
}

VOID FreeInformation(PVOID Buffer)
{
	ExFreePoolWithTag(Buffer, HELPER_ALLOC_TAG);
}

NTSTATUS ResolveSymbolicLink(PUNICODE_STRING Link, PUNICODE_STRING Resolved)
{
	OBJECT_ATTRIBUTES attribs;
	HANDLE hsymLink;
	ULONG written;
	NTSTATUS status = STATUS_SUCCESS;

	

	InitializeObjectAttributes(&attribs, Link, OBJ_KERNEL_HANDLE, NULL, NULL);

	status = ZwOpenSymbolicLinkObject(&hsymLink, GENERIC_READ, &attribs);
	if (!NT_SUCCESS(status))
		return status;


	status = ZwQuerySymbolicLinkObject(hsymLink, Resolved, &written);
	ZwClose(hsymLink);
	if (!NT_SUCCESS(status))
		return status;

	return status;
}


NTSTATUS NormalizeDevicePath(PCUNICODE_STRING Path, PUNICODE_STRING Normalized)
{
	UNICODE_STRING globalPrefix, dvcPrefix, sysrootPrefix;
	NTSTATUS status;
	
	RtlInitUnicodeString(&globalPrefix, L"\\??\\");
	RtlInitUnicodeString(&dvcPrefix, L"\\Device\\");
	RtlInitUnicodeString(&sysrootPrefix, L"\\SystemRoot\\");

	if (RtlPrefixUnicodeString(&globalPrefix, Path, TRUE))
	{
		OBJECT_ATTRIBUTES attribs;
		UNICODE_STRING subPath;
		HANDLE hsymLink;
		ULONG i, written, size;

		subPath.Buffer = (PWCH)((PUCHAR)Path->Buffer + globalPrefix.Length);
		subPath.Length = Path->Length - globalPrefix.Length;

		for (i = 0; i < subPath.Length; i++)
		{
			if (subPath.Buffer[i] == L'\\')
			{
				subPath.Length = (USHORT)(i * sizeof(WCHAR));
				break;
			}
		}

		if (subPath.Length == 0)
			return STATUS_INVALID_PARAMETER_1;

		subPath.Buffer = Path->Buffer;
		subPath.Length += globalPrefix.Length;
		subPath.MaximumLength = subPath.Length;

		

		InitializeObjectAttributes(&attribs, &subPath, OBJ_KERNEL_HANDLE, NULL, NULL);

		status = ZwOpenSymbolicLinkObject(&hsymLink, GENERIC_READ, &attribs);
		if (!NT_SUCCESS(status))
			return status;

		

		status = ZwQuerySymbolicLinkObject(hsymLink, Normalized, &written);
		ZwClose(hsymLink);
		if (!NT_SUCCESS(status))
			return status;

		

		size = Path->Length - subPath.Length + Normalized->Length;
		if (size > Normalized->MaximumLength)
			return STATUS_BUFFER_OVERFLOW;

		subPath.Buffer = (PWCH)((PUCHAR)Path->Buffer + subPath.Length);
		subPath.Length = Path->Length - subPath.Length;
		subPath.MaximumLength = subPath.Length;

		status = RtlAppendUnicodeStringToString(Normalized, &subPath);
		if (!NT_SUCCESS(status))
			return status;
	}
	else if (RtlPrefixUnicodeString(&dvcPrefix, Path, TRUE))
	{
		Normalized->Length = 0;
		status = RtlAppendUnicodeStringToString(Normalized, Path);
		if (!NT_SUCCESS(status))
			return status;
	}
	else if (RtlPrefixUnicodeString(&sysrootPrefix, Path, TRUE))
	{
		UNICODE_STRING subPath, resolvedLink, winDir;
		WCHAR buffer[64];
		SHORT i;

		

		subPath.Buffer = sysrootPrefix.Buffer;
		subPath.MaximumLength = subPath.Length = sysrootPrefix.Length - sizeof(WCHAR);

		resolvedLink.Buffer = buffer;
		resolvedLink.Length = 0;
		resolvedLink.MaximumLength = sizeof(buffer);

		status = ResolveSymbolicLink(&subPath, &resolvedLink);
		if (!NT_SUCCESS(status))
			return status;

		

		winDir.Length = 0;
		for (i = (resolvedLink.Length - sizeof(WCHAR)) / sizeof(WCHAR); i >= 0; i--)
		{
			if (resolvedLink.Buffer[i] == L'\\')
			{
				winDir.Buffer = resolvedLink.Buffer + i;
				winDir.Length = resolvedLink.Length - (i * sizeof(WCHAR));
				winDir.MaximumLength = winDir.Length;
				resolvedLink.Length = (i * sizeof(WCHAR));
				break;
			}
		}

		
		status = ResolveSymbolicLink(&resolvedLink, Normalized);
		if (!NT_SUCCESS(status))
			return status;

	

		subPath.Buffer = (PWCHAR)((PCHAR)Path->Buffer + sysrootPrefix.Length - sizeof(WCHAR));
		subPath.MaximumLength = subPath.Length = Path->Length - sysrootPrefix.Length + sizeof(WCHAR);

		status = RtlAppendUnicodeStringToString(Normalized, &winDir);
		if (!NT_SUCCESS(status))
			return status;

		status = RtlAppendUnicodeStringToString(Normalized, &subPath);
		if (!NT_SUCCESS(status))
			return status;
	}
	else
	{
		return STATUS_INVALID_PARAMETER;
	}

	return STATUS_SUCCESS;
}
