1

I have 5 processes. I have their process ids. Each of these process locks their own parent.lock file. I have these parent.lock file paths in an array called PARENT_LOCKS_PATHS.

Using NtQuerySystemInformation and SystemHandleInformation I got a list which returns SYSTEM_HANDLE_TABLE_ENTRY_INFO for all handles in use by these 5 processes. They are grouped by PID.

Structure of SYSTEM_HANDLE_TABLE_ENTRY_INFO:

var SYSTEM_HANDLE_TABLE_ENTRY_INFO = new ctypes.StructType('SYSTEM_HANDLE_TABLE_ENTRY_INFO', [ //typedef struct _TagHANDLEINFO
    {'UniqueProcessId': ctypes.unsigned_short},
    {'CreatorBackTraceIndex': ctypes.unsigned_short},
    {'ObjectTypeIndex': ctypes.unsigned_char},
    {'HandleAttributes': ctypes.unsigned_char},
    {'HandleValue': ctypes.unsigned_short},
    {'Object': ctypes.uint32_t},
    {'GrantedAccess': ctypes.unsigned_long}
]); //HANDLEINFO, PHANDLEINFO;

In each PID group, I know which handle is the parent.lock file, I know this because the parent.lock file is the only handle that has GrantedAccess of 1048704. So I have an object which has the PID linked with its parent.lock handle entry info.

So now the issue is: I want to identify, which parent.lock file belongs to which path in the PARENT_LOCKS_PATHS file without being able to use GetFinalPathNameByHandle (as i need to support xp). I couldn't duplicate the handle id, because the file is locked it was created/opened with:

 mLockFileHandle = CreateFileW(filePath.get(),
                               GENERIC_READ | GENERIC_WRITE,
                               0, // no sharing - of course
                               nullptr,
                               CREATE_ALWAYS,
                               0,
                               nullptr);

Here is a graphic of my situation (thanks to visio):

Noitidart
  • 35,443
  • 37
  • 154
  • 323
  • Thansk man I had posted wrong struct, I fixed it. – Noitidart Sep 29 '14 at 21:14
  • Will make that fix and update you with new code. Thanks veyr much for that note, I super appreciate that because that was an answer that was extremely important and not something I asked. Thank you @eryksun ! Just curious though: how often is it though that a PID is greather than 65536? I've never honestly seen more than 12k. – Noitidart Sep 30 '14 at 01:49
  • 1
    I don't know how often it occurs that a PID is greater than 65535, but FYI I currently have several that are above three hundred thousand (300000). I suppose it's more common when the system has several active terminal-service sessions, but even if it's rare it shouldn't cause a bug in your code. – Eryk Sun Sep 30 '14 at 01:58
  • @eryksun an interesting thought though, i would think its more easy for handleValue to go over 65,535 no? Like right now my os is running 59k handles. How come hanldeValue is short? Is this a reason for concern? – Noitidart Sep 30 '14 at 03:24
  • 1
    `SYSTEM_HANDLE_TABLE_ENTRY_INFO` is from NT 3 and 4 -- back in the 90s, i.e. what crazy process would have more than 16384 handles (remember a handle value is relative to a process, not system wide)? It's 16384 because the lower 2 bits of a handle are tags, i.e. handle values increment by 4. Nowadays `HandleValue` is a `HANDLE` (i.e. `void *`) in `SYSTEM_HANDLE_TABLE_ENTRY_INFO_EX`. – Eryk Sun Sep 30 '14 at 03:43
  • 1
    Since you named the file `parent.lock` it appears that you're using files to implement a locking mechanism. This may not be your best option. You could use Mutexes/Semaphores instead, or - depending on your situation - [Slim Reader/Writer (SRW) Locks](http://msdn.microsoft.com/en-us/library/windows/desktop/aa904937.aspx). – IInspectable Sep 30 '14 at 06:57
  • A relative to process! That's right! Thanks man. Also thanks @IInspectable my goal is to find the PID that is locking the file, is that possible with mutexes/semaphores? Or SRW? – Noitidart Sep 30 '14 at 11:32
  • @eryksun updated to use EX thanks man! https://gist.github.com/Noitidart/d752e2c59793fa2cab3c this way takes 100ms more than the without EX method but its worth it for the bug free ness. Also I noticed `GetFinalPathByHandles` takes 100-200ms longer than `NtQueryInformationFile` when looping hundreds of times. – Noitidart Sep 30 '14 at 12:37
  • Actually i made the uniqueprocessid and handlevalue from ULONG to ULONG_PTR as per structure here on [ProcessHacker :: Structure](http://processhacker.sourceforge.net/doc/struct___s_y_s_t_e_m___h_a_n_d_l_e___t_a_b_l_e___e_n_t_r_y___i_n_f_o___e_x.html) and it sped the script up to 290ms on avg which is faster then the non `EX` structure. Thanks @eryksun !! – Noitidart Sep 30 '14 at 12:59
  • 1
    For sure what you're doing is faster than `GetFinalPathnameByHandle`. I stepped through it in the debugger. It calls `BasepGetObjectNTName`, which calls `NtQueryObject` to get the full path including the device path. Then `BasepGetFileNameInformation`, which calls `NtQueryInformationFile` to get the device relative path. Then `BasepGetVolumeDosLetterNameFromNTName`, which calls `CreateFile` to open `\\.\MountPointManager` and then `DeviceIoControl` with the control code `IOCTL_MOUNTMGR_QUERY_DOS_VOLUME_PATH`. – Eryk Sun Sep 30 '14 at 16:20
  • Both comments are just so awesome, thanks man learned a ton in this topic and best of all I have something to show for it all. :) – Noitidart Sep 30 '14 at 19:42

1 Answers1

2

For XP, you can use NtQueryInformationFile() with the FileNameInformation info class.

Remy Lebeau
  • 555,201
  • 31
  • 458
  • 770
  • 2
    `NtQueryObject` for `ObjectNameInformation` returns the full NT path, including the device path, such as `\Device\HarddiskVolume1\Windows\System32\ntdll.dll`. You'll only get a device relative path from `NtQueryInformationFile`, such as `\Windows\System32\ntdll.dll`. See [this answer](http://stackoverflow.com/a/5286888). – Eryk Sun Sep 28 '14 at 23:40
  • Thank you @eryksun. And Remy. Question though is this true: `However, NtQueryInformationFile will deadlock if it can't successfully read from a locked stream. But GetFinalPathNameByHandle` doesn't suffer from that annoying side effect`. Is this true? I'm wondering because I'm getting weird errors like buffer overflow when the length shows its not overflowing: http://i.imgur.com/xPEHKml.png In my case I am trying to figure path of a locked file. – Noitidart Sep 29 '14 at 04:30
  • Hey @Remy, NtQueryInformationFile is failling for a bunch of them, it succesfully works on the ones I need it to. But for learning purposes: from [HERE](http://social.msdn.microsoft.com/Forums/en-US/93e3dea7-ea1d-4dc4-938a-31a2c821e46d/grabbing-file-name-and-path-of-open-files?forum=vblanguage) they said for each handle call `NtQueryObject` and then if it's a File object then I should call `NtQueryInformationFile` on it. From my handle entry info, I tested `ObjectTypeIndex` and many different types showed success. Same types showed fails elsewhere. http://i.imgur.com/tvESZTZ.png – Noitidart Sep 29 '14 at 09:24
  • Also quesiton please: I am only reading, so this doesn't need user to have administrator privileges right? – Noitidart Sep 29 '14 at 09:43
  • Thanks @eryksun, please ignore that, I figured it all out. However I'm having one issue. When I read the handle path with `NtQueryInfromationFile` or `GetFullPathByHandle` it's reading the same strings even though from different processes. Like pid1 is locking lock1.txt and pid2 is reading lock2.txt. But when i read the path on it, depending on which pid i run the code from, it is giving same lock.txt path. Its so odd. Do I need to maybe duplicate handle? – Noitidart Sep 29 '14 at 16:30
  • 2
    @Noitidart, the object manager uses a handle value as an index into the process handle table in order to reference the underlying kernel object. Thus a handle is only meaningful in the context of its process. You need to duplicate it into the handle table of your current process. – Eryk Sun Sep 29 '14 at 16:53
  • Thanks @eryksun when I `DuplicateHandle` with arg `hTargetProcessHandle` set to `GetCurrentProcess()` it fails to duplicate. So I created handle of the own process like this: `OpenProcess(PROCESS_DUP_HANDLE, false, currentProcessID);`, this makes the `DuplicateHandle` succeed, but the read strings having same issue, that they are not reading correct path if the handle is from another process, it is reading `lock2.txt` as `lock1.txt` from `pid1` which is where im running code from. Do you think I have to maybe give `pid1` `PROCESS_DUP_HANDLE` and use `GetCurrentProcess()` as 3rd arg method? – Noitidart Sep 29 '14 at 20:06
  • A sincere thanks for this help!!! It's so absolutely odd, when I do that it throws GetLastError of 6. This is my open process: `var opend7080 OpenProcess(PROCESS_DUP_HANDLE, false, 7080);`, and then this is my duplicate handle: `DuplicateHandle(opened7080, system_handle_info.Handles[i].HandleValue, GetCurrentProcess(), useHandle.address(), 0, false, DUPLICATE_SAME_ACCESS);`, its so so odd, after this then it throws error of 6 – Noitidart Sep 29 '14 at 21:17
  • Let us [continue this discussion in chat](http://chat.stackoverflow.com/rooms/62143/discussion-between-noitidart-and-eryksun). – Noitidart Sep 29 '14 at 21:17
  • 1
    @Noitidart, maybe you have an issue with Javascript ctypes passing the wrong value. `GetCurrentProcess()` is just `-1`, which has to be passed as a `HANDLE`. If it gets passed as an `int` the value won't be sign extended on Win64 to `0xFFFFFFFFFFFFFFFF`, but will instead be passed as `0x00000000FFFFFFFF`. – Eryk Sun Sep 29 '14 at 21:31
  • Ah ok will test and report back, right now `GetCurrentProcess` is returning to me `65535` – Noitidart Sep 29 '14 at 22:21
  • 1
    65535 is `0xFFFF`, which is -1 as a 16bit unsigned value. That makes me think you are still using the wrong data type with `GetCurrentProcess()`. Please update your question to show your actual code. – Remy Lebeau Sep 29 '14 at 22:54
  • You were very right!! I was giving it an `unsigned_short` so it would not work. The problem now is now the same as prior to duping, even though using duped handle, running code from pid1 to handles from pid2 still gives path names of pid1. so reading lock file from `pid2` is giving `lock1` still :( Code is here: https://gist.github.com/Noitidart/adc89a92713c9c635dad should i make another topic asking why its giving lock1 when it should be giving lock2 path? the handle is that of pid2's lock2.txt. The reading path name happens in lines 269 to 300. – Noitidart Sep 29 '14 at 23:40
  • A sincere thanks to everyone!!!!! I figured it out. Especially @eryksun and Remy!!!! Thank you!! I figured it out! This small thing took me 27 days of programming and 54 days ago was when the original idea came up. Thank you guys so much!!! Code was updated at the gist if you guys are interested in checking out js-ctypes :) – Noitidart Sep 29 '14 at 23:56