header image
 

Kernel debugging Windows HVMs under Xen

Having troubles setting up kernel debugging between two Windows HVMs in a Xen environment? I’ve spent a lot of time lately troubleshooting this, it just didn’t work. Finally it turned out that Xen was corrupting serial port data (yeah, I really neaded that CRLF conversion). Check out this article for details. It’s written with Qubes in mind, but it should be easy to adapt for other environments.

Running processes on the Winlogon desktop

Disclaimer: this is a Bad Idea unless you know exactly what you’re doing, why you’re doing it and there are no alternatives. Please use responsibly.

There may be circumstances where you’d like to programmatically interact with the Winlogon desktop (the one that houses the LogonUI process responsible for displaying logon tiles and the rest of, well, logon UI). Test automation, seamless VM integration, whatever. It’s not easy though, and for good reasons:

  • Logon desktop created by Winlogon has ACL that only grants access to the SYSTEM account. We need a service to access it. Changing that ACL to allow other accounts is a very bad idea.
  • When a user chooses “Switch user” from the Start Menu or when the system first boots and displays the logon UI, it’s all done in a separate session. If a new user logs on, the Winlogon session is reused for that user’s interactive logon. If there is no new logon (but a user switch or unlocking a locked session for example), the Winlogon session is destroyed.
Temporary processes in a Winlogon session after quick user switch

Temporary processes in a Winlogon session after locking/unlocking user session

So, our service needs to monitor session changes and act when a Winlogon session is created (and made interactive). The code below demonstrates how you can create a process that runs on the Winlogon desktop and can interact with it.

Service code Show

You can install the service using following command (mind the spaces):

C:\>sc create QTestService binPath= d:\test\QService.exe type= own start= demand
[SC] CreateService SUCCESS

Then start it with:

sc start QTestService

After starting, choose “Switch user” or “Lock” from the Start Menu. You should see a console window on the logon screen.

A crack on the glass

A paper I’ve been working on lately is now public. In it I dissect various Windows security mechanisms and how effective they really are for providing sandbox-like isolation for groups of arbitrary applications. This work was performed for Invisible Things Lab and Qubes OS.

The end of something

Today we bid farewell to Szatan. She was the last one of the original “Pandemonium” as we called it, the three rats that we got first.

Most distrustful at first, she became the cutest later. We will miss her.

Be careful when closing your object handles

Let’s say we want to create a new Window Station, a new Desktop and create a process that should be associated with those objects. Sounds simple, right?

int _tmain(int argc, TCHAR* argv[])
{
    HWINSTA winsta, original;
    HDESK desk;
    STARTUPINFO si;
    PROCESS_INFORMATION pi;

    winsta = CreateWindowStation(L"omeg-winsta", 0, MAXIMUM_ALLOWED, NULL);
    if (!winsta)
    {
        printf("CreateWindowStation failed: %d\n", GetLastError());
        exit(1);
    }

    original = GetProcessWindowStation();
    // we need to switch to the new window station, because CreateDesktop uses a current one
    if (!SetProcessWindowStation(winsta))
    {
        printf("SetProcessWindowStation(new) failed: %d\n", GetLastError()
        exit(1);
    }
    desk = CreateDesktop(L"omeg-desk", NULL, NULL, 0, MAXIMUM_ALLOWED, NULL);
    SetProcessWindowStation(original);
    if (!desk)
    {
        printf("CreateDesktop failed: %d\n", GetLastError()
        exit(1);
    }

    ZeroMemory(&si, sizeof(si));
    si.cb = sizeof(si);
    si.lpDesktop = L"omeg-winsta\\omeg-desk";
    CreateProcess(0, L"calc.exe", 0, 0, 0, 0, 0, 0, &si, &pi);

    CloseHandle(pi.hThread);
    CloseHandle(pi.hProcess);
    CloseDesktop(desk);
    CloseWindowStation(original);
    CloseWindowStation(winsta);
    return 0;
}

Let’s try that!

---------------------------
calc.exe - Application Error
---------------------------
The application was unable to start correctly (0xc0000142). Click OK to close the application. 
---------------------------
OK   
---------------------------

…oh.

So what’s happening? At first I thought that maybe that’s supposed to happen — MSDN says that the only interactive window station is WinSta0, a predefined one created with the interactive user session. But why give the ability to create new ones then? System services run in separate sessions and have separate window stations/desktops. In the past (pre-Vista) they all ran in the user’s session, but that was changed to mitigate Shatter attacks.

Anyway, services used to be able to display GUI even on their separate window stations in Vista (with the help of a helper service). Apparently that should still be possible on Windows 7 (where I run my tests). Why didn’t it work then?

I was even more confused when calc.exe appeared to work when I launched my test program from the Visual Studio debugger (but still refused to work if I launched it from a command shell). Also if there was one instance of calc.exe already running in my new desktop, test also worked when launched from command prompt, explorer or wherever. What is this, black magic?

And then it struck me. When running under debugger, I had a breakpoint set just after the CreateProcess call. That means the child process was created, but handles to the new window station and desktop were not immediately closed (because of debugger pause). If running standalone, on the other hand, handles were immediately closed. Why is that significant?

Window stations and desktops are both Windows kernel objects under the hood. And all kernel objects are reference-counted. That means the Windows Object Manager can (and will) destroy any kernel object that is no longer referenced. The example program above contains a race condition — by closing all handles to the newly-created objects we indirectly destroy them, usually before the child process can initialize properly. How to fix that? Add a call to WaitForInputIdle after CreateProcess. That way we wait for the child process to finish its initialization before closing handles. The new desktop and window station won’t be destroyed because now the child keeps their handles.

Moral of the story: don’t close your object handles until you know you really don’t need to keep them.

Creating COM wrappers programatically

In my previous post I wrote about using Windows Installer’s COM object to edit MSI packages. To easily use COM methods you need a managed wrapper assembly for the particular native COM server DLL. Visual Studio can generate one automatically, or you can use TlbImp, a tool from Windows SDK. But what if you don’t have any of that? Don’t worry, there is still hope!

It’s very easy in fact, (almost) all you need is aptly named TypeLibConverter class in System.Runtime.InteropServices namespace. You also need AssemblyBuilder to actually create the assembly. And finally we need one import from oleaut32.dll — LoadTypeLibEx, which allows us to load type library embedded in an exe/dll without extracting it ourselves. So without further ado, here is the code:

using System;
using System.Reflection;
using System.Reflection.Emit;
using System.Runtime.InteropServices;

// This utility extracts COM type library definition from unmanaged DLL and creates matching managed wrapper assembly.

namespace Utils
{
    static class TlbConvert
    {
        private enum RegKind
        {
            RegKind_Default = 0,
            RegKind_Register = 1,
            RegKind_None = 2
        }

        [DllImport("oleaut32.dll", CharSet = CharSet.Unicode, PreserveSig = false)]
        private static extern void LoadTypeLibEx(String strTypeLibName, RegKind regKind,
            [MarshalAs(UnmanagedType.Interface)] out Object typeLib);

        static int Main(string[] args)
        {
            if (args.Length < 2)
            {
                Console.WriteLine("Usage: tlb-convert <unmanaged COM DLL path> <managed wrapper path> [root namespace]");
                return 2;
            }

            try
            {
                string srcDll = args[0];
                string outDll = args[1];
                string rootNamespace = args.Length == 3 ? args[2] : null;

                Object typeLib;
                LoadTypeLibEx(srcDll, RegKind.RegKind_None, out typeLib);
                TypeLibConverter tlbConv = new TypeLibConverter();
                AssemblyBuilder asm = tlbConv.ConvertTypeLibToAssembly(typeLib, outDll, 0, new ConversionEventHandler(), null, null, rootNamespace, null);
                asm.Save(outDll);
            }
            catch (Exception e)
            {
                Console.WriteLine("Exception: {0}\n{1}", e.Message, e.StackTrace);
                return 1;
            }

            Console.WriteLine("\nConversion successful.");
            return 0;
        }
    }

    public class ConversionEventHandler : ITypeLibImporterNotifySink
    {
        public void ReportEvent(ImporterEventKind eventKind, int eventCode, string eventMsg)
        {
            Console.WriteLine("{0}", eventMsg);
        }

        public Assembly ResolveRef(object typeLib)
        {
            return null;
        }
    }
}

And another one…

Klocek passed away this Wednesday. He was the oldest of our bunch — over two years and nine months. Rat this old is an ancient one but still he was one of the luckiest, not really suffering any illness until the old age got to him. Now only Szatan remains from the original three (well, Klocek/Półklocek weren’t here from the start but still)…

We also have yet another new one: Sutek, a youngish male around the age of Zabrak. He’s of the black and white variety, reminds us of Szpadla… I really should get my camera going and make more pictures. Someone left him with a cage near a garbage dump, he was rescued and landed with us. He’s really friendly and totally tame, why would someone leave him just like that? He wasn’t accustomed to other rats but is now fully “assimilated”. Let’s hope he’ll be more happy here.

Installing two copies of a single MSI package

Ever had a problem that you can’t install two copies of a MSI installer on the same system? That scenario appeared when I was writing a script that prepared build environment for a certain library. Windows doesn’t have any centralized package management so I needed to take care of getting all dependencies myself (there are 3rd party solutions like Chocolatey but they don’t really help with code libraries).

Some dependencies I could get as a simple archive, but others were only available as MSI installers. I wanted the build environment to be as “sandboxed” as possible so I couldn’t either depend on or interfere with existing software on the system. The ideal scenario was to install my copy of the MSI into selected directory without worrying if there is another copy installed. It’s not that simple though — Windows Installer will refuse to just install another copy, all you can do is repair/reinstall or whatever other options that particular MSI has.

The reason for that behavior is that all MSI packages are identified by a GUID. If you change that GUID to a new one, you will be able to install a second copy because it’ll be something new to Windows Installer. The question is, how to do that?

Continue reading ‘Installing two copies of a single MSI package’

Panta Rhei

I’ve been quiet lately. Mainly because it’s the curse of any blog: after some time you start to kind of forget it exists. Also it’s summer and my brain melts under the heat, it’s hard to do anything. Hopefully autumn arrives soon.

This update is mostly about our rats though. It’s not easy talking about a friend that just passed away, even if that friend is “just a rat”. It’s never easy.

Półklocek (Half-block), Szufla (Shovel), Szpadla (Spade) and Fantom (Phantom) are no longer with us. The few months that passed weren’t kind.

Continue reading ‘Panta Rhei’

Torment: Resurrection? Who would have thought…

Torment: Tides of Numenera

So inXile, the company behind a pretty successful Wasteland 2 kickstarter, a totally oldschool postapocalyptic cRPG, has launched a second kickstarter. I think you can guess which one. A new Torment game. I would have never thought something like that happens, not even a year ago, but I guess things like amazing success of Project Eternity has proven that people like their good old cRPGs.

We’re talking about Torment here though. The original is the best game for me, period. It’s widely regarded as the best cRPG ever. If you haven’t played it, do that. Now. If you have, read the original vision statement. I have mixed feelings for such an undertaking. Of course the original wasn’t perfect: the combat was mostly awful, you needed certain stats to get “the best game experience” and other minor things. But what if the new Torment won’t be on par with the original? Just the name itself holds so much weight for so many people. Now, the new Torment won’t be set in Planescape because of licensing issues. Numenera will be the new setting and it does sound like a good fit for a Torment’s spiritual successor. Dev team has a healthy dose of people who worked on the original. While Chris Avellone isn’t directly involved, he gave the project his thumbs up and I wouldn’t rule out his possible help later as a stretch goal. Colin McComb is the creative lead, he worked closely with Avellone on the original and contributed a lot to the core AD&D rulebooks. I’m cautiously optimistic.

The kickstarter itself is an astounding success already. They got their target amount in just a few hours. I believe it has also broken the record of fastest kickstarter to get one million dollars. Will it beat Project Eternity? I think there’s a fair chance. Now decide for yourself if you want to give them money (hint: you probably do) and go do that.