Перехват запуска приложений

Тема в разделе "WASM.WIN32", создана пользователем Serzh, 13 ноя 2006.

  1. Serzh

    Serzh New Member

    Публикаций:
    0
    Регистрация:
    13 ноя 2006
    Сообщения:
    20
    В общем-то, требуется ваша помощь относительно сабжа))
    Стоит такая задача- написать софтину, которая бы ограничивала запуск пользователями некоторых приложений по определенным критериям (имя файла, размер- это все детали). Очевидно, что задача должна решаться при помощи перехвата системных функций, но вот каких? Перехватывать ли мне Win32 API или же Native API. Можно конечно перехватить вызов CreateProcess (даже из userspace), но в случае функций Win32 можно легко мою программку одурачить (как известно, есть масса способов обмануть перехватчик Win32 API). Или перехватывать Native API, но и тут мне не все понятно:dntknw: Я конечно не могу похвастаться глубоким знанием Native-функций, но беглый просмотр справочника показал, что эти функции принимают уже указатели на какие-то системные структуры, а не на строчку с именем exe-файла, как в функциях Win32. Мне же важно получить имя файла для проверки определенных условий и генерации решения- запустить или запретить выполнение.
    В общем, без вашей помощи врядли разберусь... Заранее спасибо;)
     
  2. apple

    apple Виктор

    Публикаций:
    0
    Регистрация:
    26 апр 2005
    Сообщения:
    907
    Адрес:
    Russia
    zwResumeThread, zwCreateProcess, goOgle.Com
     
  3. Serzh

    Serzh New Member

    Публикаций:
    0
    Регистрация:
    13 ноя 2006
    Сообщения:
    20
    Может быть конечно я чего-то не понимаю =)
    Функция ZwCreateProcess НЕ принимает в качестве параметра имя запускаемого модуля. Она возвращает хэндл созданного объекта ПРОЦЕСС в случае успешного завершения. Но в качестве входных параметров она принимает в основном служебные данные- требуемый уровень доступа, атрибуты и прочее. Разве можно каким-то образом через них добраться до имени исполняемого файла? Если можно, то просветите, пожалуйста:)
    Об этом я и писал в первом посте- перехват WIn32 API слишком ненадежен, а функции Native API работают уже с более низкоуровневыми объектами...
     
  4. Asterix

    Asterix New Member

    Публикаций:
    0
    Регистрация:
    25 фев 2003
    Сообщения:
    3.576
    ObjectAttributes->ObjectName
     
  5. Serzh

    Serzh New Member

    Публикаций:
    0
    Регистрация:
    13 ноя 2006
    Сообщения:
    20
    Насколько я знаю, структура ObjectAttributes не обязательно должна заполняться, очень часто в вызовах ZwCreateProcess значение параметра ObjectAttributes равно 0... Т.е. если мы хотим предоставить информацию- предоставляем, а не хотим ставим NULL. Может быть я и не прав, но похоже, что из вызова ZwCreateProcess имя файла вытащить все-таки не удастся (по крайней мере, в общем случае). А что если установить перехват на ZwCreate(Open)File? Вы не в курсе, как такой глобальный hook повлияет на производительность?
     
  6. gilg

    gilg New Member

    Публикаций:
    0
    Регистрация:
    19 май 2005
    Сообщения:
    527
    После вызова оригинальной ZwCreateProcess в параметре ProcessHandle будет содержаться хендл создаваемого процесса, по нему можно получить все, что нужно.
    Это, имхо, не самый простой путь. Все-равно кроме хендла ничего не получишь, только это будет хендл файла, а не процесса, соответственно, меньше полезной информации.

    Посмотри статью из Phrak`а "Invisibility on NT boxes" ("Как стать невидимым в Windows NT"), 7 раздел.
     
  7. Serzh

    Serzh New Member

    Публикаций:
    0
    Регистрация:
    13 ноя 2006
    Сообщения:
    20
    Как я понимаю, хэндл процесса в ProcessHandle устанавливается уже после того, как ZwCreateProcess успешно отработает, т.е. это выходной параметр. Т.е. если я перехватываю ZwCreateProcess, то ProcessHandle будет лишь указывать на переменную, которая и получит значение ProcessHandle после завершения оригинальной функции, само значение мне будет недоступно, ибо мой перехватчик должен передать управление true-ZwCreateProcess. Или я что-то не так понял?
    P.S. Погуглив, наткнулся на несколько обсуждений подобной задачи. Там предлагают использовать перехват ZwCreateSection или ZwCreateThread. Насколько я могу понять, в первом случае я получаю хэндл файла (как и в случае Create/OpenFile), а во втором случае получаю УЖЕ хэндл процесса, в адресном пространстве которого этот поток запускается. Я правильно рассуждаю?
    P.P.S. А за ссылочку на Phrack спасибо, почитаю :) Жаль только их официальный сайт прикрыли власти самой демократической демократии в мире :dntknw:
     
  8. gilg

    gilg New Member

    Публикаций:
    0
    Регистрация:
    19 май 2005
    Сообщения:
    527
    Схематично механизм такой:
    Код (Text):
    1. Перехваченный_ZwCreateProcess(PHANDLE handle) {
    2.     original_ZwCreateProcess(handle);
    3.     Имя_файла, размер_образа, и т.д. получаются по хендлу.
    4.     Если запретить создание {
    5.         ZwTerminateProcess(handle);
    6.         return STATUS_ACCESS_VIOLATION;
    7.     }
    8. }
    Если ZwTerminateProcess() здесь не прокатит (процесс еще полностью не создан, так что хз, будет ли здесь работать), то можно выполнить его точно также в перехвате ZwResumeThread
     
  9. TarasCo

    TarasCo New Member

    Публикаций:
    0
    Регистрация:
    2 фев 2005
    Сообщения:
    106
    IMHO после отработки оригинальной ф. ZwCreateProcess узнать имя модуля не удастся. Я точно не помню, но порядок вызовов при создании пользовательского процесса такой:

    ZwCreateProcess
    RtlCreateProcessPrameters
    RtlCreateUserProcess
    NtResumeThread

    ф. RtlCreateProcessPrameters как раз имеет нужные параметры - десктоп, имя модуля и.т.п. Если процесс не должен быть создан, нужно не дать отработать NtResumeThread, после чего уже честно замочить его через ZwTerminateProcess.
     
  10. Serzh

    Serzh New Member

    Публикаций:
    0
    Регистрация:
    13 ноя 2006
    Сообщения:
    20
    Код (Text):
    1. Перехваченный_ZwCreateProcess(PHANDLE handle) {
    2.     original_ZwCreateProcess(handle);
    3.     Имя_файла, размер_образа, и т.д. получаются по хендлу.
    4.     Если запретить создание {
    5.         ZwTerminateProcess(handle);
    6.         return STATUS_ACCESS_VIOLATION;
    7.     }
    8. }
    Меня вот что интересует- действительно ли можно перехватить некоторую функцию, сделать что-то, потом передать управление оригинальному обработчику, потом опять получить управление на перехватчик и что-то опять сделать? (Как в Вашем примере: после отработки оригинальной ZwCreateProcess перехватчик снова получает управление и мы в зависимости от неких условий прибиваем процесс). Не может ли случиться так, что оригинальная функция вернет управление в какое-нибудь другое место? Может быть это и глупые вопросы, но, честно сказать до сегодняшнего дня с темой перехвата Native API был знаком лишь отдаленно:)
    А что касается ZwCreateProcess, то скорее всего, имя файла там может и не всплыть... В основном люди пользуются NtResumeThread, реже- NtCreateThread
     
  11. Guest

    Guest Guest

    Публикаций:
    0
    Незнаю че там не прокатит, вот кусок кода который у меня удачно работал долгое время, описывать не стану:
    Код (Text):
    1. NTSTATUS NewZwCreateFile(PHANDLE FileHandle, ACCESS_MASK DesiredAccess, POBJECT_ATTRIBUTES ObjectAttributes, PIO_STATUS_BLOCK IoStatusBlock,
    2.         PLARGE_INTEGER AllocationSize, ULONG FileAttributes, ULONG ShareAccess, ULONG CreateDisposition, ULONG CreateOptions, PVOID EaBuffer,
    3.         ULONG EaLength)
    4. {  
    5.     NTSTATUS    status;
    6.     WCHAR       *pExt;
    7.     CHAR        szProcess[NT_PROCNAMELEN];
    8.    
    9.     status = OldZwCreateFile(FileHandle, DesiredAccess, ObjectAttributes, IoStatusBlock, AllocationSize,
    10.             FileAttributes, ShareAccess, CreateDisposition, CreateOptions, EaBuffer, EaLength);
    11.  
    12.     if (wcsrchr(ObjectAttributes->ObjectName->Buffer, '\\') < wcsrchr(ObjectAttributes->ObjectName->Buffer, '.'))
    13.     {
    14.         if (wcslen(ObjectAttributes->ObjectName->Buffer) < 5) return status;
    15.         pExt = wcsrchr(ObjectAttributes->ObjectName->Buffer, '.');
    16.         if (pExt == NULL) return status;
    17.         GetProcessName(szProcess);
    18.         if (bIncExt) if (!TestMask(szInclude, &pExt[1])) return status;
    19.         if (bExcExt) if (TestMask(szExclude,  &pExt[1])) return status;
    20.         if (bIncProgram) if (!TestProgram(szPrograms, szProcess))   return status;
    21.         if (bExcProgram) if (TestProgram(szExcPrograms, szProcess)) return status;
    22.  
    23.         PCREATE_BUFFER pBuffer = AddCreateEntry();
    24.         if (pBuffer != NULL)
    25.         {
    26.             LARGE_INTEGER time, time_local;
    27.             TIME_FIELDS   tf;
    28.  
    29.             pBuffer->hfile   = *FileHandle;
    30.             wcsncpy(pBuffer->szBuffer, ObjectAttributes->ObjectName->Buffer, 255);
    31.             strncpy(pBuffer->szProcess, szProcess, NT_PROCNAMELEN);
    32.  
    33.             KeQuerySystemTime(&time);
    34.             ExSystemTimeToLocalTime(&time, &time_local);
    35.             RtlTimeToTimeFields(&time_local, &tf);
    36.  
    37.             if (tf.Hour < 10)
    38.             {
    39.                 strcpy(pBuffer->szTime, "0");
    40.                 _itoa(tf.Hour, (pBuffer->szTime+1), 10);
    41.             }
    42.             else _itoa(tf.Hour, pBuffer->szTime, 10);
    43.             strcat(pBuffer->szTime, ":");
    44.  
    45.             if (tf.Minute < 10)
    46.             {
    47.                 strcat(pBuffer->szTime, "0");
    48.                 _itoa(tf.Minute, (pBuffer->szTime+4), 10);
    49.             }
    50.             else _itoa(tf.Minute, (pBuffer->szTime+3), 10);
    51.             strcat(pBuffer->szTime, ":");
    52.            
    53.             if (tf.Second < 10)
    54.             {
    55.                 strcat(pBuffer->szTime, "0");
    56.                 _itoa(tf.Second, (pBuffer->szTime+7), 10);
    57.             }
    58.             else _itoa(tf.Second, (pBuffer->szTime+6), 10);
    59.  
    60.             dprintf("In the \"ZwCreateFile  :\" %ws - %s, handle - %d\n", ObjectAttributes->ObjectName->Buffer, pBuffer->szProcess, *FileHandle);
    61.         }
    62.     }
    63.     return status;
    64. }
    немножко нето, но сути не меняет.
     
  12. gilg

    gilg New Member

    Публикаций:
    0
    Регистрация:
    19 май 2005
    Сообщения:
    527
    TarasCo
    Точно

    Да. Все функции в Windows гарантируют нормальный возврат, либо (об этом всегда говорится в документации) выбрасывают исключение. Еще вариант - бсод, но он я думаю не рассматривается :)
     
  13. Guest

    Guest Guest

    Публикаций:
    0
    Перехват делается так:
    Код (Text):
    1. #define NTCALL(_function)KeServiceDescriptorTable->ntoskrnl.ServiceTable[_function]
    2. typedef NTSTATUS (*ZWCREATEFILE)(OUT PHANDLE FileHandle, IN ACCESS_MASK DesiredAccess,
    3.                                  IN POBJECT_ATTRIBUTES ObjectAttributes,  OUT PIO_STATUS_BLOCK                  
    4.                                                                  IoStatusBlock,
    5.                                  IN PLARGE_INTEGER AllocationSize OPTIONAL, IN ULONG FileAttributes,
    6.                                  IN ULONG ShareAccess, IN ULONG CreateDisposition, IN ULONG
    7.                                                                  CreateOptions,
    8.                                  IN PVOID EaBuffer OPTIONAL, IN ULONG EaLength);
    9. ZWCREATEFILE OldZwCreateFile;
    10. -----------------------------------------------------------------------------
    11. ULONG CR0Reg;
    12.         OldZwCreateFile = (ZWCREATEFILE)NTCALL(nZwCreateFile);
    13.         if (NTCALL(nZwCreateFile) != NewZwCreateFile)
    14.         {
    15.             __asm
    16.             {
    17.                 cli
    18.                 mov eax, cr0
    19.                 mov CR0Reg,eax
    20.                 and eax, 0xFFFEFFFF
    21.                 mov cr0, eax
    22.             }
    23.             NTCALL(nZwCreateFile) = NewZwCreateFile;
    24.             __asm
    25.             {
    26.                 mov eax, CR0Reg
    27.                 mov cr0, eax
    28.                 sti
    29.             }
    30.         }
    31. -----------------------------------------------------------------------------
    В аттаче номера функций "_function".
     
  14. n0name

    n0name New Member

    Публикаций:
    0
    Регистрация:
    5 июн 2004
    Сообщения:
    4.336
    Адрес:
    Russia
    Скорее всего нет.
    TarasCo
    Угу.
     
  15. Serzh

    Serzh New Member

    Публикаций:
    0
    Регистрация:
    13 ноя 2006
    Сообщения:
    20
    Большое всем спасибо за оказанную помощь, теперь думаю разберусь:)
    Меня всегда интересовал такой вопрос: перехватчик функций Native API всегда должен проверять номер версии Windows NT по той причине, что номера этих самых функций (im1111 спасибо за Functions.h) меняются от релиза к релизу или даже после установки очередного сервис-пака. Или мы проверяем версию и корректно устанавливаем драйвер или же система вылетает в БСОД. Перехватчик, написанный Свеном Шрайбером и находящийся на компакте к его книжке, прекрасно работал в Win2k, но приводит к BSOD в Windows XP. Чего и следовало ожидать- номера функций поменялись. Но, уже много лет как существуют утилиты FileMon и RegMon от Марка Руссиновича, которые прекрасно работали и в Windows NT 4.0 и в Win2k и в WinXP. Насколько я знаю, эти программы также используют тактику перехвата Native API, но неужели за столько лет не изменился ни один номер функции? Или же эти проги используют какую-то другую методику перехвата? Точно уверен, что бинарник был один и тот же, т.е. код драйвера не переписывался
     
  16. gilg

    gilg New Member

    Публикаций:
    0
    Регистрация:
    19 май 2005
    Сообщения:
    527
    Посмотри в дизассемблере код любой функции с префиксом Zw*. Там есть инструкция "mov eax,<number>". number - номер сервиса в sdt. Простейший дизассемблер - и не надо хардкодить никакие номера.
     
  17. Serzh

    Serzh New Member

    Публикаций:
    0
    Регистрация:
    13 ноя 2006
    Сообщения:
    20
    Да, смотрел:) А в мониторах Марка Руссиновича именно такой способ применялся? Т.е. по имени функции я ищу её адрес, потом нахожу инструкцию "mov eax,<number>", вытаскиваю оттуда number, независимо от версии? У Шрайбера это, кстати, тоже написано, но вот не помню, почему он такой подход отбросил... А эта инструкция всегда стоит первой по счету? Если не так, то придется писать полный дизасм 386го, что врядли можно назвать тривиальной задачей ;)
     
  18. gilg

    gilg New Member

    Публикаций:
    0
    Регистрация:
    19 май 2005
    Сообщения:
    527
    Да (Win64 не в счет). Кроме того, можно проверять по опкоду "mov eax,immediate". Да и вообще зачем писать свой дизассемблер, когда чужих полно :)
     
  19. Serzh

    Serzh New Member

    Публикаций:
    0
    Регистрация:
    13 ноя 2006
    Сообщения:
    20
    Скажите тогда вот что: метод дизассеблирования функции с целью определения ее номера, он реально применялся кем-нибудь? Ну, Вами, например :) Просто хочется узнать, реально ли это на практике осуществить и стоит ли за этот дизассемблер браться. Ибо других надежных способов на моей памяти что-то нет:)
    Может быть, в статьях на wasm.ru про перехват уже все про это написано, но я, честно сказать, туда еще не смотрел... Пока что руководствуюсь мутными воспоминаниями пятилетней давности =)
     
  20. n0name

    n0name New Member

    Публикаций:
    0
    Регистрация:
    5 июн 2004
    Сообщения:
    4.336
    Адрес:
    Russia
    Serzh
    Достаточно простого дизассемблера длин, можешь воспользоваться LDasm'ом от MsRem'a.