sábado, 1 de outubro de 2011

Unpack me if you can

Profiting from Desktop heap information

Having followed all the late buzz about the Windows graphics and messaging subsystem kernel part, I decided to take a look at it to understand how it worked, and see if I could find anything interesting and useful. Inspired by Alex Ionescu online presentation, I dove into the realms of the desktop heap, and started to understand the richness of information available. This post is about a little game that shows some of the potential behind that data. The game I'm proposing here will be to run a packed application and, from user mode, try to get some critical information from that process without interfering with it. As passive and non-invasive as possible. Without calling the kernel and not touching the packed process in any way.

Note: As always, the full paper is available here

For the setting, I grabbed a notepad.exe executable from a XP machine and packed it with UPX.
If you want to know why I didn't use notepad.exe from Windows 7 read my last post: "MUI hell". I renamed notepad.exe to popo.exe as to avoid any confusion with names and I ran it. Running ProcExp afterwards, one can see that the popo.exe process has high entropy, so it is identified as packed images "purple".

Figure 1: Packed process

Next, as I needed another GUI process and I didn't want to lock explorer.exe, I run another notepad.exe. This time I used the one from Windows 7.

As a side note, I'm going to jump around between kernel and user debuggers. Please try to keep in mind that the kernel debugger is just used to validate some of the demonstration data.

Continuing, find a GUI process, :):

!process 0 0 notepad.exe
PROCESS 85cb7d40  SessionId: 1  Cid: 0a14    Peb: 7ffd4000  ParentCid: 0804
    DirBase: 1be3b000  ObjectTable: 998c05d0  HandleCount:  58.
    Image: notepad.exe

From that process, get a GUI thread.

PROCESS 85cb7d40  SessionId: 1  Cid: 0a14    Peb: 7ffd4000  ParentCid: 0804
    DirBase: 1be3b000  ObjectTable: 998c05d0  HandleCount:  58.
    Image: notepad.exe
    VadRoot 84444c88 Vads 70 Clone 0 Private 1454. Modified 1084. Locked 0.

        THREAD 8458d838  Cid 0a14.0a1c  Teb: 7ffdf000 Win32Thread: ff9ba008 WAIT: (WrUserRequest) UserMode Non-Alertable
            84caaec8  SynchronizationEvent

.thread 8458d838 
Implicit thread is now 8458d838

  *** Stack trace for last set context - .thread/.cxr resets it
ChildEBP RetAddr 
96b4eb10 82c9ad75 nt!KiSwapContext+0x26
96b4eb48 82c99bd3 nt!KiSwapThread+0x266
96b4eb70 82c9388f nt!KiCommitThreadWait+0x1df
96b4ebe8 915296d6 nt!KeWaitForSingleObject+0x393
96b4ec44 915294e3 win32k!xxxRealSleepThread+0x1d7
96b4ec60 91526550 win32k!xxxSleepThread+0x2d
96b4ecb8 91529aa2 win32k!xxxRealInternalGetMessage+0x4b2
96b4ed1c 82c7487a win32k!NtUserGetMessage+0x3f
96b4ed1c 779c70c6 nt!KiFastCallEntry+0x12a
000ff998 76d7cde0 ntdll!KiIntSystemCall+0x6
000ff99c 76d7ce13 USER32!NtUserGetMessage+0xc
000ff9b8 0080148a USER32!GetMessageW+0x33
000ff9f8 008016ec notepad!WinMain+0xe6
000ffa88 77473c45 notepad!_initterm_e+0x1a1
000ffa94 779e37f5 kernel32!BaseThreadInitThunk+0xe
000ffad4 779e37c8 ntdll!__RtlUserThreadStart+0x70
000ffaec 00000000 ntdll!_RtlUserThreadStart+0x1b

Ok, now we need to get information from the desktop heap, where it's mapped.

TEB at 7ffdf000

dt nt!_TEB 7ffdf000 win*
   +0x040 Win32ThreadInfo : 0xff9ba008 Void
   +0x6cc Win32ClientInfo : [62] 0x20000188
   +0xf6c WinSockData : (null)

dt win32k!tagCLIENTINFO 0x7ffdf6cc
+0x000 CI_flags         : 0x20000188
   +0x018 pDeskInfo        : 0x01d00578 tagDESKTOPINFO
   +0x01c ulClientDelta    : 0xfcb00000
   +0x028 CallbackWnd      : _CALLBACKWND
   +0x03c pClientThreadInfo : 0x01d0fd68 tagCLIENTTHREADINFO
   +0x064 hKL              : 0x08160816 HKL__
   +0x068 CodePage         : 0x4e4
   +0x06a achDbcsCF        : [2]  ""
   +0x06c msgDbcsCB        : tagMSG
   +0x088 lpdwRegisteredClasses : 0x76dc90d4  -> 0x90

dt win32k!tagDESKTOPINFO 0x01d00578
   +0x000 pvDesktopBase    : 0xfe800000 Void
   +0x004 pvDesktopLimit   : 0xff400000 Void
   +0x008 spwnd            : 0xfe800618 tagWND
   +0x00c fsHooks          : 0x4000
   +0x010 aphkStart        : [16] (null)
   +0x050 spwndShell       : 0xfe8082c0 tagWND
   +0x054 ppiShellProcess  : 0xff9c9608 tagPROCESSINFO
   +0x058 spwndBkGnd       : 0xfe808508 tagWND
   +0x05c spwndTaskman     : 0xfe804428 tagWND
   +0x060 spwndProgman     : (null)
   +0x064 pvwplShellHook   : 0xff919638 VWPL
   +0x068 cntMBox          : 0n0
   +0x06c spwndGestureEngine : (null)
   +0x070 pvwplMessagePPHandler : (null)
   +0x074 fComposited      : 0y0
   +0x074 fIsDwmDesktop    : 0y1

!kvas 0xfe800000
kvas : Show region containing fe800000
### Start    End        Length (  MB)    Count Type   
000 fdc00000 ffbfffff  2400000 (  36)        8 SessionSpace

!ptelist 0xfe800000
ptelist : Using fe800000 as VA
VA       |PDE                                 |PTE                               
FE800000 |Hard Pfn=128E0 Attr=---DA--KWEV     |Hard Pfn=13FE1 Attr=---DA--KWEV

Here, we have the kernel address (0xfe800000) of an executable heap in 32 bits architecture.
According to the help from windbg: "Executable page. For platforms that do not support a hardware execute/noexecute bit, including many x86  systems, the E is always displayed.". Basically where NX is not present - Can anyone out there validate the executability of this heap on 0x64?
A kernel address that we got from user mode and a heap we can iterate and validate, also from user mode.
A heap we can fill with GUI objects. Shellcode anyone? Stack pivoting?
This heap is called the Desktop heap and is shared and mapped read only in the user mode region, between all processes that share the same desktop and session, independently of its integrity level.

!vad 84444c88
VAD     level      start      end    commit
85ccf578 ( 5)         10       1f         0 Mapped       READWRITE          Pagefile-backed section
8458f448 ( 1)        800      82f         4 Mapped  Exe  EXECUTE_WRITECOPY  \Windows\System32\notepad.exe
8518bb90 ( 5)        830     182f        41 Private      READWRITE        
85206270 ( 4)       1830     18f7         0 Mapped       READONLY           Pagefile-backed section
845ed280 ( 5)       1900     19de         0 Mapped       READONLY           Pagefile-backed section
84600e10 ( 3)       19e0     19ef         1 Private      READWRITE        
85935718 ( 5)       19f0     1aef       128 Private      NO_ACCESS        
85cb43e0 ( 4)       1af0     1bef       130 Private      NO_ACCESS        
845770c0 ( 2)       1bf0     1cf0         0 Mapped       READONLY           Pagefile-backed section
85220368 ( 4)       1d00     28ff         0 Mapped       READONLY           Pagefile-backed section
85e32560 ( 3)       2900     29ff         2 Private      NO_ACCESS        

Although this alone might get you interested in this portion of memory, I'm not going, in this post, to talk about the potential of using this heap on privilege escalation exploitation cases.
I'm going to focus only on tagCLS type objects and what we can do with the information they provide from a user mode perspective.

The tagCLS objects are created by the win32k module, specifically by the ClassAlloc function, that allocates them in the Desktop heap when called by InternalRegisterClassEx. This USER32 system call is invoked whenever the user32.dll counterpart, RegisterClassEx function, is used to register a window class (tagWNDCLASSEX).

typedef struct tagWNDCLASSEX {
  UINT      cbSize;
  UINT      style;
  WNDPROC   lpfnWndProc;
  int       cbClsExtra;
  int       cbWndExtra;
  HINSTANCE hInstance;
  HICON     hIcon;
  HCURSOR   hCursor;
  HBRUSH    hbrBackground;
  LPCTSTR   lpszMenuName;
  LPCTSTR   lpszClassName;
  HICON     hIconSm;

Let's see what the Desktop heap has for us, from user mode. To calculate its address we grab the pDeskInfo member from the TEB's Win32ClientInfo offset, and subtract the size of heap metadata (0x570). Having the heap's base address we can dump it now:

dt nt!_heap 0x01d00000
   +0x000 Entry            : _HEAP_ENTRY
   +0x008 SegmentSignature : 0xffeeffee
   +0x00c SegmentFlags     : 1
   +0x010 SegmentListEntry : _LIST_ENTRY [ 0xfe8000a8 - 0xfe8000a8 ]
   +0x018 Heap             : 0xfe800000 _HEAP
   +0x01c BaseAddress      : 0xfe800000 Void
   +0x020 NumberOfPages    : 0xc00
   +0x024 FirstEntry       : 0xfe800570 _HEAP_ENTRY
   +0x028 LastValidEntry   : 0xff400000 _HEAP_ENTRY
   +0x02c NumberOfUnCommittedPages : 0xbaa
   +0x030 NumberOfUnCommittedRanges : 1
   +0x034 SegmentAllocatorBackTraceIndex : 0
   +0x036 Reserved         : 0
   +0x038 UCRSegmentList   : _LIST_ENTRY [ 0xfe855ff0 - 0xfe855ff0 ]
   +0x064 Signature        : 0xeeffeeff
   +0x0a0 VirtualAllocdBlocks : _LIST_ENTRY [ 0xfe8000a0 - 0xfe8000a0 ]
   +0x0a8 SegmentList      : _LIST_ENTRY [ 0xfe800010 - 0xfe800010 ]
   +0x0b8 BlocksIndex      : 0xfe800138 Void
   +0x0c4 FreeLists        : _LIST_ENTRY [ 0xfe81ef70 - 0xfe8489b8 ]
   +0x0d0 CommitRoutine    : 0x91497343     long  win32k!UserCommitDesktopMemory+0
   +0x0d4 FrontEndHeap     : (null)
   +0x0d8 FrontHeapLockCount : 0
   +0x0da FrontEndHeapType : 0 ''
   +0x0dc Counters         : _HEAP_COUNTERS
   +0x130 TuningParameters : _HEAP_TUNING_PARAMETERS

All this information pertains to kernel mode, but we can easily determine an offset from the kernel mode values to user mode:

Evaluate expression: -55574528 = fcb00000

As we know that the first chunk of memory for a heap allocation is a HEAP_ENTRY, notice that the tagDESKTOPINFO is the first entry of the heap:

Evaluate expression: -25164424 = fe800578

Evaluate expression: 30410104 = 01d00578

Knowing the offset between kernel mode and user mode mappings of the Desktop heap, we can adjust the kernel addresses found and rebase them to user mode addresses. Doing so allows us to iterate through the whole heap, chunk by chunk.

dt nt!_HEAP_ENTRY 01d00570
   +0x000 Size             : 0x10

Evaluate expression: 30410224 = 01d005f0

dt nt!_HEAP_ENTRY 01d005f0
   +0x000 Size             : 4

Evaluate expression: 30410256 = 01d00610

dt nt!_HEAP_ENTRY 01d00610
   +0x000 Size             : 0x17

Etc. But I said we're going to hunt for tagCLS objects. Let's have a look at them. The size of a tagCLS structure is:

Evaluate expression: 100 = 00000064

But as 64 isn't a multiple of eight we need to round it up, giving:

Evaluate expression: 13 = 0000000d

So we need to seek for heap entries of size 0xd.

Recapitulating, the first heap chunk is a tagDESKTOPINFO.

Evaluate expression: 120 = 00000078

Evaluate expression: 30410224 = 01d005f0

So, our first seekable element is at 01d005f0, which is validated by our previous manual heap walking.
Although I could loop through all the heap entries, like before, I just need to find a first valid tagCLS to validate data. Let's then search for hints on objects sized 0xd then:

s -w 01d005f0 l1000 d
01d006c8  000d 0001 0017 0c00 06d0 ff68 8001 8001  ..........h.....
01d00734  000d 0900 3323 3732 3936 fe00 0018 0001  ....#32769......
01d00800  000d 0001 0018 0c00 0808 ff68 c039 c039  ..........h.9.9.

dt nt!_HEAP_ENTRY 01d006c8 
   +0x000 Size             : 0xd
   +0x002 Flags            : 0x1 ''
   +0x003 SmallTagIndex    : 0 ''
   +0x000 SubSegmentCode   : 0x0001000d Void
   +0x004 PreviousSize     : 0x17

This seems a valid entry. Let's dump it as tagCLS:

dt win32k!tagCLS 01d006c8+8 
   +0x000 pclsNext         : 0xff6806d0 tagCLS
   +0x004 atomClassName    : 0x8001
   +0x006 atomNVClassName  : 0x8001
   +0x008 fnid             : 0x29d
   +0x00c rpdeskParent     : 0x8585d678 tagDESKTOP
   +0x010 pdce             : (null)
   +0x014 hTaskWow         : 0
   +0x016 CSF_flags        : 0x41
   +0x018 lpszClientAnsiMenuName : (null)
   +0x01c lpszClientUnicodeMenuName : (null)
   +0x020 spcpdFirst       : (null)
   +0x024 pclsBase         : 0xffb22758 tagCLS
   +0x028 pclsClone        : (null)
   +0x02c cWndReferenceCount : 0n1
   +0x030 style            : 8
   +0x034 lpfnWndProc      : 0x914fcd91     long  win32k!xxxDesktopWndProc+0
   +0x038 cbclsExtra       : 0n0
   +0x03c cbwndExtra       : 0n0
   +0x040 hModule          : 0x91460000 Void
   +0x044 spicn            : (null)
   +0x048 spcur            : 0xffb75608 tagCURSOR
   +0x04c hbrBackground    : 0x00000002 HBRUSH__
   +0x050 lpszMenuName     : (null)
   +0x054 lpszAnsiClassName : 0xfe800738  "#32769"
   +0x058 spicnSm          : (null)

This is what we need. Let's get the tagDESKTOP address and validate it. This time we’ll be using the kernel to verify its address, by grabbing Win32ThreadInfo member from the TEB.

dt win32k!tagTHREADINFO 0xff9ba008 rpdesk
   +0x0c8 rpdesk : 0x8585d678 tagDESKTOP

Yes, it is the same. Now, if we search for all occurrences of this tagDESKTOP address in the Desktop heap, subtract 0xc from the obtained address, we get a list of all tagCLS objects in the heap.

Note that if I were to do a program or script to do this, I'd register a tagWNDCLASSEX with a known lpfnWndProc       like 0xbadecafe, seek for this value to locate my tagCLS object and get my corresponding tagDESKTOP; then, iterate all the heap items grabbing those with the same desktop. But, as I'm using the debugger for the demo, it's easier and simpler to do it this way.

s -d 01d005f0 l1000000 0x8585d678
01d00624  8585d678 fe800618 00040000 82000100  x...............
01d006dc  8585d678 00000000 00410000 00000000  x.........A.....
01d00754  8585d678 fe800748 00040000 80000100  x...H...........
01d00814  8585d678 00000000 00410000 00000000  x.........A.....
01d0088c  8585d678 fe800880 00040000 80000100  x...............
01d0096c  8585d678 00000000 03410000 00000000  x.........A.....
01d009e4  8585d678 fe8009d8 00000000 00000000  x...............
01d00a2c  8585d678 fe800a20 00000000 00000000  x... ...........
01d00a54  8585d678 00000000 00410000 00000000  x.........A........

The most interesting parts of a tagCLS are lpfnWndProc and hModule because these are user mode addresses that we can use and validate from user mode.

.foreach (meuval {s -[1]d 01d005f0 l1000000 0x8585d678 } )  {
   .if ((poi(${meuval}-0x14)&0x0000ffff) >= 0000000d) {
      .printf "ObjAddr=[%08x], hInstance[%08x], WndProc=",
       dds (${meuval}+28) l1;

Note: The search size given in the command above is greater or equal to the size of tagCLS, because the kernel can reserve some extra space if requested by the user, as indicated in the cbclsExtra class member.

ObjAddr=[fe800ac0], hInstance[00800000], WndProc=01d00af4  008072c5 notepad!NpSaveDialogHookProc+0x97
ObjAddr=[fe800b58], hInstance[76d60000], WndProc=01d00b8c  76d70666 USER32!ImeWndProcW
ObjAddr=[fe800dd0], hInstance[77090000], WndProc=01d00e04  770941b5 MSCTF!UIWndProc
ObjAddr=[fe800e38], hInstance[77090000], WndProc=01d00e6c  770fdd77 MSCTF!UIComposition::CompWndProc
ObjAddr=[fe805228], hInstance[749f0000], WndProc=01d0525c  74a1fe38 COMCTL32!CListView::s_WndProc
ObjAddr=[fe805490], hInstance[749f0000], WndProc=01d054c4  74a16022 COMCTL32!Header_WndProc
ObjAddr=[fe807320], hInstance[01000000], WndProc=01d07354  01003429
ObjAddr=[fe814950], hInstance[70830000], WndProc=01d14984  708432bc
ObjAddr=[fe814ba8], hInstance[70830000], WndProc=01d14bdc  708431dc
ObjAddr=[fe814c40], hInstance[70830000], WndProc=01d14c74  70842954
ObjAddr=[fe82b580], hInstance[77820000], WndProc=01d2b5b4  778663e5 ole32!OleMainThreadWndProc
ObjAddr=[fe82cb70], hInstance[00800000], WndProc=01d2cba4  008014de notepad!NPWndProc
ObjAddr=[fe842790], hInstance[77090000], WndProc=01d427c4  770fdd77 MSCTF!UIComposition::CompWndProc
ObjAddr=[fe8428a8], hInstance[76d60000], WndProc=01d428dc  76db3f06 USER32!EditWndProcW
ObjAddr=[fe842910], hInstance[749f0000], WndProc=01d42944  749f99d0 COMCTL32!Edit_WndProc
ObjAddr=[fe842ac8], hInstance[749f0000], WndProc=01d42afc  74a90d49 COMCTL32!StatusWndProc
ObjAddr=[fe843c78], hInstance[01010000], WndProc=01d43cac  01044270

Dumping lpfnWndProc and hModule from all the tagCLS objects found in the desktop heap, we can observe that all the user windows message dispatch handlers registered in the system for the current desktop are available for analysis from user mode. From the dump above I've identified two from notepad: NpSaveDialogHookProc and NPWndProc which is the main window dispatcher function; and module 0x01000000. This entire post is about module 0x01000000.

Module 0x01000000 is popo.exe as can be seen from the procExp.

Figure 2: Packed popo.exe module address.

And the address from WndProc at memory position 0x1d07354, 0x01003429 is the NPWndProc windows message handler dispatcher function from popo.exe as seen in the figure. The figure shows the unpacked version of popo.exe so that symbol resolution is available.
 Figure 3: Window dispatcher registration.

Figure 4: Window dispatcher registration arguments.

So here you have it. A fine simple non-invasive way of getting some information from packed or protected applications.

Other thoughts: Do you think that the same info is available for Protected (Media Path) processes? Those to which we were not supposed to get any info about?
Is there any information available for other desktops windows if in the same session? Can we link this data in any way with the aheList?

Hope you enjoyed it.

Sem comentários: