-6

I've written a code that terminates egui.exe. But my callback function doesn't run after the WM_CLOSE message is processed. Why?

#include <iostream>
#include <windows.h>
#include <psapi.h>

using namespace std;

HWND ESETWindow = 0;    //Variable to store a handle to the main window
                        // of ESET Smart Security
HWND ConfirmationWindow = 0;    //Variable to store a handle to the
                                // confirmation dialog box that appears
                                // when a WM_CLOSE message is sent to
                                // the ESET window.
DWORD EGUIPID = 0;  //Variable to store the process identifier of egui.exe
BOOL CALLBACK EnumWindowsProc(HWND hwnd, LPARAM lParam);
VOID SendAsyncProc(HWND hwnd, UINT uMsg, ULONG_PTR dwData, LRESULT lResult);

int main()
{
    cout << "Terminating ESET Smart Security..." << endl;

    //Obtain an array of process identifiers including the PID of egui.exe
    DWORD PIDs[1024]; //The array of process identifiers
    DWORD bytesReturned;
    if (!EnumProcesses(PIDs, sizeof(PIDs), &bytesReturned))
    {
        cerr << "Unable to enumerate the processes." << endl;
        return 0;
    }

    //Enumerate the PIDs array to find the process of egui.exe.
    DWORD nProcesses = bytesReturned / sizeof(DWORD);
    for (DWORD i = 0;i < nProcesses;i++)
    {
        //Open the process to examine its executable file name.
        HANDLE process = OpenProcess(PROCESS_QUERY_INFORMATION | PROCESS_VM_READ, FALSE, PIDs[i]);
        //Create a buffer for the name of the executable file.
        PSTR processName = (PSTR) VirtualAlloc((LPVOID) NULL, (DWORD) 10, MEM_COMMIT, PAGE_READWRITE);
        //Get the name of the executable file name.
        GetModuleBaseName(process, NULL, processName, 10);
        //Check if the executable file name is egui.exe.
        if (!lstrcmpi(processName, TEXT("egui.exe")))
        {
            EGUIPID = PIDs[i];
            break;
        }
        VirtualFree(processName, 0, MEM_RELEASE);
        CloseHandle(process);
    }

    //Display an error message if the process identifier of egui.exe
    //could not be found.
    if (!EGUIPID)
    {
        cerr << "Unable to find process identifier of egui.exe" << endl;
        return 0;
    }

    //Enumerate the top-level windows to find the main window of
    // ESET Smart Security and store a handle to that window in the
    // ESETWindow variable.
    EnumWindows(EnumWindowsProc, 0);
    //Display an error message if the window could not be found.
    if (!ESETWindow)
    {
        cerr << "Unable to find the primary window of ESET Smart Security." << endl;
        return 0;
    }

    //Send a WM_CLOSE message to the main window of egui.exe in order
    // to display a confirmation dialog box.
    if (!SendMessageCallback(ESETWindow, WM_CLOSE, 0, 0, SendAsyncProc, 0))
    {
        cerr << "Unable to send a WM_CLOSE message to the primary window of ESET Smart Security." << endl;
        return 0;
    }

    //Wait a second for the confirmation dialog box to appear...
    Sleep(1000);
    //Enumerate the windows again to find the confirmation dialog box.
    EnumWindows(EnumWindowsProc, 1);
    //Display an error message if the confirmation dialog box
    // could not be found.
    if (!ConfirmationWindow)
    {
        cerr << "Unable to find confirmation message." << endl;
        cout << "If you have ESET NOD32, it is probably terminated successfully.";
        cin.get();
    }

    //Find the Yes button in the confirmation dialog box and display
    // an error message if failed.
    HWND button = FindWindowEx(ConfirmationWindow, NULL, NULL, TEXT("&Yes"));
    if (!button)
    {
        cerr << "Unable to find Yes button in the message box." << endl;
        return 0;
    }

    //Activate the confirmation dialog box and simulate a mouse click
    // on the Yes button.
    SetActiveWindow(ConfirmationWindow);
    SendMessage(button, BM_CLICK, 0, 0);
    cout << "ESET Smart Security was successfully terminated!";

    //Keep the program running until the user presses Enter
    // in the console window.
    cin.get();
    return 0;
}

//If lParam is 0, the function below finds the main window of
//ESET Smart Security, otherwise, it finds the confirmation
//dialog box.
BOOL CALLBACK EnumWindowsProc(HWND hwnd, LPARAM lParam)
{
    //Check if hwnd belongs to a window of egui.exe.
    DWORD PID = 0;
    GetWindowThreadProcessId(hwnd, &PID);
    if (PID != EGUIPID)
        return TRUE; //Exit function and continue the enumeration.

    //Check if the title of the window is "ESET Smart Security"
    // or "ESET Node32 Antivirus".
    int len = GetWindowTextLength(hwnd);
    PSTR title = (PSTR) VirtualAlloc((LPVOID) NULL, (DWORD) (len + 1), MEM_COMMIT, PAGE_READWRITE);
    GetWindowText(hwnd, title, (len + 1));
    if ((lstrcmp(title, TEXT("ESET Smart Security"))) && (lstrcmp(title, TEXT("ESET NOD32 Antivirus"))))
        return TRUE; //Exit function and continue the enumeration.

    if (lParam)
    {
        //If lParam is nonzero and hwnd refers to the main
        // window of egui.exe, exit function and continue
        // the enumeration.
        if (hwnd == ESETWindow)
            return TRUE;
        //Otherwise hwnd refers to the confirmation dialog box.
        //So store it in the ConfirmationWindow variable.
        ConfirmationWindow = hwnd;
        return FALSE; //Exit function and stop the enumeration.
    }
    else
    {
        //hwnd refers to the main window of ESET Smart Security.
        //So store it in the ESETWindow variable.
        ESETWindow = hwnd;
        return FALSE; //Exit function and stop the enumeration.
    }
}
VOID SendAsyncProc(HWND hwnd, UINT uMsg, ULONG_PTR dwData, LRESULT lResult)
{
    MessageBox(0, TEXT("ESET Smart Security was successfully terminated."), TEXT("Result"), MB_OK);
}

Although this program accomplishes its task perfectly, the callback function SendAsyncProc doesn't run after the WM_CLOSE message is processed and egui.exe is terminated. Could you tell me why?

Javad Bayat
  • 160
  • 13
  • 2
    This is not a bug. Desktop programs can be closed. Anyway, do you have a question for us? – David Heffernan Mar 08 '18 at 19:35
  • @DavidHeffernan OK, an another question is at the end of my post, please read it carefully. – Javad Bayat Mar 08 '18 at 19:40
  • @DavidHeffernan Plus, ESET Smart Security has a self-defense module. It should not be closed easily! – Javad Bayat Mar 08 '18 at 19:44
  • It's just the GUI. The anti malware is still running. Wouldn't it just be easier to terminate the process? – David Heffernan Mar 08 '18 at 19:47
  • By "anti-malware", you mean "ekrn.exe"? – Javad Bayat Mar 08 '18 at 19:55
  • 1
    Honestly, if this program didn't want to be closed, why would it offer a dialog allowing you to close it? – David Heffernan Mar 08 '18 at 20:02
  • @DavidHeffernan All right. I know what you mean. ESET Smart Security software has two processes: egui.exe and ekrn.exe. egui.exe is not very important. As you said, it's just the GUI. If I wanted to create a real malware software, I would write a program which terminates ekrn.exe. ekrn.exe is active and detects viruses even if egui.exe is terminated, unless the user has disabled real-time protection. Anyway, answer my second question, "Why doesn't my callback function run?" – Javad Bayat Mar 08 '18 at 20:26
  • That's a real question. It would help if you removed all the rest and asked just that. – David Heffernan Mar 08 '18 at 20:31
  • OK, I'm sorry. I'll edit my question as soon as possible. But it takes a few minutes. Please be patient... – Javad Bayat Mar 08 '18 at 20:39
  • @Ron: Shutting down the client GUI of an anti-malware suite is hardly harmful, a bug, and even less a security vulnerability, or an exploit. At worst, it (temporarily) cuts off the user from controlling their anti-malware software. If they want that, there are easier ways to do it, e.g. a right-click in Task Manager. – IInspectable Mar 08 '18 at 21:21
  • @IInspectable Maybe you're right. However, the users can't right-click on the process of egui.exe in Task Manager and then click on End Process from the context-menu, in order to terminate egui.exe; if they do so, they get an "Access is denied" error. The users can hold down shift and right click on the title bar of ESET Smart Security window, and then click on Close Window to close ESET Smart Security. Programmatically shutting down the client GUI may be an exploit. But I don't really want to give virus makers ideas about how to programmatically get rid of egui.exe. – Javad Bayat Mar 08 '18 at 22:28

1 Answers1

4

The documentation for SendMessageCallback tells you, why your callback will not ever be called:

If the target window belongs to a different thread from the caller, then the callback function is called only when the thread that called SendMessageCallback also calls GetMessage, PeekMessage, or WaitMessage.

The target window obviously belongs to a different thread, because it runs in a different process. Your code doesn't perform any message retrieval. No callback.

IInspectable
  • 46,945
  • 8
  • 85
  • 181