This post first appeared on Within Windows on August 13, 2011
So, back in Part 1, I managed to find software for the obscure Hello Kitty IZMO USB toy and patched it to sort of work on Windows 7. While the toy lit up at start, it didn’t quite function correctly – that is, every time I pressed keys on the keyboard, nothing happened. (This worked just fine on my Windows XP virtual machine.)
Y U NO SEE MY KEYS?
Before I could move on, I had to figure out how this software captured keystrokes. Scanning through the executable’s imports, I came across SetWindowsHookEx. SetWindowsHook is an old school API used to tap into various system events; it supports a number of hook types including those involving the keyboard (WH_KEYBOARD) and mouse (WH_MOUSE).
I set a few breakpoints, all on calls to SetWindowsHookEx, and only one was actually used. It looked like so:
SetWindowsHookExA(WH_JOURNALRECORD, myJournalRecordProc, GetModuleHandle(NULL), 0 /* dwThreadId */);
I was puzzled by the use of WH_JOURNALRECORD here, given the toy only monitors keyboard input. Perhaps the developers, at some point, were looking to capture mouse input as well. Or maybe this was a cut/paste job. Regardless, I wasn’t going to spend time changing it. (It would have required some manual assembly writing work.) Instead I focused on the GetLastError value being returned to me -- ERROR_ACCESS_DENIED.
We need more power
Elevating the application did no good. I was stuck. Out of options at the time, I decided to disable User Account Control (UAC) on my system. The program started working, holy assembler Batman! But, I was confused – why was this thing working with UAC off, but not when elevated? It turns out, disabling UAC also has a collateral impact on the state of another Windows feature called User Interface Privilege Isolation (UIPI). Introduced back in Windows Vista days, along with UAC, UIPI acts as an insulating layer of protection for processes with higher privileges. In other words, this layer prevents the little guys like Calculator from sending special messages to security-critical processes like an elevated Command Prompt preventing a specific type of evil – shatter attacks. (This insulting layer is what prevents you from dragging files onto an elevated Notepad instance, for example.)
Additional research into UIPI led to me a blurb on MSDN explaining what breaks with UIPI turned on:
A lower privilege process cannot:
- Perform a window handle validation of higher process privilege.
- SendMessage or PostMessage to higher privilege application windows. These application programming interfaces (APIs) return success but silently drop the window message.
- Use thread hooks to attach to a higher privilege process.
- Use Journal hooks to monitor a higher privilege process.
- Perform dynamic link-library (DLL) injection to a higher privilege process.
Okay, so I knew UIPI was the culprit here... but I thought: How do I workaround this? Or more importantly, promote the Hello Kitty application to a first class Windows citizen? After hours of stumbling around on MSDN, I came across a useful attribute you can throw into your application’s manifest – uiAccess. You may have seen a manifest before, with a segment that looked something like this:
<requestedExecutionLevel uiAccess="false" level="requireAdministrator" />
It turns out, uiAccess is a special attribute that whitelists the application, letting it pass through UIPI’s gates system-wide. (This is typically reserved for rare UI assistive technology, like screen readers.) Okay, so I created a plain manifest, set uiAccess to true, and fired up Hello Kitty.exe. Still nothing.
The software still didn’t work, with my skim of the documentation to blame. I re-read the page, this time with a discerning eye, and came across the following warning (emphasis mine):
Important: Applications with the uiAccess flag set to true must be Authenticode signed to start properly. In addition, the application must reside in a protected location in the file system. \Program Files\ and \windows\system32\ are currently the two allowable protected locations.
I remember thinking, could they have made this more difficult? Thankfully, I already had a legit code-signing certificate ready to rock and roll. (I've signed various titles, including Geosense for Windows.) A few minutes with signtool.exe and the binary was digitally signed. I copied the relevant binaries (executable, library, sound file) to a new folder, as the instructions stated, within \Program Files (x86), crossed my fingers, and tried again. (Actually, the documentation claims Program Files\ and \System32 were the only two valid folders. This is not the case on x64 systems. Both Program Files\ and Program Files (x86)\ work in this case.)
In this case, I:
... came across supporting software bundled with InstallShield 3.0 (16-bit executable)
... unpacked the InstallShield special cabinets manually
... virtualized Windows XP to ensure the device worked
... disassembled TenxHID.dll, to discover there was a change to Windows that affected the way we interact with HID devices
... disassembled Hello Kitty.exe to discover its use of Journal Hooks to receive keyboard/mouse input
... nearly gave up and disabled UAC on my system, revealing its impact on the application
... discovered UIPI shuts down if UAC is disabled
... discovered uiAccess whitelists applications for UIPI pass-through and requires you follow a very specific set of rules for use
... learned more about solving application compatibility problems