diff --git a/Brovan/Core/Emulation/OS/Windows/Misc/NtQueryObject.cs b/Brovan/Core/Emulation/OS/Windows/Misc/NtQueryObject.cs index 119f652..cb96f73 100644 --- a/Brovan/Core/Emulation/OS/Windows/Misc/NtQueryObject.cs +++ b/Brovan/Core/Emulation/OS/Windows/Misc/NtQueryObject.cs @@ -264,6 +264,8 @@ private string GetObjectTypeName(BinaryEmulator Instance, ulong Handle, IHandleO return "TpWorkerFactory"; if (HandleObject is WinWaitCompletionPacket) return "WaitCompletionPacket"; + if (HandleObject is WinJob) + return "Job"; return HandleObject.ObjectType.ToString(); } @@ -302,6 +304,8 @@ private string GetObjectName(BinaryEmulator Instance, ulong Handle, IHandleObjec return WorkerFactory.Name ?? string.Empty; if (HandleObject is WinWaitCompletionPacket Packet) return Packet.Name ?? string.Empty; + if (HandleObject is WinJob Job) + return Job.Name ?? string.Empty; return string.Empty; } diff --git a/Brovan/Core/Emulation/OS/Windows/Process/NtAssignProcessToJobObject.cs b/Brovan/Core/Emulation/OS/Windows/Process/NtAssignProcessToJobObject.cs new file mode 100644 index 0000000..6e947ef --- /dev/null +++ b/Brovan/Core/Emulation/OS/Windows/Process/NtAssignProcessToJobObject.cs @@ -0,0 +1,22 @@ +using static Brovan.Core.Helpers.BinaryHelpers; + +namespace Brovan.Core.Emulation.OS.Windows +{ + internal class NtAssignProcessToJobObject : IWinSyscall + { + public NTSTATUS Handle(BinaryEmulator Instance) + { + if (Instance._binary.Architecture == BinaryArchitecture.x64) + { + ulong JobHandle = Instance.WinHelper.GetArg64(0); + ulong ProcessHandle = Instance.WinHelper.GetArg64(1); + return Instance.WinHelper.AssignProcessToJobHandle(JobHandle, ProcessHandle); + } + + uint SP = Instance.ReadRegister32(Registers.UC_X86_REG_ESP); + uint JobHandle32 = Instance.ReadMemoryUInt(SP + 4); + uint ProcessHandle32 = Instance.ReadMemoryUInt(SP + 8); + return Instance.WinHelper.AssignProcessToJobHandle(JobHandle32, ProcessHandle32); + } + } +} diff --git a/Brovan/Core/Emulation/OS/Windows/Process/NtCreateJobObject.cs b/Brovan/Core/Emulation/OS/Windows/Process/NtCreateJobObject.cs new file mode 100644 index 0000000..3cfc6da --- /dev/null +++ b/Brovan/Core/Emulation/OS/Windows/Process/NtCreateJobObject.cs @@ -0,0 +1,70 @@ +using static Brovan.Core.Helpers.BinaryHelpers; + +namespace Brovan.Core.Emulation.OS.Windows +{ + internal class NtCreateJobObject : IWinSyscall + { + public NTSTATUS Handle(BinaryEmulator Instance) + { + if (Instance._binary.Architecture == BinaryArchitecture.x64) + { + ulong JobHandlePtr = Instance.WinHelper.GetArg64(0); + ulong DesiredAccess = (uint)Instance.WinHelper.GetArg64(1); + ulong ObjectAttributesPtr = Instance.WinHelper.GetArg64(2); + + if (JobHandlePtr == 0) + return NTSTATUS.STATUS_INVALID_PARAMETER; + + if (!Instance.IsRegionMapped(JobHandlePtr, 8)) + return NTSTATUS.STATUS_ACCESS_VIOLATION; + + string Name = string.Empty; + if (ObjectAttributesPtr != 0) + { + if (!Instance.IsRegionMapped(ObjectAttributesPtr, 0x30)) + return NTSTATUS.STATUS_ACCESS_VIOLATION; + + if (!StructSerializer.ParseStruct(Instance, ObjectAttributesPtr, out OBJECT_ATTRIBUTES64 ObjectAttrs)) + return NTSTATUS.STATUS_ACCESS_VIOLATION; + + if (ObjectAttrs.ObjectName != 0 && !Instance.WinHelper.TryReadUnicodeString64(ObjectAttrs.ObjectName, out Name, out NTSTATUS NameStatus)) + return NameStatus; + } + + WinHandle Handle = Instance.WinHelper.CreateJobHandle(Name, (AccessMask)(uint)DesiredAccess); + if (!Instance._emulator.WriteMemory(JobHandlePtr, Handle.Handle)) + return NTSTATUS.STATUS_ACCESS_VIOLATION; + + return NTSTATUS.STATUS_SUCCESS; + } + + uint SP = Instance.ReadRegister32(Registers.UC_X86_REG_ESP); + uint JobHandlePtr32 = Instance.ReadMemoryUInt(SP + 4); + uint DesiredAccess32 = Instance.ReadMemoryUInt(SP + 8); + uint ObjectAttributesPtr32 = Instance.ReadMemoryUInt(SP + 12); + + if (JobHandlePtr32 == 0) + return NTSTATUS.STATUS_INVALID_PARAMETER; + + if (!Instance.IsRegionMapped(JobHandlePtr32, 4)) + return NTSTATUS.STATUS_ACCESS_VIOLATION; + + string Name32 = string.Empty; + if (ObjectAttributesPtr32 != 0) + { + if (!Instance.IsRegionMapped(ObjectAttributesPtr32, 0x18)) + return NTSTATUS.STATUS_ACCESS_VIOLATION; + + uint ObjectNamePtr32 = Instance.ReadMemoryUInt(ObjectAttributesPtr32 + 0x08); + if (ObjectNamePtr32 != 0 && !Instance.WinHelper.TryReadUnicodeString32(ObjectNamePtr32, out Name32, out NTSTATUS NameStatus32)) + return NameStatus32; + } + + WinHandle Handle32 = Instance.WinHelper.CreateJobHandle(Name32, (AccessMask)DesiredAccess32); + if (!Instance._emulator.WriteMemory(JobHandlePtr32, (uint)Handle32.Handle)) + return NTSTATUS.STATUS_ACCESS_VIOLATION; + + return NTSTATUS.STATUS_SUCCESS; + } + } +} diff --git a/Brovan/Core/Emulation/OS/Windows/Process/NtIsProcessInJob.cs b/Brovan/Core/Emulation/OS/Windows/Process/NtIsProcessInJob.cs new file mode 100644 index 0000000..d94232b --- /dev/null +++ b/Brovan/Core/Emulation/OS/Windows/Process/NtIsProcessInJob.cs @@ -0,0 +1,29 @@ +using System.Reflection.Metadata; +using static Brovan.Core.Helpers.BinaryHelpers; + +namespace Brovan.Core.Emulation.OS.Windows +{ + internal class NtIsProcessInJob : IWinSyscall + { + public NTSTATUS Handle(BinaryEmulator Instance) + { + if (Instance._binary.Architecture == BinaryArchitecture.x64) + { + ulong ProcessHandle = Instance.WinHelper.GetArg64(0); + ulong JobHandle = Instance.WinHelper.GetArg64(1); + if (!Instance.WinHelper.HandleExists(JobHandle)) + return NTSTATUS.STATUS_INVALID_HANDLE; + bool IsInJob = Instance.WinHelper.IsProcessInJob(ProcessHandle, JobHandle); + return IsInJob ? NTSTATUS.STATUS_PROCESS_IN_JOB : NTSTATUS.STATUS_SUCCESS; + } + + uint SP = Instance.ReadRegister32(Registers.UC_X86_REG_ESP); + uint ProcessHandle32 = Instance.ReadMemoryUInt(SP + 4); + uint JobHandle32 = Instance.ReadMemoryUInt(SP + 8); + bool IsInJob32 = Instance.WinHelper.IsProcessInJob(ProcessHandle32, JobHandle32); + if (!Instance.WinHelper.HandleExists(JobHandle32)) + return NTSTATUS.STATUS_INVALID_HANDLE; + return IsInJob32 ? NTSTATUS.STATUS_PROCESS_IN_JOB : NTSTATUS.STATUS_SUCCESS; + } + } +} diff --git a/Brovan/Core/Emulation/OS/Windows/Process/NtQueryInformationJobObject.cs b/Brovan/Core/Emulation/OS/Windows/Process/NtQueryInformationJobObject.cs new file mode 100644 index 0000000..b50f2d6 --- /dev/null +++ b/Brovan/Core/Emulation/OS/Windows/Process/NtQueryInformationJobObject.cs @@ -0,0 +1,366 @@ +using System; +using System.Buffers.Binary; +using System.Linq; +using static Brovan.Core.Helpers.BinaryHelpers; + +namespace Brovan.Core.Emulation.OS.Windows +{ + internal class NtQueryInformationJobObject : IWinSyscall + { + public NTSTATUS Handle(BinaryEmulator Instance) + { + if (Instance._binary.Architecture == BinaryArchitecture.x64) + { + ulong JobHandle = Instance.WinHelper.GetArg64(0); + ulong JobObjectInformationClass = (uint)Instance.WinHelper.GetArg64(1); + ulong JobObjectInformation = Instance.WinHelper.GetArg64(2); + ulong JobObjectInformationLength = (uint)Instance.WinHelper.GetArg64(3); + ulong ReturnLength = Instance.WinHelper.GetArg64(4); + return HandleQuery(Instance, JobHandle, JobObjectInformationClass, JobObjectInformation, JobObjectInformationLength, ReturnLength); + } + + uint SP = Instance.ReadRegister32(Registers.UC_X86_REG_ESP); + uint JobHandle32 = Instance.ReadMemoryUInt(SP + 4); + uint JobObjectInformationClass32 = Instance.ReadMemoryUInt(SP + 8); + uint JobObjectInformation32 = Instance.ReadMemoryUInt(SP + 12); + uint JobObjectInformationLength32 = Instance.ReadMemoryUInt(SP + 16); + uint ReturnLength32 = Instance.ReadMemoryUInt(SP + 20); + return HandleQuery(Instance, JobHandle32, JobObjectInformationClass32, JobObjectInformation32, JobObjectInformationLength32, ReturnLength32); + } + + private static NTSTATUS HandleQuery(BinaryEmulator Instance, ulong JobHandle, ulong JobObjectInformationClass, ulong JobObjectInformation, ulong JobObjectInformationLength, ulong ReturnLength) + { + NTSTATUS ResolveStatus = ResolveJob(Instance, JobHandle, out WinJob Job); + if (ResolveStatus != NTSTATUS.STATUS_SUCCESS) + return ResolveStatus; + + if (JobObjectInformationLength > uint.MaxValue) + return NTSTATUS.STATUS_INVALID_PARAMETER; + + JOBOBJECTINFOCLASS InfoClass = (JOBOBJECTINFOCLASS)JobObjectInformationClass; + uint RequiredLength = GetRequiredLength(Instance, Job, InfoClass, out NTSTATUS Status); + if (Status != NTSTATUS.STATUS_SUCCESS) + return Status; + + if (ReturnLength != 0) + { + if (!Instance.IsRegionMapped(ReturnLength, 4)) + return NTSTATUS.STATUS_ACCESS_VIOLATION; + + if (!Instance._emulator.WriteMemory(ReturnLength, RequiredLength)) + return NTSTATUS.STATUS_ACCESS_VIOLATION; + } + + if (JobObjectInformationLength < RequiredLength) + return NTSTATUS.STATUS_INFO_LENGTH_MISMATCH; + + if (JobObjectInformation == 0) + return NTSTATUS.STATUS_INVALID_PARAMETER; + + if (!Instance.IsRegionMapped(JobObjectInformation, RequiredLength)) + return NTSTATUS.STATUS_ACCESS_VIOLATION; + + switch (InfoClass) + { + case JOBOBJECTINFOCLASS.JobObjectBasicAccountingInformation: + return WriteBasicAccountingInformation(Instance, Job, JobObjectInformation); + case JOBOBJECTINFOCLASS.JobObjectBasicAndIoAccountingInformation: + return WriteBasicAndIoAccountingInformation(Instance, Job, JobObjectInformation); + case JOBOBJECTINFOCLASS.JobObjectBasicLimitInformation: + return WriteBasicLimitInformation(Instance, Job, JobObjectInformation); + case JOBOBJECTINFOCLASS.JobObjectBasicProcessIdList: + return WriteBasicProcessIdList(Instance, Job, JobObjectInformation, RequiredLength); + case JOBOBJECTINFOCLASS.JobObjectBasicUIRestrictions: + return WriteUInt32Value(Instance, JobObjectInformation, Job.UiRestrictionsClass); + case JOBOBJECTINFOCLASS.JobObjectEndOfJobTimeInformation: + return WriteUInt32Value(Instance, JobObjectInformation, Job.EndOfJobTimeAction); + case JOBOBJECTINFOCLASS.JobObjectExtendedLimitInformation: + return WriteExtendedLimitInformation(Instance, Job, JobObjectInformation); + case JOBOBJECTINFOCLASS.JobObjectNotificationLimitInformation: + return WriteNotificationLimitInformation(Instance, Job, JobObjectInformation); + case JOBOBJECTINFOCLASS.JobObjectCpuRateControlInformation: + return WriteCpuRateControlInformation(Instance, Job, JobObjectInformation); + case JOBOBJECTINFOCLASS.JobObjectNetRateControlInformation: + return WriteNetRateControlInformation(Instance, Job, JobObjectInformation); + case JOBOBJECTINFOCLASS.JobObjectGroupInformation: + case JOBOBJECTINFOCLASS.JobObjectGroupInformationEx: + case JOBOBJECTINFOCLASS.JobObjectLimitViolationInformation: + case JOBOBJECTINFOCLASS.JobObjectLimitViolationInformation2: + case JOBOBJECTINFOCLASS.JobObjectNotificationLimitInformation2: + case JOBOBJECTINFOCLASS.JobObjectReserved1Information: + return NTSTATUS.STATUS_NOT_SUPPORTED; + default: + return NTSTATUS.STATUS_INVALID_INFO_CLASS; + } + } + + private static NTSTATUS ResolveJob(BinaryEmulator Instance, ulong JobHandle, out WinJob Job) + { + Job = null; + + if (JobHandle == 0) + { + WinProcess CurrentProcess = Instance.WinHelper.WinProcesses.FirstOrDefault(p => p.PID == Instance.WinHelper.PID); + if (CurrentProcess == null || CurrentProcess.JobObjectHandle == 0) + return NTSTATUS.STATUS_INVALID_HANDLE; + + Job = Instance.WinHelper.GetJobByHandle(CurrentProcess.JobObjectHandle, AccessMask.GiveTemp); + return Job != null ? NTSTATUS.STATUS_SUCCESS : NTSTATUS.STATUS_INVALID_HANDLE; + } + + if (!Instance.WinHelper.HandleManager.HandleExists(JobHandle, HandleType.JobHandle)) + return NTSTATUS.STATUS_INVALID_HANDLE; + + Job = Instance.WinHelper.GetJobByHandle(JobHandle, AccessMask.GiveTemp); + return Job != null ? NTSTATUS.STATUS_SUCCESS : NTSTATUS.STATUS_INVALID_HANDLE; + } + + private static uint GetRequiredLength(BinaryEmulator Instance, WinJob Job, JOBOBJECTINFOCLASS InfoClass, out NTSTATUS Status) + { + Status = NTSTATUS.STATUS_SUCCESS; + + switch (InfoClass) + { + case JOBOBJECTINFOCLASS.JobObjectBasicAccountingInformation: + return 0x30; + case JOBOBJECTINFOCLASS.JobObjectBasicAndIoAccountingInformation: + return 0x60; + case JOBOBJECTINFOCLASS.JobObjectBasicLimitInformation: + return Instance._binary.Architecture == BinaryArchitecture.x64 ? 0x40u : 0x2Cu; + case JOBOBJECTINFOCLASS.JobObjectBasicProcessIdList: + return GetProcessIdListSize(Instance, Job); + case JOBOBJECTINFOCLASS.JobObjectBasicUIRestrictions: + return 0x04; + case JOBOBJECTINFOCLASS.JobObjectEndOfJobTimeInformation: + return 0x04; + case JOBOBJECTINFOCLASS.JobObjectExtendedLimitInformation: + return Instance._binary.Architecture == BinaryArchitecture.x64 ? 0x90u : 0x6Cu; + case JOBOBJECTINFOCLASS.JobObjectNotificationLimitInformation: + return 0x2C; + case JOBOBJECTINFOCLASS.JobObjectCpuRateControlInformation: + return 0x08; + case JOBOBJECTINFOCLASS.JobObjectNetRateControlInformation: + return 0x10; + case JOBOBJECTINFOCLASS.JobObjectGroupInformation: + case JOBOBJECTINFOCLASS.JobObjectGroupInformationEx: + case JOBOBJECTINFOCLASS.JobObjectLimitViolationInformation: + case JOBOBJECTINFOCLASS.JobObjectLimitViolationInformation2: + case JOBOBJECTINFOCLASS.JobObjectNotificationLimitInformation2: + case JOBOBJECTINFOCLASS.JobObjectReserved1Information: + Status = NTSTATUS.STATUS_NOT_SUPPORTED; + return 0; + default: + Status = NTSTATUS.STATUS_INVALID_INFO_CLASS; + return 0; + } + } + + private static uint GetProcessIdListSize(BinaryEmulator Instance, WinJob Job) + { + uint PtrSize = Instance._binary.Architecture == BinaryArchitecture.x64 ? 8u : 4u; + uint Count = (uint)Job.ProcessIds.Distinct().Count(); + return 8u + (PtrSize * Count); + } + + private static NTSTATUS WriteUInt32Value(BinaryEmulator Instance, ulong Address, uint Value) + { + Span Buffer = stackalloc byte[4]; + BinaryPrimitives.WriteUInt32LittleEndian(Buffer, Value); + return Instance._emulator.WriteMemory(Address, Buffer) ? NTSTATUS.STATUS_SUCCESS : NTSTATUS.STATUS_ACCESS_VIOLATION; + } + + private static NTSTATUS WriteBasicAccountingInformation(BinaryEmulator Instance, WinJob Job, ulong Address) + { + Span Buffer = stackalloc byte[0x30]; + if (!TryWriteBasicAccounting(Instance, Job, Buffer)) + return NTSTATUS.STATUS_ACCESS_VIOLATION; + + return Instance._emulator.WriteMemory(Address, Buffer) ? NTSTATUS.STATUS_SUCCESS : NTSTATUS.STATUS_ACCESS_VIOLATION; + } + + private static NTSTATUS WriteBasicAndIoAccountingInformation(BinaryEmulator Instance, WinJob Job, ulong Address) + { + Span Buffer = stackalloc byte[0x60]; + Buffer.Clear(); + if (!TryWriteBasicAccounting(Instance, Job, Buffer.Slice(0x00, 0x30))) + return NTSTATUS.STATUS_ACCESS_VIOLATION; + + // IO_COUNTERS is intentionally zeroed for now. + return Instance._emulator.WriteMemory(Address, Buffer) ? NTSTATUS.STATUS_SUCCESS : NTSTATUS.STATUS_ACCESS_VIOLATION; + } + + private static bool TryWriteBasicAccounting(BinaryEmulator Instance, WinJob Job, Span Buffer) + { + ulong TotalUserTime = 0; + ulong TotalKernelTime = 0; + uint TotalProcesses = 0; + uint ActiveProcesses = 0; + + foreach (uint ProcessId in Job.ProcessIds.Distinct()) + { + TotalProcesses++; + WinProcess Process = Instance.WinHelper.WinProcesses.FirstOrDefault(P => P.PID == ProcessId); + if (Process == null) + continue; + + Instance.WinHelper.UpdateProcessTimes(Process); + TotalUserTime += (ulong)Process.UserTime; + TotalKernelTime += (ulong)Process.KernelTime; + if (Process.ExitTime == 0) + ActiveProcesses++; + } + + uint TotalTerminatedProcesses = TotalProcesses >= ActiveProcesses ? TotalProcesses - ActiveProcesses : 0; + + BinaryPrimitives.WriteUInt64LittleEndian(Buffer.Slice(0x00, 8), TotalUserTime); + BinaryPrimitives.WriteUInt64LittleEndian(Buffer.Slice(0x08, 8), TotalKernelTime); + BinaryPrimitives.WriteUInt64LittleEndian(Buffer.Slice(0x10, 8), TotalUserTime); + BinaryPrimitives.WriteUInt64LittleEndian(Buffer.Slice(0x18, 8), TotalKernelTime); + BinaryPrimitives.WriteUInt32LittleEndian(Buffer.Slice(0x20, 4), 0); + BinaryPrimitives.WriteUInt32LittleEndian(Buffer.Slice(0x24, 4), TotalProcesses); + BinaryPrimitives.WriteUInt32LittleEndian(Buffer.Slice(0x28, 4), ActiveProcesses); + BinaryPrimitives.WriteUInt32LittleEndian(Buffer.Slice(0x2C, 4), TotalTerminatedProcesses); + return true; + } + + private static NTSTATUS WriteBasicLimitInformation(BinaryEmulator Instance, WinJob Job, ulong Address) + { + if (Instance._binary.Architecture == BinaryArchitecture.x64) + { + Span Buffer = stackalloc byte[0x40]; + WriteBasicLimitInformationToSpan(Job, Buffer); + return Instance._emulator.WriteMemory(Address, Buffer) ? NTSTATUS.STATUS_SUCCESS : NTSTATUS.STATUS_ACCESS_VIOLATION; + } + + Span Buffer32 = stackalloc byte[0x2C]; + WriteBasicLimitInformationToSpan(Job, Buffer32); + return Instance._emulator.WriteMemory(Address, Buffer32) ? NTSTATUS.STATUS_SUCCESS : NTSTATUS.STATUS_ACCESS_VIOLATION; + } + + private static void WriteBasicLimitInformationToSpan(WinJob Job, Span Buffer) + { + bool IsX64 = Buffer.Length >= 0x40; + BinaryPrimitives.WriteUInt64LittleEndian(Buffer.Slice(0x00, 8), Job.PerProcessUserTimeLimit); + BinaryPrimitives.WriteUInt64LittleEndian(Buffer.Slice(0x08, 8), Job.PerJobUserTimeLimit); + BinaryPrimitives.WriteUInt32LittleEndian(Buffer.Slice(0x10, 4), Job.LimitFlags); + + if (IsX64) + { + BinaryPrimitives.WriteUInt64LittleEndian(Buffer.Slice(0x18, 8), Job.MinimumWorkingSetSize); + BinaryPrimitives.WriteUInt64LittleEndian(Buffer.Slice(0x20, 8), Job.MaximumWorkingSetSize); + BinaryPrimitives.WriteUInt32LittleEndian(Buffer.Slice(0x28, 4), Job.ActiveProcessLimit); + BinaryPrimitives.WriteUInt64LittleEndian(Buffer.Slice(0x30, 8), Job.Affinity); + BinaryPrimitives.WriteUInt32LittleEndian(Buffer.Slice(0x38, 4), Job.PriorityClass); + BinaryPrimitives.WriteUInt32LittleEndian(Buffer.Slice(0x3C, 4), Job.SchedulingClass); + return; + } + + BinaryPrimitives.WriteUInt32LittleEndian(Buffer.Slice(0x14, 4), (uint)Job.MinimumWorkingSetSize); + BinaryPrimitives.WriteUInt32LittleEndian(Buffer.Slice(0x18, 4), (uint)Job.MaximumWorkingSetSize); + BinaryPrimitives.WriteUInt32LittleEndian(Buffer.Slice(0x1C, 4), Job.ActiveProcessLimit); + BinaryPrimitives.WriteUInt32LittleEndian(Buffer.Slice(0x20, 4), (uint)Job.Affinity); + BinaryPrimitives.WriteUInt32LittleEndian(Buffer.Slice(0x24, 4), Job.PriorityClass); + BinaryPrimitives.WriteUInt32LittleEndian(Buffer.Slice(0x28, 4), Job.SchedulingClass); + } + + private static NTSTATUS WriteBasicProcessIdList(BinaryEmulator Instance, WinJob Job, ulong Address, uint RequiredLength) + { + uint PtrSize = Instance._binary.Architecture == BinaryArchitecture.x64 ? 8u : 4u; + uint Count = (uint)Job.ProcessIds.Distinct().Count(); + if (RequiredLength < 8u + (PtrSize * Count)) + return NTSTATUS.STATUS_INFO_LENGTH_MISMATCH; + + Span Buffer = stackalloc byte[(int)RequiredLength]; + Buffer.Clear(); + BinaryPrimitives.WriteUInt32LittleEndian(Buffer.Slice(0x00, 4), Count); + BinaryPrimitives.WriteUInt32LittleEndian(Buffer.Slice(0x04, 4), Count); + + int Offset = 0x08; + foreach (uint ProcessId in Job.ProcessIds.Distinct()) + { + if (PtrSize == 8) + { + BinaryPrimitives.WriteUInt64LittleEndian(Buffer.Slice(Offset, 8), ProcessId); + Offset += 8; + } + else + { + BinaryPrimitives.WriteUInt32LittleEndian(Buffer.Slice(Offset, 4), ProcessId); + Offset += 4; + } + } + + return Instance._emulator.WriteMemory(Address, Buffer) ? NTSTATUS.STATUS_SUCCESS : NTSTATUS.STATUS_ACCESS_VIOLATION; + } + + private static NTSTATUS WriteExtendedLimitInformation(BinaryEmulator Instance, WinJob Job, ulong Address) + { + if (Instance._binary.Architecture == BinaryArchitecture.x64) + { + Span Buffer = stackalloc byte[0x90]; + Buffer.Clear(); + WriteBasicLimitInformationToSpan(Job, Buffer.Slice(0x00, 0x40)); + // IO_COUNTERS block is intentionally zeroed. + BinaryPrimitives.WriteUInt64LittleEndian(Buffer.Slice(0x70, 8), Job.ProcessMemoryLimit); + BinaryPrimitives.WriteUInt64LittleEndian(Buffer.Slice(0x78, 8), Job.JobMemoryLimit); + BinaryPrimitives.WriteUInt64LittleEndian(Buffer.Slice(0x80, 8), Job.PeakProcessMemoryUsed); + BinaryPrimitives.WriteUInt64LittleEndian(Buffer.Slice(0x88, 8), Job.PeakJobMemoryUsed); + return Instance._emulator.WriteMemory(Address, Buffer) ? NTSTATUS.STATUS_SUCCESS : NTSTATUS.STATUS_ACCESS_VIOLATION; + } + + Span Buffer32 = stackalloc byte[0x6C]; + Buffer32.Clear(); + WriteBasicLimitInformationToSpan(Job, Buffer32.Slice(0x00, 0x2C)); + // IO_COUNTERS block is intentionally zeroed. + BinaryPrimitives.WriteUInt32LittleEndian(Buffer32.Slice(0x5C, 4), (uint)Job.ProcessMemoryLimit); + BinaryPrimitives.WriteUInt32LittleEndian(Buffer32.Slice(0x60, 4), (uint)Job.JobMemoryLimit); + BinaryPrimitives.WriteUInt32LittleEndian(Buffer32.Slice(0x64, 4), (uint)Job.PeakProcessMemoryUsed); + BinaryPrimitives.WriteUInt32LittleEndian(Buffer32.Slice(0x68, 4), (uint)Job.PeakJobMemoryUsed); + return Instance._emulator.WriteMemory(Address, Buffer32) ? NTSTATUS.STATUS_SUCCESS : NTSTATUS.STATUS_ACCESS_VIOLATION; + } + + private static NTSTATUS WriteNotificationLimitInformation(BinaryEmulator Instance, WinJob Job, ulong Address) + { + Span Buffer = stackalloc byte[0x2C]; + BinaryPrimitives.WriteUInt64LittleEndian(Buffer.Slice(0x00, 8), Job.IoReadBytesLimit); + BinaryPrimitives.WriteUInt64LittleEndian(Buffer.Slice(0x08, 8), Job.IoWriteBytesLimit); + BinaryPrimitives.WriteUInt64LittleEndian(Buffer.Slice(0x10, 8), Job.NotificationPerJobUserTimeLimit); + BinaryPrimitives.WriteUInt64LittleEndian(Buffer.Slice(0x18, 8), Job.NotificationJobMemoryLimit); + BinaryPrimitives.WriteUInt32LittleEndian(Buffer.Slice(0x20, 4), Job.NotificationRateControlTolerance); + BinaryPrimitives.WriteUInt32LittleEndian(Buffer.Slice(0x24, 4), Job.NotificationRateControlToleranceInterval); + BinaryPrimitives.WriteUInt32LittleEndian(Buffer.Slice(0x28, 4), Job.NotificationLimitFlags); + return Instance._emulator.WriteMemory(Address, Buffer) ? NTSTATUS.STATUS_SUCCESS : NTSTATUS.STATUS_ACCESS_VIOLATION; + } + + private static NTSTATUS WriteCpuRateControlInformation(BinaryEmulator Instance, WinJob Job, ulong Address) + { + Span Buffer = stackalloc byte[0x08]; + BinaryPrimitives.WriteUInt32LittleEndian(Buffer.Slice(0x00, 4), Job.CpuRateControlFlags); + + if ((Job.CpuRateControlFlags & 0x10) != 0) + { + BinaryPrimitives.WriteUInt16LittleEndian(Buffer.Slice(0x04, 2), Job.CpuRateControlMinRate); + BinaryPrimitives.WriteUInt16LittleEndian(Buffer.Slice(0x06, 2), Job.CpuRateControlMaxRate); + } + else if ((Job.CpuRateControlFlags & 0x2) != 0) + { + BinaryPrimitives.WriteUInt32LittleEndian(Buffer.Slice(0x04, 4), Job.CpuRateControlWeight); + } + else + { + BinaryPrimitives.WriteUInt32LittleEndian(Buffer.Slice(0x04, 4), Job.CpuRateControlValue); + } + + return Instance._emulator.WriteMemory(Address, Buffer) ? NTSTATUS.STATUS_SUCCESS : NTSTATUS.STATUS_ACCESS_VIOLATION; + } + + private static NTSTATUS WriteNetRateControlInformation(BinaryEmulator Instance, WinJob Job, ulong Address) + { + Span Buffer = stackalloc byte[0x10]; + BinaryPrimitives.WriteUInt64LittleEndian(Buffer.Slice(0x00, 8), Job.NetRateControlMaxBandwidth); + BinaryPrimitives.WriteUInt32LittleEndian(Buffer.Slice(0x08, 4), Job.NetRateControlFlags); + Buffer[0x0C] = Job.NetRateControlDscpTag; + return Instance._emulator.WriteMemory(Address, Buffer) ? NTSTATUS.STATUS_SUCCESS : NTSTATUS.STATUS_ACCESS_VIOLATION; + } + } +} diff --git a/Brovan/Core/Emulation/OS/Windows/Process/NtSetInformationJobObject.cs b/Brovan/Core/Emulation/OS/Windows/Process/NtSetInformationJobObject.cs new file mode 100644 index 0000000..525e3a6 --- /dev/null +++ b/Brovan/Core/Emulation/OS/Windows/Process/NtSetInformationJobObject.cs @@ -0,0 +1,256 @@ +using System; +using System.Buffers.Binary; +using static Brovan.Core.Helpers.BinaryHelpers; + +namespace Brovan.Core.Emulation.OS.Windows +{ + internal class NtSetInformationJobObject : IWinSyscall + { + public NTSTATUS Handle(BinaryEmulator Instance) + { + if (Instance._binary.Architecture == BinaryArchitecture.x64) + { + ulong JobHandle = Instance.WinHelper.GetArg64(0); + ulong JobObjectInformationClass = (uint)Instance.WinHelper.GetArg64(1); + ulong JobObjectInformation = Instance.WinHelper.GetArg64(2); + ulong JobObjectInformationLength = (uint)Instance.WinHelper.GetArg64(3); + return HandleSet(Instance, JobHandle, JobObjectInformationClass, JobObjectInformation, JobObjectInformationLength); + } + + uint SP = Instance.ReadRegister32(Registers.UC_X86_REG_ESP); + uint JobHandle32 = Instance.ReadMemoryUInt(SP + 4); + uint JobObjectInformationClass32 = Instance.ReadMemoryUInt(SP + 8); + uint JobObjectInformation32 = Instance.ReadMemoryUInt(SP + 12); + uint JobObjectInformationLength32 = Instance.ReadMemoryUInt(SP + 16); + return HandleSet(Instance, JobHandle32, JobObjectInformationClass32, JobObjectInformation32, JobObjectInformationLength32); + } + + private static NTSTATUS HandleSet(BinaryEmulator Instance, ulong JobHandle, ulong JobObjectInformationClass, ulong JobObjectInformation, ulong JobObjectInformationLength) + { + if (JobHandle == 0) + return NTSTATUS.STATUS_INVALID_HANDLE; + + if (!Instance.WinHelper.HandleManager.HandleExists(JobHandle, HandleType.JobHandle)) + return NTSTATUS.STATUS_INVALID_HANDLE; + + WinJob Job = Instance.WinHelper.GetJobByHandle(JobHandle, AccessMask.GiveTemp); + if (Job == null) + return NTSTATUS.STATUS_INVALID_HANDLE; + + JOBOBJECTINFOCLASS InfoClass = (JOBOBJECTINFOCLASS)JobObjectInformationClass; + uint RequiredLength = GetRequiredLength(Instance, InfoClass, out NTSTATUS Status); + if (Status != NTSTATUS.STATUS_SUCCESS) + return Status; + + if (JobObjectInformationLength < RequiredLength) + return NTSTATUS.STATUS_INFO_LENGTH_MISMATCH; + + if (JobObjectInformation == 0) + return NTSTATUS.STATUS_INVALID_PARAMETER; + + if (!Instance.IsRegionMapped(JobObjectInformation, RequiredLength)) + return NTSTATUS.STATUS_ACCESS_VIOLATION; + + Span Data = Instance.WinHelper.ReadMemorySpan(JobObjectInformation, RequiredLength); + if (Data.Length < RequiredLength) + return NTSTATUS.STATUS_ACCESS_VIOLATION; + + switch (InfoClass) + { + case JOBOBJECTINFOCLASS.JobObjectBasicLimitInformation: + return SetBasicLimitInformation(Instance, Job, Data); + case JOBOBJECTINFOCLASS.JobObjectBasicUIRestrictions: + Job.UiRestrictionsClass = ReadUInt32(Data, 0x00); + return NTSTATUS.STATUS_SUCCESS; + case JOBOBJECTINFOCLASS.JobObjectEndOfJobTimeInformation: + Job.EndOfJobTimeAction = ReadUInt32(Data, 0x00); + return NTSTATUS.STATUS_SUCCESS; + case JOBOBJECTINFOCLASS.JobObjectAssociateCompletionPortInformation: + return SetAssociateCompletionPortInformation(Instance, Job, Data); + case JOBOBJECTINFOCLASS.JobObjectExtendedLimitInformation: + return SetExtendedLimitInformation(Instance, Job, Data); + case JOBOBJECTINFOCLASS.JobObjectNotificationLimitInformation: + return SetNotificationLimitInformation(Job, Data); + case JOBOBJECTINFOCLASS.JobObjectCpuRateControlInformation: + return SetCpuRateControlInformation(Job, Data); + case JOBOBJECTINFOCLASS.JobObjectNetRateControlInformation: + return SetNetRateControlInformation(Job, Data); + case JOBOBJECTINFOCLASS.JobObjectGroupInformation: + case JOBOBJECTINFOCLASS.JobObjectGroupInformationEx: + case JOBOBJECTINFOCLASS.JobObjectLimitViolationInformation: + case JOBOBJECTINFOCLASS.JobObjectLimitViolationInformation2: + case JOBOBJECTINFOCLASS.JobObjectNotificationLimitInformation2: + case JOBOBJECTINFOCLASS.JobObjectReserved1Information: + return NTSTATUS.STATUS_NOT_SUPPORTED; + default: + return NTSTATUS.STATUS_INVALID_INFO_CLASS; + } + } + + private static uint GetRequiredLength(BinaryEmulator Instance, JOBOBJECTINFOCLASS InfoClass, out NTSTATUS Status) + { + Status = NTSTATUS.STATUS_SUCCESS; + + switch (InfoClass) + { + case JOBOBJECTINFOCLASS.JobObjectBasicLimitInformation: + return Instance._binary.Architecture == BinaryArchitecture.x64 ? 0x40u : 0x2Cu; + case JOBOBJECTINFOCLASS.JobObjectBasicUIRestrictions: + case JOBOBJECTINFOCLASS.JobObjectEndOfJobTimeInformation: + return 0x04; + case JOBOBJECTINFOCLASS.JobObjectAssociateCompletionPortInformation: + return Instance._binary.Architecture == BinaryArchitecture.x64 ? 0x10u : 0x08u; + case JOBOBJECTINFOCLASS.JobObjectExtendedLimitInformation: + return Instance._binary.Architecture == BinaryArchitecture.x64 ? 0x90u : 0x6Cu; + case JOBOBJECTINFOCLASS.JobObjectNotificationLimitInformation: + return 0x2C; + case JOBOBJECTINFOCLASS.JobObjectCpuRateControlInformation: + return 0x08; + case JOBOBJECTINFOCLASS.JobObjectNetRateControlInformation: + return 0x10; + case JOBOBJECTINFOCLASS.JobObjectGroupInformation: + case JOBOBJECTINFOCLASS.JobObjectGroupInformationEx: + case JOBOBJECTINFOCLASS.JobObjectLimitViolationInformation: + case JOBOBJECTINFOCLASS.JobObjectLimitViolationInformation2: + case JOBOBJECTINFOCLASS.JobObjectNotificationLimitInformation2: + case JOBOBJECTINFOCLASS.JobObjectReserved1Information: + Status = NTSTATUS.STATUS_NOT_SUPPORTED; + return 0; + default: + Status = NTSTATUS.STATUS_INVALID_INFO_CLASS; + return 0; + } + } + + private static NTSTATUS SetBasicLimitInformation(BinaryEmulator Instance, WinJob Job, Span Data) + { + bool IsX64 = Instance._binary.Architecture == BinaryArchitecture.x64; + Job.PerProcessUserTimeLimit = ReadUInt64(Data, 0x00); + Job.PerJobUserTimeLimit = ReadUInt64(Data, 0x08); + Job.LimitFlags = ReadUInt32(Data, 0x10); + + if (IsX64) + { + Job.MinimumWorkingSetSize = ReadUInt64(Data, 0x18); + Job.MaximumWorkingSetSize = ReadUInt64(Data, 0x20); + Job.ActiveProcessLimit = ReadUInt32(Data, 0x28); + Job.Affinity = ReadUInt64(Data, 0x30); + Job.PriorityClass = ReadUInt32(Data, 0x38); + Job.SchedulingClass = ReadUInt32(Data, 0x3C); + return NTSTATUS.STATUS_SUCCESS; + } + + Job.MinimumWorkingSetSize = ReadUInt32(Data, 0x14); + Job.MaximumWorkingSetSize = ReadUInt32(Data, 0x18); + Job.ActiveProcessLimit = ReadUInt32(Data, 0x1C); + Job.Affinity = ReadUInt32(Data, 0x20); + Job.PriorityClass = ReadUInt32(Data, 0x24); + Job.SchedulingClass = ReadUInt32(Data, 0x28); + return NTSTATUS.STATUS_SUCCESS; + } + + private static NTSTATUS SetAssociateCompletionPortInformation(BinaryEmulator Instance, WinJob Job, Span Data) + { + if (Instance._binary.Architecture == BinaryArchitecture.x64) + { + Job.CompletionKey = ReadUInt64(Data, 0x00); + Job.CompletionPort = ReadUInt64(Data, 0x08); + } + else + { + Job.CompletionKey = ReadUInt32(Data, 0x00); + Job.CompletionPort = ReadUInt32(Data, 0x04); + } + + return NTSTATUS.STATUS_SUCCESS; + } + + private static NTSTATUS SetExtendedLimitInformation(BinaryEmulator Instance, WinJob Job, Span Data) + { + NTSTATUS Status = SetBasicLimitInformation(Instance, Job, Data); + if (Status != NTSTATUS.STATUS_SUCCESS) + return Status; + + if (Instance._binary.Architecture == BinaryArchitecture.x64) + { + Job.ProcessMemoryLimit = ReadUInt64(Data, 0x70); + Job.JobMemoryLimit = ReadUInt64(Data, 0x78); + Job.PeakProcessMemoryUsed = ReadUInt64(Data, 0x80); + Job.PeakJobMemoryUsed = ReadUInt64(Data, 0x88); + } + else + { + Job.ProcessMemoryLimit = ReadUInt32(Data, 0x5C); + Job.JobMemoryLimit = ReadUInt32(Data, 0x60); + Job.PeakProcessMemoryUsed = ReadUInt32(Data, 0x64); + Job.PeakJobMemoryUsed = ReadUInt32(Data, 0x68); + } + + return NTSTATUS.STATUS_SUCCESS; + } + + private static NTSTATUS SetNotificationLimitInformation(WinJob Job, Span Data) + { + Job.IoReadBytesLimit = ReadUInt64(Data, 0x00); + Job.IoWriteBytesLimit = ReadUInt64(Data, 0x08); + Job.NotificationPerJobUserTimeLimit = ReadUInt64(Data, 0x10); + Job.NotificationJobMemoryLimit = ReadUInt64(Data, 0x18); + Job.NotificationRateControlTolerance = ReadUInt32(Data, 0x20); + Job.NotificationRateControlToleranceInterval = ReadUInt32(Data, 0x24); + Job.NotificationLimitFlags = ReadUInt32(Data, 0x28); + return NTSTATUS.STATUS_SUCCESS; + } + + private static NTSTATUS SetCpuRateControlInformation(WinJob Job, Span Data) + { + Job.CpuRateControlFlags = ReadUInt32(Data, 0x00); + + if ((Job.CpuRateControlFlags & 0x10) != 0) + { + Job.CpuRateControlMinRate = (ushort)ReadUInt16(Data, 0x04); + Job.CpuRateControlMaxRate = (ushort)ReadUInt16(Data, 0x06); + Job.CpuRateControlValue = 0; + Job.CpuRateControlWeight = 0; + } + else if ((Job.CpuRateControlFlags & 0x2) != 0) + { + Job.CpuRateControlWeight = ReadUInt32(Data, 0x04); + Job.CpuRateControlValue = 0; + Job.CpuRateControlMinRate = 0; + Job.CpuRateControlMaxRate = 0; + } + else + { + Job.CpuRateControlValue = ReadUInt32(Data, 0x04); + Job.CpuRateControlWeight = 0; + Job.CpuRateControlMinRate = 0; + Job.CpuRateControlMaxRate = 0; + } + + return NTSTATUS.STATUS_SUCCESS; + } + + private static NTSTATUS SetNetRateControlInformation(WinJob Job, Span Data) + { + Job.NetRateControlMaxBandwidth = ReadUInt64(Data, 0x00); + Job.NetRateControlFlags = ReadUInt32(Data, 0x08); + Job.NetRateControlDscpTag = Data[0x0C]; + return NTSTATUS.STATUS_SUCCESS; + } + + private static uint ReadUInt32(Span Data, int Offset) + { + return BinaryPrimitives.ReadUInt32LittleEndian(Data.Slice(Offset, 4)); + } + + private static ulong ReadUInt64(Span Data, int Offset) + { + return BinaryPrimitives.ReadUInt64LittleEndian(Data.Slice(Offset, 8)); + } + + private static ushort ReadUInt16(Span Data, int Offset) + { + return BinaryPrimitives.ReadUInt16LittleEndian(Data.Slice(Offset, 2)); + } + } +} diff --git a/Brovan/Core/Emulation/OS/Windows/Process/NtTerminateJobObject.cs b/Brovan/Core/Emulation/OS/Windows/Process/NtTerminateJobObject.cs new file mode 100644 index 0000000..21baac7 --- /dev/null +++ b/Brovan/Core/Emulation/OS/Windows/Process/NtTerminateJobObject.cs @@ -0,0 +1,77 @@ +using System.Linq; +using static Brovan.Core.Helpers.BinaryHelpers; + +namespace Brovan.Core.Emulation.OS.Windows +{ + internal class NtTerminateJobObject : IWinSyscall + { + public NTSTATUS Handle(BinaryEmulator Instance) + { + if (Instance._binary.Architecture == BinaryArchitecture.x64) + { + ulong JobHandle = Instance.WinHelper.GetArg64(0); + ulong ExitCode = (uint)Instance.WinHelper.GetArg64(1); + return TerminateJob(Instance, JobHandle, ExitCode); + } + + uint SP = Instance.ReadRegister32(Registers.UC_X86_REG_ESP); + uint JobHandle32 = Instance.ReadMemoryUInt(SP + 4); + uint ExitCode32 = Instance.ReadMemoryUInt(SP + 8); + return TerminateJob(Instance, JobHandle32, ExitCode32); + } + + private static NTSTATUS TerminateJob(BinaryEmulator Instance, ulong JobHandle, ulong ExitCode) + { + if (JobHandle == 0 || !Instance.WinHelper.HandleManager.HandleExists(JobHandle, HandleType.JobHandle)) + return NTSTATUS.STATUS_INVALID_HANDLE; + + WinJob Job = Instance.WinHelper.GetJobByHandle(JobHandle, AccessMask.GiveTemp); + if (Job == null) + return NTSTATUS.STATUS_INVALID_HANDLE; + + Job.IsTerminated = true; + + long ExitTime = Instance.GetEmulatedSystemTimeFileTimeUtc(); + uint ExitStatus = unchecked((uint)ExitCode); + bool CurrentProcessInJob = false; + + foreach (uint ProcessId in Job.ProcessIds.Distinct()) + { + WinProcess Process = Instance.WinHelper.WinProcesses.FirstOrDefault(P => P.PID == ProcessId); + if (Process == null) + continue; + + Instance.WinHelper.UpdateProcessTimes(Process); + if (Process.ExitTime == 0) + Process.ExitTime = ExitTime; + + if (Process.PID == Instance.WinHelper.PID) + CurrentProcessInJob = true; + } + + if (CurrentProcessInJob) + { + Instance.TriggerEventMessage($"[{(ExitStatus == 0 ? '+' : '!')}] Job asked to be terminated with exit code 0x{ExitStatus:X}", LogFlags.Important); + + foreach (EmulatedThread ProcessThread in Instance.Threads.Values) + { + if (ProcessThread == null) + continue; + + Instance.WinHelper.AbandonMutexesOwnedByThread(ProcessThread.ThreadId); + ProcessThread.ExitCode = unchecked((int)ExitStatus); + ProcessThread.State = EmulatedThreadState.Terminated; + ProcessThread.WaitActive = false; + ProcessThread.WaitHandles = null; + ProcessThread.WaitDeadline = -1; + ProcessThread.WaitTimedOut = false; + ProcessThread.WaitSatisfiedIndex = -1; + } + + Instance.StopEmulation(); + } + + return NTSTATUS.STATUS_SUCCESS; + } + } +} diff --git a/Brovan/Core/Emulation/OS/Windows/WinHelperConstants.cs b/Brovan/Core/Emulation/OS/Windows/WinHelperConstants.cs index 31df29b..808b161 100644 --- a/Brovan/Core/Emulation/OS/Windows/WinHelperConstants.cs +++ b/Brovan/Core/Emulation/OS/Windows/WinHelperConstants.cs @@ -145,7 +145,8 @@ public enum NTSTATUS : uint STATUS_INVALID_LOCK_RANGE = 0xC00001A1, STATUS_MUTANT_NOT_OWNED = 0xC0000046, STATUS_SEMAPHORE_LIMIT_EXCEEDED = 0xC0000047, - STATUS_NOT_A_REPARSE_POINT = 0xC0000275 + STATUS_NOT_A_REPARSE_POINT = 0xC0000275, + STATUS_PROCESS_IN_JOB = 0x00000124, } public enum THREADINFOCLASS : int @@ -778,6 +779,27 @@ public enum KEY_VALUE_INFORMATION_CLASS : uint KeyValuePartialInformationAlign64 = 4 } + public enum JOBOBJECTINFOCLASS : uint + { + JobObjectBasicAccountingInformation = 1, + JobObjectBasicLimitInformation = 2, + JobObjectBasicProcessIdList = 3, + JobObjectBasicUIRestrictions = 4, + JobObjectEndOfJobTimeInformation = 6, + JobObjectAssociateCompletionPortInformation = 7, + JobObjectBasicAndIoAccountingInformation = 8, + JobObjectExtendedLimitInformation = 9, + JobObjectGroupInformation = 11, + JobObjectNotificationLimitInformation = 12, + JobObjectLimitViolationInformation = 13, + JobObjectGroupInformationEx = 14, + JobObjectCpuRateControlInformation = 15, + JobObjectNetRateControlInformation = 32, + JobObjectNotificationLimitInformation2 = 33, + JobObjectLimitViolationInformation2 = 34, + JobObjectReserved1Information = 35 + } + [Flags] public enum AccessMask : uint { @@ -895,7 +917,8 @@ public enum HandleType WaitCompletionPacketHandle = 12, Window = 13, EtwRegistrationHandle = 14, - SemaphoreHandle = 15 + SemaphoreHandle = 15, + JobHandle = 16 } public sealed class WinToken : IHandleObject @@ -989,6 +1012,7 @@ public class WinProcess : IHandleObject public BinaryArchitecture Arch; public WinToken PrimaryToken; public ulong InstrumentationCallback; + public ulong JobObjectHandle; public string ObjectId => PID.ToString(); public HandleType ObjectType => HandleType.ProcessHandle; @@ -1530,4 +1554,53 @@ public sealed class WinPort : IHandleObject public string ObjectId => Name; public HandleType ObjectType => HandleType.PortHandle; } + + public sealed class WinJob : IHandleObject + { + public string Name; + public bool IsTerminated; + public List ProcessIds = new List(); + + public ulong PerProcessUserTimeLimit; + public ulong PerJobUserTimeLimit; + public uint LimitFlags; + public ulong MinimumWorkingSetSize; + public ulong MaximumWorkingSetSize; + public uint ActiveProcessLimit; + public ulong Affinity; + public uint PriorityClass; + public uint SchedulingClass; + + public uint UiRestrictionsClass; + public uint EndOfJobTimeAction; + + public ulong CompletionPort; + public ulong CompletionKey; + + public ulong ProcessMemoryLimit; + public ulong JobMemoryLimit; + public ulong PeakProcessMemoryUsed; + public ulong PeakJobMemoryUsed; + + public ulong IoReadBytesLimit; + public ulong IoWriteBytesLimit; + public ulong NotificationPerJobUserTimeLimit; + public ulong NotificationJobMemoryLimit; + public uint NotificationRateControlTolerance; + public uint NotificationRateControlToleranceInterval; + public uint NotificationLimitFlags; + + public uint CpuRateControlFlags; + public uint CpuRateControlValue; + public uint CpuRateControlWeight; + public ushort CpuRateControlMinRate; + public ushort CpuRateControlMaxRate; + + public ulong NetRateControlMaxBandwidth; + public uint NetRateControlFlags; + public byte NetRateControlDscpTag; + + public string ObjectId => Name; + public HandleType ObjectType => HandleType.JobHandle; + } } \ No newline at end of file diff --git a/Brovan/Core/Emulation/OS/Windows/WinSyscallsHelper.cs b/Brovan/Core/Emulation/OS/Windows/WinSyscallsHelper.cs index 97bda72..80c3890 100644 --- a/Brovan/Core/Emulation/OS/Windows/WinSyscallsHelper.cs +++ b/Brovan/Core/Emulation/OS/Windows/WinSyscallsHelper.cs @@ -968,6 +968,7 @@ private static long SaturatingMillisecondsToFileTimeDuration(long Milliseconds) public List WinSections = new List(); public List WinPorts = new List(); public List WinEtwRegistrations = new List(); + public List WinJobs = new List(); public ulong EtwNotificationEventHandle; internal PebLdrTracker LdrTracker; public readonly Dictionary WinWindows = new(); @@ -4018,6 +4019,83 @@ public WinHandle CreateEventHandle(string Name, uint EventType, bool InitialStat return HandleManager.GetObjectByHandle(Handle); } + public WinHandle CreateJobHandle(string Name, AccessMask Permissions) + { + if (string.IsNullOrEmpty(Name)) + Name = "Job_" + GenerateRandomPID().ToString(); + + WinJob Job = WinJobs.FirstOrDefault(j => j.Name.Equals(Name, StringComparison.OrdinalIgnoreCase)); + if (Job == null) + { + Job = new WinJob { Name = Name }; + WinJobs.Add(Job); + } + + WinHandle Handle = HandleManager.AddHandle(Job, Permissions); + WinHandles.Add(Handle); + return Handle; + } + + public WinJob? GetJobByHandle(ulong Handle, AccessMask Purpose) + { + if (!HandleManager.HandleExists(Handle, HandleType.JobHandle)) + return null; + + if (!HandleManager.CheckAccess(Handle, Purpose)) + return null; + + return HandleManager.GetObjectByHandle(Handle); + } + + public bool IsProcessInJob(ulong ProcessHandle, ulong JobHandle) + { + WinProcess Process = null; + + if (ProcessHandle == HandleManager.CurrentProcess || ProcessHandle == uint.MaxValue) + Process = WinProcesses.FirstOrDefault(p => p.PID == PID); + else + Process = GetProcessByHandle(ProcessHandle, AccessMask.GiveTemp); + + if (Process == null) + return false; + + if (JobHandle == 0) + return Process.JobObjectHandle != 0; + + WinJob Job = GetJobByHandle(JobHandle, AccessMask.GiveTemp); + if (Job == null) + return false; + + return Process.JobObjectHandle == JobHandle || Job.ProcessIds.Contains(Process.PID); + } + + public NTSTATUS AssignProcessToJobHandle(ulong JobHandle, ulong ProcessHandle) + { + WinJob Job = GetJobByHandle(JobHandle, AccessMask.GiveTemp); + if (Job == null) + return NTSTATUS.STATUS_INVALID_HANDLE; + + WinProcess Process; + + if (ProcessHandle == HandleManager.CurrentProcess || ProcessHandle == uint.MaxValue) + Process = WinProcesses.FirstOrDefault(p => p.PID == PID); + else + Process = GetProcessByHandle(ProcessHandle, AccessMask.GiveTemp); + + if (Process == null) + return NTSTATUS.STATUS_INVALID_HANDLE; + + if (Process.JobObjectHandle != 0 && Process.JobObjectHandle != JobHandle) + return NTSTATUS.STATUS_ACCESS_DENIED; + + Process.JobObjectHandle = JobHandle; + + if (!Job.ProcessIds.Contains(Process.PID)) + Job.ProcessIds.Add(Process.PID); + + return NTSTATUS.STATUS_SUCCESS; + } + public WinHandle CreateSemaphoreHandle(string Name, int InitialCount, int MaximumCount, AccessMask Permissions) { if (string.IsNullOrEmpty(Name))