On a clean Windows XP SP2 installation running inside a VirtualBox VM, when doing a snapshot with vboxmangage debugvm --dumpguestcore and analyzing it in Volatility, I always find 9 VADs with PAGE_EXECUTE_READWRITE permissions in winlogon.exe process.

I have already asked this question here, but for winlogon and csrss process: VADs with RWX permission in winlogon and csrss processes

I was able to find out that RWX VADs in csrss.exe processes are heaps. Unforunately I can't confirm this for the VADs in winlogon.exe.

But I've read an article "Integrity verification of user space code" in the Digital Investigations journal, where there is some information about these RWX VADs. The article can be found here: http://www.dfrws.org/2013/proceedings/DFRWS2013-12.pdf

In this article Andrew White and his colleagues make the following statement:

Aside from the default executable sections, Windows XP also exhibits some default behaviour in two system pro- cesses that create unverifiable allocations. The first process is winlogon.exe, in which 9 executable and writable allocations are created that appear to onlycontain data. Each of these allocations are 4 pages long, and are sparsely populated. The locations of these allocations can be found in an array within the .data section of winlogon.exe, typically starting at winlogon.exe + 0x72b0c.

Unforunately I can't confirm this. Which I assume is due to me using Windows XP SP2 while they use XP SP3. Which probably results in a change of this address.

The code they've written for their approach is open source (it is based on volatility plugins), you can find it here: https://github.com/a-white/Hashtest The function they've written to extract the array in the data section is:

def get_winlogon_allocations(self, task, ps_ad):
    """Get the EXECUTE_READWRITE pages used by winlogon"""
    if str(task.ImageFileName) == "winlogon.exe":
        base = task.SectionBaseAddress
        addrs = []
        # TODO - determine better method of finding offset
        start = 0x72b0c  # will change for other versions of winlogon
        current = start
        while True:
            value = ps_ad.zread(base + current, 4)
            value = struct.unpack("<L", value)[0]
            if value > 0 and value % 0x1000 == 0:
                current += 0x8
        return addrs
        return []

Do you have an idea how they found this address? Or any hints that can point me to the correct address?

Thank you in advance and best regards!

2 Answers 2


winlogon iirc allocates more than 9 RWX pages but does not free only these 9 pages until you create the dump

you observe these pages in dump because dump is premature death

if you wait sufficiently in shut down/logoff path you can probably catch the VirtualFree()'s

i already posted in the previous post you linked that almost all these pages contain code that saves teh flags and registers with pushf pushad sequence and jumps to some address via jmp eax

below is a live stack where code is executed from an RWX page (this page will be freed wont be available when you create dump)

one way to find out the address in your setup could be to search the dump for the specific DWORD that is shown in !vad output a RWX page in winlogon memory a sample of which is also pasted below

kd> kb
ChildEBP RetAddr  Args to Child              
01e8f8b8 01010cf0 00000000 000080fd 00004000 winlogon!RmvpOpenNtDeviceFromWin32Path+0x23b
01e8f904 01010bd8 000002a4 000002a4 00000000 winlogon!RmvpOpenNtDeviceFromWin32Path+0x1ea
01e8f91c 0100e477 000002a4 000002a4 00000000 winlogon!RmvpOpenNtDeviceFromWin32Path+0xd2
01e8fc04 01058b0a 00000001 01058c00 01e8fc4c winlogon!CheckForUserObjectUpdates+0xb3
01e8fc70 02f7179d 000002a4 01049efc 00000000 winlogon!AutoPtrBase<_SECURITY_DESCRIPTOR>::AutoPtrBase<_SECURITY_DESCRIPTOR>+0x20
WARNING: Frame IP not in any known module. Following frames may be wrong.
01e8fef8 7c92796d 000002a4 7c97e460 000cfb20 0x2f7179d
01e8ff40 7c9279ab 01049efc 000002a4 00000000 ntdll!RtlpWorkerCallout+0x70
01e8ff60 7c927a6d 00000000 000002a4 000cfb20 ntdll!RtlpExecuteWorkerRequest+0x1a
01e8ff74 7c927a44 7c927991 00000000 000002a4 ntdll!RtlpApcCallout+0x11
01e8ffb4 7c80b713 00000000 00000000 00000000 ntdll!RtlpWorkerThread+0x87
01e8ffec 00000000 7c910250 00000000 00000000 kernel32!BaseThreadStart+0x37

one page in call stack is not in any known module disassemble and look

kd> ub 02f7179d
02f71789 0000            add     byte ptr [eax],al
02f7178b 0000            add     byte ptr [eax],al
02f7178d 0000            add     byte ptr [eax],al
02f7178f 0000            add     byte ptr [eax],al
02f71791 53              push    ebx
02f71792 52              push    edx
02f71793 e8f8ca09fe      call    winlogon!DeleteRasConnections+0x3c (0100e290)
02f71798 e8d78a0efe      call    winlogon!CreateAndHoldWPAGlobalMutex (0105a274)
kd> u 02f7179d
02f7179d 9c              pushfd
02f7179e 60              pushad
02f7179f 56              push    esi
02f717a0 57              push    edi
02f717a1 6a08            push    8
02f717a3 e854cb09fe      call    winlogon!DeleteRasConnections+0xa8 (0100e2fc)
02f717a8 ffe0            jmp     eax
02f717aa 90              nop

so winlogon!CreateAndHoldWPAGlobalMutex (0105a274) is being executed from an unknown page when our access breakpoint broke

kd> r
eax=968dab54 ebx=00010000 ecx=000080fd edx=0000ffff esi=00000000 edi=00010000
eip=01010d41 esp=01e8f8b0 ebp=01e8f8b8 iopl=0         nv up ei ng nz na po nc
cs=001b  ss=0023  ds=0023  es=0023  fs=003b  gs=0000             efl=00000282
001b:01010d41 c1f810          sar     eax,10h  <--- eip is +1 when hardware bp is hit

kd> ub @eip
01010d25 03c6            add     eax,esi
01010d27 5e              pop     esi
01010d28 5d              pop     ebp
01010d29 c20800          ret     8
01010d2c a1042b0701      mov     eax,dword ptr [winlogon!MMX_available+0x39c (01072b04)]  < read access not hit 
01010d31 69c0fd430300    imul    eax,eax,343FDh
01010d37 05c39e2600      add     eax,269EC3h
01010d3c a3042b0701      mov     dword ptr [winlogon!MMX_available+0x39c (01072b04)],eax  < we hit write access here match eax with data 

and 72b0c also holds the page address from where execution is happening try !vad on winlogon and confirm if it is RWX and search winlogon memory

kd> dd 01072b04 l4
01072b04  968dab54 00000002 02f70000 00000000

kd> !process 0 0 winlogon.exe
PROCESS 81167cf8  SessionId: 0  Cid: 0218    Peb: 7ffdf000  ParentCid: 0168
    DirBase: 088f5000  ObjectTable: e14a0600  HandleCount: 185.
    Image: winlogon.exe

kd> !grep -i -c "!vad @@c++(((nt!_EPROCESS *) 0x81167cf8)->VadRoot)" -e "execute_readwrite"

812287d8 ( 9)       2f70     2f73         4 Private      EXECUTE_READWRITE 

kd> dd 01072b04 
01072b04  968dab54 00000002 02f70000 00000000
01072b14  00000000 00000000 00000000 00000000
01072b24  00000000 00000000 00000000 00000000
01072b34  00000000 00000000 00000000 00000000
01072b44  00000000 00000000 00000000 00000000
01072b54  00000000 00000000 00000000 00000000
01072b64  00000000 00000000 00000000 00000000
01072b74  00000000 00000000 00000000 00000000

kd> lm m winlo*
start    end        module name
01000000 01081000   winlogon   (pdb symbols)    

kd> s -d winlogon l?81000 02f70000
01072b0c  02f70000 00000000 00000000 00000000  ................

you can search your dump for rwx page address instead of 72b0c if it showed say 68900 you will be able to find cross referances to the address in winlogon address space

(windbg command # (search assembly pattern )

kd> # 72b0c 1008000 l?11000
0100e40d 8b04c50c2b0701  mov     eax,dword ptr winlogon!MMX_available+0x3a4 (01072b0c)[eax*8]
01010812 8b04f50c2b0701  mov     eax,dword ptr winlogon!MMX_available+0x3a4 (01072b0c)[esi*8]
01010c5d 8934fd0c2b0701  mov     dword ptr winlogon!MMX_available+0x3a4 (01072b0c)[edi*8],esi

the last one is a write location and edi contains the count

here is the RWX VirtualAlloc in xp sp3 you should be able to find similar pattern in sp2 also

01010CDC  PUSH    40               ; /Protect = PAGE_EXECUTE_READWRITE
01010CDE  PUSH    3000             ; |AllocationType = MEM_COMMIT|MEM_RESERVE
01010CE3  PUSH    4000             ; |Size = 4000 (16384.)
01010CE8  PUSH    ESI              ; |/Arg2 = FFFFFFFF
01010CE9  PUSH    0                ; ||Arg1 = 00000000
01010CEB  CALL    winlogon.01010D05; |\winlogon.01010D05
01010CF0  IMUL    EAX, EBX         ; |
01010CF3  ADD     EAX, EDI         ; |ntdll.7C910228
01010CF5  PUSH    EAX              ; |Address = NULL
01010CF6  CALL    K32.VirtualAlloc]; \VirtualAlloc

I was able to find the correct address in winlogon.exe process for Windows XP SP2. It is 0x71ac4.

I dumped the winlogon.exe process from memory using volatility. Then with a hex-editor I was able to find the addresses of the RWX VADs in the process, the array of addresses starts at 0x71ac4. The problem why I could not find these addresses in the first place is simple: it's the endianness. I searched for little-endian occurrences of the addresses but they are in big-endian.

Thanks to one of my co-workers for pointing me on this. :-)

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.