Windows System Software -- Consulting, Training, Development -- Unique Expertise, Guaranteed Results

Home NTDEV
Before Posting...
Please check out the Community Guidelines in the Announcements and Administration Category.

More Info on Driver Writing and Debugging


The free OSR Learning Library has more than 50 articles on a wide variety of topics about writing and debugging device drivers and Minifilters. From introductory level to advanced. All the articles have been recently reviewed and updated, and are written using the clear and definitive style you've come to expect from OSR over the years.


Check out The OSR Learning Library at: https://www.osr.com/osr-learning-library/


ZwQueryInformationProcess Return Length

zinoberdevelopzinoberdevelop Member Posts: 7
edited January 2 in NTDEV

Hi everyone.
First of all, I have to say that I am a novice! :smile:
I was looking for a way to get the file name in my PsSetCreateProcessNotifyRoutine.
In my research, I came across this link:
https://www.osronline.com/article.cfm^article=472.htm
But I do not understand a subject here.
Why does it reduce the size of the Unicode_String from the returnedLength?

status = ZwQueryInformationProcess( NtCurrentProcess(),
ProcessImageFileName,
NULL, // buffer
0, // buffer size
&returnedLength);

bufferLength = returnedLength - sizeof(UNICODE_STRING);

if (ProcessImageName->MaximumLength < bufferLength) {

    ProcessImageName->Length = (USHORT) bufferLength;

    return STATUS_BUFFER_OVERFLOW;

}

Comments

  • ThatsBerkanThatsBerkan Member Posts: 38
    edited January 2

    It reduces the size by sizeof(UNICODE_STRING) because the buffer includes two things, the UNICODE_STRING (at offset 0x00) that describes the process name and its length, and then after that structure, you have the actual string/file path, which is also pointed to by UNICODE_STRING->Buffer.

    So the Buffer looks something like this :
    [
    0x00 - UNICODE_STRING
    0x10 - The path...
    ]

    /// <summary>
    /// Gets the image file path of the given process.
    /// </summary>
    /// <param name="Process">The process object.</param>
    /// <param name="OutProcessName">The returned process image file path.</param>
    NTSTATUS ProcessUtils::GetProcessImageFilePath(CONST PEPROCESS Process, OUT WCHAR OutProcessName[MAXIMUM_FILENAME_LENGTH])
    {
        NTSTATUS Status = { };
    
        // 
        // Verify the passed arguments.
        // 
    
        if (Process == nullptr)
        {
            return STATUS_INVALID_PARAMETER_1;
        }
    
        if (OutProcessName == nullptr)
        {
            return STATUS_INVALID_PARAMETER_2;
        }
    
        // 
        // Open a handle to the process.
        // 
    
        auto* ProcessHandle = ZwCurrentProcess();
    
        if (PsGetCurrentProcess() != Process)
        {
            if (!NT_SUCCESS(Status = ObOpenObjectByPointer(Process, OBJ_KERNEL_HANDLE, NULL, GENERIC_ALL, *PsProcessType, KernelMode, &ProcessHandle)))
            {
                return Status;
            }
        }
    
        // 
        // Retrieve the file path of the main module of the process.
        // 
    
        ULONG ReturnedLength = 0;
        BYTE Buffer[MAXIMUM_FILENAME_LENGTH + sizeof(UNICODE_STRING)] = { };
        UNICODE_STRING* ProcessName = (UNICODE_STRING*) Buffer;
    
        Status = ZwQueryInformationProcess(ProcessHandle, ProcessImageFileName, &Buffer, sizeof(Buffer), &ReturnedLength);
    
        // 
        // Close the handle to the process.
        // 
    
        if (ProcessHandle != ZwCurrentProcess())
        {
            ZwClose(ProcessHandle);
        }
    
        // 
        // If the query was successful, copy the image filename to the caller's buffer.
        // 
    
        if (NT_SUCCESS(Status))
        {
            RtlZeroMemory(&OutProcessName[0], max(sizeof(OutProcessName), ProcessName->Length + sizeof(WCHAR)));
            RtlCopyMemory(&OutProcessName[0], ProcessName->Buffer, ProcessName->Length);
        }
    
        return Status;
    }
    
  • zinoberdevelopzinoberdevelop Member Posts: 7

    @ThatsBerkan said:
    It reduces the size by sizeof(UNICODE_STRING) because the buffer includes two things, the UNICODE_STRING (at offset 0x00) that describes the process name and its length, and then after that structure, you have the actual string/file path, which is also pointed to by UNICODE_STRING->Buffer.

    So the Buffer looks something like this :
    [
    0x00 - UNICODE_STRING
    0x10 - The path...
    ]

    /// <summary>
    /// Gets the image file path of the given process.
    /// </summary>
    /// <param name="Process">The process object.</param>
    /// <param name="OutProcessName">The returned process image file path.</param>
    NTSTATUS ProcessUtils::GetProcessImageFilePath(CONST PEPROCESS Process, OUT WCHAR OutProcessName[MAXIMUM_FILENAME_LENGTH])
    {
      NTSTATUS Status = { };
    
      // 
      // Verify the passed arguments.
      // 
    
      if (Process == nullptr)
      {
          return STATUS_INVALID_PARAMETER_1;
      }
    
      if (OutProcessName == nullptr)
      {
          return STATUS_INVALID_PARAMETER_2;
      }
    
      // 
      // Open a handle to the process.
      // 
    
      auto* ProcessHandle = ZwCurrentProcess();
    
      if (PsGetCurrentProcess() != Process)
      {
          if (!NT_SUCCESS(Status = ObOpenObjectByPointer(Process, OBJ_KERNEL_HANDLE, NULL, GENERIC_ALL, *PsProcessType, KernelMode, &ProcessHandle)))
          {
              return Status;
          }
      }
    
      // 
      // Retrieve the file path of the main module of the process.
      // 
    
      ULONG ReturnedLength = 0;
      BYTE Buffer[MAXIMUM_FILENAME_LENGTH + sizeof(UNICODE_STRING)] = { };
      UNICODE_STRING* ProcessName = (UNICODE_STRING*) Buffer;
    
      Status = ZwQueryInformationProcess(ProcessHandle, ProcessImageFileName, &Buffer, sizeof(Buffer), &ReturnedLength);
    
      // 
      // Close the handle to the process.
      // 
    
      if (ProcessHandle != ZwCurrentProcess())
      {
          ZwClose(ProcessHandle);
      }
    
      // 
      // If the query was successful, copy the image filename to the caller's buffer.
      // 
    
      if (NT_SUCCESS(Status))
      {
          RtlZeroMemory(&OutProcessName[0], max(sizeof(OutProcessName), ProcessName->Length + sizeof(WCHAR)));
          RtlCopyMemory(&OutProcessName[0], ProcessName->Buffer, ProcessName->Length);
      }
      
      return Status;
    }
    

    So you mean the ZwQueryInformationProcess adds the real length value to the Unicode_String structure and then return?

  • ThatsBerkanThatsBerkan Member Posts: 38
    edited January 2

    Yes, the UNICODE_STRING structure contains a '->Length' field that specifies the length (in bytes) of the string.

  • Peter_Viscarola_(OSR)Peter_Viscarola_(OSR) Administrator Posts: 8,253
    edited January 2

    I have a couple of random comments.

    First, I can't believe how ambiguous the docs for ZwQueryInformationProcess/ProcessImageFileName are. Sure... you should be able to "figure it out" given what's written -- but the docs just need to be explicit that when ProcessInformationClass is ProcessImageFileName that the buffer at ProcessInformation contains a UNICODE_STRING (at offset zero) that describes the processes image file name.

    Second, the code in that 15-year-old example (the function "GetProcessImageName") is needlessly complicated and kinda... sucks, and some of the comments in it suck even worse. The caller (of the GetProcessImageName) supplies a fixed-length buffer... so, if that's the case, why go through the little game of calling ZwQueryInformationProcess with null/0? Instead, just ExAllocate a buffer that's the same size as that supplied by the user plus sizeof(UNICODE_STRING) and just use that. If the supplied buffer is too small, you can still return the required size as the example does.

    These things conspire together to make understanding these functions more difficult for Mr. @zinoberdevelop than it should be.

    While I'm here, I need to also mention a common issue in the code that Mr. @ThatsBerkan provided: It's a buffer overflow problem waiting to happen. PLEASE don't write code like this. First, MAXIMUM_FILENAME_LENGTH is not the right value to use (it hasn't in fact been correct for years... the max file path is 32K plus 4 characters). Second, you really HAVE to pass the length parameter to avoid the buffer overflow. And if you're gonna DO that, why not just pass in PUNICODE_STRING as the argument?

    Anyhow... your very fundamental question turned out to be a lot more interesting in terms of details than I expected.

    Peter

    Peter Viscarola
    OSR
    @OSRDrivers

  • Dejan_MaksimovicDejan_Maksimovic Member - All Emails Posts: 374
    via Email
    >
    > First, MAXIMUM_FILENAME_LENGTH is not the right value to use (it hasn't in
    > fact been correct for years... the max file path is 32K plus 4 bytes).

    32k + 4?
  • zinoberdevelopzinoberdevelop Member Posts: 7

    @Peter_Viscarola_(OSR) said:
    I have a couple of random comments.

    First, I can't believe how ambiguous the docs for ZwQueryInformationProcess/ProcessImageFileName are. Sure... you should be able to "figure it out" given what's written -- but the docs just need to be explicit that when ProcessInformationClass is ProcessImageFileName that the buffer at ProcessInformation contains a UNICODE_STRING (at offset zero) that describes the processes image file name.

    Second, the code in that 15-year-old example (the function "GetProcessImageName") is needlessly complicated and kinda... sucks, and some of the comments in it suck even worse. The caller (of the GetProcessImageName) supplies a fixed-length buffer... so, if that's the case, why go through the little game of calling ZwQueryInformationProcess with null/0? Instead, just ExAllocate a buffer that's the same size as that supplied by the user plus sizeof(UNICODE_STRING) and just use that. If the supplied buffer is too small, you can still return the required size as the example does.

    These things conspire together to make understanding these functions more difficult for Mr. @zinoberdevelop than it should be.

    While I'm here, I need to also mention a common issue in the code that Mr. @ThatsBerkan provided: It's a buffer overflow problem waiting to happen. PLEASE don't write code like this. First, MAXIMUM_FILENAME_LENGTH is not the right value to use (it hasn't in fact been correct for years... the max file path is 32K plus 4 characters). Second, you really HAVE to pass the length parameter to avoid the buffer overflow. And if you're gonna DO that, why not just pass in PUNICODE_STRING as the argument?

    Anyhow... your very fundamental question turned out to be a lot more interesting in terms of details than I expected.

    Peter

    Thank you for your very interesting and good explanation.

  • Peter_Viscarola_(OSR)Peter_Viscarola_(OSR) Administrator Posts: 8,253
    edited January 3

    32k + 4?

    That’s 32K+4 characters (I edited immediately after writing it) — It’s the max possible path spec PLUS the preceding “\\?\”

    Peter

    Post edited by Peter_Viscarola_(OSR) on

    Peter Viscarola
    OSR
    @OSRDrivers

  • Dejan_MaksimovicDejan_Maksimovic Member - All Emails Posts: 374
    via Email
    >> 32k + 4?
    >
    > That’s 32K+4 characters (I edited immediately after writing it) — It’s the
    > max possible path spec PLUS the preceding “\?\”

    I thought the 32767 limit (+NULL character for 32768 chars total)
    included the preceeding "\\?\"?

    Can't remember, because I tried it almost 20 years ago (never saw
    anything over 500 characters used).
    Good thing I never hardcode buffer sizes and presume I will never get more.
  • Peter_Viscarola_(OSR)Peter_Viscarola_(OSR) Administrator Posts: 8,253

    @Dejan_Maksimovic... You may be right. I just went Googling for this, and it seems that the whole issue is a complete mess. Different system components have differing maximum path sizes, and enforce differing size expectations. Plus there are the Win10 1607 changes that the system owner can optionally opt-in for.

    Some day, in my copious free time, I'll have to write a native user-mode program that tries to create a file with a Very Long Path Name (something on the order of 32K). Until that time, your policy is the best one: Don't just assume the max length possible; Check for overflow.

    Peter

    Peter Viscarola
    OSR
    @OSRDrivers

Sign In or Register to comment.

Howdy, Stranger!

It looks like you're new here. If you want to get involved, click one of these buttons!

Upcoming OSR Seminars
OSR has suspended in-person seminars due to the Covid-19 outbreak. But, don't miss your training! Attend via the internet instead!
Writing WDF Drivers 7 Dec 2020 LIVE ONLINE
Internals & Software Drivers 25 Jan 2021 LIVE ONLINE
Developing Minifilters 8 March 2021 LIVE ONLINE