Sysinternals Homepage
Forum Home Forum Home > Windows Discussions > Development
  New Posts New Posts RSS Feed - Overview - Handle Enumeration
  FAQ FAQ  Forum Search   Events   Register Register  Login Login

Overview - Handle Enumeration

 Post Reply Post Reply Page  12>
Author
Message
Matts_User_Name View Drop Down
Senior Member
Senior Member
Avatar

Joined: 10 August 2006
Location: USA
Status: Offline
Points: 692
Post Options Post Options   Thanks (0) Thanks(0)   Quote Matts_User_Name Quote  Post ReplyReply Direct Link To This Post Topic: Overview - Handle Enumeration
    Posted: 01 May 2008 at 6:54am

So... You want to know how to get these:





Seeing that this seems to be a hot topic in the Development area of this forum I decided to elaborate on how to enumerate handles of any process:

I did this in VB6, and if it can be done there, it can be done with any language that has access the Windows API.

(Note: The following structues and APIs can be found in winternl.h
http://source.winehq.org/source/include/winternl.h )


1. Enumerate
Enumerate all handles with NtQuerySystemInformation:
Use 16 for SYSTEM_INFORMATION_CLASS --> which says to it "I want to query handles"

Note: Remember to allocate enough room in your buffer to hold the data. In VB6 I have seen some use a Byte Array, or you can just use VirtualAlloc. (Remember to use VirtualFree when you have your data)



2. Query
Copy the buffer into SYSTEM_HANDLE (AKA - SYSTEM_HANDLE_ENTRY).
This is found 4 bytes after the buffer's pointer (the first 4 bytes are the number of handles found)

Remember: NTQSI with 16 asks it to give up SYSTEM_HANDLE_INFORMATION, which as you can see in winternl.h, the data follows the 4 bytes.


3. Manipulate
Walk the list, and manipulate/search it to your liking. It might be useful to store these in an array, depending on your purpose.
The way I did it was:
  1. Get number of total handles and store into variable (first 4 bytes in buffer.
  2. Get each SYSTEM_HANDLE. In the buffer, the next one starts where the previous one ends.
          SYSTEM_HANDLE's size is 10. So the buffer's data is outlined as:

          0-4 = Entries
          4-14 = Handle 1
          14-24 = Handle 2
          24-(size of SH) = Handle x

         
The best way to store these is to create a loop and increase an offset variable (which starts at 4) and once the handle is stored, the offset is increased by Length of SH.
Then once you have that offset, read it from memory. Ex: CopyMemory (RtlMoveMemory)

Note: All these handles in the buffer are not just your process's handles, it is every handle in the system.
To get the handles for each process, look for the handle's PID from SYSTEM_HANDLE



4. Need more data?
In order to gain the Handle's Type and Name the handle must exist in your process. To do this, you must make a copy of the handle using NtDuplicateHandle (Or just DuplicateHandle) and create a copy of it inside your process.

Once the copy is in your process, use NtQueryObject with the OBJECT_INFORMATION_CLASS of 1 telling it to hand over its Name (In UNICODE_STRING format)

Note: NtQueryObject needs the handle's value. So basically this can convert a handle value into the Handle Name (Or Type).
If you are wondering why earlier I said the handle must exist in your process, this is why: Because NtQueryObject was only made to query a local handle's data. Just like how CloseHandle or NtClose is only for local handles.

So to get the handle name for every process:
  • Create local copy of a handle - NtDuplicateHandle
  • Get the name - NtQueryObject (with 1)
  • Query the string from ONI.Name which is from the UNICODE_STRING buffer

The same idea for handle Type is used. The only difference is that OBJECT_TYPE_INFORMATION (2) is used instead of OBJECT_NAME_INFORMATION (1).


Note: Process and Thread handles do not have a name. They can be obtained from:
NtQueryInformationProcess ---> Process_Basic_Information.PID and
NtQueryInformationThread ---> Thread_Basic_Information.CliendID.TID

File path names can be converted from Device Path to Logical Drive path using GetLogicalDriveStrings, and converting them with QueryDosDevice.
Once you know which drive goes with which device, you can convert the \Device\... to X:\


IMPORTANT! If you NtQueryObject with ONI (1) on a special NamedPipe handle, your thread will hang. Infact in my testing, I wasn't even able to kill the process nor shut down my computer.


To avoid these I found a undocumented similarity. Skip the NtQueryObject - ONI call if the Handle's access value is "12019F" (0x0012019F) ---> Dec = 1180063.

EP_X0FF and I talk about this here:
http://forum.sysinternals.com/forum_posts.asp?TID=14435&PN=1

He also suggested spawning another thread for the query, but I don't have that much experience with thread creation, so I luckily found this common factor.

Note: This bug is only for ONI (Name) = 1 and not OTI (Type) = 2.
This avoidance method appears to be foolproof and I have yet to experience a hung thread when avoiding these troublesome File-type NamedPipe handles.




5. Need a CloseHandleEx? (Remote Handle Closing)

As you probably know, NtClose and CloseHandle do not work on remote process handles.
Luckily NtDuplicateObject will do this for us with just one specific flag.
What is this special little flag you ask? --> DUPLICATE_CLOSE_SOURCE  (= 1)

Even though NTDO is used to create a copy of the handle into some other process, using that flag will close the source of the handle. You dont even need to bother closing the local handle that is created, because if you just pass it a null for the destination process handle, it cant go anywhere.
In other words, if no target process is specified and the DCS flag is used, the handle is closed remotely without even making another copy.

Note: The documented API that calls NtDuplicateObject is called DuplicateHandle, which will basically explain what NtDuplicateObject does too.
http://msdn.microsoft.com/en-us/library/ms724251(VS.85).aspx

Example code:

Private Sub CloseHandleEx(ByVal lPID As Long, ByVal lHandle As Long)
    Dim hProcess As Long
    hProcess = OpenProcess(PROCESS_ALL_ACCESS, 0, lPID)
    Call NtDuplicateObject(hProcess, lHandle, 0, ByVal 0, 0, 0, DUPLICATE_CLOSE_SOURCE)
    Call NtClose(hProcess)
End Sub

I am sure there are other methods to perform this remote handle close, but this technique seems the easiest to me.
Another other way could include using CRT (CreateRemoteThread) and telling it to execute NtClose or CloseHandle, which would be similar to remote dll unloading with CRT telling the remote process to execute FreeLibrary.



Feel free to add any comments, questions, improvements, examples, or clarifications if you want.

Mods: You might want to pin this if you feel it could help others.




Edited by Matts_User_Name - 01 May 2008 at 10:07am
Back to Top
SamCPP View Drop Down
Newbie
Newbie


Joined: 24 June 2008
Location: Australia
Status: Offline
Points: 9
Post Options Post Options   Thanks (0) Thanks(0)   Quote SamCPP Quote  Post ReplyReply Direct Link To This Post Posted: 01 July 2008 at 9:31pm
Regarding step 4, how can I find the name information for USB/COM port device file handles? OBJECT_INFORMATION_CLASS of 1 returns an NTSTATUS error of 0xc00000bb (STATUS_NOT_SUPPORTED), whereas all other file handles for hyperterminal are returned fine.

Attached is what process explorer displays (and is exactly what I am trying to get programmatically).
uploads/20080701_212826_device_usbser00.zip

Cheers,
Sam
Back to Top
Matts_User_Name View Drop Down
Senior Member
Senior Member
Avatar

Joined: 10 August 2006
Location: USA
Status: Offline
Points: 692
Post Options Post Options   Thanks (0) Thanks(0)   Quote Matts_User_Name Quote  Post ReplyReply Direct Link To This Post Posted: 01 July 2008 at 11:20pm
Hey there.

Hmm that is interesting.
I have noticed PE adds "artificial" names to many objects (like threads, processes, ect, by using functions like NtQueryInformation[ObjectType]

Infact I had a similar question but with access tokens:
http://forum.sysinternals.com/forum_posts.asp?TID=15261&PN=1

I am not really sure how to get that, although it is a handle to a File object.
So basically NtQueryObject with ONI is returning "STATUS_NOT_SUPPORTED" ONLY on this specific file, and nothing else?

Did you try using the alternative approach of NtQueryInformationFile with FILE_NAME_INFORMATION structure?

Perhaps it is one of those handles which NtQueryObject will not work with even if it is a "File" object. Might be similar to the behavior of a NamedPipe, except instead of freezing, NTQO reports that it doesn't support the retrieval of that kind of File object's name.

Obviously PE uses a driver so perhaps that is why it is able to get its name. After all, PE is able to get the name of NamedPipes which will cause a freeze for us. Most likely it uses the aid of its driver for that, so indeed this could be a similar case.

I wish I could help more but my knowledge of Windows pretty much ends at usermode :P.

Back to Top
SamCPP View Drop Down
Newbie
Newbie


Joined: 24 June 2008
Location: Australia
Status: Offline
Points: 9
Post Options Post Options   Thanks (0) Thanks(0)   Quote SamCPP Quote  Post ReplyReply Direct Link To This Post Posted: 02 July 2008 at 12:07am
Originally posted by Matts_User_Name Matts_User_Name wrote:

I am not really sure how to get that, although it is a handle to a File object.
So basically NtQueryObject with ONI is returning "STATUS_NOT_SUPPORTED" ONLY on this specific file, and nothing else?

Yes, all files in hyperterminal with the COM port opened work except for the COM port handle. I thought I was soooo close to having what I wanted till that moment!

Originally posted by Matts_User_Name Matts_User_Name wrote:

Did you try using the alternative approach of NtQueryInformationFile with FILE_NAME_INFORMATION structure?

I'll give that a go now.

Originally posted by Matts_User_Name Matts_User_Name wrote:

Perhaps it is one of those handles which NtQueryObject will not work with even if it is a "File" object. Might be similar to the behavior of a NamedPipe, except instead of freezing, NTQO reports that it doesn't support the retrieval of that kind of File object's name.

Obviously PE uses a driver so perhaps that is why it is able to get its name. After all, PE is able to get the name of NamedPipes which will cause a freeze for us. Most likely it uses the aid of its driver for that, so indeed this could be a similar case.

That's what I think... PE must be doing something sneakier.

Well that's a good start at least. I'll try to hack something together and post my results when it is going. I want to get the object type lookup working properly too (i.e. no hard coded tables like I have atm).

Cheers,
Sam


Edited by SamCPP - 02 July 2008 at 12:09am
Back to Top
molotov View Drop Down
Moderator Group
Moderator Group
Avatar

Joined: 04 October 2006
Status: Offline
Points: 17531
Post Options Post Options   Thanks (0) Thanks(0)   Quote molotov Quote  Post ReplyReply Direct Link To This Post Posted: 02 July 2008 at 3:45am
Quote Obviously PE uses a driver so perhaps that is why it is able to get its name.
If you don't run PE as an admin, PE will still run but will not load the driver.  So, if the information that's available differs between when you load PE as admin and as standard user...
 
Note: when PE is first launched as admin (and the driver loads), and is subsequently launched as a standard user - the driver will remain loaded, but I'm not sure if the instance of PE launched as a standard user will use it.
Daily affirmation:
net helpmsg 4006
Back to Top
SamCPP View Drop Down
Newbie
Newbie


Joined: 24 June 2008
Location: Australia
Status: Offline
Points: 9
Post Options Post Options   Thanks (0) Thanks(0)   Quote SamCPP Quote  Post ReplyReply Direct Link To This Post Posted: 02 July 2008 at 5:52pm
Originally posted by SamCPP SamCPP wrote:

Originally posted by Matts_User_Name Matts_User_Name wrote:

Did you try using the alternative approach of NtQueryInformationFile with FILE_NAME_INFORMATION structure?

I'll give that a go now.

Seems to give the same status error. I'll poke around with the other enums and see what I can actually get access to.
Back to Top
digger666 View Drop Down
Newbie
Newbie


Joined: 19 December 2008
Status: Offline
Points: 4
Post Options Post Options   Thanks (0) Thanks(0)   Quote digger666 Quote  Post ReplyReply Direct Link To This Post Posted: 19 December 2008 at 1:55am
The NtQueryInformationFile(FileNameInformation) returns name that begins with \. How to get drive letter? Additionally, it returns directories too , like
"\Program\Microsoft Visual Studio\vc98\Include".
 
How to distinguish directory?
 
What is SYSTEM_HANDLE_INFORMATION->Flags?
Back to Top
molotov View Drop Down
Moderator Group
Moderator Group
Avatar

Joined: 04 October 2006
Status: Offline
Points: 17531
Post Options Post Options   Thanks (0) Thanks(0)   Quote molotov Quote  Post ReplyReply Direct Link To This Post Posted: 19 December 2008 at 3:10am
Use NtQueryObject with ObjectNameInformation to get the name, and translate \Device\HarddiskVolumeX\ to the drive letter, for example using the technique described here and here.

Quote What is SYSTEM_HANDLE_INFORMATION->Flags?
See the flags here.
Daily affirmation:
net helpmsg 4006
Back to Top
YuraQ View Drop Down
Newbie
Newbie


Joined: 19 December 2008
Status: Offline
Points: 1
Post Options Post Options   Thanks (0) Thanks(0)   Quote YuraQ Quote  Post ReplyReply Direct Link To This Post Posted: 19 December 2008 at 4:38pm
I read many posts about "Enumerating file handle".
And all of them contain Ring3-code.
 
I have a question. I can (and want) get filename from handle at Ring0.
Can it simplify this algorithm?
 
PS
But I can't access to undocumented internal structures Unhappy. I must write portable code (Win2k-Vista).


Edited by YuraQ - 19 December 2008 at 4:42pm
Back to Top
digger666 View Drop Down
Newbie
Newbie


Joined: 19 December 2008
Status: Offline
Points: 4
Post Options Post Options   Thanks (0) Thanks(0)   Quote digger666 Quote  Post ReplyReply Direct Link To This Post Posted: 19 December 2008 at 9:04pm
Works !
But  I think I have problem with buffer resizing. If I put size big enough initially, everything is OK.
 
I call , as in sample codes:
 
dwSize = sizeof(OBJECT_NAME_INFORMATION);  
pObjectInfo = (POBJECT_NAME_INFORMATION)(malloc(dwSize));       
ntReturn = pNtQueryObject(hLocal, ObjectNameInformation, pObjectInfo, dwSize, &dwSize);       
and get INFO_LENGTH_MISMATCH with return dwSize = 0.I expect STATUS_BUFFER_OVERFLOW and required size in dwSize. What should be correct dwSize in 1st call? Should be  pObjectInfo->Name (struct UNICODE_STRING) initialized?
 
 
For names , I can consider QueryDosDevice for A:..Z: , and then memcmp.
 
Does anyone have source code that follows code of QueryDosDevice  in kernel32.dll (to avoid "missing function")?


Edited by digger666 - 19 December 2008 at 10:12pm
Back to Top
 Post Reply Post Reply Page  12>
  Share Topic   

Forum Jump Forum Permissions View Drop Down

Forum Software by Web Wiz Forums® version 11.06
Copyright ©2001-2016 Web Wiz Ltd.