РАЗБОР
Статья
Древовидные списки "TreeView" - от иерархии до уязвимостей
[ обложка статьи ]
Режим чтения
Элемент управления TreeView - один из самых широко используемых компонентов графического интерфейса Win. Он знаком каждому пользователю по левой панели Проводника, где папки и файлы выстроены в иерархическую структуру. Но TreeView это не просто удобная навигация - это мощный инструмент для визуализации любых данных, имеющих отношение "родитель-потомок", от организационной структуры компании, и до синтаксического дерева софта. Несмотря на кажущуюся простоту, работа с TreeView сопряжена с рядом технических нюансов, и даже ..исторических уязвимостей, которые должен знать каждый разработчик.
Оглавление:
1. Что такое TreeView, и где он применяется
TV представляет данные в виде иерархического списка, где каждый элемент может содержать дочерние элементы, которые можно сворачивать и разворачивать, что позволяет эффективно работать с большими объёмами структурированной информации.
Области применения:
2. Создание элемента управления
В WinAPI элемент создаётся стандартным вызовом
При создании нужно указать стили внешнего вида: TVS_HASBUTTONS (кнопки сворачивания), TVS_HASLINES (соединительные линии), TVS_LINESATROOT (линии на корневом уровне), TVS_DISABLEDRAGDROP (запрет на перетаскивание drag-and-drop).
2.1. Добавление элементов и частая ошибка новичков
Для добавления элемента используется сообщение TVM_INSERTITEM, которому передаётся структура
Для создания иерархии:
Вот пример простого окна с использованием элемента TreeView - всего 1 заголовок/родитель, который раскрывается при нажатии на плюс(+), и внутри в цикле можно добавить хоть сотню детей.
2.2. Уведомления и взаимодействие
Элемент TreeView активно общается с родительским окном через механизм WM_NOTIFY. Родительское окно получает уведомления о следующих ключевых событиях внутри элемента:
Для программного управления используются сообщения:
2.3. Механизм обратного вызова "Callback"
Одна из самых интересных возможностей TreeView - ленивая загрузка данных через callback-механизм. Вместо немедленного предоставления текста, иконки или информации о наличии потомков, приложение может указать специальные значения ниже. В этом случае элемент отправляет родителю уведомление TVN_GETDISPINFO с запросом информации. Это позволяет строить огромные деревья без загрузки всех данных сразу в память (отложенная загрузка):
Однако здесь есть важный нюанс. Если вы подставляете строку вместо копирования в предоставленный буфер, эта строка не может быть изменена до тех пор, пока не придёт как минимум два доп.уведомления TVN_GETDISPINFO, или элемент не будет удалён. Нарушение этого правила приведёт к ошибкам доступа к памяти AccessViolation.
3. Историческая уязвимость: CVE-2012-0158
Нельзя говорить об элементе TreeView, не упомянув самую известную уязвимость. CVE-2012-0158 - критическая лазейка для удалённого выполнения кода, затронувшая ActiveX-компоненты ListView и TreeView в библиотеке mscomctl.ocx. Она возникала из-за некорректной обработки входных данных и управления памятью. Злоумышленник мог создать сайт, доку Office или RTF-файл, который при открытии вызывал сбой системы, и приводил к выполнению произвольного кода.
Проблема затронула широкий спектр продуктов Microsoft:
Уязвимость активно эксплуатировалась в дикой природе в апреле 2012 года. Согласно технической классификации, уязвимость относится к CWE-121: Heap-based Buffer Overflow (переполнение буфера в куче). CVE-2012-0158 это напоминание о том, что даже проверенные компоненты системы могут содержать критические уязвимости.
3.1. Защитные меры
Конечно-же Microsoft уже давно заткнула дыру своей заплаткой MS12-027, но проблема в том, что не все юзеры с завидной постоянностью обновляются. Если вы относитесь к их числу, вот несколько советов, как проверить свой периметр сподручными средствами.
Все обновления, и бюллетень MS12-027 в том числе, поставляется майкаим в виде т.н. KB или "Knowledge Base". Если зайти в апплет "Обновления Windows" комбинацией клавиш
Вы можете найти её или в окне апплета, или просто ввести в ком.строке:
Однако если обновление MS12-027 шло в составе пакета для Office или стороннего софта (например, Visual Studio), утилита
С другой стороны, все эти обновы просто заменяют уязвимый файл MSCOMCTL.OCX версии 6.01.9545 (лежит в папке win\system32), как минимум на v6.01.9833 или новее. Так-что достаточно проверить его версию в свойствах, и если сборка выше-равна 9833, то можете спать спокойно.
3.2. Что такое ActiveX компоненты
Для тех кто в танке напомним некоторые моменты.
ActiveX - это древняя технология, которая позволяла сайтам и программам запускать мини-приложения прямо внутри себя для расширения возможностей. Она работала как конструктор из готовых деталей. Вместо того чтобы писать сложные функции с нуля, разработчики вставляли в документ или веб-пагу готовый маленький блок кода - это и есть элемент ActiveX. Благодаря этой фишке на сайтах могли работать интерактивные игры, сложные анимации, проигрыватели видео/аудио, инструменты для работы с банками, камерами видео-наблюдения и т.д.
Но у технологии был огромный минус - безопасность! Поскольку элементы ActiveX получали прямой доступ к ОС, это было открытой форточкой для всякого рода малвари. Поэтому сейчас все современные браузеры (Chrome, Edge, Firefox, Safari) полностью отказались от поддержки ActiveX, а сами майки заменили технологию на более безопасные стандарты, например HTML5, JavaScript и расширения. Сегодня элементы ActiveX изредка можно встретить только внутри старых корпоративных программ, закрытых систем, или в редких настройках макросов для Excel.
А проблема заключалась в том, как код внутри MSCOMCTL.OCX обрабатывал входящие данные. Разбор уязвимости показывает, что в одной из функций при вызове происходит выделение небольшого (~20 байт) буфера в стеке. Однако из-за ошибки в логике проверки границ, код мог продолжить запись данных так, чтобы из этого буфера торчал специальным образом сформированный хвост, который правил адрес возврата на нужный участок кода. Это давало возможность перехватить управление выполнением программы и запустить вредоносный код.
Таким образом, уязвимость была не в самом факте использования элемента управления TreeView, а во внутренней реализации этого ActiveX-компонента в конкретной версии либы MSCOMCTL.OCX.
А как на счёт примера выше с окном участников группы Rammstein? Даже если и была-бы на машине уязвимая mscomctl.ocx, программного кода WinAPI это никак не касалось-бы, поскольку хоть они и одного поля ягоды, но находятся на разных уровнях абстракций, и никак не конфликтуют между собой. TreeView из mscomctl.ocx - это ActiveX-оболочка, созданная Microsoft в основном для VB6 и VBA. Она под катом использует стандартный (и глубоко зарытый) элемент управления TreeView из системной comctl32.dll, а не наоборот.
4. Заключение
TreeView - это мощный и гибкий инструмент, который остаётся актуальным на протяжении десятилетий. Он предоставляет богатый API для управления иерархическими данными, поддерживает ленивую загрузку и drag-and-drop. Однако, как и любой сложный компонент, он требует внимательного отношения к деталям: от установки флага TVIF_TEXT до понимания исторических уязвимостей вроде CVE-2012-0158. Грамотное использование TreeView - это баланс между функциональностью, производительностью и безопасностью.
Оглавление:
1. Область применения
2. Создание элемента управления
3. Историческая уязвимость CVE-2012-0158
4. Заключение
1. Что такое TreeView, и где он применяется
TV представляет данные в виде иерархического списка, где каждый элемент может содержать дочерние элементы, которые можно сворачивать и разворачивать, что позволяет эффективно работать с большими объёмами структурированной информации.
Области применения:
1. Файловые менеджеры - классический пример "проводник Win"
2. Системные утилиты - просмотр процессов, потоков, модулей
3. Конфигурационные панели - настройки с иерархической организацией
4. Навигация по документам - оглавление с вложенными разделами
2. Создание элемента управления
В WinAPI элемент создаётся стандартным вызовом
CreateWindowEx() с указанием класса окна WC_TREEVIEW. Критически важно перед этим вызвать InitCommonControls(), иначе библиотека общих элементов управления comctl32.dll не загрузится, и план потерпит фиаско. Вот базовая последовательность действий:• Вызвать InitCommonControls() для загрузки библиотеки
• Создать элемент через CreateWindowEx() с классом WC_TREEVIEW
• Опционально отправить WM_SETFONT для настройки шрифта
• Добавить элементы через сообщение TVM_INSERTITEM
При создании нужно указать стили внешнего вида: TVS_HASBUTTONS (кнопки сворачивания), TVS_HASLINES (соединительные линии), TVS_LINESATROOT (линии на корневом уровне), TVS_DISABLEDRAGDROP (запрет на перетаскивание drag-and-drop).
2.1. Добавление элементов и частая ошибка новичков
Для добавления элемента используется сообщение TVM_INSERTITEM, которому передаётся структура
TV_INSERTSTRUCT. Внутри неё находится TV_ITEM с полями для текста, изображения, состояния и пользовательских данных. Самая распространённая ошибка: забыть установить флаг TVIF_TEXT в поле mask структуры. Без этого флага Win просто не узнает, что мы передали текст и элемент останется пустым. Это классический пример того, как невнимательность приводит к загадочному поведению.
Код:
struct TV_ITEM
mask dd ?,?
hItem dq ?
state dd ?
stateMask dd ?
pszText dq ?
cchTextMax dd ?
iImage dd ?
iSelectedImage dd ?
cChildren dd ?
lParam dd ?
ends
struct TV_INSERTSTRUCT
hParent dq ?
hInsertAfter dq ?
item TV_ITEM
ends
Для создания иерархии:
• Корневой элемент: hParent = TVI_ROOT
• Дочерний элемент: hParent = handle (родителя, полученный из предыдущего TVM_INSERTITEM)
• Позиция вставки: TVI_LAST, TVI_FIRST, TVI_SORT и другие
Вот пример простого окна с использованием элемента TreeView - всего 1 заголовок/родитель, который раскрывается при нажатии на плюс(+), и внутри в цикле можно добавить хоть сотню детей.
C-подобный:
format pe64 gui 6.0
include 'win64ax.inc'
include 'encoding\win1251.inc'
entry start
;//-----------------
section '.data' data readable writeable
ID_TV = 1000
hTree dq 0
hModule dq 0
hDlg dq 0
hFont dq 0
tvis TV_INSERTSTRUCT
strRoot db ' Rammstein',0
strChild1 db ' Тилль Линдеманн ',0
strChild2 db ' Рихард Круспе ',0
strChild3 db ' Кристиан Лоренц ',0
strChild4 db ' Кристофер Шнайдер',0
strChild5 db ' Пауль Ландерс ',0
strChild6 db ' Оливер Ридель ',0
;//-----------------
section '.text' code readable executable
start: sub rsp,8
invoke InitCommonControls ;//<------ обязательно вызвать!
invoke GetModuleHandle,0
mov [hModule],rax
invoke DialogBoxParam, rax, 100, 0, DialogProc, 0
invoke ExitProcess, 0
proc DialogProc hwnd, msg, wparam, lparam
mov [hwnd],rcx
mov [wparam],r8
cmp rdx,WM_INITDIALOG
je @init
cmp rdx,WM_COMMAND
je @exit
cmp rdx,WM_CLOSE
je @close
jmp @next
;//******************************************************
;//****** Обработчик сообщения "WM_INITDIALOG" **********
;//******************************************************
@init:
;//------ Получить дескриптор TreeView
invoke GetDlgItem,[hwnd],ID_TV
mov [hTree],rax
;//------ Фон TreeView как у родительского окна
invoke GetSysColor, COLOR_BTNFACE
invoke SendMessage,[hTree],TVM_SETBKCOLOR,0,eax
if 0
;//------ Можно изменить шрифт в окне TreeView
invoke CreateFont, 14,0,0,0,FW_NORMAL,\ ;//<--- FW_BOLD
0,0,0,DEFAULT_CHARSET,\
OUT_DEFAULT_PRECIS,\
CLIP_DEFAULT_PRECIS,\
DEFAULT_QUALITY,\
FIXED_PITCH + FF_MODERN, "Consolas"
mov [hFont],rax
invoke SendMessage,[hTree],WM_SETFONT,[hFont],1
end if
;//------ Непосредственно работа с элементом "TreeView" -------------
;// Вставка корневого элемента (Root)
mov [tvis.item.mask],TVIF_TEXT ;// мы передаём текст
mov [tvis.hParent],0 ;// родителя нет
mov eax,TVI_ROOT
mov [tvis.hInsertAfter],rax ;// вставить как корень
mov [tvis.item.pszText],strRoot ;// адрес строки
invoke SendMessage,[hTree],TVM_INSERTITEMA,0,tvis ;// первый пошёл (заголовок)
mov [tvis.hParent], rax ;// сохраняем хендл родителя для следующего шага
;// Вставка дочерних элементов (Child)
mov eax,TVI_LAST
mov [tvis.hInsertAfter],rax ;// вставить в конец списка детей Root'а
mov rcx,6 ;// длина цикла
mov rax,strChild1 ;// первая строка
@@: push rax rcx ;// запомнить
mov [tvis.item.pszText],rax ;// адрес очередной строки
invoke SendMessage,[hTree],TVM_INSERTITEMA,0,tvis
pop rcx rax ;// восстановить цикл и адрес строки
add rax,19 ;// переход к следующей строке
loop @b ;// промотать CX-раз..
jmp @next
;//****************************************************
;//****** Обработчики "WM_CLOSE/COMMAND" **************
;//****************************************************
@exit: cmp [wparam],BN_CLICKED shl 16 + IDCANCEL
jne @next
@close: invoke EndDialog,[hwnd], 0
@next: xor eax,eax
ret
endp
;//---------------
section '.idata' import data readable writeable
library kernel32,'kernel32.DLL',user32,'user32.DLL',\
comctl32,'comctl32.dll',gdi32,'gdi32.dll'
include 'api\kernel32.inc'
include 'api\comctl32.inc'
include 'api\user32.inc'
include 'api\gdi32.inc'
;//------------------
section '.rsrc' data resource readable
directory RT_DIALOG, dialogs
resource dialogs,100,LANG_ENGLISH + SUBLANG_DEFAULT, mainform
dialog mainform,'TreeView example', 0,0,180,110, WS_CAPTION + WS_SYSMENU + DS_CENTER,,,'Verdana',8
dialogitem 'Button','Выход',IDCANCEL, 020,090,070,013,WS_VISIBLE + BS_DEFPUSHBUTTON
dialogitem TREEVIEW_CLASS,'',ID_TV, 010,010,170,070,WS_VISIBLE + TVS_HASBUTTONS +\
TVS_HASLINES + TVS_LINESATROOT
enddialog
2.2. Уведомления и взаимодействие
Элемент TreeView активно общается с родительским окном через механизм WM_NOTIFY. Родительское окно получает уведомления о следующих ключевых событиях внутри элемента:
• TVN_SELCHANGING / TVN_SELCHANGED - смена выделенного элемента.• TVN_ITEMEXPANDING / TVN_ITEMEXPANDED - сворачивание/разворачивание узла.• TVN_BEGINDRAG - начало перетаскивания элемента [I](если стиль TVS_DISABLEDRAGDROP не установлен)[/I].Для программного управления используются сообщения:
TVM_GETITEM, TVM_SETITEM, TVM_GETNEXTITEM, TVM_SELECTITEM, TVM_EXPAND, TVM_ENSUREVISIBLE2.3. Механизм обратного вызова "Callback"
Одна из самых интересных возможностей TreeView - ленивая загрузка данных через callback-механизм. Вместо немедленного предоставления текста, иконки или информации о наличии потомков, приложение может указать специальные значения ниже. В этом случае элемент отправляет родителю уведомление TVN_GETDISPINFO с запросом информации. Это позволяет строить огромные деревья без загрузки всех данных сразу в память (отложенная загрузка):
LPSTR_TEXTCALLBACK - для текстаI_IMAGECALLBACK - для индекса иконкиI_CHILDRENCALLBACK - для информации о дочерних элементахОднако здесь есть важный нюанс. Если вы подставляете строку вместо копирования в предоставленный буфер, эта строка не может быть изменена до тех пор, пока не придёт как минимум два доп.уведомления TVN_GETDISPINFO, или элемент не будет удалён. Нарушение этого правила приведёт к ошибкам доступа к памяти AccessViolation.
3. Историческая уязвимость: CVE-2012-0158
Нельзя говорить об элементе TreeView, не упомянув самую известную уязвимость. CVE-2012-0158 - критическая лазейка для удалённого выполнения кода, затронувшая ActiveX-компоненты ListView и TreeView в библиотеке mscomctl.ocx. Она возникала из-за некорректной обработки входных данных и управления памятью. Злоумышленник мог создать сайт, доку Office или RTF-файл, который при открытии вызывал сбой системы, и приводил к выполнению произвольного кода.
Проблема затронула широкий спектр продуктов Microsoft:
Office 2003/2007/2010, SQL Server 2000/2005/2008, BizTalk Server, Commerce Server, Visual FoxPro и Visual Basic 6.0 Runtime.Уязвимость активно эксплуатировалась в дикой природе в апреле 2012 года. Согласно технической классификации, уязвимость относится к CWE-121: Heap-based Buffer Overflow (переполнение буфера в куче). CVE-2012-0158 это напоминание о том, что даже проверенные компоненты системы могут содержать критические уязвимости.
3.1. Защитные меры
Конечно-же Microsoft уже давно заткнула дыру своей заплаткой MS12-027, но проблема в том, что не все юзеры с завидной постоянностью обновляются. Если вы относитесь к их числу, вот несколько советов, как проверить свой периметр сподручными средствами.
Все обновления, и бюллетень MS12-027 в том числе, поставляется майкаим в виде т.н. KB или "Knowledge Base". Если зайти в апплет "Обновления Windows" комбинацией клавиш
Win+R -> control update, то заплатка на CVE-2012-0158 будет числится в розысках под одной из следующих кличек:• KB2664258 - для систем Windows• KB2598374 - для Microsoft Office 2010• KB2597986 - для Microsoft Office 2007• KB2687441 - для Microsoft Office 2003Вы можете найти её или в окне апплета, или просто ввести в ком.строке:
systeminfo | find "KB2664258"Однако если обновление MS12-027 шло в составе пакета для Office или стороннего софта (например, Visual Studio), утилита
systeminfo может не найти этот пакет, так как она ориентирована на компоненты ОС. Если команда выше не сработала, более надежный способ для поиска обновлений Office - использовать PowerShell:get-hotfix | Where-Object {$_.HotFixID -eq "KB2664258"}С другой стороны, все эти обновы просто заменяют уязвимый файл MSCOMCTL.OCX версии 6.01.9545 (лежит в папке win\system32), как минимум на v6.01.9833 или новее. Так-что достаточно проверить его версию в свойствах, и если сборка выше-равна 9833, то можете спать спокойно.
3.2. Что такое ActiveX компоненты
Для тех кто в танке напомним некоторые моменты.
ActiveX - это древняя технология, которая позволяла сайтам и программам запускать мини-приложения прямо внутри себя для расширения возможностей. Она работала как конструктор из готовых деталей. Вместо того чтобы писать сложные функции с нуля, разработчики вставляли в документ или веб-пагу готовый маленький блок кода - это и есть элемент ActiveX. Благодаря этой фишке на сайтах могли работать интерактивные игры, сложные анимации, проигрыватели видео/аудио, инструменты для работы с банками, камерами видео-наблюдения и т.д.
Но у технологии был огромный минус - безопасность! Поскольку элементы ActiveX получали прямой доступ к ОС, это было открытой форточкой для всякого рода малвари. Поэтому сейчас все современные браузеры (Chrome, Edge, Firefox, Safari) полностью отказались от поддержки ActiveX, а сами майки заменили технологию на более безопасные стандарты, например HTML5, JavaScript и расширения. Сегодня элементы ActiveX изредка можно встретить только внутри старых корпоративных программ, закрытых систем, или в редких настройках макросов для Excel.
А проблема заключалась в том, как код внутри MSCOMCTL.OCX обрабатывал входящие данные. Разбор уязвимости показывает, что в одной из функций при вызове происходит выделение небольшого (~20 байт) буфера в стеке. Однако из-за ошибки в логике проверки границ, код мог продолжить запись данных так, чтобы из этого буфера торчал специальным образом сформированный хвост, который правил адрес возврата на нужный участок кода. Это давало возможность перехватить управление выполнением программы и запустить вредоносный код.
Таким образом, уязвимость была не в самом факте использования элемента управления TreeView, а во внутренней реализации этого ActiveX-компонента в конкретной версии либы MSCOMCTL.OCX.
А как на счёт примера выше с окном участников группы Rammstein? Даже если и была-бы на машине уязвимая mscomctl.ocx, программного кода WinAPI это никак не касалось-бы, поскольку хоть они и одного поля ягоды, но находятся на разных уровнях абстракций, и никак не конфликтуют между собой. TreeView из mscomctl.ocx - это ActiveX-оболочка, созданная Microsoft в основном для VB6 и VBA. Она под катом использует стандартный (и глубоко зарытый) элемент управления TreeView из системной comctl32.dll, а не наоборот.
4. Заключение
TreeView - это мощный и гибкий инструмент, который остаётся актуальным на протяжении десятилетий. Он предоставляет богатый API для управления иерархическими данными, поддерживает ленивую загрузку и drag-and-drop. Однако, как и любой сложный компонент, он требует внимательного отношения к деталям: от установки флага TVIF_TEXT до понимания исторических уязвимостей вроде CVE-2012-0158. Грамотное использование TreeView - это баланс между функциональностью, производительностью и безопасностью.
AI-выжимка
сгенерировано ИИ
Тезисы статьи скоро
Статья читается полностью. Тезисы со ссылками на разделы появятся позже.
Содержание
Codeby Academy
Практика и мастерство
От основ до продвинутого — программы для практиков Codeby.
Перейти к курсу →
Поиск в обсуждении
Комментарии
0