Friday, September 30, 2016

Abiathar incorrectly detected as malware by Windows Defender

I just updated my laptop to Windows 10 v1611 today. Soon after, I got a notification from Dropbox on my desktop noting that several dozen files had been deleted from my Dropbox folder. That seemed odd, so I went to the Dropbox website to look at the event history. All the deletions were of my various copies and versions of Abiathar.exe and unlzexe.exe. When I restored them from the website, the laptop started producing notices from Windows Defender, saying that those files had been detected as malware: Win32/Rundas!plock.

Abiathar is definitely legitimate, and we've been over the UNLZEXE issue before. I suspect the new Windows Defender definitions don't like Abiathar's auto-update feature.

Thursday, September 29, 2016

Program not starting? Check the event logs

Today I investigated a problem in which some application wasn't starting. I say the vague "some application" because I didn't know what was actually supposed to come up; I just knew something should have appeared after invoking a command in a different program. I went and checked the event logs (in Event Viewer) to see if anything unusual happened.

Indeed, there were errors in the Application log stating that AcroRd32.exe had crashed. Adobe Reader seemed like a reasonable application to come up in this instance, and sure enough, reinstalling it let the original application put up a PDF it created.

If you find an application crash recorded in the event logs and you don't recognize the process name, try Googling for it; that usually turns up something relevant.

Wednesday, September 28, 2016

PowerShell-based Windows version of "stat"

Unix-like OSes have a "stat" command that produces certain interesting information on a file system object. One questioner was wondering what the Windows equivalent is. I don't believe there's a single command to produce all that, but I did put together a PowerShell script:

$obj = $args[0]
If ($obj.GetType().Name -eq 'String') {$obj = gi $obj}
'File: ' + $obj.FullName
'Size: ' + $obj.Length
$extents = [string](fsutil file queryextents "$($obj.FullName)")
If (-$extents.StartsWith('i')) {
  'Clusters: ' + ($extents.Substring(26) -split 'LCN')[0]
  'LCN: ' + $extents.Substring(42)
} Else {
  'Clusters: stored in file table'
}
'Attributes: ' + $obj.Attributes
$volumeinfo = (fsutil fsinfo volumeinfo "$([System.IO.Path]::GetPathRoot($obj.FullName)[0] + ':')")
$volumeinfo | ? {$_.StartsWith('Volume Serial')} | % {$_.Replace(' :', ':')}
$fileid = (fsutil file queryfileid "$($obj.FullName)")
'File ID: ' + $fileid.Substring(11)
$links = (fsutil hardlink list "$($obj.FullName)")
'Links: ' + ([string[]]$links).Length
'Owner: ' + $obj.GetAccessControl().Owner
''
'Access: ' + $obj.LastAccessTime
'Modify: ' + $obj.LastWriteTime
'Create: ' + $obj.CreationTime
'' # Extra blank line for readability

Tuesday, September 27, 2016

Customizing the PowerShell prompt

By default, the PowerShell prompt is PS followed by the current directory, followed by a greater-than sign. That can easily be changed, however, by defining a function called Prompt. Such a function takes no arguments, and its return value is used as the text to display as the prompt. It can take other actions too, like setting the window title. It's likely that you would want to place that function in your PowerShell profile script rather than retyping it every time.

For example, this removes the current directory from the prompt:

Function Prompt {
  'PS> '
}

Monday, September 26, 2016

Artificial Intelligence Stack Exchange moderator pro tempore

The Artificial Intelligence Stack Exchange site has been going for 55 days now. Earlier today, its slate of three moderators pro tempore (temporary moderators) was appointed. As it happens, I am one of those three. It's an honor, and the level of responsibility is a little scary. The other two selections are amazing contributors to the site, and - unlike myself - actually work with artificial intelligence.

AI.SE is still a small site, so there's very little to be moderated at the moment. Hopefully it will grow!

My per-user usercard hasn't been redrawn to include the diamond, but it should update eventually:

Sunday, September 25, 2016

Policy Plus - Filters

Today I finally wrote the IsPolicySupported function that determines whether a given policy is considered "supported" by a list of products. It wasn't as difficult as I was expecting. The trickiest part was handling ranges correctly. I did run into some challenges, though, in getting the main form's ShouldShowPolicy to work in a reasonably expected way when confronted with filter-by-comment on dual-section policies. It seems to be working pretty well now. I also adjusted the filter options dialog slightly to automatically check all sub-entries of a newly checked product. That's important when dealing with ranges.

The changes are live on GitHub.

Saturday, September 24, 2016

The empty folders matter for Mavis Beacon Teaches Typing

I was recently asked to deploy Mavis Beacon Teaches Typing (a classic typing instruction program) to a lab full of machines. Bizarrely enough, it includes a very deep folder structure with a lot of empty folders. My first attempt at the deployment was to unzip the whole deal, but I was using a program that loses empty folders when decompressing ZIPs. Mavis Beacon appeared to work, but after the first person to used it logged off, it started failing in strange ways for all users; it would ask for a license key or just hang at a black screen. When I finagled the deployment to keep the empty folders, it worked fine. Evidently, it uses them for storage but won't recreate them if they're missing.

Friday, September 23, 2016

Network mysteriously not working? Check for loops

Today I investigated a networking issue. An entire lab of computers lost all network connectivity; they had link lights, but couldn't get DHCP or reach anywhere. I decided to do some cable tracing, since link lights appear even when there's a connection to a switch that doesn't go anywhere. I found several problems: first, two switches seemed to be connected cyclically. Unplugging one cable restored connectivity for some computers. Then, I found another switch looped back into itself. Fixing that and reconnecting the cable to the right switch did the trick for the rest of the machines.

When working with simple (i.e. cheap, non-fancy) switches, loops can cause everything to grind to a halt. Broadcasts just keep flying around and clogging up the cables, thereby blocking more interesting communication.

Thursday, September 22, 2016

NTFS files have unique identifiers

On Linux-like systems, every file system object has an inode, a unique identifier. A lesser known fact is that Windows does too! Files can be opened by a 16-byte identifier (which isn't really a GUID, but is the same length as one). You can see a file's ID with the fsutil utility:

fsutil file queryfileid path\to\file.ext

Wednesday, September 21, 2016

Namecheap URL redirects take a while before going into effect

I just added a URL redirect record into my Namecheap domain, and at first it seemed that the redirect was not working; the new subdomain wasn't resolved. One of their help documents said that it can take 30 minutes for new records to start working, but I think I waited that long for no results. Eventually, though, it did start working.

If yours doesn't, and you're using the Advanced DNS tab (as opposed to the redirection section of the initial tab), make sure the subdomain part doesn't include the full domain. For example, when redirecting subsite.example.com, you would only put subsite in the host field.

Tuesday, September 20, 2016

When there are "insufficient resources" to log on a user

I have recently been dealing with the problem that several shared computers are refusing to log users on, citing "insufficient resources." I found that rebooting fixed the problem every time. Evidently, this error appears when there are so many users logged on (i.e. people just locked or used "switch user" instead of actually logging off) that the system is truly low on resources. Of course, it's not easy to figure out who is logged on if you can't get in, but a reboot kicks everyone out and fixes the problem.

Monday, September 19, 2016

Policy Plus - Slow but steady filter progress

I did just a little scratching at the filter feature for Policy Plus. The Filter Options dialog can now produce a filter configuration object, but actually using those settings is proving difficult. I'm currently trying to figure out what it means for a parent product to be enabled but not some of its children. Once I come to a decision on that, I can write the chunk that checks whether a product or range is met, then everything else falls into place with the check for whether a support element (either a product/range or another support definition) is met.

Sunday, September 18, 2016

Policy Plus - Filter window

A while ago, I decided that it was time to implement a filter-by-support feature in Policy Plus, for feature parity with the real Group Policy Editor. Today I finally put together the window for its options.


The interesting thing about the tree view control is that only the node labels' background responds to the control being disabled; the rest of the background stays white. That produces an unattractive visual effect, so I had to add a line that sets the control's background to the appropriate color when it becomes disabled (i.e. when the Supported on checkbox is not checked).

Match a policy if at least one selected product is supported means that the support checking function (which I haven't yet written) will pretend all support definitions with multiple referenced products have "any" logic even if they're defined with "all." Match policies with missing or blank support definitions will approve policies that either have no support specified and policies that have a support definition that references no products.

Along the way, I decided to make the Deduplicate Policies menu item invisible for the time being. It's a destructive operation on the workspace, and I suspect it will result in bizarre behavior in certain search-related cases.

Hopefully I will able to write the actual filtering logic soon.

Saturday, September 17, 2016

CornCon: Local IT security convention

Today I attended CornCon, a local day-long IT security convention. It was a pretty neat experience.

Upon arrival, I got a bag with some interesting promotional materials on various security-related products. In the conference room, I helped set up PowerPoint to automatically run and loop a slideshow. (I guess security people aren't all PowerPoint users, heh.)

There were two presentations going on at most times. I went to these:

  • The keynote, a tour of how we're able to modify ourselves and other living organisms in surprising ways
  • An update on a law case, FTC vs. LabMD, in which the Federal Trade Commission did some pretty strange stuff in regard to data security
  • An overview of precision farming and NIST-published security processes for all organizations
  • A definition of the Internet of Things, and an introduction to the security considerations for it
  • A strategy for adopting cloud solutions and explaining those solutions to non-technical branches of a company
  • A mostly non-OS-specific review of the poor state of mobile device security
  • A short panel on careers in the IT security field
  • A history of computers as weapons of war: cyberweapons used to physically cause damage
  • A demonstration of a mild vulnerability in several Windows hooking libraries (RWX pages at constant addresses)
There was a door prize drawing for all kinds of swag. I got a $50 coupon for No Starch Press, which should be great!

Friday, September 16, 2016

WUSA has to be run as a real user

I'm experimenting with a program that creates temporary local accounts to elevate things. The manager program destroys the temporary account after the token is created and the process is launched, but the token is still perfectly usable to launch new processes. Most programs I checked - the command prompt, PowerShell, Notepad - are perfectly OK with this.

The Windows Update Standalone Installer (WUSA), on the other hand, is very much not alright with this arrangement. When launched as a user that doesn't exist, it exits with an error stating that no SIDs were mapped to user names, which makes sense because no user exists for that SID anymore. (Error code 1332, ERROR_NONE_MAPPED.)

I suppose I'll have to add logic to the managing program to not destroy the temporary account until after the new process (possibly along with its children) has exited.

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

Thursday, September 8, 2016

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

I decided yesterday, for reasons, to poke around in the internals of a certain Windows API function. I only know enough assembler to be dangerous, but I learned some things along the way. In this post, I'll take you along for the ride. Hopefully, this will be comprehensible to programmers who know very little if any assembler. I'll make sure to include lots of colorful pictures!

In which we decide our target

We're going to be examining the NetUserModalsGet function, which lets programs get information about the domain and its security settings. It takes a server name, a "level", and a pointer to a buffer. The "level" specifies which kind of information you want to get out of the function; the type of stuff that's going to be placed in the buffer depends on it. We're interested in level 2, which will yield a USER_MODALS_INFO_2 structure, containing the SAM domain's name and SID. Even more specifically, we'll only be looking at the 32-bit version, since I don't know any 64-bit assembler, and also because the tool I want to use doesn't support 64-bit stuff. Speaking of tools...

In which we acquire our tools

I like IDA Pro as a disassembler. The last version that's free is 5.0, but that will work just fine. Download a copy from the official web site, and run the installer in compatibility mode for Windows XP SP3. When the installer finishes, run IDA Pro Free. You'll get a notice about newer versions; ignore it. You'll then get a Welcome to IDA! dialog. It has a New button. Click it. You then get the option to choose what kind of thing you want to disassemble.


The MSDN documentation on our target function says at the bottom of the page that it lives in Netapi32.dll. DLLs are dynamic libraries, so select that option. Browse into \Windows\SysWOW64 and choose that file. You get a wizard that helps you load the file. All the defaults are fine, so click through until you Finish. IDA will then detect that "the input file was linked with debug information" and will allow you to have it search for symbols. Choose Yes, because that will let it fill in some names for us.

Now it actually starts disassembling. IDA does an "initial autoanalysis", which I think is what produces the fancy visual arrangements of code chunks. You'll know that pass is done when you see this:


By default, IDA shows us the function called DllEntryPoint, which is called at certain points in the DLL's lifecycle. It's not relevant to the function we're interested in.

In which we encounter an obstacle

The function we want will be exported from this DLL. To see the exports list, click the Exports tab near the top. You'll get a big report of all the exported functions and their addresses.


Find NetUserModalsGet and double-click it to move to it.


Uh oh. That's not code, that's just a bunch of function declarations! Interestingly, there's something else before the function name in one of the strings: SAMCLI. A look through a directory listing reveals that there is a DLL by that name. Choose File | Close to exit this disassembly (check Don't save the database to save on disk space). Then choose File | New and repeat the opening process from samcli.dll.

In which progress continues

A look through this DLL's Exports window shows that it does indeed have the function we want! The entry in the other DLL was a forwarder. Double-click this new entry we found, and we get real code:


Let's start from the top. At the very top (which didn't fit in the screenshot), there's the function's signature: the parameters we saw on MSDN. Then there are a bunch of things involving dword ptr. They aren't part of the assembler, IDA just shows them for our convenience as local variables. Each happens to be 32 bits (4 bytes) long, hence DWord. The last three, with names matching the arguments, indicate where those arguments can be accessed. More on this later.

Then we get into actual assembler instructions. The first is a mov. This kind of instruction always copies data from the second thing to the first thing. Those things can be registers, memory locations, or in the case of the source, a literal. Since this one is mov edi, edi, it does nothing. It copies data from one register to itself. This is to allow patching.

The next three lines are a standard prologue. They set up the stack like IDA shows above, with 0x28 bytes available for local variables. (That's how IDA knows about those variables, actually.) The ebp register now holds the address where all local variables and arguments are based. Things can be added and subtracted from it to get the address of such a variable. That's what the next line does: ebp+bufptr is the address of the bufptr argument. Since it's enclosed in brackets, the move instruction dereferences that pointer to get what's at that address. The result stored in the eax register. The next two pushes preserve the values of those registers so as to not wreck their contents. The next instruction stores the level argument in edi, just like was done for bufptr.

A bunch of moves zero out the data in each local variable. The last move writes a zero into the location specified by the address in eax. I believe the dword ptr here specifies that the written value is 32 bits long (as opposed to two or one bytes of zero). As you recall, eax received the value of bufptr, which is an address (hence the "pointer" part), so that instruction zeroes out the first four bytes of the provided buffer. I'm guessing this is to make sure the buffer is long enough.

In which choices are made

The next instruction, cmp edi, 2, compares the two values. As you recall, edi received the level, which we're assuming is 2 because that's the one we care about. Comparisons are always followed by conditional jumps, like the jnz you see at the end of this block. That instruction stands for "jump if not zero", which, since we're comparing two different values, actually means "jump if not equal." IDA provides two lines, one green and one red. The green one will be followed if the comparison is true, the red if it doesn't. edi and 2 are the same, so they're not not the same, so the red line it is. Double-click a line to follow it.


The red line we just followed is the vertical one above the the darkened left box. This sets ebx to 4. There's only one path out: the blue line to the bottom, larger chunk. That chunk stores the servername parameter in ecx. Then we meet a new instruction: lea, Load Effective Address. I don't understand why it has brackets, since it doesn't dereference anything. It just adds up ebp (the base pointer) and the offset of one of our local variables and stashes that result (which will be a valid pointer to that variable's space) in eax. esi and eax are then pushed. xor edx, edx zeros that register. Then there's a call to UaspOpenSam, a function defined in this DLL.

Double-click a function name to jump to its definition. Scrolling around in UaspOpenSam shows that it's pretty complicated, but the most interesting thing is that it calls SamConnect. If you poke around, you'll find that it's imported from samlib.dll. Let's not get bogged down in that, though. As one Stack Overflow user discovered, that other DLL's API matches the SAMR RPC interface. How convenient. Once you're done inspecting a function in IDA, you can press Escape to go back to the previous screen.

Given that eax was just pushed and that it's now being assigned to a different register and then tested, it's a pretty good guess that UaspOpenSam uses that register to return something, probably whether it succeeded. After all, it doesn't really make sense to check whether a memory location is zero if you just assigned it something that would never be zero. I don't know whether that register should be zero on success or the other way around, but let's check both. If that function returns nonzero, the jnz will jump, and we follow the green arrow.


A boatload of different chunks end up here, and a memory-freeing call follows. That sure seems like a cleanup section to me. Therefore, I conclude that that jump is made when NetUserModalsGet is bailing out, i.e. on failure of UaspOpenSam. Double-click the rightmost green arrow to go back before the jump. (Mousing over an arrow shows you the code shortly before its jump instruction.) We follow the red arrow instead.


That looks really similar. The value of the variable named var_24 is loaded into ecx, and the address of the variable named hMem (for reasons unknown) is loaded into eax, then pushed. The same thing happens for var_4. ebx (which, if you recall, holds the number 4) is copied into edx in between the previous lea and push. A literal 1 is pushed, and UaspOpenDomain is called.

The three pushes seem to match up with UaspOpenDomain's three arguments. Looking back, UaspOpenSam only has one argument, and the last push before it was the address of var_24. That variable's contents are passed to the domain opener, which suggests that it holds a handle to the SAM database. Indeed, SamrConnect produces a handle of the same type that SamrOpenDomain receives. Following that pattern, it makes sense that the var_4 variable receives the domain handle.

(If you're wondering how UaspOpenDomain got a hold of the data at var_24, it seems to have taken it via ecx, since that register's value is carefully shuffled around inside there until it's passed to SamOpenDomain.)

Anyway, another jump-if-non-zero thing follows the opening of the domain. Once again, the green path goes down to the hub of failure, while the red path goes to another branch.


We have another comparison here. Recall that edi once received the "level." A look back up the path we traveled shows that it hasn't been touched since then (assuming none of the functions called wrecked it). We're still interested in level 2, so we want those compared things to be the same. The jump-if-not-same should fail, so we follow the red path.


In which we are suspended from a cliff

We've made it a long way, but we're not there yet - we haven't arranged to return anything to the caller of NetUserModalsGet. We'll pick it up next time.

Continue: Part 2

Wednesday, September 7, 2016

Where the Windows Firewall Group Policy settings are stored

One of the many non-ADMX-based sections of the Group Policy editor is Windows Firewall, under Security Settings in Computer Configuration. These settings are actually stored in the POL file, though, and therefore appear in the policies section of the computer's Registry. Specifically, all policy-defined rules (including Connection Security Rules), are stored here:

HKLM\SOFTWARE\Policies\Microsoft\WindowsFirewall

Monday, September 5, 2016

Policy Plus - Multi-text box

Yesterday, I stumbled upon an undocumented new policy element, multiText. Today I implemented it for Policy Plus. Just like in the real Group Policy Editor, it's presented as a multiline text box, each line of which corresponds to one entry in a REG_MULTI_SZ.


Since I had to change the Element Inspector anyway to add the new element type, I refined that dialog a little more, mostly to be more consistent in where quotes are used.

I also did some poking around the real Group Policy Editor to see where it stores the settings that aren't under Administrative Templates. The simplest section is Deployed Printers, which is just in the POL file. I'm still not sure about the future of Policy Plus in regard to those other sections, though.

Sunday, September 4, 2016

Policy Plus - Element inspection

Today I added a pretty neat dialog to Policy Plus. It lets you dig into a policy's definition, examining the Registry settings it affects, its presentation elements, and its policy elements. The dialog is called Element Inspector and is accessible through the context menu entry of the same name.


Along the way, I noticed a couple mistakes I had made earlier. String values (from Registry entry lists) were never loaded. I had also neglected to look for a clientExtension attribute on policies in addition to elements.

It turns out that Windows 10 introduced a new type of policy element and a corresponding presentation element: the multi-value text box (multiText and multiTextBox, respectively). I would think that the existing list box element handles any case you might want this for, but maybe Microsoft got tired of dedicating Registry keys to lists of things, because the multi-value text box is backed by a REG_MULTI_SZ rather than a bunch of normal string values. It's not actually documented anywhere, but fortunately it looks pretty simple: the policy element only takes a Registry value, and the presentation element has no extra data. The hardest part of implementing it will just be adding support for multiple-string Registry values to the policy sources.

Saturday, September 3, 2016

"net user" doesn't tell you if the username is too long

Yesterday, I was debugging a crash in a routine that creates a new temporary user account with a random name. Even though I made sure to only use valid characters in the name, the exception message specified that the username is invalid. I then went to try one generated name with net user, like so:

net user TempUser-ReallySuperLongName RandomPassword /add

It replied with the syntax reference for that subcommand, while shorter usernames involving the same characters worked. Evidently, the maximum SAM account name length is 20 characters. The utility doesn't tell you that, but that's the limit.

Friday, September 2, 2016

Policy Plus - Support entries can hold support definitions

A while ago, I noticed that some support definitions mention other support definitions, which is a valid configuration. Today, I updated the ADMX bundler to allow support entries that reference support definitions. The support details dialog also now supports having support definitions in the entries list view.

After fixing that, I extended the Find by ID window to find products and support definitions from the unique ID found in the appropriate dialog.

The changes are live on GitHub.

Thursday, September 1, 2016

McAfee's firewall breaks IPSec authentication

Today I investigated the issue of a certain newly-domain-joined machine not being able to connect to a certain IPSec-authenticated network resource. The computer had McAfee LiveSafe antivirus, which also includes a firewall. When I checked Windows Firewall, the MMC snap-in said the firewall was managed by a McAfee "vendor application." More interestingly, the Connection Security Rules section was blank. Disabling the McAfee firewall did nothing, it was still registered.

I had to uninstall McAfee completely. After a reboot, the Connection Security Rules section was populated appropriately and the connection worked. I'm pretty sure Windows Firewall is responsible for handling CSRs and since that McAfee firewall doesn't do IPSec, the outbound data wasn't authenticated. Weirdly enough, it seems that McAfee was also causing issues with Group Policy refresh: gpupdate failed with this error (though no GPO used loopback settings), but the error went away after uninstalling McAfee.