ethical.blue Magazine

// Cybersecurity clarified.

Wykrywanie okna określonego narzędzia w C++, C# i Asemblerze x64

28.03.2022   Dawid Farbaniec
...
Ten tekst zawiera opis prostej i znanej metody przeciwko inżynierii odwrotnej kodu (RCE). Jej zasada działania polega na wykrywaniu czy okno określonego narzędzia (np. debuggera, disassemblera, piaskownicy, hex edytora itp.) jest uruchomione. W przypadku wykrycia danego narzędzia analizowana aplikacja może zmienić swój przepływ wykonania, czyli działać inaczej niż zwykle. Można zaprogramować nieoczekiwane zamknięcie aplikacji (z komunikatem ostrzegawczym lub bez) czy po prostu powodować jej błędne działanie w celu utrudnienia analizy wstecznej jej kodu. Metoda ta nie jest żadną nowością, jednak chciałbym przedstawić ją na gotowych przykładach w trzech językach programowania (C++, C#.NET oraz Asembler x64), aby początkujący programiści, którzy jej potrzebują mieli mocny punkt zaczepienia.



Lista kroków:
- Utworzenie funkcji zwrotnej EnumWindowsProc, która porównuje pobrany tytuł (GetWindowText) bieżącego okna z szukanym napisem.
- Wywołanie funkcji EnumDesktopWindows iterującej po oknach bieżącego pulpitu (podając jej adres funkcji zwrotnej typu callback).
- W przypadku wykrycia okna pulpitu o określonym tytule wyświetlany jest komunikat.

Zachowanie po wykryciu okna narzędzia należy zaprogramować wedle uznania.

Wykrywanie okna programu w Visual C++

/*

Coded in Visual C++ by ethical.blue

PL: Program sprawdza czy okno określonego
narzędzia jest uruchomione

EN: Application iterates through all
desktop windows to check specified window title
*/

#include <Windows.h>
#include <string>

BOOL CALLBACK EnumWindowsProc(HWND hwnd, LPARAM lParam)
{
    WCHAR titleText[255] = { '\0' };
    std::wstring title;

    if (GetWindowTextLength(hwnd) == 0)
    return TRUE;

    GetWindowText(hwnd, titleText, 255-1);

    title = titleText;

    if (title.find((LPCWSTR)lParam) == 0)
    {
        MessageBox(0, (std::wstring(L"Znaleziono okno o nazwie: ") + title).c_str(), L"Informacja", MB_ICONINFORMATION);
        return FALSE;
    }

    return TRUE;
}

int wmain()
{
    std::wstring targetTitle = L"Kalkulator";

    EnumDesktopWindows(0, &EnumWindowsProc, (LPARAM)targetTitle.c_str());

    return EXIT_SUCCESS;
}

Wykrywanie okna programu w Visual C#.NET

/*

Coded in C#.NET by ethical.blue

PL: Program sprawdza czy okno określonego
narzędzia jest uruchomione

EN: Application iterates through all
desktop windows to check specified window title
*/

using System;
using System.Text;
using System.Runtime.InteropServices;

namespace ConsoleApp1
{
    class Program
    {
        [DllImport("user32.dll")]
        static extern bool EnumDesktopWindows(IntPtr hDesktop, EnumDesktopWindowsDelegate lpfn, IntPtr lParam);

        [DllImport("user32.dll", EntryPoint = "GetWindowText", ExactSpelling = false, CharSet = CharSet.Auto)]
        static extern int GetWindowText(IntPtr hWnd, StringBuilder lpWindowText, int nMaxCount);

        [DllImport("user32.dll", CharSet = CharSet.Unicode)]
        static extern int MessageBox(int hWnd, string text, string caption, uint type);

        const int MB_ICONINFORMATION = 0x00000040;

        delegate bool EnumDesktopWindowsDelegate(IntPtr hWnd, int lParam);

        static void Main(string[] args)
        {
            string targetTitle = "Kalkulator";

            EnumDesktopWindowsDelegate EnumWindowsProc = (IntPtr hWnd, int lParam) =>
            {
                var title = new StringBuilder(255);
                GetWindowText(hWnd, title, title.Capacity - 1);

                if (string.IsNullOrEmpty(title.ToString()))
                    return true;

                if (title.ToString().Contains(targetTitle))
                {
                    MessageBox(0, "Znaleziono okno o nazwie: " + title, "Informacja", MB_ICONINFORMATION);
                    return false;
                }

                return true;
            };

            EnumDesktopWindows(IntPtr.Zero, EnumWindowsProc, IntPtr.Zero);
        }
    }
}

Wykrywanie okna programu w Asemblerze x64 (MASM)

;---------------------------------------------------------+

; Coded in Win64asm (MASM x64 / ML64.EXE) by ethical.blue |
;                                                         |
; PL: Program sprawdza czy okno określonego               |
;       narzędzia jest uruchomione (Unicode!)             |
;                                                         |
; EN: Application iterates through all                    |
;       desktop windows to check specified window title   |
;---------------------------------------------------------+

extrn EnumDesktopWindows : proc
extrn GetWindowTextW : proc
extrn GetWindowTextLengthW : proc
extrn MessageBoxW : proc
extrn lstrcmpW : proc
extrn ExitProcess : proc

.data
;unicode string "Kalkulator"
szTargetTitle dw "K","a","l","k","u","l","a","t","o","r", 0

;bufor na aktualnie pobrany tytuł
szCurrentWindow dw 4096 dup(0)

;unicode string "Znaleziono!"
szMessageText dw "Z","n","a","l","e","z","i","o","n","o","!",0

.code

;funkcja zwrotna (callback) pobierająca uchwyty okien pulpitu

EnumWindowsProc proc hWnd : qword, lparam : qword

;pobierz tytuł bieżącego okna
sub rsp, 30h
xor r9, r9
mov r8, 255
mov rdx, offset szCurrentWindow
call GetWindowTextW
add rsp, 30h

;jeśli napis jest pusty zwróć TRUE (szukaj dalej)
test rax, rax
jz _true

;porównaj bieżący tytuł okna z szukanym napisem
sub rsp, 30h
xor r9, r9
xor r8, r8
mov rdx, offset szTargetTitle
mov rcx, offset szCurrentWindow
call lstrcmpW
add rsp, 30h

;jeśli to nie ten tytuł to zwróć TRUE (szukaj dalej)
test rax, rax
jnz _true

;w przeciwnym wypadku wyświetl komunikat, że znaleziono :)
sub rsp, 30h
xor r9, r9
mov r8, offset szMessageText
mov rdx, offset szMessageText
xor rcx, rcx
call MessageBoxW
add rsp, 30h
jmp _false

_true:
mov rax, 01h
ret

_false:
mov rax, 0h
ret

EnumWindowsProc endp

;funkcja główna
Main proc

;wywołanie funkcji wyliczającej okna bieżącego pulpitu
sub rsp, 28h
xor r9, r9
mov r8, offset szTargetTitle
mov rdx, EnumWindowsProc
xor rcx, rcx
call EnumDesktopWindows
add rsp, 28h

;zakończ program
sub rsp, 28h
xor rcx, rcx
call ExitProcess
Main endp

end

Podsumowanie

Ta technika nigdy nie powinna być stosowana sama. Przy prawidłowym użyciu tej metody można pomyślnie utrudnić analizę próbki malware przez niebieski zespół.

Wykaz literatury

//digged from old code archives