![]() |
Работа с буфером обмена в kernel-mode
[Работа с буфером обмена в ядре.]
Решила написать такую вот небольшую заметку о работе с буфером обмена в ядре. Для начала рассмотрим типичный код для работы с буфером обмена в юзермоде. Код:
if(OpenClipboard(GetForegroundWindow()))Теперь к ресечу непосредственно. Каким образом вставить данные в буфер обмена? Обратимся к коду функции SetClipboardData. Код:
Код:
.text:BF8ECE5C mov esi, [ebp+arg_4]Код:
.text:BF8ECD89 xor esi, esiЗначит в драйвере заюзаем функции RtlCreateHeap и RtlAllocateHeap. Код:
hHeap = RtlCreateHeap(0,Теперь о том, как получить данные из буфера обмена. Все дело в том, что просто так, вызовом NtUserGetClipboardData нам данные из буфера обмена не получить. Так как эта функция возвращает хендл. Чтобы по хендлу получить нужные данные нужна функция CreateLocalMemHandle. Код внутренней функции CreateLocalMemHandle. Код:
Теперь для вызова функций по указателю напишем в драйвере Код:
typedef HANDLE (__stdcall*_NtUserGetForegroundWindow_)(VOID);Код:
pNtGetForegroundWindow = Хорошо, вроде как разобрались. Теперь другой вопрос - получение номеров нужных системных сервисов. Получать их будем конечно же в юзермодной проге. Я очень не люблю привязываться к версии оси, это крайний случай. Однако, я уже почти согласилась на него. Почему сейчас расскажу. Из всего вышеизложенного понятно, что нам надо добраться до номеров сервисов NtUserGetForegroundWindow - так как я не хотела создавать свое окно. В общем случае лучше создавать свое, конечно NtUserOpenClipboard NtUserEmptyClipboard NtUserCloseClipboard NtUserConvertMemHandle NtUserSetClipboardData NtUserGetClipboardData NtUserCreateLocalMemHandle Со списком определились. Посмотрим на код юзермодной GetForegroundWindow. Код:
7E379823 > B8 94110000 MOV EAX,1194Код:
7E380277 > 8BFF MOV EDI,EDIДля вызова функций удобно сделать такие объявления Код:
typedef HWND (__stdcall*_GetForegroundWindow_)(VOID);Код:
DWORD SearchServiceNumberStart(DWORD DllBase,LPCSTR FunctionName)Код:
// собственно трейсер, юзающий VEHКод:
// dwNested - сколько вызовов пропускатьКод:
#define NtUserGetForegroundWindow 0Код:
Насчет другой функции - GetClipboardData. Первый вызов NtUserGetClipboardData, а второй - NtUserCreateLocalMemHandle, в общем по аналогии делается (dwNested = 0 для NtUserGetClipboardData и 1 для NtUserCreateLocalMemHandle). Только не говорите, что надо мэппить все остальные либы, учитывать все возможные хуки еще и в других либах, а в коде хука может быть антитрассировка и прочее, уже не хотелось с этим заморачиваться просто. У меня вот стояли хуки комодо в ntdll, через них трейс нормально прошел. Как видите, дизассемблер тут даже не нужен. Драйвер использует функции RtlCreateHeap, RtlAllocateHeap, RtlFreeHeap. А если мы посмотрим в msdn http://msdn.microsoft.com/en-us/library/ms797739.aspx "This routine is available on Microsoft Windows XP and later." + я использую векторную обработку исключений. Так что кодес для систем начиная с XP. Этот код я протестировала на висте, своей XP SP3, SP2 и win7. Работает нормально. На этом пока прощаюсь, надеюсь кому-то было познавательно. ссылка на сорс http://dump.ru/file/2315384 |
Молодца... так держать.
|
Спасибо за статью, по больше бы таких, будет ли еще про ядро?
|
ммм... а как тебе такой метод определения номера сервиса:
вспоминаем, что в KTHREAD есть ServiceTable, в юзермоде это Shadow SDT. узнаём размер SST и создаём SST такого же размера где-нибудь, записываем где-то по адресу X UD2 столько раз, сколько записей в SST, забиваем её целиком адресами этих разных уд-шек. После чего вызываем функу, которая переводит стрелки на системный сервис и вуаля получаем инвалид опкод. Адрес, где этот опкод выполнился, узнать нетрудно, а зная адрес получаем номер как (адрес-X)/2 (2 - длина UD2) (кста можно вообще без исключений - вместо UD2 поставить инструкцию типа mov [], 1) //edit: надеюсь, все поняли, что под юзермодом следует понимать гуй :) |
0x0c0de ты ассемблерная террористка :)
|
0x0c0de спасибо за исходники, класные =)
desTiny интересная идея, а у тебя негде примерчика на завалялось? =). Насколько я понял, надо будет вызвать соответсвующюю юзермодную api в нашем треде (с фэйковой табличкой сервисов)? Т.е. определить номер сериса не выйдет не выходя из ядра? И смещение указателя на таблицу сервисов в KTHREAD изменяеться в разных версиях, как и номера сервисов. |
>> примерчика на завалялось?
нет.. просто такой вот крестьянский способ с трассировкой мне не понравился, и такой в голову пришёл. Реализацию (от меня), если раньше не будет, ждать можно не раньше июня. >> Т.е. определить номер сериса не выйдет не выходя из ядра? ну можно и из ядра, если юзермодная функция не оперирует никакими юзермодными адресами - то просто её и в ядре вызвать можно) если юзает - то поймать ошибки тоже можно - нам почти наплевать на большую часть их кода - нам только перед сисэнтером интересно, какая константа записывается >>И смещение указателя на таблицу сервисов в KTHREAD изменяеться в разных версиях разве меняется? вроде это документированная структура... а даже если и меняется... вот я только что такой чит придумал - берём какой-нить тред, для которого гарантировано сдт - это не шадоу и сканим всю структурку на известный адрес - вот и смещение) >>номера сервисов вот их-то мы-то и хотим найти-то) |
Сори, что так долго молчала. Решаю проблемы с учебой.
desTiny: крестьянский способ. Нормальный способ и рабочий (но долгий). Как я уже сказала тебе в жабере, что пока практической реализации нет, по поводу твоего способа ничего не хочу писать. В выходные постараюсь собраться и накодить если что. Сегодня реализовала через хук KiFastSystemCall. Все отлично. Никакой трассировки нет, работает быстрее. //8.05.09: исправила, KiFastSystemCall конечно же, а не KiFastSystemCallRet=\ |
Цитата:
kd> dt _KTHREAD +0x0e0 ServiceTable : Ptr32 Void а вот виста sp1: +0x12c ServiceTable : Ptr32 Void Ну и ещё она не документированная. Впрочем, вижу что тебя это не смущает =) |
Цитата:
Цитата:
Цитата:
|
Finito:
В результате продолжительного обсуждения было принято решение о том, что оказывается KiFastSystemCall и KiFastSystemCallRet экспортируются. Вроде при таком допущении номер сервиса выводится в одно действие ) |
Как и обещала, метод я реализовала. Время на радостях после успешно завершенной зачетной недели выкроилось.
Создаем свою SDT, ntoskrnl SST оставляем прежней (копируем из оригинальной SST), создаем свою таблицу вместо win32k.sys SST (при этом указатель на argument table берем из оригинальной таблицы), а адреса всех функций заполняются адресом моего обработчика, затем прописываем новую SDT в KTHREAD.ServiceTable. Обработчик для всех функций, логирующий номера вызываемых сервисов. Т.к. приложение, юзающее такое определение номеров сервисов (отсылающее драйверу ioctl запросы) консольное- проблем не возникает. Код:
http://i058.radikal.ru/0906/d7/e3c09f864cbd.jpg http://s40.radikal.ru/i089/0906/08/3e462629b25e.jpg Надо сказать, что на win7 там иногда не с первого раза определяются номера. причина этого мне неизвестна. Я сделала цикл до тех пор, пока не определится. Кстати, на скрине с семеркой видно, что первая попытка неуспешна, зато на второй раз все определилось. Однако, все равно остаюсь при своем способе. этот пускай будет |
| Время: 08:53 |