Skip to content

Instantly share code, notes, and snippets.

@katahiromz
Last active November 25, 2019 11:55
Show Gist options
  • Select an option

  • Save katahiromz/7ea1da2906aed4e233a7c8ee8f15a814 to your computer and use it in GitHub Desktop.

Select an option

Save katahiromz/7ea1da2906aed4e233a7c8ee8f15a814 to your computer and use it in GitHub Desktop.
ghost thread
typedef struct GHOST_INFO
{
HWND hwndTarget;
HANDLE GhostStartupEvent;
HANDLE GhostQuitEvent;
DWORD cTargets;
ULONG_PTR InputThreadId;
} GHOST_INFO;
// Private message PM_CREATE_GHOST:
// wParam: HWND. The target window (hung).
// lParam: VOID. Ignored.
#define PM_CREATE_GHOST (WM_APP + 1)
// Private message PM_DESTROY_GHOST:
// wParam: HWND. The target window.
// lParam: BOOL. Whether the target is to be destroyed.
#define PM_DESTROY_GHOST (WM_APP + 2)
static GHOST_INFO *gGhostInfo = NULL;
BOOL IntCreateGhost(HWND hwndTarget)
{
RTL_ATOM Atom;
DWORD style, exstyle;
RECT rc;
HWND hwndGhost, hwndOwner;
PWND pTargetWnd = ValidateHwndNoErr(hwndTarget);
// get atom for ghost prop
if (!IntGetAtomFromStringOrAtom(&GhostProp, &Atom))
ASSERT(FALSE);
// get target info
style = pTargetWnd->style;
exstyle = pTargetWnd->ExStyle;
GetWindowRect(hwndTarget, &rc);
hwndOwner = GetWindow(hwndTarget, GW_OWNER);
// create the ghost
hwndGhost = CreateWindowExW(exstyle, GHOSTCLASSNAME, NULL, style,
rc.left, rc.top, rc.right - rc.left, rc.bottom - rc.top,
hwndOwner, NULL, GetModuleHandleW(NULL), hwndTarget);
if (!hwndGhost)
{
return FALSE;
}
// set ghost prop
return UserSetProp(pTargetWnd, Atom, hwndTarget, TRUE);
}
BOOL IntResumeGhostedWindowInternal(HWND hwndTarget, BOOL bDestroyTarget)
{
HWND hwndGhost = NtUserGhostWindowFromHungWindow(hwndTarget);
return SendMessageW(hwndGhost, GWM_UNGHOST, hwndTarget, bDestroyTarget);
}
static ULONG NTAPI
GhostThreadProc(PVOID Param)
{
HWND hwndTarget = (GHOST_INFO *)Param;
PCSR_THREAD pcsrt;
HANDLE hThread;
MSG msg;
pcsrt = CsrConnectToUser();
if (pcsrt == NULL)
goto Quit;
hThread = pcsrt->ThreadHandle;
if (IntCreateGhost(hwndTarget))
{
++gGhostInfo->cTargets;
}
else
{
DPRINT1("Unable to create ghost\n");
goto Quit;
}
while (GetMessageW(&msg, NULL, 0, 0))
{
switch (msg.message)
{
case PM_CREATE_GHOST:
{
hwndTarget = (HWND)msg.wParam;
if (IntCreateGhost(hwndTarget))
{
++gGhostInfo->cTargets;
}
else
{
DPRINT1("Unable to create ghost\n");
}
break;
}
case PM_DESTROY_GHOST:
{
if (IntResumeGhostedWindowInternal((HWND)msg.wParam, (BOOL)msg.lParam))
{
--gGhostInfo->cTargets;
if (gGhostInfo->cTargets == 0)
{
PostQuitMessage(0);
}
}
else
{
DPRINT1("Unable to destroy ghost\n");
}
break;
}
}
TranslateMessage(&msg);
DispatchMessageW(&msg);
}
Quit:
if (pcsrt)
{
if (hThread != pcsrt->ThreadHandle)
DPRINT1("WARNING!! hThread (0x%p) != pcsrt->ThreadHandle (0x%p), you may expect crashes soon!!\n", hThread, pcsrt->ThreadHandle)
CsrDereferenceThread(pcsrt);
}
RtlFreeHeap(CsrHeap, 0, gGhostInfo);
NtSetEvent(gGhostInfo->GhostQuitEvent, NULL);
gGhostInfo = NULL;
RtlExitUserThread(Status);
return 0;
}
BOOL IntInitGhostFeature(HWND hwndTarget)
{
NTSTATUS Status;
CLIENT_ID ClientId;
HANDLE GhostThreadHandle;
if (gGhostInfo)
return FALSE;
gGhostInfo = RtlAllocateHeap(CsrHeap, HEAP_ZERO_MEMORY, sizeof(GHOST_INFO));
if (!gGhostInfo)
return FALSE;
gGhostInfo->hwndTarget = hwndTarget;
// create events
Status = NtCreateEvent(&gGhostInfo->GhostStartupEvent, EVENT_ALL_ACCESS,
NULL, SynchronizationEvent, FALSE);
Status = NtCreateEvent(&gGhostInfo->GhostQuitEvent, EVENT_ALL_ACCESS,
NULL, SynchronizationEvent, FALSE);
Status = RtlCreateUserThread(NtCurrentProcess(),
NULL,
TRUE, // Start the thread in suspended state
0,
0,
0,
GhostThreadProc,
hwndTarget,
&GhostThreadHandle,
&ClientId);
if (NT_SUCCESS(Status))
{
/* Add it as a static server thread and resume it */
CsrAddStaticServerThread(hGhostThread, &ClientId, 0);
Status = NtResumeThread(hGhostThread, NULL);
}
DPRINT("Thread creation hGhostThread = 0x%p, GhostThreadId = 0x%p, Status = 0x%08lx\n",
hGhostThread, ClientId.UniqueThread, Status);
NtWaitForSingleObject(gGhostInfo->GhostStartupEvent, FALSE, NULL);
NtClose(gGhostInfo->GhostStartupEvent);
gGhostInfo->GhostStartupEvent = NULL;
gGhostInfo->InputThreadId = (ULONG_PTR)ClientId.UniqueThread;
return TRUE;
}
BOOL IntMakeHungWindowGhosted(HWND hwndHung)
{
HANDLE hThread;
CLIENT_ID ClientId;
NTSTATUS Status;
PWND pHungWnd = ValidateHwndNoErr(hwndHung);
if (!pHungWnd)
{
DPRINT1("Not a window\n");
return FALSE; // not a window
}
if (IntIsGhostWindow(pHungWnd))
{
DPRINT1("IntIsGhostWindow\n");
return FALSE; // ghost window cannot be ghosted
}
if (!MsqIsHung(pHungWnd->head.pti))
{
DPRINT1("Not hung window\n");
return FALSE; // not hung window
}
if (!(pHungWnd->style & WS_VISIBLE))
return FALSE; // invisible
if (pHungWnd->style & WS_CHILD)
return FALSE; // child
if (NtUserGhostWindowFromHungWindow(hwndHung))
{
DPRINT("Already ghosting\n");
return FALSE; // already ghosting
}
if (gGhostInfo == NULL)
{
IntInitGhostFeature(hwndHung);
}
else
{
PostThreadMessageW(gGhostInfo->InputThreadId, PM_CREATE_GHOST, (WPARAM)hwndTarget, 0);
}
return TRUE;
}
BOOL IntResumeGhostedWindow(HWND hwndTarget, BOOL bDestroyTarget)
{
if (gGhostInfo == NULL)
return FALSE;
if (PostThreadMessageW(gGhostInfo->InputThreadId, PM_DESTROY_GHOST,
(WPARAM)hwndTarget, (LPARAM)bDestroyTarget))
{
return TRUE;
}
DPRINT1("Unable to resume hung window\n");
return FALSE;
}
BOOL IntFreeGhostFeature(VOID)
{
HANDLE GhostQuitEvent;
if (gGhostInfo == NULL)
return FALSE;
GhostQuitEvent = gGhostInfo->GhostQuitEvent;
if (PostThreadMessageW(gGhostInfo->InputThreadId, WM_QUIT, 0, 0))
{
NtWaitForSingleObject(GhostQuitEvent, FALSE, NULL);
NtClose(GhostQuitEvent);
return TRUE;
}
else
{
DPRINT1("Unable to free ghost feature\n");
}
return FALSE;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment