Сейчас на форуме: bartolomeo, tyns777 (+5 невидимых) |
eXeL@B —› Программирование —› Запись в адресное пространство приложения |
<< . 1 . 2 . 3 . >> |
Посл.ответ | Сообщение |
|
Создано: 15 февраля 2011 15:53 · Личное сообщение · #1 Есть приложение, которое ставит драйвер хукающий NtOpenProcess и NtQuerySystemInformation, дабы спрятать своё присутствие в списке процессов и не дать открыть свой процесс через OpenProcess. Задача: писать в память этого процесса. При восстановлении SDT и снятии хуков приложение начинает работать некорректно, поэтому этот способ отпадает. Я инжекчу свою длл в этот процесс, поэтому в принципе все данные по нему и так есть. И даже из этой длл в его адресное пространство можно писать что угодно. Вопрос вот в чём: как в данной ситуации сделать так, чтобы в память этого процесса могло писать любое другое приложение? Тот же самый ArtMoney? |
|
Создано: 17 февраля 2011 20:27 · Поправил: Nightshade · Личное сообщение · #2 Драйвер фроста Код там простой. Думаю так будет проще говорить. da5d_17.02.2011_CRACKLAB.rU.tgz - frost_32.rar и вот еще веселый кусок на закусь Code:
|
|
Создано: 18 февраля 2011 09:43 · Личное сообщение · #3 Дело было вечером, делать было нечего. Родился такой немного безумный алгоритм. Идея основана на том, что существует некоторое приложение, защищённое фростом (причём, возможно, его модификация отличается от вышеописанной, и заранее мы не знаем, сколько функций он перехватил в _KiServiceTable), а также есть чит, который либо полностью выполнен в виде исполняемого файла (экзешника), либо существует загрузчик, который посредством каких-то манипуляций в третьем кольце подгружает свои компоненты в приложение, защищённое фростом (целевое). Задача – скрыть чит от фроста, и выполнять все функции чита так, как если бы фроста не существовало. Т.е. если нужно открыть целевой процесс, то чит вызывает OpenProcess с идентификатором целевого процесса, и в ответ получает дескриптор. Метод реализации такой. Сам экзешник чита запускаем с флагом CREATE_SUSPENDED, далее подгружаем свой дров, который по технологии DKOM скроет процесс чита. Вызываем ResumeThread. Теперь нам нужно в ядре выделить NonPagedPool память для размещения адресов из _KiServiceTable. Логично, что для начала нам нужно эти самые адреса найти. Сделать это можно так – известно, что неэкспортируемая KiInitSystem() в ntoskrnl.exe (или аналогичном) выполняет инициализацию экспортируемой KeServiceDescriptorTable вот так: Code:
Собственно, именно offset _KiServiceTable указывает на те значения адресов функций, которые патчатся фростом. НО! На диске в файле ntoskrnl.exe эти значения хранятся в своём первородном виде, поэтому скопировав их оттуда (предварительно пересчитав в соответствии с базой образа ядра), мы получим неперхваченную KiServiceTable. Однако, как же найти этот адрес _KiServiceTable в файле на диске? Code:
KeServiceDescriptorTable – экспортируемое значение, KeServiceDescriptorTableShadow – можно найти. На основе двух этих значений можно организовать сигнатурный плавающий поиск, т.е. на случай, если между ними (этими значениями) код может варьироваться от версии к версии Windows, то можно найти одно значение, например, первое вхождение KeServiceDescriptorTable, потом предположить, что второе вхождение этого значения может быть в промежутке от +24h до +2Еh и т.д. Ясно, что вхождения следует искать внутри модуля ntoskrnl.exe, ImageBase и ImageSize которого можно найти даже с помощью перехваченной NtQuerySystemInformation и даже в юзермоде. (можно юзать значение KiServiceTable, полученное разыменовываением KeServiceDescriptorTable в памяти, если не боитесь, что оно будет перехвачено новыми версиями фроста). Далее, вот мы получили оригинальную KiServiceTable в нашем выделенном участке ядерной неподкачиваемой памяти. Теперь следующее. В ntdll.dll загруженного в память чита ищем вот это: Code:
7FFE0300 – может варьироваться от системы к системе. Так начинается сервис ZwAcceptConnectPort (но не факт), я не знаю утилит или руткитов, которые бы его хукали сплайсингом, поэтому на оригинальность кода можно положиться (или не положиться, и читать код с диска). Короче, найдя этот код по сигнатуре, мы будем знать начало перехводников, которые обращаются к ядру, и, в конечном итоге, к KiServiceTable. Фишка в том, что заполняя нашу ядерную выделенную память нехученными значениями сервисов, мы можем узнать их количество (конец – начало / 4). Так мы будем знать, сколько всего переходников нам нужно поправить. Но на что поправить? Давайте вспомним, как происходит вызов ядерных функций в KiServiceTable. Номер из юзермода умножается на 4, прибавляется к offset _KiServiceTable, и полученный указатель разыменовывается. Но до этого, чтоб получить offset _KiServiceTable, разыменовывается offset _KeServiceDescriptorTable: Code:
Значения KeServiceDescriptorTable и KiServiceTable патчить нельзя, тогда остаётся так выбрать номер сервиса, чтоб получить нужный нам адрес, на который потом передастся управление. Т.е. например, (глядя на вышеприведенный ассемблерный код) после первой инструкции edi = 00890540h (адреса взяты из головы для примера). После второй инструкции ebx должен быть равен 00956000 h, что соответствует адресу неперехваченной функции. Как этого добиться? Предположим, что значение 00956000h расположено по адресу 00905030h. Тогда edi+eax*4 должно равняться 00905030h. Отсюда еах = (00905030h – edi) / 4 -> еах = (00905030h – 00890540h) / 4 = 1D2BCh. Даже если результат будет отрицательный, это – не проблема, поскольку номера имеют размер ULONG. Единственное, что следует упомянуть, так это то, что память должна быть выделена так, чтобы разность делилась на 4, но в описании ExAllocatePoolWithTag не говориться, как это сделать (возможно, никак с её помощью), поэтому придётся выделить на 1 DWORD памяти больше, чем нужно, и с помощью вычислений добиться расположения первого значения так, чтоб (00905030h – edi) подобная разность делилась на 4 нацело. Теперь, когда всё готово, на финальном этапе с помощью VirtualProtectEx + WriteProcessMemory в адресном пространстве чита мы можем перенастроить переходники – переписать номера сервисов. При завершении чита драйвер должен освобождать память, ранее выделяемую под неперехваченную таблицу. Это достигается установкой нотификатора через PsSetCreateProcessNotifyRoutine. Так можно положить болт на всякие модификации этого грёбаного фроста, или нельзя, если я что-то упустил. Жду комментариев. ----- Stuck to the plan, always think that we would stand up, never ran. |
|
Создано: 18 февраля 2011 11:00 · Личное сообщение · #4 |
|
Создано: 18 февраля 2011 14:49 · Личное сообщение · #5 PE_Kill Можно, но если фрост перехватит новые функции, то придётся заново сидеть и думать, как в каждом конкретном случае эмулировать тот или иной сервис. Да, в общем случае поддерживать всю линейку Windows будет не самой простой задачей, но добиться работоспособности на конкретной оси (например, ХР SP3, даже на всём семействе ХР) я думаю, вполне реально. ----- Stuck to the plan, always think that we would stand up, never ran. |
|
Создано: 18 февраля 2011 15:02 · Личное сообщение · #6 |
|
Создано: 18 февраля 2011 15:04 · Личное сообщение · #7 |
|
Создано: 18 февраля 2011 15:05 · Личное сообщение · #8 |
|
Создано: 18 февраля 2011 15:11 · Поправил: ARCHANGEL · Личное сообщение · #9 Hellspawn Ну, если подумать, то можно так: сторонняя библиотека подгружается в АП приложения, защищённого фростом, хукается ЕР, чтоб либа отработала до начала исполнения кода, далее либа перехватывает GetProcAddress и вместо адреса того же ZwOpenProcess подсовывает какую-нибудь оччччень редко используемую функу (причём на все функции, которые нужно оставить неперехваченными, подсовывается один и тот же адрес). Тогда есть шанс, но опять же, при малейшем апдейте всё придётся переделывать. Добавлено типа, чтоб она перехватила что-то левое в ядре, которое никогда не выполнится? Да, но если нельзя патчить ничего из файлов фроста (даже в памяти нельзя менять), то это не получится сделать. ----- Stuck to the plan, always think that we would stand up, never ran. |
|
Создано: 18 февраля 2011 15:13 · Личное сообщение · #10 |
|
Создано: 18 февраля 2011 15:21 · Личное сообщение · #11 |
|
Создано: 18 февраля 2011 15:24 · Личное сообщение · #12 |
|
Создано: 18 февраля 2011 15:35 · Личное сообщение · #13 |
|
Создано: 18 февраля 2011 17:12 · Личное сообщение · #14 Я говорил не про эмуляцию сервиса, а про создание интерфейса внутри игры и подгрузку своей библиотеки-провайдера в адресное пространство чита. Перехватываем в чите апи работы с процессами, генерим инвалидный специфичный хендл при NtOpenProcess (DEADC0DE например), а все остальные апи фильтруют хендлы, если попадается наш хендл, то через ту же шаред мемори эмулируем апи, редиректя запросы на длл внутри игры. За вечер вполне можно сделать ну и вечер на отладку и при этом не трогаем дров и всё красиво и универсально. С другой стороны под силу ли это ТС. ----- Yann Tiersen best and do not fuck |
|
Создано: 18 февраля 2011 18:00 · Поправил: ajax · Личное сообщение · #15 |
|
Создано: 18 февраля 2011 21:17 · Личное сообщение · #16 ARCHANGEL > как же найти этот адрес _KiServiceTable 1. Менеджер сискалов берёт линк на SDT из описателя текущего потока, это ETHREAD.ServiceTable. Это значение не может быть изменено до конвертации потока в гуи. 2. Обычно SST не перемещают в памяти, хотя этому ничто не мешает. Можно переместить всю сст в нужное место. Можно вобще погрузить ядро и загрузить в сст ссылку на содержащуюся в нём таблицу(да и вобще перенаправить вызовы сервисов на оригинальные в новой проекции). > значениями сервисов, мы можем узнать их количество (конец – начало / 4). Так мы будем знать, сколько всего переходников нам нужно поправить. Число их описано в SST. |
|
Создано: 18 февраля 2011 21:39 · Поправил: HiEndsoft · Личное сообщение · #17 Для XP фрост ломается из юзермода патчем драйвера (чтобы выкинуть нотификаторы и не лезть в ядро), исправлением SDT через гейт physicalmemory, перенаправлением загрузки драйвера (хук CreateServiceW - там именно так загружается дров) и убийством "теневого" контрольного потока в защищаемом процессе (там на самом деле их несколько, какой из них прибить - дело другое)) ----- продавец резиновых утёнков |
|
Создано: 19 февраля 2011 13:25 · Поправил: Clerk · Личное сообщение · #18 ARCHANGEL Не поленился собрать пруфкод. Всё завязано на графы. Для поиска IDT используем также анализ графа. Находим KiSystemService(), далее kssdoit и интегрируем на это место наш код. И этот код может быть загружен в дескрипторы для сервисного прерывания. Для фастколов необходимо поправить линк в #DB. Вот исходный менеджер: Вот конечный: Разумеется это не полноценный код, а только пример показывающий на сколько просто такие манипуляции выполнять. 218f_19.02.2011_CRACKLAB.rU.tgz - Service.zip |
|
Создано: 21 февраля 2011 09:54 · Поправил: ARCHANGEL · Личное сообщение · #19 Уфф, начну по порядку. Мой вышеприведенный алгоритм требует переработки. Дело в том, что нельзя (без патчинга ядра) вызывать сервисы по произвольным номерам (стандартно). Т.е., для примера, такой код: Code:
Прекрасно отработает, а вот такой: Code:
Вернёт ошибку о неверной функции. Дело в том, что номера сервисов в ядре проходят проверку на валидность, а именно внутри _KiSystemService выполняется такой код: Code:
(кстати, видно на рисунке Clerk'a )Не трудно догадаться, что в еах – номер сервиса. Во второй строчке он сравнивается с общим числом сервисов, что происходит дальше – очевидно из имени метки. Но самым интересным является то, что число сервисов берётся не из SYSTEM_SERVICE_TABLE, на которую указывает KeServiceDescriptorTable. Оно берётся из её копии, на которую указывает ETHREAD.ServiceTable потока. Изменив эту копию (подсунув туда адрес непадченной KiServiceTable) можно добиться работы в обход перехватов. Как писал Great, это поле очень интересно (Clerk, сэнкс, что обратили на него моё внимание), но Great (в своей статье "Планировщик, потоки и процессы") писал неверно, что оно содержит содержимое (простите за повторы) либо KeServiceDescriptorTable, либо KeServiceDescriptorTableShadow. Оно содержит либо KeServiceDescriptorTable, либо обе, если поток стал Gui. Я тож собрал пруфкод, но пока ещё не до конца отладил - не разобрался, как принудительно конвертить поток в ГУИ, если я не могу исполнять код в его адресном пространстве. Думаю, через пару дней выложу. Далее, дизасм драйвера, выложенного Nightshade, дал результаты. Во-первых, сокрытие по DKOM не даст результата, поскольку дров фроста ставит нотификаторы на создание процесса и загрузку модулей – он всё равно всё сдетектит. Поэтому мы пойдём другим путём. Давайте подумаем, что он конкретно может сдетектить. Имя и контрольную сумму части образа. Поэтому переименовываем чит и упаковываем его любым упаковщиком на выбор. Но, если фрост в будущем (в текущей версии я не видел таких проверок) будет юзать поиск по объектам ядра (мьютексам, событиям, именованым каналам и т.д.), опираясь на конкретные имена, то от этого, в общем случае, не защититься. Второе, ничего не дадут перехваты с подменой номеров сервисов в юзермоде, т.к. дров фроста вычисляет значения Nt-функций, опираясь на ядро. Ну, в общем, пока всё, будут отлаживать свой код, чтоб было, что показать. В связи с отладкой вот таккой вот вопрос, прямо не касающийся фроста, но, может, кто знает. Скачал я с офсайта отладочные символы для ХР, стал их юзать, и понял, что нифига-то символы не загружаются, то ли не подходят, то ли хз ещё что. Тогда подгрузил символы из WinDbg (скачанные через него), и всё запахало. Но для Семёрки я не могу подгрузить символы из WinDbg, где бы достать нормальные? HiEndsoft Как вы используете PhysicalMemory на XP SP3? ----- Stuck to the plan, always think that we would stand up, never ran. |
|
Создано: 21 февраля 2011 21:48 · Личное сообщение · #20 ARCHANGEL > Я тож собрал пруфкод, но пока ещё не до конца отладил - не разобрался, как принудительно конвертить поток в ГУИ, если я не могу исполнять код в его адресном пространстве. Поток в гуи конвертирует функа PsConvertToGuiThread(), она загружает теневую сст и расширяет ядерный стек. Можно дёрнуть для этого любой теневой сервис, но при этом понадобится както обойти вызов пользовательского apfn-колбека. Ну с этим проблем вобщем не должно быть. Шадов не доступен из процесса System не потому, что поток не гуи, а потомучто шадов находится в адресном пространстве сессии и для отображения туда его нужно выполнить аттач, тоже и для иных сессий. Для этого нужно дёрнуть MmAttachSession(), напрямую никак, но можно через колбек ExpWin32SessionCallout, вызываемый из ObjectType-процедур для обьекта "Desktop". Дето был пруфкод мой в инетах.. Обычно это в пределах текущей сессии не делают, а просто выполняют аттач к гуи-процессу(например csrss по имени порта и тп.). |
|
Создано: 22 февраля 2011 01:31 · Личное сообщение · #21 |
|
Создано: 22 февраля 2011 10:34 · Личное сообщение · #22 Clerk пишет: Можно дёрнуть для этого любой теневой сервис Да, я так и делаю. Про PsConvertToGuiThread прочитал у Great'a, да и после перехода на _KiBBTUnexpectedRange там через несколько инструкций её вызов идёт, а выполнять код в АП требуемого процесса решил через APC. Пока вот прикладываю PoC. На картинках запусщенный HideTools с установленым драйвером, RKU показывает наличие перехватов HideTools, чтоб не подумали, будто их кто-то снял. А Pe Tools перенастроен так, что все его потоки обращаются к чистой KiServiceTable. Соответственно, Pe Tools палит HideTools. Шадов не доступен из процесса System Не вижу необходимости делать его там доступным. Clerk Скачал ваш код, посмотрел, хотелось бы задать вам несколько вопросов, но, думаю, что они не будут прямо относиться к теме топика, поэтому позже (1-2 дня на продумывание, может, сам догадаюсь) стукну в личку. Странно, что больше тут никто ничего по этому поводу не написал, наверное, всем остальным и так всё ясно. 7c61_21.02.2011_CRACKLAB.rU.tgz - Archive.rar ----- Stuck to the plan, always think that we would stand up, never ran. |
|
Создано: 22 февраля 2011 18:23 · Личное сообщение · #23 |
|
Создано: 22 февраля 2011 18:38 · Личное сообщение · #24 |
|
Создано: 23 февраля 2011 10:51 · Личное сообщение · #25 Clerk пишет: Для этого есть Ke[Stack]AttachProcess(). Наконец-то собрал всё воедино, и выкладываю рабочий загрузчик процессов. В архиве батник, в котором описан пример запуска блокнота через загрузчик. Просьба потетсить на фросте (у кого есть такая возможность), т.е. посмотреть, заработали ли читы (тот же артмани). Загрузчик совместим пока только с ХР, но если кому-то очень надо - пишите, будем что-то решать с более новыми ядрами. 54fe_22.02.2011_CRACKLAB.rU.tgz - Release.rar ----- Stuck to the plan, always think that we would stand up, never ran. |
|
Создано: 23 февраля 2011 17:34 · Поправил: Clerk · Личное сообщение · #26 |
|
Создано: 23 февраля 2011 20:33 · Личное сообщение · #27 |
|
Создано: 24 февраля 2011 09:43 · Личное сообщение · #28 HiEndsoft Хм, а в отладчике не смотрели, почему процесс не создаётся? Мало ли, мож имя не то, или нет такого процесса, попробуйте полный адрес задать, или если через батник пользуйетесь, то кириллицу из полного адреса убрать. Clerk Ну, так вы под варей попробуйте, хотя б просто запускается ли, работает ли и т.д. ----- Stuck to the plan, always think that we would stand up, never ran. |
|
Создано: 24 февраля 2011 13:03 · Поправил: HiEndsoft · Личное сообщение · #29 ARCHANGEL пишет: Хм, а в отладчике не смотрели, почему процесс не создаётся? --если честно времени нет; Мало ли, мож имя не то, или нет такого процесса, попробуйте полный адрес задать, или если через батник пользуйетесь, то кириллицу из полного адреса убрать. -- получилось заинжектится только если файл notepad'a лежит в одной папке с вашей прогой (раньше в system32 был). Функционал посмотрю позже ----- продавец резиновых утёнков |
|
Создано: 05 марта 2011 08:32 · Личное сообщение · #30 |
|
Создано: 06 марта 2011 17:53 · Личное сообщение · #31 |
<< . 1 . 2 . 3 . >> |
eXeL@B —› Программирование —› Запись в адресное пространство приложения |