{"id":1273,"date":"2015-06-05T21:01:23","date_gmt":"2015-06-05T19:01:23","guid":{"rendered":"http:\/\/blog.rewolf.pl\/blog\/?p=1273"},"modified":"2015-06-05T21:01:23","modified_gmt":"2015-06-05T19:01:23","slug":"wow64-internals-unexpected-behaviour-of-ntquerydirectoryobject","status":"publish","type":"post","link":"http:\/\/blog.rewolf.pl\/blog\/?p=1273","title":{"rendered":"WoW64 internals: Unexpected behaviour of NtQueryDirectoryObject"},"content":{"rendered":"<p style=\"text-align: justify;\">Some time ago I was writing a small class that was supposed to list items from windows objects directory (like <strong>WinObj <\/strong>from <strong>Sysinternals<\/strong>). Given the fact that there are a lot of examples out there on the internet, it seemed like an easy task. I&#8217;ve started coding it without reading any documentation, except required functions definitions:<\/p>\n<pre lang=\"cpp\">NTSTATUS WINAPI NtOpenDirectoryObject(\r\n  _Out_ PHANDLE            DirectoryHandle,\r\n  _In_  ACCESS_MASK        DesiredAccess,\r\n  _In_  POBJECT_ATTRIBUTES ObjectAttributes\r\n);\r\n\r\nNTSTATUS WINAPI NtQueryDirectoryObject(\r\n  _In_      HANDLE  DirectoryHandle,\r\n  _Out_opt_ PVOID   Buffer,\r\n  _In_      ULONG   Length,\r\n  _In_      BOOLEAN ReturnSingleEntry,\r\n  _In_      BOOLEAN RestartScan,\r\n  _Inout_   PULONG  Context,\r\n  _Out_opt_ PULONG  ReturnLength\r\n);\r\n<\/pre>\n<p><!--more--><\/p>\n<p style=\"text-align: justify;\">I&#8217;ve quickly came up with a working example, which later turned out to work only under <strong>WOW64 <\/strong>layer. What went wrong ? I assumed, that calling <strong>NtQueryDirectoryObject <\/strong>with <em><strong>Buffer<\/strong><\/em> set to nullptr and <em><strong>Length<\/strong><\/em> set to 0 would return required size of the buffer in the <em><strong>ReturnLength<\/strong><\/em> variable. Unfortunately it doesn&#8217;t work that way (except <strong>WOW64 <\/strong>of course). Below there are results of such call on native x86 &#038; x64 systems and under <strong>WOW64<\/strong>:<\/p>\n<pre><b>Native x86:<\/b>\r\nReturned NTSTATUS = STATUS_MORE_ENTRIES (0x00000105)\r\nContext           = 0x00000000\r\nReturnLength      = 0x00000010\r\n\r\n<strong>Native x64:<\/strong>\r\nReturned NTSTATUS = STATUS_MORE_ENTRIES (0x00000105)\r\nContext           = 0x00000000\r\nReturnLength      = 0x00000020\r\n\r\n<strong>WOW64 layer:<\/strong>\r\nReturned NTSTATUS = <b><font color=\"red\">STATUS_BUFFER_TOO_SMALL<\/font><\/b> (0xC0000023)\r\nContext           = 0x00000000\r\nReturnLength      = <b><font color=\"red\">0x00000A1A<\/font><\/b>\r\n<\/pre>\n<p style=\"text-align: justify;\">Without WOW64 <em><strong>ReturnLength <\/strong><\/em>is set to size of one <strong>OBJECT_DIRECTORY_INFORMATION<\/strong> structure and error code is <strong>STATUS_MORE_ENTRIES<\/strong>. The <em><strong>ReturnLength <\/strong><\/em>value in this scenario is actually set <em>accidentally<\/em>. Let&#8217;s take a look at <strong>NtQueryDirectoryObject <\/strong>implementation from <strong>WRK <\/strong>(comments in below snippet are original):<\/p>\n<pre lang=\"cpp\">\/\/ ...\r\n\tPOBJECT_DIRECTORY_INFORMATION DirInfo;\r\n\t\/\/ ...\r\n\tTotalLengthNeeded = sizeof( *DirInfo );\r\n\t\/\/ ...\r\n\r\n\t\/\/\r\n\t\/\/  If there isn't enough room then take the following error\r\n\t\/\/  path.   If the user wanted a single entry then tell the\r\n\t\/\/  caller what length is really needed and say the buffer was\r\n\t\/\/  too small.  Otherwise the user wanted multiple entries,\r\n\t\/\/  so we'll just say there are more entries in the directory.\r\n\t\/\/  In both cases we drop down the entry number because we\r\n\t\/\/  weren't able to fit it in on this call\r\n\t\/\/\r\n\tif ((TotalLengthNeeded + LengthNeeded) > Length) \r\n\t{\r\n\t\tif (ReturnSingleEntry) \r\n\t\t{\r\n\t\t\tTotalLengthNeeded += LengthNeeded;\r\n\t\t\tStatus = STATUS_BUFFER_TOO_SMALL;\r\n\t\t} else \r\n\t\t{\r\n\t\t\tStatus = STATUS_MORE_ENTRIES;\r\n\t\t}\r\n\t\tEntryNumber -= 1;\r\n\t\tgoto querydone;\r\n\t}\r\n\t\/\/ ...\r\n\r\n\t\/\/\r\n\t\/\/  In all cases we'll tell the caller how much space if really needed\r\n\t\/\/  provided the user asked for this information\r\n\t\/\/\r\n\tif (ARGUMENT_PRESENT( ReturnLength )) \r\n\t{\r\n\t\t*ReturnLength = TotalLengthNeeded;\r\n\t}\t\r\n<\/pre>\n<p style=\"text-align: justify;\">Under <strong>WOW64 <\/strong>layer, results are completely different. The difference stems from the implementation of <b>whNT32QueryDirectoryObject<\/b> inside <b>wow64.dll<\/b>. To properly convert 64bit version of <strong>OBJECT_DIRECTORY_INFORMATION<\/strong> structures to 32bit Microsoft calls 64bit version of <b>NtQueryDirectoryObject<\/b> in the loop, to obtain all entries before conversion. Then it proceeds to the loop that calculates required size for 32bit structures. After this loop there is very interesting check:<\/p>\n<pre lang=\"cpp\">\tif ( _input_length < _required_length )\r\n\t{\r\n\t\tif ( !_number_of_32bit_structs )\r\n\t\t{\r\n\t\t\tWriteReturnLengthSilent(returnLength, _required_length);\r\n\t\t\treturn STATUS_BUFFER_TOO_SMALL;\r\n\t\t}\r\n\t\tret = STATUS_MORE_ENTRIES;\r\n\t}\r\n<\/pre>\n<p style=\"text-align: justify;\">The most notable condition here is the comparison of the <em><b>_number_of_32bit_structs<\/b><\/em> to 0. It checks if at least one <strong>OBJECT_DIRECTORY_INFORMATION<\/strong> structure fits into user supplied buffer and if not, it explicitly returns required size with error code <b>STATUS_BUFFER_TOO_SMALL<\/b>. Well, it rather looks like intentional behaviour rather than a mistake. I bet that the person that implemented <b>whNT32QueryDirectoryObject<\/b> made the same assumption about <b>NtQueryDirectoryObject<\/b> as I've made at the beginning. Of course it would be nice if native version of <b>NtQueryDirectoryObject<\/b> behave also like this, as it would simplify many things, but unfortunately the truth is different.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>Some time ago I was writing a small class that was supposed to list items from windows objects directory (like WinObj from Sysinternals). Given the fact that there are a lot of examples out there on the internet, it seemed like an easy task. I&#8217;ve started coding it without reading any documentation, except required functions [&hellip;]<\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":[],"categories":[10,3,16],"tags":[],"_links":{"self":[{"href":"http:\/\/blog.rewolf.pl\/blog\/index.php?rest_route=\/wp\/v2\/posts\/1273"}],"collection":[{"href":"http:\/\/blog.rewolf.pl\/blog\/index.php?rest_route=\/wp\/v2\/posts"}],"about":[{"href":"http:\/\/blog.rewolf.pl\/blog\/index.php?rest_route=\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"http:\/\/blog.rewolf.pl\/blog\/index.php?rest_route=\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"http:\/\/blog.rewolf.pl\/blog\/index.php?rest_route=%2Fwp%2Fv2%2Fcomments&post=1273"}],"version-history":[{"count":26,"href":"http:\/\/blog.rewolf.pl\/blog\/index.php?rest_route=\/wp\/v2\/posts\/1273\/revisions"}],"predecessor-version":[{"id":1299,"href":"http:\/\/blog.rewolf.pl\/blog\/index.php?rest_route=\/wp\/v2\/posts\/1273\/revisions\/1299"}],"wp:attachment":[{"href":"http:\/\/blog.rewolf.pl\/blog\/index.php?rest_route=%2Fwp%2Fv2%2Fmedia&parent=1273"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"http:\/\/blog.rewolf.pl\/blog\/index.php?rest_route=%2Fwp%2Fv2%2Fcategories&post=1273"},{"taxonomy":"post_tag","embeddable":true,"href":"http:\/\/blog.rewolf.pl\/blog\/index.php?rest_route=%2Fwp%2Fv2%2Ftags&post=1273"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}