Jump to content

How EAC detects cheat windows through EnumWindows and how to bypass this


_xvi

579 views

EAC (Easy Anti-Cheat) is software used to protect online games from cheaters. One of its functions is to detect processes and windows related to cheats. In this article, we will look at how EAC uses the EnumWindows feature to detect cheat windows.

The EnumWindows function is part of the Windows API library and is used to list all windows on the system. When EAC starts up, it starts enumerating all the top windows on the system using this function. However, EAC does not just enumerate all windows, it looks for certain window characteristics associated with cheats.

What exactly are the characteristics EAC looks for in windows? Some of them may be:

  • Window name: some cheats have unique window names that can be used to detect them.
  • Window size: if a cheat window has a fixed size, EAC can check all windows for this size.
  • Window styles: e.g. the window skips clicks or is rendered over the screen.
  • Process ID: if EAC already knows the process ID of the cheat, it can check all windows to see if they belong to the same process.

If EAC finds a window that matches one of these characteristics then it can conclude that a cheat is running in the system. In this case the EAC can "mark" the player or even block access to the online game.

The EnumWindows feature is a very powerful tool for detecting cheat windows for EAC. However, cheat developers can try to bypass this detection using methods such as changing the window name or size, injecting it into a legit(signed) process, remove window from the NtUserBuildHwndList and similar functions.

  • In Windows, a signed legit process refers to a process that is digitally signed with a valid certificate issued by a trusted certificate authority. The digital signature provides a secure way to verify the authenticity and integrity of the software before it is executed.
  • NtUserBuildHwndList is a function in the Windows operating system that builds a list of all top-level windows that belong to the calling thread's desktop. This function is part of the Win32 API and is used by applications to iterate through all the windows that are currently open on the desktop.

Pseudo code by chatgpt >3:

void __fastcall enum_windows_callbacks(__int64 context) 
{
    struct_v10* v10 = v1;
    __int64 window1 = context;
    __int64 window2 = getGetForegroundWindow_pid(context, v2);
    
    // Check if windows are valid
    if (!window2 || window2 == window1) 
        return;
    
    // Get window styles and check if window is visible
    eAc_Get_api_callback(&unk_D8CB8, Get_GetWindowLongPtrW, &GetWindowLongPtrW);
    __int64 window_exstyle = GetWindowLongPtrW ? GetWindowLongPtrW(window1, 0xFFFFFFECi64) : 0i64;
    
    if (_bittest64(&window_exstyle, 0x13u)) {
        unsigned int (__fastcall* IsWindowVisible)(__int64);
        __int64 user32 = get_user32();
        IsWindowVisible = (unsigned int(__fastcall*)(__int64))get_exp(user32, &unk_C9288, 0i64);
        
        if (!IsWindowVisible || !IsWindowVisible(window1))
            return;
        
        // Get window dimensions and check if it's within foreground window
        tagRECT window_rect, window_Rect;
        eAc_Get_api_callback(&unk_D8CC0, Get_GetWindowRect, &GetWindowRect);
        
        if (!GetWindowRect || !(unsigned int)GetWindowRect(window1, &window_rect)) 
            return;
        
        if (!GetWindowRect || !(unsigned int)GetWindowRect(window2, &window_Rect)) 
            return;
        
        int width = window_rect.right - window_rect.left;
        int height = window_rect.bottom - window_rect.top;
        
        if (width <= 0 || height <= 0) 
            return;
        
        if (window_rect.left >= window_Rect.right ||
            window_rect.right <= window_Rect.left ||
            window_rect.top >= window_Rect.bottom ||
            window_rect.bottom <= window_Rect.top)
            return;
        
        // Get window title and class name
        __int128 WindowText, ClassName;
        sub_59E58(&WindowText, 0i64, 148i64);
        
        if ((signed int)imp_GetWindowTextW(window1, &v51, 0x40i64) > 0)
            cus_memcpy(&WindowText, 0x40i64, &v51);
            
        if ((signed int)imp_GetClassNameW(window1, &v51, 0x40i64) > 0)
            cus_memcpy(&ClassName, 0x40i64, &v51);
            
        // Get window thread/process IDs and styles
        eAc_Get_api_callback(&unk_D8CB8, Get_GetWindowLongPtrW, &GetWindowLongPtrW);
        int window_style = GetWindowLongPtrW ? GetWindowLongPtrW(window1, 0xFFFFFFF0i64) : 0;
        int window_exstyle_1 = window_exstyle;
        int pid = 0, tid = imp_GetWindowThreadProcessId(window1, &pid);
        int pid_1 = 0, tid_2 = tid_1 = imp_GetWindowThreadProcessId(window1, &pid_1);
        tagRECT window_rect_1 = window_rect;
        
        // Check window properties
        if ((unsigned int)eAc_Crc_or_Xor(&WindowText, get_bytes_Count(&WindowText), 0i64) != 0x3BC41165 ||
            (unsigned int)eAc_Crc_or_Xor(&ClassName, get_bytes_Count(&ClassName), 0i64) != 0x3BC41165 ||
            window_style != 2483027968 || window_exstyle_1 != 0x8280028 ||
            pid != pid_1 || tid_2 != tid_1 ||
            (unsigned int)cmp_window_size((unsigned __int8*)&window_Rect, (unsigned __int8*)&window_rect, 16i64)) {
            return;
        }
        
        // Store window data in struct and increment result count
        __int64 v29 = v10->qword10;
        __int64 v30 = *(_QWORD*)(v29 + 8);
        
        if ((_QWORD*)(v29 + 0x10) == v30)
            sub_AB2E4(v29, v30, &WindowText);
        else {
            *(_OWORD*)v30 = WindowText;
            *(_OWORD*)(v30 + 16) = ClassName;
            *(_DWORD*)(v30 + 48) = pid;
            *(_DWORD*)(v30 + 52) = tid;
            *(_QWORD*)(v30 + 56) = window_style;
            *(_QWORD*)(v30 + 64) = window_exstyle_1;
            *(_QWORD*)(v30 + 72) = width;
            *(_QWORD*)(v30 + 80) = height;
        
        	v10->qword10 += 88i64;
        	++v10->dword8;
    	}
}

Sources:

https://blog.adeltax.com/window-z-order-in-windows-10/

https://blog.renjing.wang/702.html

https://www.unknowncheats.me/forum/anti-cheat-bypass/581931-fullscreen-overlays-window-bands.html

https://www.unknowncheats.me/forum/anti-cheat-bypass/306271-ring-1-bypass.html

  • Like 1

0 Comments


Recommended Comments

There are no comments to display.

Guest
Add a comment...

×   Pasted as rich text.   Paste as plain text instead

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   Your previous content has been restored.   Clear editor

×   You cannot paste images directly. Upload or insert images from URL.

×
×
  • Create New...