Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How does GetWindowText get the name of a window owned by another process without a syscall to read that process's memory?

I wanted to figure out what the syscalls behind GetWindowText are. I wrote a simple program to call GetWindowText with a handle to a window in a different process.

int CALLBACK WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow)
{
    MessageBox(0,"Attach debugger and set bp","on GetWindowTextA",0);

    HWND winmine = FindWindow(NULL,"Minesweeper");

    if(winmine != NULL)
    {
        char buf[255] = "";
        GetWindowTextA(winmine, buf, 254);
        MessageBox(0,buf,"Found",0);
    }
    else
    {
        MessageBox(0,"?","Found nothing",0);
    }

    return 0;
}

I attached a debugger and stepped through the GetWindowTextA call, manually stepping through everything except these API calls (in order):

  • GetWindowThreadProcessId (in GetWindowLong)
  • InterlockedIncrement
  • WCSToMBEx (which is basically WideCharToMultiByte)
  • InterlockedDecrement

None of these API calls seem to be able to read a string in memory not owned by the calling process. I used a usermode debugger so I certainly didn't end up in kernelmode while stepping without realizing it. This means that GetWindowText got the window name without performing a context switch. Which seems to imply that the text for every window that exists is accessible without a context switch.. and that can't be right because there's no way Windows keeps a copy of the text for every single window/control on the system, on every single process.

I have read this article. It mentions that window names are stored in quote "a special place", but does not explain how this "special place" can be accessed from a different process without a syscall/context switching.

So I'm looking for any explanations as to how this is done. Any information you can provide is greatly appreciated.

like image 471
higaki Avatar asked Feb 02 '14 08:02

higaki


People also ask

How does getwindowtext work in Windows 10?

If the target window is owned by the current process, GetWindowText causes a WM_GETTEXT message to be sent to the specified window or control. If the target window is owned by another process and has a caption, GetWindowText retrieves the window caption text.

Why is getwindowtext() hanging?

According to the first rule, if you are trying to get text from a window in your own process, and the window is hung, then GetWindowText () will also hang. But since the window belongs to your process, it’s your own fault and you deserve to lose.

How can I get text from a window in another process?

Since the most common reason for getting text from a window in another process is to get the title of the frame, and since frame windows typically do not do custom window text manipulation, this usually gets the right string. The documentation simplifies this as “GetWindowText () cannot retrieve text from a window from another application.”

What happens when a window class manages its window text manually?

On the other hand, if a window class manages its window text manually, the system will not do any special handling, and it is the window class’s responsibility to respond to the WM_GETTEXT/WM_SETTEXT messages and return/save the strings explicitly. Frame windows typically let the system manage their window text.


2 Answers

GetWindowText got the window name without performing a context switch. Which seems to imply that the text for every window that exists is accessible without a context switch.

This info is stored in memory that is shared between all the processes that use user32.dll. You may try to search virtual space of your process for unicode names of other processes' windows.

It gets mapped into the process address space during user32.dll loading. There are some kernel structures/sections involved: win32k!gSharedInfo, win32k!ghSectionShared, win32k!gpsi and others (which I don't know of).

Actually, the lower 16 bits of HWND represents index into window info array with base address *(&user32!gSharedInfo + 1). The first field of this window info is the kernel address of another structure which contains all the shared window information. Subtracting the difference between kernel address of the section and its user-space mapping (which is stored in TEB!Win32ClientInfo) you can get relevant info.

user32!ValidateHwnd is the function that converts window handle into this address which can be used by inner user32 functions like user32!DefWindowProcWorker.

Pseudocode of GetWindowTextW looks like (excluding error-handling):

    GetWindowTextW(HWND hwnd, wchar_t* buf, int size)
    {
        inner_hwnd = ValidateHwnd(hwnd);
        if (TestWindowProcess(inner_hwnd))
            SendMessageWorker(inner_hwnd, WM_GETTEXT, size, buf, FALSE);
        else
            DefWindowProcWorker(inner_hwnd, WM_GETTEXT, size, buf, FALSE);
    }

DefWindowProcWorker which is called in your case with WM_GETTEXT will just parse the structure referenced by inner_hwnd and copy window's name into buf.


it seems that the text for EDITTEXT controls are not stored in this manner

I never knew all the info that was stored in there though it seems like a good choice to not pollute processes' virtual space with all kinds of user/gdi params. Besides, lower integrity processes should not be able to get higher integrity processes sensitive information.

like image 121
qwm Avatar answered Sep 30 '22 19:09

qwm


because there's no way Windows keeps a copy of the text for every single window

The text most certainly exists, just not as a copy. The text for a window is stored in the virtual memory of the process that owns the window. Might be in RAM, not terribly likely if the process has been dormant for a while, definitely on disk in the paging file. Which doesn't stop GetWindowText() from making a copy. On-the-fly, when you call it.

GetWindowText() is limited, it is documented to only being capable of copying the caption text of a window, so it probably uses the desktop heap for the session to retrieve the text. Not otherwise a restriction to a winapi function like SendMessage(), you can use WM_GETTEXT to obtain a gigabyte from an Edit control. That certainly crosses the process boundary.

As an operating system function, SendMessage can of course break all the rules that apply to normal processes. The OS has no trouble addressing the VM of an arbitrary process. Rules that are routinely broken, your debugger does it as well. With functions that you can use to also break the rules, ReadProcessMemory() and WriteProcessMemory().

like image 30
Hans Passant Avatar answered Sep 30 '22 18:09

Hans Passant