Thursday, September 15, 2016

Windows no longer respects "No auto-restart with logged on users for scheduled automatic updates installations"

Windows 10 just rebooted for updates without asking me first, despite my having enabled the "No auto-restart with logged on users for scheduled automatic updates installations" policy setting.

After the reboot, though, Windows tried to restore the programs I had. Previously, I had Chrome, Visual Studio, Notepad (with a little unsaved data), XPS Viewer, and Excel running. It relaunched the XPS Viewer, but without the document I was looking at. It brought Excel back with the spreadsheet I was working on, but in recovery mode. Visual Studio came back fully, even to the spot where I had my cursor. Chrome and Notepad were nowhere to be found.

Wednesday, September 14, 2016

Creating a process for a different logon session with LogonUserExExW

I found yesterday that it's not possible to change the logon SID of an existing token, e.g. one that you get from LogonUser. I mentioned LsaLogonUser, but again, it looks insanely difficult to PInvoke correctly from managed code. Today, I stumbled upon the interestingly-named LogonUserExExW function. It's not much harder to PInvoke than LogonUser; it just has a few extra out parameters, which fortunately are optional! Best of all, it takes a TOKEN_GROUPS, which you can use to add the SID of the logon session under which the process should run. In fact, you can directly supply to it the pointer you get from GetTokenInformation on the real user's token, like you might get from WTSQueryUserToken.

I have tested this approach and it works perfectly. The token will have a different logon ID, but since it has the right group membership, it can act normally in desktops belonging to the desired logon session.

Tuesday, September 13, 2016

SetTokenInformation cannot change the TokenLogonSid

The SetTokenInformation function can change many properties of a Windows token. Unfortunately, it cannot change the logon SID (TokenLogonSid). If you pass that value to indicate that you want to change the logon SID, the function will fail with code 87, "the parameter is incorrect." It doesn't seem to matter whether the calling process has SeTcbPrivilege.

It seems new tokens created with something like LogonUser always have the logon SID of their creating process. LsaLogonUser looks like a good alternative, since it lets you specify the groups the token includes (so you could add the logon SID there). The only disadvantage with that approach is that it requires a lot of special marshaling if you're calling it from managed code.

Monday, September 12, 2016

When Visual Studio menu items are missing

A couple days ago while writing the assembler miniseries, I tried to step through a function in Visual Studio's disassembly view. I immediately noticed that I didn't have the relevant menu items mentioned by several Internet answers.

I then went to check on the menu customizations, in the Commands tab of Tools | Customize.


Pulling down the Menu bar dropdown lets you look at any menu or submenu. From there, you can click Add Command, pick the appropriate category, pick the specific command, and click OK.


The menu item is then added to the menu.

Sunday, September 11, 2016

When Hyper-V machines fail to start with error 32791

Today, after upgrading the configuration version of a Hyper-V virtual machine, I found that it would no longer start. Specifically, it just gave the error number 32791. I consulted the Admin event log under the Hyper-V-Worker category and found event 12010, which gives a more useful error:

"The chain of virtual hard disks is corrupted. There is a mismatch in the identifiers of the parent virtual hard disk and differencing disk."

Googling that error message brought me to a useful blog post. My virtual disk was already as small as possible, but forcibly re-linking the snapshot (AVHDX) to the parent VHDX fixed the problem. That step can be accomplished by clicking Edit Disk in the Hyper-V Manager, opening the AVHDX, choosing Reconnect, and checking the box that says Ignore ID mismatch. There is a warning about possible data loss, but since I legitimately had the correct, most recent differencing disk for the main virtual disk, no data loss occurred.

Saturday, September 10, 2016

An arbitrary dive into assembler and certain Windows internals, part 3

Continuing the adventure from part 2.

In which we get organized

This is where we left off:


It's kind of hard to mentally keep track of what the various local variables mean when they're only identified by a number. Fortunately, IDA has a way to rename them. Right-click a local variable in the function's first chunk and choose Rename. Enter the new name, then hit OK. For instance, let's rename var_14 to domainInfoPtr, since it holds a pointer to the domain information buffer that we got from SamQueryInformationDomain. Similarly, hMem should really be called something like domainSidPtr, since it has a pointer to the SID for the SAM domain. The other local variables we've used so far won't really show up again, but I'll rename them just for fun. var_24 becomes samHandle and var_4 becomes domainHandle.

In which we tackle a challenging chunk

First, the domain information pointer is copied into ecx. Then, eax receives the sum of esi and ebx. Coming into this chunk, esi was a pointer to a newly allocated block of memory, and ebx was its size. Therefore, eax now points one byte past the end of that memory block. That data is then stored in the var_28 variable, which would more descriptively be named something like allocEndPlus1Ptr.


Much better.

Moving on, eax receives the address of that variable. esi (pointer to the start of the new memory block) and eax are pushed. ecx is still the pointer to the domain information, so the movzx instruction assigns the RPC_UNICODE_STRING's text length in bytes to edx. eax receives an address 8 bytes past the start of the memory block, and we'll see why soon. ecx receives the 32-bit value starting 4 bytes past the memory address in it, which, looking again at the definition of RPC_UNICODE_STRING in the DTYP protocol, is a pointer to a buffer of WCHARs (wide characters). The memory block's start plus 8 (eax) is pushed. edx, the domain name's length in bytes, is shifted to the right by one bit, dividing it by 2 and producing the domain name's length in wide characters. Since NetpCopyStringToBuffer is a proc near, it can receive data from the ecx and edx registers in addition to the stack.

Based on NetpCopyStringToBuffer's name, I think it's fair to assume that it just copied edx characters from the location specified in ecx (which points to the domain name's Buffer field) into our memory chunk starting 8 bytes past the start. Evidently, the string copying function returns zero on failure, since the green path (for a jz), goes down to a failure-reporting chunk. After all, we still need to provide the SID we collected. Follow the very short red path.

In which we deliver on the promise


I guess we're done checking the level, since this chunk starts out by placing the pointer we have to our SID into edi. The address 4 bytes past the start of the memory block is stored in eax, and it's pushed after a literal 1. The address just past the end of the memory block is retrieved and pushed. The bufptr variable is actually a pointer to a pointer, and after two moves and an addition, eax holds the address 8 bytes past the start of the buffer, which should be the address of the start of the string that was just copied in. That's pushed, followed by the pointer to the domain SID.

That one last push passes data to RtlLengthSid, while the others are for the upcoming NetpCopyDataToBuffer call. The address of the SID and the length of it (retrieved from RtlLengthSid via eax) are passed to the data copier via registers.

Remember that we're trying to fill out a USER_MODALS_INFO_2 structure. Also notice from the documentation on NetUserModalsGet that a single call to NetApiBufferFree releases all resources used up by the original call. Therefore, it's very likely that both the returned structure (which is just two pointers) and the things the pointers point to are all in the same block of memory for easy freeing. An RPC_UNICODE_STRING's length doesn't include the null terminator, while that terminator is necessary for an LPWSTR. That explains why the memory block has to be 10 bytes larger than the sum of the SID's length and the string's length in bytes: it needs 8 bytes to store the returned structure, plus 2 for the string's null terminator. Despite being variable-length, SIDs don't need null terminators because they include the count of subauthorities.

It's also interesting that we never have to assign the members of the structure explicitly. The NetpCopy-prefixed copying functions do it for us, otherwise there would be no reason to e.g. provide NetpCopyStringToBuffer with the start address of the returned structure. Somehow, the copied string is placed after the copied SID. I really can't tell what makes that happen.

Anyway, after the last copy call, there's another test and jump-if-zero. Once again, a return value of zero means failure - the row of things that assign to esi all seem to include error codes. We follow the red line again.


In which we clean up

That xor just sets esi to zero, as opposed to all the other destinations in that row, which assign it a code. Then we end up in a familiar place:


This time, the arrival here doesn't mean failure. It just means we're done, and it's time to clean up all the SAM handles and related things we've allocated in the course of our data collection. A series of tests for zero on various things follows, freeing them appropriately if they actually contain anything. This one shown above retrieves a local variable that wasn't used on level 2's path. Therefore, we skip over the call to SamFreeMemory. There are quite a few of these arrangements.


Not all of them need to be released with SamFreeMemory, there are other types of resources too.


Finally, we get to the function's epilogue:


Remember that stdcall functions return their result in eax. Therefore, when coming down the blue or green path as we do for success, esi (which was used by the hub of failure to store the error code, or was zeroed in case of success) is copied into eax. The red arrow comes in from some early failure case; it sets eax directly. Finally, the calling function's registers are restored, the stack pointer is adjusted back to how it was, and the function returns (retn), clearing 12 (0xC) bytes of arguments off the stack.

Friday, September 9, 2016

An arbitrary dive into assembler and certain Windows internals, part 2

Resuming the adventure from last time.

In which we regain our bearings

When we left off, we had a SAM handle at the var_24 local variable, and a domain handle at var_4. Something unknown is possibly at hMem, since we also passed that address to UaspOpenDomain. We find ourselves here:


In which we continue the journey

Local variable var_14 was zeroed at the start of NetUserModalsGet and, looking through the path we traveled, hasn't been changed since. Its address is pushed, followed by a literal 5, followed by the contents of var_4 (our domain handle). A call to SamQueryInformationDomain follows. Double-clicking its name shows that it's an imported function.


We saw __stdcall on NetUserModalsGet too. It's a common calling convention for cross-library function calls. MSDN explains that functions with this calling convention expect their arguments to be pushed in the opposite order that they're declared. The callee "cleans the stack," i.e. is responsible for popping the arguments. Press Escape to go back to NetUserModalsGet.

Notice how the first thing pushed is the address of var_14. Consulting the callee's definition again, it corresponds to the Buffer parameter, so after SamQueryInformationDomain returns, we should have a pointer to the desired information at var_14. But what will the structure of that information be?

In which we search through documentation

I can't find an information page on PSAMPR_DOMAIN_INFO_BUFFER or the non-P-prefixed version in that documentation set, so we'll go directly to the full interface definition for the SAMR protocol. Ctrl+F for "info_buffer" and the first result is a type definition. It's a switched union, which means it has completely different members in different situations. This one depends on a DOMAIN_INFORMATION_CLASS, which is an enumeration fortunately defined right above the union. The second argument to SamQueryInformationDomain has that type too, and in this case we're passing the value of 5: DomainNameInformation. So, we see from the switch that the info buffer will contain a SAMPR_DOMAIN_NAME_INFORMATION. Ctrl+F for that, and we find that its definition contains only one member: DomainName, of type RPC_UNICODE_STRING. This will be important soon.

In which another choice is made

Immediately after the call to SamQueryInformationDomain, eax is tested against itself, then there's a js instruction: "jump if signed." It doesn't really make sense to make a decision based on the highest bit of a memory location, so I'm confident that the function we called puts its return value in eax. And indeed, that's true of stdcall functions.

I don't know whether the result should have the high bit set, so let's follow both arrows. Suppose it is signed, and we take the green path.


NetpNtStatusToApiStatus_Access sure sounds like a function designed to convert return values from one set of values to another. To me, that seems like something done when bailing out and reporting failure. Let's follow the unconditional jump (blue arrow) just to be sure.


That's the hub of failure again. If this path is followed, we'll exit without returning anything useful. Double-click the rightmost blue arrow to backtrack, then double-click the incoming green arrow on the previous chunk to get back to the js. Apparently, SamQueryInformationDomain will return a non-signed value on success. Follow the red path.

In which we make use of our resources


Interesting, the hMem local variable appears again. The last time we saw it, it was zero and its address was passed to UaspOpenDomain. Now it's being passed to RtlLengthSid, which expects a pointer to a valid SID. Therefore, it would seem that UaspOpenDomain populated it with the SID of the current domain.

Immediately after the call, ecx receives the value of local variable var_14, which holds a pointer to the buffer we got from SamQueryInformationDomain. Now we meet some new syntax and a new instruction:

movzx ebx, word ptr [ecx]

A search reveals that movzx means "move and zero-extend," so it seems that our source is going to be smaller in width than the target. And so it is. word ptr before a dereference specifies that we only want one word (two bytes) instead of four bytes. So, that instruction means "store the two bytes starting at the memory location specified by ecx in ebx, zero-extending to fit in the wide register." Since ecx had a pointer to the domain info buffer, we're storing the buffer's first word in ebx. But why?

The SAMR interface definition page helpfully has a link to the IDL that specifies basic data types. Ctrl+F that for "RPC_UNICODE_STRING" and you find that type's definition. Sure enough, its first member - an unsigned short, only two bytes long - is the length of the string. ebx now holds the length of the domain name in bytes.

Two addition instructions follow. add just adds the second thing to the first thing, so we add 10 (0xA) and eax to the length of our domain name. RtlLengthSid, like all stdcall functions, gives its return value in eax, so we're adding the SID length. (Its return type, ULONG, is only 32 bits wide, so it fits in a normal register.) It seems like we're about to allocate some memory, though I don't know why we just added that 10 to the byte count.

Anyway, let's follow the blue line.

In which memory is indeed allocated


Here, lea is just used as a fancy adder - ebx is a size, not a memory location. A jump is made, essentially, if ebx plus 2 is less than or equal to ebx. That will never happen unless ebx is so large that adding 2 to it makes it overflow. The green path is clearly for errors, and it indeed goes down to the hub of failure, assigning 0x216 (ERROR_ARITHMETIC_OVERFLOW) to esi. Let's follow the red path on that jbe.


The first instruction here, inc, increments a register by 1. and sets a register to the binary-AND of itself and another thing. This particular and preserves all but the last bit, so the first two instructions here just round ebx up to a multiple of 2 if necessary.

Then there's another call, this time to LocalAlloc. As IDA helpfully points out, it specifies ebx as the uBytes parameter (number of bytes desired in the block) and 0x40 as the uFlags parameter (options to the allocator). Consulting that function's documentation, 0x40 means LMEM_ZEROINIT: make sure the memory is all zeros at first.

The value of the bufptr variable - the pointer to the buffer the caller wants the data in - is copied into ecx. eax is the HLOCAL (handle to the allocated memory chunk) received from LocalAlloc, and it's put into esi. Then it's copied into the memory address specified by ecx, which results in the HLOCAL being copied into the first four bytes of the caller-provided buffer. Since the LocalAlloc call didn't specify LMEM_MOVEABLE, the HLOCAL is actually a perfectly usable pointer. Then, esi is tested to see if it's zero. If it is zero, that means the allocation failed, and NetUserModalsGet bails out. Follow the red path.


edi has still not been touched since it was assigned the level, so we still want it to be 2. Follow the red path.

In which we wait for the thrilling conclusion


In this post, we discovered that we have a SID in the hMem variable, and we got a pointer to some domain information in the var_14 variable. We have a pointer to a chunk of memory (with size ebx) at esi. We'll finish it off next time.

Continue: Part 3