Last active
November 25, 2019 11:55
-
-
Save katahiromz/7ea1da2906aed4e233a7c8ee8f15a814 to your computer and use it in GitHub Desktop.
ghost thread
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| 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