共用方式為


How the Clipboard Works, Part 1

Recently I had the opportunity to debug the clipboard in Windows, and I thought I’d share some of the things I learned.  The clipboard is one of those parts of Windows that many of us use dozens (hundreds?) of times a day and don’t really think about. Before working on this case, I had never even considered how it works under the hood.  It turns out that there’s more to it than you might think. In this first article, I’ll describe how applications store different types of data to the clipboard and how it is retrieved.  My next post will describe how applications can hook the clipboard to monitor it for changes.  In both, I’ll include debug notes to show you how to access the data from the debugger.

 

Let’s start by discussing clipboard formats.  A clipboard format is used to describe what type of data is placed on the clipboard.  There are a number of predefined standard formats that an application can use, such as bitmap, ANSI text, Unicode text, and TIFF.  Windows also allows an application to specify its own formats. For example, a word processor may want to register a format that includes text, formatting, and images.  Of course, this leads to one problem, what happens if you want to copy from your word processor and paste into Notepad, which doesn’t understand all of the formatting and pictures?

 

The answer is to allow multiple formats to be stored in the clipboard at one time.  When I used to think of the clipboard I thought of it as a single object (“my text” or “my image”), but in reality the clipboard usually has my data in several different forms.  The destination program gets a format it can use when I paste.

 

So how does this data end up on the clipboard?  Simple, an application first takes ownership of the clipboard via the OpenClipboard function.   Once it has done that, it can empty the clipboard with EmptyClipboard.  Finally, it is ready to place data on the clipboard using SetClipboardData.  SetClipboardData takes two parameters; the first is the identifier of one of the clipboard formats we discussed above.  The second is a handle to the memory containing the data in that format. The application can continue to call SetClipboardData for each of the various formats it wishes to provide going from best to worst (since the destination application will select the first format in the list it recognizes).  To make things easier for the developer, Windows will automatically provide converted formats for some clipboard format types.  Once the program is done, it calls CloseClipboard.

 

When a user hits paste, the destination application will call OpenClipboard and one of these functions to determine what data format(s) are available: IsClipboardFormatAvailable, GetPriorityClipboardFormat, or EnumClipboardFormats. Assuming the application finds a format it can use, it will then call GetClipboardData with the desired format identifier as a parameter to get a handle to the data.  Finally, it will call CloseClipboard.

 

Now let’s take a look at how you can find what data being written to the clipboard using the debugger. (Note that all of my notes are taken from a Win7/2008 R2 system – so things might vary slightly in different versions of the OS.)   Since the clipboard is part of Win32k.sys, you’ll need to use a kernel debugger.  I like to use win32k!InternalSetClipboardData+0xe4 as a breakpoint.  The nice thing about this offset is that it is right after we’ve populated the RDI register with data from SetClipboardData in a structure known as tagCLIP.

 

kd> u win32k!InternalSetClipboardData+0xe4-c L5

win32k!InternalSetClipboardData+0xd8:

fffff960`0011e278 894360          mov     dword ptr [rbx+60h],eax

fffff960`0011e27b 8937            mov     dword ptr [rdi],esi

fffff960`0011e27d 4c896708        mov     qword ptr [rdi+8],r12

fffff960`0011e281 896f10          mov     dword ptr [rdi+10h],ebp

fffff960`0011e284 ff15667e1900    call    qword ptr[win32k!_imp_PsGetCurrentProcessWin32Process (fffff960`002b60f0)]

 

kd> dt win32k!tagCLIP

   +0x000 fmt              : Uint4B

   +0x008 hData            : Ptr64 Void

   +0x010fGlobalHandle     : Int4B

 

Here’s what a call to SetClipboardData from Notepad looks like:

kd> k

Child-SP          RetAddr           Call Site

fffff880`0513a940 fffff960`0011e14f win32k!InternalSetClipboardData+0xe4

fffff880`0513ab90 fffff960`000e9312 win32k!SetClipboardData+0x57

fffff880`0513abd0 fffff800`01482ed3 win32k!NtUserSetClipboardData+0x9e

fffff880`0513ac20 00000000`7792e30ant!KiSystemServiceCopyEnd+0x13

00000000`001dfad8 00000000`7792e494 USER32!ZwUserSetClipboardData+0xa

00000000`001dfae0 000007fe`fc5b892b USER32!SetClipboardData+0xdf

00000000`001dfb20 000007fe`fc5ba625 COMCTL32!Edit_Copy+0xdf

00000000`001dfb60 00000000`77929bd1 COMCTL32!Edit_WndProc+0xec9

00000000`001dfc00 00000000`779298da USER32!UserCallWinProcCheckWow+0x1ad

00000000`001dfcc0 00000000`ff5110bc USER32!DispatchMessageWorker+0x3b5

00000000`001dfd40 00000000`ff51133c notepad!WinMain+0x16f

00000000`001dfdc0 00000000`77a2652d notepad!DisplayNonGenuineDlgWorker+0x2da

00000000`001dfe80 00000000`77b5c521 kernel32!BaseThreadInitThunk+0xd

00000000`001dfeb0 00000000`00000000ntdll!RtlUserThreadStart+0x1d

 

So here, we can dt RDI as a tagCLIP to see what was written:

kd> dt win32k!tagCLIP @rdi

   +0x000 fmt              : 0xd

   +0x008 hData            : 0x00000000`00270235 Void

   +0x010fGlobalHandle     : 0n1

 

Fmt is the clipboard format. 0xd is 13, which indicates this data is Unicode text.  We can’t just ‘du’ the value in hData, however, because this is a handle, not a direct pointer to the data.  So now we need to look up the handle.  To do that, we need to look at a win32k global structure – gSharedInfo:

kd> ?win32k!gSharedInfo

Evaluate expression: -7284261440224 = fffff960`002f3520

kd> dt win32k!tagSHAREDINFO fffff960`002f3520

   +0x000 psi              : 0xfffff900`c0980a70 tagSERVERINFO

   +0x008 aheList          : 0xfffff900`c0800000 _HANDLEENTRY

   +0x010 HeEntrySize      : 0x18

   +0x018 pDispInfo        : 0xfffff900`c0981e50 tagDISPLAYINFO

   +0x020ulSharedDelta     : 0

   +0x028 awmControl       : [31] _WNDMSG

   +0x218DefWindowMsgs     : _WNDMSG

   +0x228DefWindowSpecMsgs : _WNDMSG

 

aheList in gSharedInfo contains an array of handle entries, and the last 2 bytes of hData multiplied by the size of a handle entry the address of our handle entry:

kd> ?0x00000000`00270235 & FFFF

Evaluate expression: 565 = 00000000`00000235

kd> ??sizeof(win32k!_HANDLEENTRY)

unsigned int64 0x18

kd> ? 0xfffff900`c0800000 + (0x235*0x18)

Evaluate expression: -7693351766792 = fffff900`c08034f8

 

kd> dt win32k!_HANDLEENTRY fffff900`c08034f8

   +0x000 phead            : 0xfffff900`c0de0fb0 _HEAD

   +0x008 pOwner           : (null)

   +0x010 bType            : 0x6 ''

   +0x011 bFlags           : 0 ''

   +0x012 wUniq            : 0x27

 

If we look in phead at offset 14, we’ll get our data (this offset may vary on different platforms):

kd> du fffff900`c0de0fb0 + 0x14

fffff900`c0de0fc4 "Hi NTDebugging readers!"

 

Let’s consider one other scenario.  I copied some text out of Wordpad, and a number of SetClipboardData calls were made to accommodate different formats. The Unicode format entry looks like this:

Breakpoint 0 hit

win32k!InternalSetClipboardData+0xe4:

fffff960`0011e284 ff15667e1900   call    qword ptr[win32k!_imp_PsGetCurrentProcessWin32Process (fffff960`002b60f0)]

kd> dt win32k!tagCLIP @rdi

   +0x000 fmt              : 0xd

   +0x008 hData            : (null)

   +0x010fGlobalHandle    : 0n0

 

hData is null!  Why is that?  It turns out that the clipboard allows an application to pass in null to SetClipboardData for a given format.  This indicates that the application can provide the data in that format, but is deferring doing so until it is actually needed.  Sure enough, if I paste the text into Notepad, which needs the text in Unicode, Windows sends a WM_RENDERFORMAT message to the WordPad window, and WordPad provides the data in the new format.  Of course, if the application exits before populating all of its formats, Windows needs all of the formats rendered.  In this case, Windows will send the WM_RENDERALLFORMATS message so other applications can use the clipboard data after the source application has exited.

 

That’s all for now.  Next time we’ll look at how applications can monitor the clipboard for changes using two hooks.  If you want to know more about using the clipboard in your code, this is a great reference.

 

-Matt Burrough

 

Part 2 of this article can be found here:

https://blogs.msdn.com/b/ntdebugging/archive/2012/03/29/how-the-clipboard-works-part-2.aspx

Comments

  • Anonymous
    March 20, 2012
    Very interesting topic, thanks. I'm using a lot of clipboard functionality when working with remote desktops and Citrix, this will certainly help me to understand the issues I'm encountering from time to time better.

  • Anonymous
    March 22, 2012
    Sometime when working with multi rdp session, clipboard had strange behavior, the past command doesn't work, such the copy command remain into rdp session and not "copied" into client clipboard. may be there an update?

  • Anonymous
    March 23, 2012
    Dev Center - Desktop > Learn > Reference > Windows Application UI Development > Data Exchange > Clipboard > Clipboard Overviews > Using the Clipboard ...Lol... [Indeed, that is a helpful resource. I think the main difference
    between that document and this post is that article focuses on how to approach
    the clipboard as a developer, while this one shows how to examine it through
    the debugger.]

  • Anonymous
    March 27, 2012
    The comment has been removed