ANTICHAT

ANTICHAT (https://forum.antichat.xyz/index.php)
-   Защита ОС: вирусы, антивирусы, файрволы. (https://forum.antichat.xyz/forumdisplay.php?f=80)
-   -   Дефейс окон в Windows (https://forum.antichat.xyz/showthread.php?t=591959)

Marylin 30.12.2025 13:24

Оконная подсистема Win хранит в себе много тайн и загадок, некоторые из которых мутируют в огромные дыры безопасности. Её можно сравнить с "Марианской впадиной", которая начиная с пользовательского пространства в лице библиотеки User32.dll, и уходит глубоко в ядро к драйверу win32k.sys. На системах WinXP передачей окон юзеру занималась подсистема клиент-сервера csrss.exe, а начиная с Win7 появился диспетчер "Desktop Window Manager" dwm.ехе, для реализации графического интерфейса "Windows Aero" с такими фишками как прозрачность, различные 3D-эффекты и прочее. В данной статье мы рассмотрим методы управления чужими окнами - по сути ничего новаторского, но полезно знать.

1. Модель оконных сообщений
2. Практика - дефейс калькулятора
3. Проблемы сообщений таймера
4. Постскриптум

1. Модель оконных сообщений

GUI составляющая Win полностью построена на оконных сообщениях "Window Message", которые отождествляются константами с префиксом WM_xx. У любой программы с граф.интерфейсом имеется собственная процедура обратного вызова
Код:

WindowProc()
- она отвечает за обработку всех сообщений, отправляемых системой окну. Процедура в бесконечном цикле должна идентифицировать поступающие мессаги, и выполнять с ними все необходимые действия. Прототип
Код:

WindowProc()
выглядит так (4 указанных ниже аргумента передаёт графическая подсистема):

C-подобный:


Код:

CALLBACK
WindowProc
(
HWND    hWnd
,
UINT    uMsg
,
WPARAM  wParam
,
LPARAM  lParam
)
--
--
--
--
--
--
--
--
--
--
-
hWnd
=
дескриптор окна
,
кому адресовано сообщение
uMsg
=
сама мессага WM_xx
wParam
=
основной параметр сообщения
lParam
=
доп
.
параметр
(
зависит от типа WM_xx
)

В результате различных событий в системе генерируются сотни сообщений (в доках зарезервированы первые 400h констант), а из этого пула приложение юзера выбирает и обрабатывает только нужные себе. Но архитектура окон построена так, что если есть сообщение, оно обязательно должно быть кем-то обработано, иначе хаос и бардак. Чтобы гарантировать обработку буквально всех поступающих сообщений, Win предоставляет дефолтную свою процедуру
Код:

DefWindowProc()
для обработки мессаг, которые наш юзерский колбек пропустил между ног. Как правило в системную процедуру мы передаём управление в конце своей
Код:

WindowProc()
, чтобы необработанные сообщения могли быть переданы в процедуру по умолчанию.

Разрабатывая подсистему управления окнами майки преследовали основную цель - надёжность подсистемы, чтобы ни один поток не мог нарушить работу других потоков. В дефолте передача сообщения в окно всегда осуществляется синхронно чз
Код:

SendMessage()
: отправитель ждёт, пока окно получателя не обработает его сообщение и не вернёт ответ. Но если на обработку потребуется длительное время (или окно адресата вообще зависнет), напрочь заморозится и отправитель, а значит такая ОС не вправе называться надёжной. Так появились асинхронные сообщения
Код:

PostMessage()
, когда отправитель послав мессагу не дожидается ответа, и сразу продолжает заниматься своими делами.

Каждому потоку с графическим интерфейсом система выделяет свою собственную очередь сообщений "Message Queue", которая независит от других потоков. Как результат потоки выполняются в такой среде, где они считают себя единственными. Изначально, создавая какой-либо поток система предполагает, что он не будет работать с графическим интерфейсом - это позволяет уменьшить объём выделяемых ему системных ресурсов. Но, как только поток обратится к той или иной GUI-функции (например создание окна), система на автомате выделит ему нужные ресурсы для поддержки оконных сообщений. Эти ресурсы заворачиваются в структуру
Код:

THREADINFO
драйвера win32k.sys, которая сопоставляется с данным потоком.

Код:

THREADINFO
- это фундамент всей подсистемы передачи сообщений. Каждый поток имеет не одну, а целых три очереди - указатели на них прописываются именно в данной структуре. Первая - это очередь синхронных Sent-мессаг, куда выстроившись в ряд оседают сообщения от функции
Код:

SendMessage()
. Вторая очередь для асинхронных
Код:

PostMessage()
, и третья для ответных сообщений "Reply-Message Queue" (реализована как ReceiveList). Помимо того в структуре имеется и переменная под флаги пробуждения потока "Wake Flags". Фрагмент этой структуры с перечисленными выше полями представлен ниже:

Код:


Код:

0: kd> dt win32k!tagTHREADINFO
.....
  +0x198  TIF_flags      : Uint4B
  +0x1a0  pstrAppName    : Ptr64 _UNICODE_STRING
  +0x1a8  psmsSent        : Ptr64 tagSMS
  +0x1b0  psmsCurrent    : Ptr64 tagSMS
  +0x1b8  psmsReceiveList : Ptr64 tagSMS
  +0x1d0  exitCode        : Int4B
.....

Если поток вызывает синхронную
Код:

SendMessage()
для посылки сообщения своему окну, то функция просто обращается к своей оконной процедуре
Код:

WindowProc()
, и в ответ получает некое значение (зависит от типа мессаги). Но если поток посылает сообщение чужому окну, всё значительно усложняется.

Во-первых, переданное сообщение присоединяется к очереди приёмника, и для него устанавливается флаг
Код:

QS_SENDMESSAGE
. Во-вторых, если поток приёмника в данный момент чем-то занят, система не прервёт его работу для немедленной обработки поступившего сообщения. Если-же поток свободен и сообщений в его очереди больше нет, флаг
Код:

QS_SENDMESSAGE
сбрасывается. Пока приёмник обрабатывает мессагу, отправивший
Код:

SendMessage()
поток простаивает, ожидая ответа в своей очереди ответных сообщений Reply. С этого момента поток-отправитель просыпается и возобновляет работу в обычном режиме.

Поскольку Win обрабатывает межпоточные мессаги описанным выше образом, в ожидании ответа наш поток может заснуть навсегда. Задумаемся, что произойдёт с вызвавшим
Код:

SendMessage()
потоком, если по каким-либо причинам получатель войдёт в бесконечный цикл? Значит-ли это, что ошибка в одном приложении уронит другое? Ответ - да, и это является дырой в подсистеме безопасности! Выход из этой непростой ситуации один - использовать безопасные функции типа
Код:

SendMessageTimeout()
, последний аргумент которой ограничивает время ожидания ответа. Можно использовать асинхронную
Код:

PostMessage()
, но тогда мы не узнаем, обработал получатель наш запрос или нет, что не всегда удобно.

2. Практика - дефейс калькулятора

Чтобы дефейснуть любую форточку мастдая много ума не надо, хотя на системах Win7+ имеется одно ограничение - наш уровень доверенности "Integrity Level" должен быть не меньше жертвы. Узнать свои полномочия можно в программе "Process Hacker", а для их повышения достаточно зайти в систему под админом. На скрине ниже я атакую текстовый редактор "AkelPad" своим софтом WinDeface.exe, и как видно в столбце "Integrity" наши уровни совпадают, хотя до System я уже не дотянусь:

Рассмотрим такой пример, где я нахожу окно калькулятора функцией
Код:

FindWindow()
, и на всю его рабочую зону вывожу произвольную надпись, не забыв изменить и заголовок окна с "Калькулятор" на "Happy New Year!". Если для смены заголовка окна достаточно через
Код:

SendMessage()
отправить потоку калькулятора сообщение
Код:

WM_SETTEXT
, то с выводом самой надписи не всё так просто.

Здесь нужно задействовать функции из либы gdi32.dll, чтобы сначала рассчитать позицию для вывода в клиентскую область окна
Код:

GetClientRect()
, далее создать шрифт требуемого размера
Код:

CreateFont()
, активировать его посредством
Код:

SelectObject()
, задать цвет и атрибут прозрачности
Код:

SetTextColor()
+
Код:

SetBkMode()
, и только потом напечатать в окно
Код:

TextOut()
. Более того, нужно предварительно захватить контекст устройства вывода "Device Context" через
Код:

GetDC()
и на выходе освободить его
Код:

ReleaseDC()
. Вот код и что в итоге из этого получилось:

C-подобный:


Код:

format  pe64 console
include
'win64ax.inc'
entry    start
;
//----------
.
data
rect    RECT
hWnd    dd
0
dc      dd
0
font    dq
0
;
//sTxt    db  'Hello World!',0
;
//sTxt    db  'Hackerlab!',0
sTxt    db
'Codeby.net'
,
0
txtLen
=
$
-
sTxt
;
//----------
section
'.code'
code readable executable
start
:
push    rbp

        invoke  FindWindow
,
0
,

or      eax
,
eax
        jnz    @ok
        cinvoke  printf
,

jmp    @exit

@ok
:
mov
[
hWnd
]
,
eax
        invoke  GetClientRect
,
eax
,
rect
        invoke  GetDC
,
[
hWnd
]
mov
[
dc
]
,
eax
;
// Рассчитываем размер шрифта по размеру окна
mov    ecx
,
[
rect
.
bottom
]
shr    ecx
,
1
mov    eax
,
[
rect
.
right
]
mov    ebx
,
txtLen
        xor    edx
,
edx
        div    ebx
        xchg    eax
,
edx
        invoke  CreateFont
,
rcx
,
rdx
,
0
,
0
,
600
,
0
,
0
,
0
,
\
                            ANSI_CHARSET
,
0
,
0
,
0
,
\
                            FF_MODERN
,

mov
[
font
]
,
rax

        invoke  SelectObject
,
[
dc
]
,
[
font
]
invoke  SetBkMode
,
[
dc
]
,
TRANSPARENT
;
// Цвет текста будем выбирать рандомом
rdtsc
        and      eax
,
0xffffff
invoke  SetTextColor
,
[
dc
]
,
eax
        mov      eax
,
[
rect
.
bottom
]
shr      eax
,
3
invoke  TextOut
,
[
dc
]
,
0
,
eax
,
sTxt
,
txtLen
;
// Сменить заголовок окна, и освободить контекст девайса
invoke  SendMessage
,
[
hWnd
]
,
WM_SETTEXT
,
0
,

invoke  ReleaseDC
,
[
hWnd
]
,
[
dc
]
cinvoke  printf
,

,
[
hWnd
]
@exit
:
cinvoke  _getch
        cinvoke  exit
,
0
;
//----------
section
'.idata'
import data readable
library  msvcrt
,
'msvcrt.dll'
,
kernel32
,
'kernel32.dll'
,
\
        user32
,
'user32.dll'
,
gdi32
,
'gdi32.dll'
include
'api\msvcrt.inc'
include
'api\kernel32.inc'
include
'api\user32.inc'
include
'api\gdi32.inc'

Если изменить аргумент
Код:

FindWindow()
, можно аналогичным образом дефейснуть любое окно, как в примере ниже "AkelPad". Отметим, что это просто безобидный мод активного на данный момент окна, чтобы навести на юзера жути - при следующем перезапуске "Calc" и "AkelPad" всё восстанавливается в дефолт, и никаких надписей уже не будет. Это не фотошоп, и всё реально выглядит так:

3. Проблемы сообщений таймера

Среди всех оконных сообщений притаилось в засаде одно из интересных (и в то-же время опасных) мессаг - это
Код:

WM_TIMER=0113h
с его собратом
Код:

WM_SYSTIMER=0118h
. Согласно описанию на MSDN, они имеют привязанные функции обратного вызова "Callback". То-есть мы определяем в своём коде процедуру с любым содержимым (например шелл-код), и по истечении указанного нами времени в таймере, она получает управление. Красота!

Только вот всю малину портит то, что начиная с Висты инженеры ограничили возможности этого сообщения - теперь его можно посылать исключительно своему окну, и функция терпит крах, если мы отправляем мессагу таймера чужой форточке. При хороших обстоятельствах
Код:

WM_TIMER
могла-бы послужить отличной альтернативой традиционным способам внедрения шелл-кодов в чужой процесс, через давно уже палённую
Код:

CreateRemoteThread()
с последующим
Код:

WriteProcessMemoryEx()
. Например такой бесхитростный код наглым образом забирает управление у "Калькулятора", и передает его по адресу
Код:

0x004060Е8
.

C-подобный:


Код:

invoke FindWindow
,
0
,

invoke SendMessage
,
eax
,
WM_TIMER
,
0
,
0x4060E8

Запреты такого рода только разжигают интерес хакеров, и конечно-же они нашли выход из этого положения. По сути запреты и создаются, чтобы их нарушать. Если мыслить логически, то раз уж есть ограничение, значит где-то должна быть проверка типа сообщения, и если обнаружится, что это таймер, то секьюрити тут-же должен проводить нас на выход. И такая проверка действительно существует, причём ни где-то в нёдрах системы, а прямо у нас под носом в библиотеке user32.dll, которая проецируется системой в адресное пространство нашего процесса.

Как уже упоминалось выше, все мессаги из оконной процедуры
Код:

WindowProc()
диспетчеризуются в user32.dll, где и происходит их фактическая обработка внутренней функцией
Код:

DispatchMessageWorker()
. На скрине ниже фрагмент из листинга этой функции, с тестом мессаги на
Код:

WM_TIMER
. Здесь достаточно изменить адрес перехода с
Код:

0x7DC6792D
на адрес чуть ниже
Код:

0x7DC67740
(у вас он может быть другим) и всё.. таймер "пойдёт танцевать в пьяную", захватывая в свои объятия абсолютно любые окна:


4. Постскриптум

Подсистема графических окон Win дырява как сито, и что особенно важно, инженеры ничего не могут с этим поделать. Для ускорения отрисовки окон, компонент ядра в лице драйвера win32k.sys отображает большую часть своих структур в пространство пользователя, от куда мы можем их без проблем читать, но не модифицировать. Однако для разведки и этого достаточно, а дальше уже по обстоятельствам. В промапленной в наш процесс либе user32.dll есть куча уязвимых мест, и нам остаётся лишь использовать их в своих корыстных (и не очень) целях. В скрепку кладу исполняемый файл для тестов (запускать при активном калькуляторе), всех с наступающим, пока!


Время: 08:20