/*++ * @name NtQuerySecurityObject * @implemented NT4 * * The NtQuerySecurityObject routine <FILLMEIN> * * @param Handle * <FILLMEIN> * * @param SecurityInformation * <FILLMEIN> * * @param SecurityDescriptor * <FILLMEIN> * * @param Length * <FILLMEIN> * * @param ResultLength * <FILLMEIN> * * @return STATUS_SUCCESS or appropriate error value. * * @remarks None. * *--*/ NTSTATUS NTAPI NtQuerySecurityObject(IN HANDLE Handle, IN SECURITY_INFORMATION SecurityInformation, OUT PSECURITY_DESCRIPTOR SecurityDescriptor, IN ULONG Length, OUT PULONG ResultLength) { KPROCESSOR_MODE PreviousMode = ExGetPreviousMode(); PVOID Object; POBJECT_HEADER Header; POBJECT_TYPE Type; ACCESS_MASK DesiredAccess; NTSTATUS Status; PAGED_CODE(); /* Check if we came from user mode */ if (PreviousMode != KernelMode) { /* Enter SEH */ _SEH2_TRY { /* Probe the SD and the length pointer */ ProbeForWrite(SecurityDescriptor, Length, sizeof(ULONG)); ProbeForWriteUlong(ResultLength); } _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER) { /* Return the exception code */ _SEH2_YIELD(return _SEH2_GetExceptionCode()); } _SEH2_END; }
NTSTATUS NTAPI NtAlertResumeThread(IN HANDLE ThreadHandle, OUT PULONG SuspendCount) { KPROCESSOR_MODE PreviousMode = ExGetPreviousMode(); PETHREAD Thread; NTSTATUS Status; ULONG PreviousState; /* Check if we came from user mode with a suspend count */ if ((SuspendCount) && (PreviousMode != KernelMode)) { /* Enter SEH for probing */ _SEH2_TRY { /* Probe the count */ ProbeForWriteUlong(SuspendCount); } _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER) { /* Return the exception code */ _SEH2_YIELD(return _SEH2_GetExceptionCode()); } _SEH2_END; }
NTSTATUS NTAPI NtQueryInformationPort( IN HANDLE PortHandle OPTIONAL, IN PORT_INFORMATION_CLASS PortInformationClass, OUT PVOID PortInformation, IN ULONG Length, OUT PULONG ReturnLength OPTIONAL ) { KPROCESSOR_MODE PreviousMode; NTSTATUS Status; PLPCP_PORT_OBJECT PortObject; PAGED_CODE(); // // Get previous processor mode and probe output argument if necessary. // PreviousMode = KeGetPreviousMode(); if (PreviousMode != KernelMode) { try { ProbeForWrite( PortInformation, Length, sizeof( ULONG ) ); if (ARGUMENT_PRESENT( ReturnLength )) { ProbeForWriteUlong( ReturnLength ); } } except( EXCEPTION_EXECUTE_HANDLER ) { return( GetExceptionCode() ); } } if (ARGUMENT_PRESENT( PortHandle )) { Status = ObReferenceObjectByHandle( PortHandle, GENERIC_READ, LpcPortObjectType, PreviousMode, &PortObject, NULL ); if (!NT_SUCCESS( Status )) { return( Status ); } ObDereferenceObject( PortObject ); return STATUS_SUCCESS; } else { return STATUS_INVALID_INFO_CLASS; } }
NTSTATUS NtSuspendThread( IN HANDLE ThreadHandle, OUT PULONG PreviousSuspendCount OPTIONAL ) /*++ Routine Description: This function suspends the target thread, and optionally returns the previous suspend count. Arguments: ThreadHandle - Supplies a handle to the thread object to suspend. PreviousSuspendCount - An optional parameter, that if specified points to a variable that receives the thread's previous suspend count. Return Value: return-value - Description of conditions needed to return value. - or - None. --*/ { PETHREAD Thread; NTSTATUS st; ULONG LocalPreviousSuspendCount; KPROCESSOR_MODE Mode; PAGED_CODE(); try { Mode = KeGetPreviousMode(); if ( Mode != KernelMode ) { if (ARGUMENT_PRESENT(PreviousSuspendCount)) { ProbeForWriteUlong(PreviousSuspendCount); } } } except (EXCEPTION_EXECUTE_HANDLER) { return GetExceptionCode(); } st = ObReferenceObjectByHandle( ThreadHandle, THREAD_SUSPEND_RESUME, PsThreadType, Mode, (PVOID *)&Thread, NULL ); if ( !NT_SUCCESS(st) ) { return st; } try { if ( Thread != PsGetCurrentThread() ) { if ( Thread->HasTerminated ) { ObDereferenceObject(Thread); return STATUS_THREAD_IS_TERMINATING; } LocalPreviousSuspendCount = (ULONG) KeSuspendThread(&Thread->Tcb); } else { LocalPreviousSuspendCount = (ULONG) KeSuspendThread(&Thread->Tcb); } ObDereferenceObject(Thread); if (ARGUMENT_PRESENT(PreviousSuspendCount)) *PreviousSuspendCount = LocalPreviousSuspendCount; } except (EXCEPTION_EXECUTE_HANDLER) { st = GetExceptionCode(); // // Either the suspend, or the store could cause an // exception. The store is a partial success, while the // suspend exception is an error // if ( st == STATUS_SUSPEND_COUNT_EXCEEDED ) { ObDereferenceObject(Thread); } else { st = STATUS_SUCCESS; } return st; } return STATUS_SUCCESS; }
NTSTATUS NtAlertResumeThread( IN HANDLE ThreadHandle, OUT PULONG PreviousSuspendCount OPTIONAL ) /*++ Routine Description: description-of-function. Arguments: argument-name - Supplies | Returns description of argument. . . Return Value: return-value - Description of conditions needed to return value. - or - None. --*/ { PETHREAD Thread; NTSTATUS st; ULONG LocalPreviousSuspendCount; KPROCESSOR_MODE Mode; PAGED_CODE(); try { Mode = KeGetPreviousMode(); if ( Mode != KernelMode ) { if (ARGUMENT_PRESENT(PreviousSuspendCount)) { ProbeForWriteUlong(PreviousSuspendCount); } } } except (EXCEPTION_EXECUTE_HANDLER) { return GetExceptionCode(); } st = ObReferenceObjectByHandle( ThreadHandle, THREAD_SUSPEND_RESUME, PsThreadType, Mode, (PVOID *)&Thread, NULL ); if ( !NT_SUCCESS(st) ) { return st; } LocalPreviousSuspendCount = (ULONG) KeAlertResumeThread(&Thread->Tcb); ObDereferenceObject(Thread); try { if (ARGUMENT_PRESENT(PreviousSuspendCount)) *PreviousSuspendCount = LocalPreviousSuspendCount; } except (EXCEPTION_EXECUTE_HANDLER) { return STATUS_SUCCESS; } return STATUS_SUCCESS; }
NTSTATUS NtQueryEvent ( IN HANDLE EventHandle, IN EVENT_INFORMATION_CLASS EventInformationClass, OUT PVOID EventInformation, IN ULONG EventInformationLength, OUT PULONG ReturnLength OPTIONAL ) /*++ Routine Description: This function queries the state of an event object and returns the requested information in the specified record structure. Arguments: EventHandle - Supplies a handle to an event object. EventInformationClass - Supplies the class of information being requested. EventInformation - Supplies a pointer to a record that is to receive the requested information. EventInformationLength - Supplies the length of the record that is to receive the requested information. ReturnLength - Supplies an optional pointer to a variable that is to receive the actual length of information that is returned. Return Value: TBS --*/ { PKEVENT Event; KPROCESSOR_MODE PreviousMode; LONG State; NTSTATUS Status; EVENT_TYPE EventType; // // Check argument validity. // if (EventInformationClass != EventBasicInformation) { return STATUS_INVALID_INFO_CLASS; } if (EventInformationLength != sizeof(EVENT_BASIC_INFORMATION)) { return STATUS_INFO_LENGTH_MISMATCH; } // // Establish an exception handler, probe the output arguments, reference // the event object, and return the specified information. If the probe // fails, then return the exception code as the service status. Otherwise // return the status value returned by the reference object by handle // routine. // try { // // Get previous processor mode and probe output arguments if necessary. // PreviousMode = KeGetPreviousMode(); if (PreviousMode != KernelMode) { ProbeForWrite(EventInformation, sizeof(EVENT_BASIC_INFORMATION), sizeof(ULONG)); if (ARGUMENT_PRESENT(ReturnLength)) { ProbeForWriteUlong(ReturnLength); } } // // Reference event object by handle. // Status = ObReferenceObjectByHandle(EventHandle, EVENT_QUERY_STATE, ExEventObjectType, PreviousMode, (PVOID *)&Event, NULL); // // If the reference was successful, then read the current state of // the event object, deference event object, fill in the information // structure, and return the length of the information structure if // specified. If the write of the event information or the return // length fails, then do not report an error. When the caller accesses // the information structure or length an access violation will occur. // if (NT_SUCCESS(Status)) { State = KeReadStateEvent(Event); EventType = Event->Header.Type; ObDereferenceObject(Event); try { ((PEVENT_BASIC_INFORMATION)EventInformation)->EventType = EventType; ((PEVENT_BASIC_INFORMATION)EventInformation)->EventState = State; if (ARGUMENT_PRESENT(ReturnLength)) { *ReturnLength = sizeof(EVENT_BASIC_INFORMATION); } } except(ExSystemExceptionFilter()) { } } // // If an exception occurs during the probe of the output arguments, then // always handle the exception and return the exception code as the status // value. // } except(ExSystemExceptionFilter()) { return GetExceptionCode(); } // // Return service status. // return Status; }
NTSTATUS NtQuerySection( IN HANDLE SectionHandle, IN SECTION_INFORMATION_CLASS SectionInformationClass, OUT PVOID SectionInformation, IN ULONG SectionInformationLength, OUT PULONG ReturnLength OPTIONAL ) /*++ Routine Description: This function returns information about an opened section object. This function provides the capability to determine the base address, size, granted access, and allocation of an opened section object. Arguments: SectionHandle - Supplies an open handle to a section object. SectionInformationClass - The section information class about which to retrieve information. SectionInformation - A pointer to a buffer that receives the specified information. The format and content of the buffer depend on the specified section class. SectionInformation Format by Information Class: SectionBasicInformation - Data type is PSECTION_BASIC_INFORMATION. SECTION_BASIC_INFORMATION Structure PVOID BaseAddress - The base virtual address of the section if the section is based. LARGE_INTEGER MaximumSize - The maximum size of the section in bytes. ULONG AllocationAttributes - The allocation attributes flags. AllocationAttributes Flags SEC_BASED - The section is a based section. SEC_TILE - The section must be allocated in the first 512mb of the virtual address space. SEC_FILE - The section is backed by a data file. SEC_RESERVE - All pages of the section were initially set to the reserved state. SEC_COMMIT - All pages of the section were initially to the committed state. SEC_IMAGE - The section was mapped as an executable image file. SECTION_IMAGE_INFORMATION SectionInformationLength - Specifies the length in bytes of the section information buffer. ReturnLength - An optional pointer which, if specified, receives the number of bytes placed in the section information buffer. Return Value: Returns the status TBS --*/ { PSECTION Section; KPROCESSOR_MODE PreviousMode; NTSTATUS Status; PAGED_CODE(); // // Get previous processor mode and probe output argument if necessary. // PreviousMode = KeGetPreviousMode(); if (PreviousMode != KernelMode) { // // Check arguments. // try { ProbeForWrite(SectionInformation, SectionInformationLength, sizeof(ULONG)); if (ARGUMENT_PRESENT (ReturnLength)) { ProbeForWriteUlong(ReturnLength); } } except (EXCEPTION_EXECUTE_HANDLER) { // // If an exception occurs during the probe or capture // of the initial values, then handle the exception and // return the exception code as the status value. // return GetExceptionCode(); } } // // Check argument validity. // if ((SectionInformationClass != SectionBasicInformation) && (SectionInformationClass != SectionImageInformation)) { return STATUS_INVALID_INFO_CLASS; } if (SectionInformationClass == SectionBasicInformation) { if (SectionInformationLength < (ULONG)sizeof(SECTION_BASIC_INFORMATION)) { return STATUS_INFO_LENGTH_MISMATCH; } } else { if (SectionInformationLength < (ULONG)sizeof(SECTION_IMAGE_INFORMATION)) { return STATUS_INFO_LENGTH_MISMATCH; } } // // Reference section object by handle for READ access, get the information // from the section object, dereference the section // object, fill in information structure, optionally return the length of // the information structure, and return service status. // Status = ObReferenceObjectByHandle(SectionHandle, SECTION_QUERY, MmSectionObjectType, PreviousMode, (PVOID *)&Section, NULL); if (NT_SUCCESS(Status)) { try { if (SectionInformationClass == SectionBasicInformation) { ((PSECTION_BASIC_INFORMATION)SectionInformation)->BaseAddress = (PVOID)Section->Address.StartingVpn; ((PSECTION_BASIC_INFORMATION)SectionInformation)->MaximumSize = Section->SizeOfSection; ((PSECTION_BASIC_INFORMATION)SectionInformation)->AllocationAttributes = 0; if (Section->u.Flags.Image) { ((PSECTION_BASIC_INFORMATION)SectionInformation)->AllocationAttributes = SEC_IMAGE; } if (Section->u.Flags.Based) { ((PSECTION_BASIC_INFORMATION)SectionInformation)->AllocationAttributes |= SEC_BASED; } if (Section->u.Flags.File) { ((PSECTION_BASIC_INFORMATION)SectionInformation)->AllocationAttributes |= SEC_FILE; } if (Section->u.Flags.NoCache) { ((PSECTION_BASIC_INFORMATION)SectionInformation)->AllocationAttributes |= SEC_NOCACHE; } if (Section->u.Flags.Reserve) { ((PSECTION_BASIC_INFORMATION)SectionInformation)->AllocationAttributes |= SEC_RESERVE; } if (Section->u.Flags.Commit) { ((PSECTION_BASIC_INFORMATION)SectionInformation)->AllocationAttributes |= SEC_COMMIT; } if (Section->Segment->ControlArea->u.Flags.GlobalMemory) { ((PSECTION_BASIC_INFORMATION)SectionInformation)->AllocationAttributes |= SEC_GLOBAL; } if (ARGUMENT_PRESENT(ReturnLength)) { *ReturnLength = sizeof(SECTION_BASIC_INFORMATION); } } else { if (Section->u.Flags.Image == 0) { Status = STATUS_SECTION_NOT_IMAGE; } else { *((PSECTION_IMAGE_INFORMATION)SectionInformation) = *Section->Segment->ImageInformation; if (ARGUMENT_PRESENT(ReturnLength)) { *ReturnLength = sizeof(SECTION_IMAGE_INFORMATION); } } } } except (EXCEPTION_EXECUTE_HANDLER) { } ObDereferenceObject ((PVOID)Section); } return Status; }
NTSTATUS NtQueryTimer ( IN HANDLE TimerHandle, IN TIMER_INFORMATION_CLASS TimerInformationClass, OUT PVOID TimerInformation, IN ULONG TimerInformationLength, OUT PULONG ReturnLength OPTIONAL ) /*++ Routine Description: This function queries the state of an timer object and returns the requested information in the specified record structure. Arguments: TimerHandle - Supplies a handle to an timer object. TimerInformationClass - Supplies the class of information being requested. TimerInformation - Supplies a pointer to a record that is to receive the requested information. TimerInformationLength - Supplies the length of the record that is to receive the requested information. ReturnLength - Supplies an optional pointer to a variable that is to receive the actual length of information that is returned. Return Value: TBS --*/ { PETIMER ExTimer; PKTIMER KeTimer; KPROCESSOR_MODE PreviousMode; BOOLEAN State; NTSTATUS Status; LARGE_INTEGER TimeToGo; // // Establish an exception handler, probe the output arguments, reference // the timer object, and return the specified information. If the probe // fails, then return the exception code as the service status. Otherwise // return the status value returned by the reference object by handle // routine. // try { // // Get previous processor mode and probe output arguments if necessary. // PreviousMode = KeGetPreviousMode(); if (PreviousMode != KernelMode) { ProbeForWrite(TimerInformation, sizeof(TIMER_BASIC_INFORMATION), sizeof(ULONG)); if (ARGUMENT_PRESENT(ReturnLength)) { ProbeForWriteUlong(ReturnLength); } } // // Check argument validity. // if (TimerInformationClass != TimerBasicInformation) { return STATUS_INVALID_INFO_CLASS; } if (TimerInformationLength != sizeof(TIMER_BASIC_INFORMATION)) { return STATUS_INFO_LENGTH_MISMATCH; } // // Reference timer object by handle. // Status = ObReferenceObjectByHandle(TimerHandle, TIMER_QUERY_STATE, ExTimerObjectType, PreviousMode, (PVOID *)&ExTimer, NULL); // // If the reference was successful, then read the current state, // compute the time remaining, dereference the timer object, fill in // the information structure, and return the length of the information // structure if specified. If the write of the time information or the // return length fails, then do not report an error. When the caller // accesses the information structure or the length, an violation will // occur. // if (NT_SUCCESS(Status)) { KeTimer = &ExTimer->KeTimer; State = KeReadStateTimer(KeTimer); KiQueryInterruptTime(&TimeToGo); TimeToGo.QuadPart = KeTimer->DueTime.QuadPart - TimeToGo.QuadPart; ObDereferenceObject(ExTimer); try { ((PTIMER_BASIC_INFORMATION)TimerInformation)->TimerState = State; ((PTIMER_BASIC_INFORMATION)TimerInformation)->RemainingTime = TimeToGo; if (ARGUMENT_PRESENT(ReturnLength)) { *ReturnLength = sizeof(TIMER_BASIC_INFORMATION); } } except(ExSystemExceptionFilter()) { } } // // If an exception occurs during the probe of the current state address, // then always handle the exception and return the exception code as the // status value. // } except(ExSystemExceptionFilter()) { return GetExceptionCode(); } // // Return service status. // return Status; }
NTSTATUS NtReplyWaitReceivePortEx( __in HANDLE PortHandle, __out_opt PVOID *PortContext, __in_opt PPORT_MESSAGE ReplyMessage, __out PPORT_MESSAGE ReceiveMessage, __in_opt PLARGE_INTEGER Timeout ) /*++ Routine Description: See NtReplyWaitReceivePort. Arguments: See NtReplyWaitReceivePort. Timeout - Supplies an optional timeout value to use when waiting for a receive. Return Value: See NtReplyWaitReceivePort. --*/ { PLPCP_PORT_OBJECT PortObject; PLPCP_PORT_OBJECT ReceivePort; PORT_MESSAGE CapturedReplyMessage; KPROCESSOR_MODE PreviousMode; KPROCESSOR_MODE WaitMode; NTSTATUS Status; PLPCP_MESSAGE Msg; PETHREAD CurrentThread; PETHREAD WakeupThread; LARGE_INTEGER TimeoutValue ; PLPCP_PORT_OBJECT ConnectionPort = NULL; PAGED_CODE(); CurrentThread = PsGetCurrentThread(); TimeoutValue.QuadPart = 0 ; // // Get previous processor mode // PreviousMode = KeGetPreviousMode(); WaitMode = PreviousMode; if (PreviousMode != KernelMode) { try { if (ARGUMENT_PRESENT( PortContext )) { ProbeForWriteUlong( (PULONG)PortContext ); } if (ARGUMENT_PRESENT( ReplyMessage)) { ProbeForReadSmallStructure( ReplyMessage, sizeof( *ReplyMessage ), sizeof( ULONG )); CapturedReplyMessage = *ReplyMessage; } if (ARGUMENT_PRESENT( Timeout )) { TimeoutValue = ProbeAndReadLargeInteger( Timeout ); Timeout = &TimeoutValue ; } ProbeForWriteSmallStructure( ReceiveMessage, sizeof( *ReceiveMessage ), sizeof( ULONG )); } except( EXCEPTION_EXECUTE_HANDLER ) { return GetExceptionCode(); } } else { // // Kernel mode threads call with wait mode of user so that their // kernel // stacks are swappable. Main consumer of this is // SepRmCommandThread // if ( IS_SYSTEM_THREAD(CurrentThread) ) {
NTSTATUS NtW32Call ( IN ULONG ApiNumber, IN PVOID InputBuffer, IN ULONG InputLength, OUT PVOID *OutputBuffer, OUT PULONG OutputLength ) /*++ Routine Description: This function calls a W32 function. Arguments: ApiNumber - Supplies the API number. InputBuffer - Supplies a pointer to a structure that is copied to the user stack. InputLength - Supplies the length of the input structure. Outputbuffer - Supplies a pointer to a variable that recevies the output buffer address. Outputlength - Supplies a pointer to a variable that recevies the output buffer length. Return Value: TBS. --*/ { PVOID ValueBuffer; ULONG ValueLength; NTSTATUS Status; ASSERT(KeGetPreviousMode() == UserMode); // // If the current thread is not a GUI thread, then fail the service // since the thread does not have a large stack. // if (KeGetCurrentThread()->Win32Thread == (PVOID)&KeServiceDescriptorTable[0]) { return STATUS_NOT_IMPLEMENTED; } // // Probe the output buffer address and length for writeability. // try { ProbeForWriteUlong((PULONG)OutputBuffer); ProbeForWriteUlong(OutputLength); // // If an exception occurs during the probe of the output buffer or // length, then always handle the exception and return the exception // code as the status value. // } except(EXCEPTION_EXECUTE_HANDLER) { return GetExceptionCode(); } // // Call out to user mode specifying the input buffer and API number. // Status = KeUserModeCallback(ApiNumber, InputBuffer, InputLength, &ValueBuffer, &ValueLength); // // If the callout is successful, then the output buffer address and // length. // if (NT_SUCCESS(Status)) { try { *OutputBuffer = ValueBuffer; *OutputLength = ValueLength; } except(EXCEPTION_EXECUTE_HANDLER) { } } return Status; }
NTSTATUS NtSystemDebugControl ( IN SYSDBG_COMMAND Command, IN PVOID InputBuffer, IN ULONG InputBufferLength, OUT PVOID OutputBuffer, IN ULONG OutputBufferLength, OUT PULONG ReturnLength OPTIONAL ) /*++ Routine Description: This function controls the system debugger. Arguments: Command - The command to be executed. One of the following: SysDbgQueryTraceInformation SysDbgSetTracepoint SysDbgSetSpecialCall SysDbgClearSpecialCalls SysDbgQuerySpecialCalls InputBuffer - A pointer to a buffer describing the input data for the request, if any. The structure of this buffer varies depending upon Command. InputBufferLength - The length in bytes of InputBuffer. OutputBuffer - A pointer to a buffer that is to receive the output data for the request, if any. The structure of this buffer varies depending upon Command. OutputBufferLength - The length in bytes of OutputBuffer. ReturnLength - A optional pointer to a ULONG that is to receive the output data length for the request. Return Value: Returns one of the following status codes: STATUS_SUCCESS - normal, successful completion. STATUS_INVALID_INFO_CLASS - The Command parameter did not specify a valid value. STATUS_INFO_LENGTH_MISMATCH - The value of the Length field in the Parameters buffer was not correct. STATUS_ACCESS_VIOLATION - Either the Parameters buffer pointer or a pointer within the Parameters buffer specified an invalid address. STATUS_INSUFFICIENT_RESOURCES - Insufficient system resources exist for this request to complete. --*/ { NTSTATUS status = STATUS_SUCCESS; BOOLEAN releaseModuleResoure = FALSE; ULONG length = 0; KPROCESSOR_MODE PreviousMode; PreviousMode = KeGetPreviousMode(); if (!SeSinglePrivilegeCheck( SeDebugPrivilege, PreviousMode)) { return STATUS_ACCESS_DENIED; } // // Operate within a try block in order to catch errors. // try { // // Probe input and output buffers, if previous mode is not // kernel. // if ( PreviousMode != KernelMode ) { if ( InputBufferLength != 0 ) { ProbeForRead( InputBuffer, InputBufferLength, sizeof(ULONG) ); } if ( OutputBufferLength != 0 ) { ProbeForWrite( OutputBuffer, OutputBufferLength, sizeof(ULONG) ); } if ( ARGUMENT_PRESENT(ReturnLength) ) { ProbeForWriteUlong( ReturnLength ); } } // // Switch on the command code. // switch ( Command ) { #ifdef _X86_ case SysDbgQueryTraceInformation: status = KdGetTraceInformation( OutputBuffer, OutputBufferLength, &length ); break; case SysDbgSetTracepoint: if ( InputBufferLength != sizeof(DBGKD_MANIPULATE_STATE64) ) { return STATUS_INFO_LENGTH_MISMATCH; } KdSetInternalBreakpoint( InputBuffer ); break; case SysDbgSetSpecialCall: if ( InputBufferLength != sizeof(PVOID) ) { return STATUS_INFO_LENGTH_MISMATCH; } KdSetSpecialCall( InputBuffer, NULL ); break; case SysDbgClearSpecialCalls: KdClearSpecialCalls( ); break; case SysDbgQuerySpecialCalls: status = KdQuerySpecialCalls( OutputBuffer, OutputBufferLength, &length ); break; #endif case SysDbgBreakPoint: if (KdDebuggerEnabled) { DbgBreakPointWithStatus(DBG_STATUS_DEBUG_CONTROL); } else { status = STATUS_UNSUCCESSFUL; } break; default: // // Invalid Command. // status = STATUS_INVALID_INFO_CLASS; } if ( ARGUMENT_PRESENT(ReturnLength) ) { *ReturnLength = length; } } except ( EXCEPTION_EXECUTE_HANDLER ) { if ( releaseModuleResoure ) { ExReleaseResource( &PsLoadedModuleResource ); KeLeaveCriticalRegion(); } status = GetExceptionCode(); } return status; } // NtSystemDebugControl
NTSTATUS NtQueryMutant ( IN HANDLE MutantHandle, IN MUTANT_INFORMATION_CLASS MutantInformationClass, OUT PVOID MutantInformation, IN ULONG MutantInformationLength, OUT PULONG ReturnLength OPTIONAL ) /*++ Routine Description: This function queries the state of a mutant object and returns the requested information in the specified record structure. Arguments: MutantHandle - Supplies a handle to a mutant object. MutantInformationClass - Supplies the class of information being requested. MutantInformation - Supplies a pointer to a record that is to receive the requested information. MutantInformationLength - Supplies the length of the record that is to receive the requested information. ReturnLength - Supplies an optional pointer to a variable that will receive the actual length of the information that is returned. Return Value: TBS --*/ { BOOLEAN Abandoned; BOOLEAN OwnedByCaller; LONG Count; PVOID Mutant; KPROCESSOR_MODE PreviousMode; NTSTATUS Status; // // Establish an exception handler, probe the output arguments, reference // the mutant object, and return the specified information. If the probe // fails, then return the exception code as the service status. Otherwise // return the status value returned by the reference object by handle // routine. // try { // // Get previous processor mode and probe output arguments if necessary. // PreviousMode = KeGetPreviousMode(); if (PreviousMode != KernelMode) { ProbeForWrite(MutantInformation, sizeof(MUTANT_BASIC_INFORMATION), sizeof(ULONG)); if (ARGUMENT_PRESENT(ReturnLength)) { ProbeForWriteUlong(ReturnLength); } } // // Check argument validity. // if (MutantInformationClass != MutantBasicInformation) { return STATUS_INVALID_INFO_CLASS; } if (MutantInformationLength != sizeof(MUTANT_BASIC_INFORMATION)) { return STATUS_INFO_LENGTH_MISMATCH; } // // Reference mutant object by handle. // Status = ObReferenceObjectByHandle(MutantHandle, MUTANT_QUERY_STATE, ExMutantObjectType, PreviousMode, &Mutant, NULL); // // If the reference was successful, then read the current state and // abandoned status of the mutant object, dereference mutant object, // fill in the information structure, and return the length of the // information structure if specified. If the write of the mutant // information or the return length fails, then do not report an error. // When the caller accesses the information structure or length an // access violation will occur. // if (NT_SUCCESS(Status)) { Count = KeReadStateMutant((PKMUTANT)Mutant); Abandoned = ((PKMUTANT)Mutant)->Abandoned; OwnedByCaller = (BOOLEAN)((((PKMUTANT)Mutant)->OwnerThread == KeGetCurrentThread())); ObDereferenceObject(Mutant); try { ((PMUTANT_BASIC_INFORMATION)MutantInformation)->CurrentCount = Count; ((PMUTANT_BASIC_INFORMATION)MutantInformation)->OwnedByCaller = OwnedByCaller; ((PMUTANT_BASIC_INFORMATION)MutantInformation)->AbandonedState = Abandoned; if (ARGUMENT_PRESENT(ReturnLength)) { *ReturnLength = sizeof(MUTANT_BASIC_INFORMATION); } } except(ExSystemExceptionFilter()) { } } // // If an exception occurs during the probe of the output arguments, then // always handle the exception and return the exception code as the status // value. // } except(ExSystemExceptionFilter()) { return GetExceptionCode(); } // // Return service status. // return Status; }
NTSTATUS NtConnectPort( OUT PHANDLE PortHandle, IN PUNICODE_STRING PortName, IN PSECURITY_QUALITY_OF_SERVICE SecurityQos, IN OUT PPORT_VIEW ClientView OPTIONAL, OUT PREMOTE_PORT_VIEW ServerView OPTIONAL, OUT PULONG MaxMessageLength OPTIONAL, IN OUT PVOID ConnectionInformation OPTIONAL, IN OUT PULONG ConnectionInformationLength OPTIONAL ) /*++ Routine Description: A client process can connect to a server process by name using the NtConnectPort service. The PortName parameter specifies the name of the server port to connect to. It must correspond to an object name specified on a call to NtCreatePort. The service sends a connection request to the server thread that is listening for them with the NtListenPort service. The client thread then blocks until a server thread receives the connection request and responds with a call to the NtCompleteConnectPort service. The server thread receives the ID of the client thread, along with any information passed via the ConnectionInformation parameter. The server thread then decides to either accept or reject the connection request. The server communicates the acceptance or rejection with the NtCompleteConnectPort service. The server can pass back data to the client about the acceptance or rejection via the ConnectionInformation data block. If the server accepts the connection request, then the client receives a communication port object in the location pointed to by the PortHandle parameter. This object handle has no name associated with it and is private to the client process (i.e. it cannot be inherited by a child process). The client uses the handle to send and receive messages to/from the server process using the NtRequestWaitReplyPort service. If the ClientView parameter was specified, then the section handle is examined. If it is a valid section handle, then the portion of the section described by the SectionOffset and ViewSize fields will be mapped into both the client and server process' address spaces. The address in client address space will be returned in the ViewBase field. The address in the server address space will be returned in the ViewRemoteBase field. The actual offset and size used to map the section will be returned in the SectionOffset and ViewSize fields. If the server rejects the connection request, then no communication port object handle is returned, and the return status indicates an error occurred. The server may optionally return information in the ConnectionInformation data block giving the reason the connection requests was rejected. If the PortName does not exist, or the client process does not have sufficient access rights then the returned status will indicate that the port was not found. Arguments: PortHandle - A pointer to a variable that will receive the client communication port object handle value. !f PortName - A pointer to a port name string. The form of the name is [\name...\name]\port_name. !f SecurityQos - A pointer to security quality of service information to be applied to the server on the client's behalf. ClientView - An optional pointer to a structure that specifies the section that all client threads will use to send messages to the server. !b !u ClientView Structure ULONG !f Length - Specifies the size of this data structure in bytes. HANDLE !f SectionHandle - Specifies an open handle to a section object. ULONG !f SectionOffset - Specifies a field that will receive the actual offset, in bytes, from the start of the section. The initial value of this parameter specifies the byte offset within the section that the client's view is based. The value is rounded down to the next host page size boundary. ULONG !f ViewSize - Specifies a field that will receive the actual size, in bytes, of the view. If the value of this parameter is zero, then the client's view of the section will be mapped starting at the specified section offset and continuing to the end of the section. Otherwise, the initial value of this parameter specifies the size, in bytes, of the client's view and is rounded up to the next host page size boundary. PVOID !f ViewBase - Specifies a field that will receive the base address of the section in the client's address space. PVOID !f ViewRemoteBase - Specifies a field that will receive the base address of the client's section in the server's address space. Used to generate pointers that are meaningful to the server. ServerView - An optional pointer to a structure that will receive information about the server process' view in the client's address space. The client process can use this information to validate pointers it receives from the server process. !b !u ServerView Structure ULONG !f Length - Specifies the size of this data structure in bytes. PVOID !f ViewBase - Specifies a field that will receive the base address of the server's section in the client's address space. ULONG !f ViewSize - Specifies a field that will receive the size, in bytes, of the server's view in the client's address space. If this field is zero, then server has no view in the client's address space. MaxMessageLength - An optional pointer to a variable that will receive maximum length of messages that can be sent to the server. The value of this parameter will not exceed MAX_PORTMSG_LENGTH bytes. ConnectionInformation - An optional pointer to uninterpreted data. This data is intended for clients to pass package, version and protocol identification information to the server to allow the server to determine if it can satisify the client before accepting the connection. Upon return to the client, the ConnectionInformation data block contains any information passed back from the server by its call to the NtCompleteConnectPort service. The output data overwrites the input data. ConnectionInformationLength - Pointer to the length of the ConnectionInformation data block. The output value is the length of the data stored in the ConnectionInformation data block by the server's call to the NtCompleteConnectPort service. This parameter is OPTIONAL only if the ConnectionInformation parameter is null, otherwise it is required. Return Value: return-value - Description of conditions needed to return value. - or - None. --*/ { PLPCP_PORT_OBJECT ConnectionPort; PLPCP_PORT_OBJECT ClientPort; HANDLE Handle; KPROCESSOR_MODE PreviousMode; NTSTATUS Status; ULONG ConnectionInfoLength; PVOID SectionToMap; PLPCP_MESSAGE Msg; PLPCP_CONNECTION_MESSAGE ConnectMsg; PETHREAD CurrentThread = PsGetCurrentThread(); LARGE_INTEGER SectionOffset; PORT_VIEW CapturedClientView; SECURITY_QUALITY_OF_SERVICE CapturedQos; PAGED_CODE(); // // Get previous processor mode and probe output arguments if necessary. // PreviousMode = KeGetPreviousMode(); ConnectionInfoLength = 0; if (PreviousMode != KernelMode) { try { ProbeForWriteHandle( PortHandle ); if (ARGUMENT_PRESENT( ClientView )) { if (ClientView->Length != sizeof( *ClientView )) { return( STATUS_INVALID_PARAMETER ); } CapturedClientView = *ClientView; ProbeForWrite( ClientView, sizeof( *ClientView ), sizeof( ULONG ) ); } if (ARGUMENT_PRESENT( ServerView )) { if (ServerView->Length != sizeof( *ServerView )) { return( STATUS_INVALID_PARAMETER ); } ProbeForWrite( ServerView, sizeof( *ServerView ), sizeof( ULONG ) ); } if (ARGUMENT_PRESENT( MaxMessageLength )) { ProbeForWriteUlong( MaxMessageLength ); } if (ARGUMENT_PRESENT( ConnectionInformationLength )) { ProbeForWriteUlong( ConnectionInformationLength ); ConnectionInfoLength = *ConnectionInformationLength; } if (ARGUMENT_PRESENT( ConnectionInformation )) { ProbeForWrite( ConnectionInformation, ConnectionInfoLength, sizeof( UCHAR ) ); } ProbeForRead( SecurityQos, sizeof( SECURITY_QUALITY_OF_SERVICE ), sizeof( ULONG ) ); CapturedQos = *SecurityQos; } except( EXCEPTION_EXECUTE_HANDLER ) { return( GetExceptionCode() ); } } else { if (ARGUMENT_PRESENT( ClientView )) {
NTSTATUS NtFreeVirtualMemory( IN HANDLE ProcessHandle, IN OUT PVOID *BaseAddress, IN OUT PULONG RegionSize, IN ULONG FreeType ) /*++ Routine Description: This function deletes a region of pages within the virtual address space of a subject process. Arguments: ProcessHandle - An open handle to a process object. BaseAddress - The base address of the region of pages to be freed. This value is rounded down to the next host page address boundary. RegionSize - A pointer to a variable that will receive the actual size in bytes of the freed region of pages. The initial value of this argument is rounded up to the next host page size boundary. FreeType - A set of flags that describe the type of free that is to be performed for the specified region of pages. FreeType Flags MEM_DECOMMIT - The specified region of pages is to be decommitted. MEM_RELEASE - The specified region of pages is to be released. Return Value: Returns the status TBS --*/ { PMMVAD_SHORT Vad; PMMVAD_SHORT NewVad; PMMVAD PreviousVad; PMMVAD NextVad; PEPROCESS Process; KPROCESSOR_MODE PreviousMode; PVOID StartingAddress; PVOID EndingAddress; NTSTATUS Status; ULONG Attached = FALSE; ULONG CapturedRegionSize; PVOID CapturedBase; PMMPTE StartingPte; PMMPTE EndingPte; ULONG OldQuota; ULONG QuotaCharge; ULONG CommitReduction; PVOID OldEnd; PAGED_CODE(); // // Check to make sure FreeType is good. // if ((FreeType & ~(MEM_DECOMMIT | MEM_RELEASE)) != 0) { return STATUS_INVALID_PARAMETER_4; } // // One of MEM_DECOMMIT or MEM_RELEASE must be specified, but not both. // if (((FreeType & (MEM_DECOMMIT | MEM_RELEASE)) == 0) || ((FreeType & (MEM_DECOMMIT | MEM_RELEASE)) == (MEM_DECOMMIT | MEM_RELEASE))) { return STATUS_INVALID_PARAMETER_4; } PreviousMode = KeGetPreviousMode(); // // Establish an exception handler, probe the specified addresses // for write access and capture the initial values. // try { if (PreviousMode != KernelMode) { ProbeForWriteUlong ((PULONG)BaseAddress); ProbeForWriteUlong (RegionSize); } // // Capture the base address. // CapturedBase = *BaseAddress; // // Capture the region size. // CapturedRegionSize = *RegionSize; } except (ExSystemExceptionFilter()) { // // If an exception occurs during the probe or capture // of the initial values, then handle the exception and // return the exception code as the status value. // return GetExceptionCode(); } #if DBG if (MmDebug & MM_DBG_SHOW_NT_CALLS) { if ( !MmWatchProcess ) { DbgPrint("freevm processhandle %lx base %lx size %lx type %lx\n", ProcessHandle, CapturedBase, CapturedRegionSize, FreeType); } } #endif // // Make sure the specified starting and ending addresses are // within the user part of the virtual address space. // if (CapturedBase > MM_HIGHEST_USER_ADDRESS) { // // Invalid base address. // return STATUS_INVALID_PARAMETER_2; } if ((ULONG)MM_HIGHEST_USER_ADDRESS - (ULONG)CapturedBase < CapturedRegionSize) { // // Invalid region size; // return STATUS_INVALID_PARAMETER_3; } EndingAddress = (PVOID)(((ULONG)CapturedBase + CapturedRegionSize - 1) | (PAGE_SIZE - 1)); StartingAddress = (PVOID)PAGE_ALIGN(CapturedBase); if ( ProcessHandle == NtCurrentProcess() ) { Process = PsGetCurrentProcess(); } else { // // Reference the specified process handle for VM_OPERATION access. // Status = ObReferenceObjectByHandle ( ProcessHandle, PROCESS_VM_OPERATION, PsProcessType, PreviousMode, (PVOID *)&Process, NULL ); if (!NT_SUCCESS(Status)) { return Status; } } // // If the specified process is not the current process, attach // to the specified process. // if (PsGetCurrentProcess() != Process) { KeAttachProcess (&Process->Pcb); Attached = TRUE; } // // Get the address creation mutex to block multiple threads from // creating or deleting address space at the same time and // get the working set mutex so virtual address descriptors can // be inserted and walked. Block APCs to prevent page faults while // we own the working set mutex. // LOCK_WS_AND_ADDRESS_SPACE (Process); // // Make sure the address space was not deleted. // if (Process->AddressSpaceDeleted != 0) { Status = STATUS_PROCESS_IS_TERMINATING; goto ErrorReturn; } Vad = (PMMVAD_SHORT)MiLocateAddress (StartingAddress); if (Vad == NULL) { // // No Virtual Address Descriptor located for Base Address. // Status = STATUS_MEMORY_NOT_ALLOCATED; goto ErrorReturn; } // // Found the associated Virtual Address Descriptor. // if (Vad->EndingVa < EndingAddress) { // // The entire range to delete is not contained within a single // virtual address descriptor. Return an error. // Status = STATUS_UNABLE_TO_FREE_VM; goto ErrorReturn; } // // Check to ensure this Vad is deletable. Delete is required // for both decommit and release. // if ((Vad->u.VadFlags.PrivateMemory == 0) || (Vad->u.VadFlags.PhysicalMapping == 1)) { Status = STATUS_UNABLE_TO_DELETE_SECTION; goto ErrorReturn; } if (Vad->u.VadFlags.NoChange == 1) { // // An attempt is being made to delete a secured VAD, check // to see if this deletion is allowed. // if (FreeType & MEM_RELEASE) { // // Specifiy the whole range, this solves the problem with // splitting the VAD and trying to decide where the various // secure ranges need to go. // Status = MiCheckSecuredVad ((PMMVAD)Vad, Vad->StartingVa, (PCHAR)Vad->EndingVa - (PCHAR)Vad->StartingVa, MM_SECURE_DELETE_CHECK); } else { Status = MiCheckSecuredVad ((PMMVAD)Vad, CapturedBase, CapturedRegionSize, MM_SECURE_DELETE_CHECK); } if (!NT_SUCCESS (Status)) { goto ErrorReturn; } } PreviousVad = MiGetPreviousVad (Vad); NextVad = MiGetNextVad (Vad); if (FreeType & MEM_RELEASE) { // // ***************************************************************** // MEM_RELEASE was specified. // ***************************************************************** // // // The descriptor for the address range is deletable. Remove or split // the descriptor. // // // If the region size is zero, remove the whole VAD. // if (CapturedRegionSize == 0) { // // If the region size is specified as 0, the base address // must be the starting address for the region. // if (CapturedBase != Vad->StartingVa) { Status = STATUS_FREE_VM_NOT_AT_BASE; goto ErrorReturn; } // // This Virtual Address Descriptor has been deleted. // StartingAddress = Vad->StartingVa; EndingAddress = Vad->EndingVa; MiRemoveVad ((PMMVAD)Vad); ExFreePool (Vad); } else { // // Regions size was not specified as zero, delete the // whole VAD or split the VAD. // if (StartingAddress == Vad->StartingVa) { if (EndingAddress == Vad->EndingVa) { // // This Virtual Address Descriptor has been deleted. // MiRemoveVad ((PMMVAD)Vad); ExFreePool (Vad); } else { // // This Virtual Address Descriptor has a new starting // address. // CommitReduction = MiCalculatePageCommitment ( StartingAddress, EndingAddress, (PMMVAD)Vad, Process ); Vad->StartingVa = (PVOID)((ULONG)EndingAddress + 1L); Vad->u.VadFlags.CommitCharge -= CommitReduction; ASSERT ((LONG)Vad->u.VadFlags.CommitCharge >= 0); MiReturnPageFileQuota (CommitReduction, Process); MiReturnCommitment (CommitReduction); Process->CommitCharge -= CommitReduction; PreviousVad = (PMMVAD)Vad; } } else { // // Starting address is greater than start of VAD. // if (EndingAddress == Vad->EndingVa) { // // Change the ending address of the VAD. // CommitReduction = MiCalculatePageCommitment ( StartingAddress, EndingAddress, (PMMVAD)Vad, Process ); Vad->u.VadFlags.CommitCharge -= CommitReduction; MiReturnPageFileQuota (CommitReduction, Process); MiReturnCommitment (CommitReduction); Process->CommitCharge -= CommitReduction; Vad->EndingVa = (PVOID)((ULONG)StartingAddress - 1L); PreviousVad = (PMMVAD)Vad; } else { // // Split this VAD as the address range is within the VAD. // // // Allocate an new VAD under an exception handler // as there may not be enough quota. // NewVad = ExAllocatePoolWithTag (NonPagedPool, sizeof(MMVAD_SHORT), 'SdaV'); if ( NewVad == NULL ) { Status = STATUS_INSUFFICIENT_RESOURCES; goto ErrorReturn; } CommitReduction = MiCalculatePageCommitment ( StartingAddress, EndingAddress, (PMMVAD)Vad, Process ); OldQuota = Vad->u.VadFlags.CommitCharge - CommitReduction; OldEnd = Vad->EndingVa; *NewVad = *Vad; Vad->EndingVa = (PVOID)((ULONG)StartingAddress - 1L); NewVad->StartingVa = (PVOID)((ULONG)EndingAddress + 1L); // // Set the commit charge to zero so MiInsertVad will // not charge committment for splitting the VAD. // NewVad->u.VadFlags.CommitCharge = 0; try { // // Insert the VAD, this could get an exception // on charging quota. // MiInsertVad ((PMMVAD)NewVad); } except (EXCEPTION_EXECUTE_HANDLER) { // // Inserting the Vad failed, reset the original // VAD, free new vad and return an error. // Vad->EndingVa = OldEnd; ExFreePool (NewVad); Status = GetExceptionCode(); goto ErrorReturn; } Vad->u.VadFlags.CommitCharge -= CommitReduction; MiReturnPageFileQuota (CommitReduction, Process); MiReturnCommitment (CommitReduction); Process->CommitCharge -= CommitReduction; // // As we have split the original VAD into 2 seperate VADs // there is know way of knowing what the commit charge // is for each VAD. Calculate the charge and reset // each VAD. Note that we also use the previous value // to make sure the books stay balanced. // QuotaCharge = MiCalculatePageCommitment (Vad->StartingVa, Vad->EndingVa, (PMMVAD)Vad, Process ); Vad->u.VadFlags.CommitCharge = QuotaCharge; // // Give the remaining charge to the new VAD. // NewVad->u.VadFlags.CommitCharge = OldQuota - QuotaCharge; PreviousVad = (PMMVAD)Vad; NextVad = (PMMVAD)NewVad; } } } // // Return commitment for page table pages if possibible. // MiReturnPageTablePageCommitment (StartingAddress, EndingAddress, Process, PreviousVad, NextVad); // // Get the PFN mutex so the MiDeleteVirtualAddresses can be called. // MiDeleteFreeVm (StartingAddress, EndingAddress); UNLOCK_WS (Process); CapturedRegionSize = 1 + (ULONG)EndingAddress - (ULONG)StartingAddress; // // Update the virtual size in the process header. // Process->VirtualSize -= CapturedRegionSize; UNLOCK_ADDRESS_SPACE (Process); if (Attached) { KeDetachProcess(); } if ( ProcessHandle != NtCurrentProcess() ) { ObDereferenceObject (Process); } // // Establish an exception handler and write the size and base // address. // try { *RegionSize = CapturedRegionSize; *BaseAddress = StartingAddress; } except (EXCEPTION_EXECUTE_HANDLER) { // // An exception occurred, don't take any action (just handle // the exception and return success. } #if DBG if (MmDebug & MM_DBG_SHOW_NT_CALLS) { if ( MmWatchProcess ) { if ( MmWatchProcess == PsGetCurrentProcess() ) { DbgPrint("\n--- FREE Type 0x%lx Base %lx Size %lx\n", FreeType, StartingAddress, CapturedRegionSize); MmFooBar(); } } } #endif #if DBG if (RtlAreLogging( RTL_EVENT_CLASS_VM )) { RtlLogEvent( MiFreeVmEventId, RTL_EVENT_CLASS_VM, StartingAddress, CapturedRegionSize, FreeType ); } #endif // DBG return STATUS_SUCCESS; } // // ************************************************************** // // MEM_DECOMMIT was specified. // // ************************************************************** // // // Check to ensure the complete range of pages is already committed. // if (CapturedRegionSize == 0) { if (CapturedBase != Vad->StartingVa) { Status = STATUS_FREE_VM_NOT_AT_BASE; goto ErrorReturn; } EndingAddress = Vad->EndingVa; } #if 0 if (FreeType & MEM_CHECK_COMMIT_STATE) { if ( !MiIsEntireRangeCommitted(StartingAddress, EndingAddress, Vad, Process)) { // // The entire range to be decommited is not committed, // return an errror. // Status = STATUS_UNABLE_TO_DECOMMIT_VM; goto ErrorReturn; } } #endif //0 // // The address range is entirely committed, decommit it now. // // // Calculate the initial quotas and commit charges for this VAD. // StartingPte = MiGetPteAddress (StartingAddress); EndingPte = MiGetPteAddress (EndingAddress); CommitReduction = 1 + EndingPte - StartingPte; // // Check to see if the entire range can be decommitted by // just updating the virtual address descriptor. // CommitReduction -= MiDecommitPages (StartingAddress, EndingPte, Process, Vad); // // Adjust the quota charges. // ASSERT ((LONG)CommitReduction >= 0); MiReturnPageFileQuota (CommitReduction, Process); MiReturnCommitment (CommitReduction); Vad->u.VadFlags.CommitCharge -= CommitReduction; Process->CommitCharge -= CommitReduction; ASSERT ((LONG)Vad->u.VadFlags.CommitCharge >= 0); UNLOCK_WS (Process); UNLOCK_ADDRESS_SPACE (Process); if (Attached) { KeDetachProcess(); } if ( ProcessHandle != NtCurrentProcess() ) { ObDereferenceObject (Process); } // // Establish an exception handler and write the size and base // address. // try { *RegionSize = 1 + (ULONG)EndingAddress - (ULONG)StartingAddress; *BaseAddress = StartingAddress; } except (EXCEPTION_EXECUTE_HANDLER) { NOTHING; } #if DBG if (RtlAreLogging( RTL_EVENT_CLASS_VM )) { RtlLogEvent( MiFreeVmEventId, RTL_EVENT_CLASS_VM, StartingAddress, 1 + (ULONG)EndingAddress - (ULONG)StartingAddress, FreeType ); } #endif // DBG return STATUS_SUCCESS; ErrorReturn: UNLOCK_WS (Process); UNLOCK_ADDRESS_SPACE (Process); if (Attached) { KeDetachProcess(); } if ( ProcessHandle != NtCurrentProcess() ) { ObDereferenceObject (Process); } return Status; }