Forbidding the Clipboard for the specified process

Written by:
Vladimir Shamray,
Senior Developer

Contents

There are two approaches. The first approach is to hook SetClipboardData and GetClipboardData in the user mode. Both functions are well documented and, that is much more important, they are exported. This is the main advantage. But this approach requires setting hooks for all processes in the system. Also we need to track creation of the new processes to set hooks in their address spaces too.

The second approach, which is described in this article, is to hook kernel-mode functions NtUserSetClipboardData and NtUserGetClipboardData. In this case we need to set hooks only once. But both functions are not exported from win32k.sys, so it is quite a problem to find them.

Project implementation

How to find an unexported function?

Hooking a couple of functions is not a big problem. But before that we need to find them. How to find a function which is not exported? One way is to hardcode the function address in the program. The address can be easily found manually, using WinDbg, for instance. Unfortunately, it is necessary to keep a base of these addresses for each operating system (and, probably, for each service pack) which you’re going to support.

Other way is to keep not an address but some sequence of bytes from which that address can be obtained. This sequence of bytes is called unique signature. It can be a piece of the function’s code or a piece of code from where the function is called. Though this way is not very portable too, it much less depends on differences between the various operating system versions.

Finding the signatures

First of all, we need to find the unique signatures for two functions: NtUserSetClipboardData and NtUserGetClipboardData. Both of them are in the win32k.sys. To do it, we will use WinDbg. Let’s start with the signature for NtUserGetClipboardData.

As I mentioned the function we’re looking for is in the win32k.sys and this module is mapped to a session address space, it means that it is available only in the context of some interactive process, like explorer.exe. To switch to explorer.exe, you can use !process 0 0 explorer.exe to get the address of its EPROCESS and then use .process command.

kd> !process 0 0 explorer.exe
PROCESS 81946990  SessionId: 0  Cid: 063c    Peb: 7ffd4000  ParentCid: 060c
    DirBase: 09b85000  ObjectTable: e19cafb8  HandleCount: 260.
    Image: explorer.exe

kd> .process /p 81946990
Implicit process is now 81946990
.cache forcedecodeuser done

Let’s take a look at the NtUserGetClipboardData. We can see its disassembly using u debugger command

kd> u win32k!NtUserGetClipboardData win32k!NtUserGetClipboardData+93
win32k!NtUserGetClipboardData:
bf8e9569 6a20            push    20h
bf8e956b 6888b198bf      push    offset win32k!`string'+0x268 (bf98b188)
bf8e9570 e84376f1ff      call    win32k!_SEH_prolog (bf800bb8)
...

Now, look at the piece in the middle of the function code, where xxxGetClipboardData is called. The first function argument is copied to a local variable, then the values of the three registers are placed into the stack and finally the xxxGetClipboardData is called. That piece of code seems to be suitable for using as the unique signature. On the one hand, it can be unique (actually, it is) and on the other hand, we can hope that it can be used not only for this version of Windows.

The last byte of the second instruction is an offset of a local variable which can be easily changed in another build. So, it won’t be included in the signature, as well as the last byte of the third instruction.

After checking some other versions of Windows we decide to use the described sequence of bytes as the unique signature for the NtUserGetClipboardData function. Unique signature for the NtUserSetClipboardData can be found in the same way.

Function search implementation

Signatures of the functions are pieces of binary code inside them. Therefore, the process of finding a function address consists of two steps: search forward for the signature and search back for the start bytes of the function.

More by Author

Must Read