персональное ядро

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

  1. cresta

    cresta Active Member

    Публикаций:
    0
    Регистрация:
    13 июн 2004
    Сообщения:
    2.257
    Можно загрузить в своё адресное пространство персональную копию какой-либо dll (допустим advapi), пересчитать адреса импорта, и пользоваться ею вместо той, что была подгружена при запуске процесса.



    Вопрос такой: можно ли реализовать такую штуку в кернеле, и загрузить себе персональное ядро? Не с целью выполнять его код, а для того, чтобы иметь некий эталон для сравнения. И какие могут быть подводные камни?

    Если нет, то почему?
     
  2. Foamplast

    Foamplast New Member

    Публикаций:
    0
    Регистрация:
    6 ноя 2003
    Сообщения:
    80
    Адрес:
    Russia
    1) Какова основная цель?

    2) Реализовать можно, особенно если не выполнять код. Если грузить код для сравнения, то можно загружать ядро как двоичный файл. В этом случае всё элементарно: CreateFile(), ReadFile(), CloseHandle() или что-нибудь аналогичное.
     
  3. Nothing

    Nothing New Member

    Публикаций:
    0
    Регистрация:
    4 авг 2003
    Сообщения:
    139
    Адрес:
    Russia
    Можно загрузить (сэмулировав системный загрузчик) и настроить все импорты, релоки, защиту секций и пр. Может быть даже удастся выполнить какую-нибудь простенькую функцию. Можно как в ядре, так и в юзермоде, они в этом смысле ничем не отличаются. Еще даже флажок есть у LoadLibraryEx (см. MSDN) как раз для этих целей. Только действительно зачем?



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



    А потом основные изменения вносятся (имхо) в область данных, а она в образе на диске отсутствует вообще, так что и сравнить будет не с чем. Ну не настраивать же ворох структур ядра заново! Можно ведь сделать, чтобы код совпадал побайтно, а функция все равно делала не то, что надо :)
     
  4. cresta

    cresta Active Member

    Публикаций:
    0
    Регистрация:
    13 июн 2004
    Сообщения:
    2.257
    Нужна таблица SDT (неискаженная) В принципе я это сделал в юзере, но из драйвера по-моему это значительно проще будет. Код выполнять нет необходимости, просто получить заведомо непатченные адреса вызовов, и по возможности проверить код, начинающийся с этих адресов на идентичность. Данные понятно что изменяются по ходу работы системы, с этим ничего не сделаешь, хотя можно попробовать перенастроить адреса.
     
  5. NullSessi0n

    NullSessi0n New Member

    Публикаций:
    0
    Регистрация:
    20 янв 2006
    Сообщения:
    322
    Кстати, через LoadLibrary ещё и не всякая dll загрузится. С TLS-callback'ами не загружается. Далее, а запросы настоящего ядра ты игнорировать будешь? А копию ядра так просто не создашь, не уронив системы. Не все страницы в память загружены.
     
  6. 90210

    90210 New Member

    Публикаций:
    0
    Регистрация:
    30 авг 2002
    Сообщения:
    16
    Адрес:
    somewhere from Russia
  7. cresta

    cresta Active Member

    Публикаций:
    0
    Регистрация:
    13 июн 2004
    Сообщения:
    2.257
    90210



    Вот как раз это и надо, спасибо.
     
  8. cresta

    cresta Active Member

    Публикаций:
    0
    Регистрация:
    13 июн 2004
    Сообщения:
    2.257
    Ещё вопрос по теме:

    Если вызывать сервисы напрямую, минуя SDT, например так:
    Код (Text):
    1.     push    NULL
    2.     push    memSize
    3.     push    memPtr
    4.     push    SystemProcessesAndThreadsInformation
    5.     mov     ecx,8057A7BAh
    6.     call    ecx


    то при нахождении в контексте процесса system (например по DriverEntry), имеем на выходе STATUS_SUCCESS. Если вызывать таким способом по DeviceIoControl, то имеем ошибку STATUS_ACCESS_VIOLATION. Как я понимаю, из-за того, что находимся в контексте юзермодного процесса. Если причина неполучения доступа в этом, то как можно поправить это дело?
     
  9. Saint German

    Saint German New Member

    Публикаций:
    0
    Регистрация:
    13 сен 2003
    Сообщения:
    222
    дело в том, что скорее всего это из-за PrevMode.
     
  10. cresta

    cresta Active Member

    Публикаций:
    0
    Регистрация:
    13 июн 2004
    Сообщения:
    2.257
    Насколько я понимаю, PrevMode указывает валидность буфера, переданного при I/O: the driver must be able to determine whether the caller's pointers are valid in user mode or kernel mode. Я же не пытаюсь записывать в буфер или читать из буфера, просто вызываю непосредственный адрес в ядре.

    Или я неправильно понял?
     
  11. Four-F

    Four-F New Member

    Публикаций:
    0
    Регистрация:
    31 авг 2002
    Сообщения:
    1.237
    <font color="gray][ cresta</font><!--color--><font color="gray]: Как я понимаю, из-за того, что находимся в контексте юзермодного процесса. ]</font><!--color-->



    Нет.



    Если PreviousMode = UserMode, то NtQuerySystemInformation первым делом проверит, лежат ли переданные её буферы в юзермоде. В том числе и ReturnLength. Поставив SEH будет звать ProbeForWrite. А твой memPtr кажет, наверное, на ядерную память и не проходит EndAddress < MM_USER_PROBE_ADDRESS.


    Код (Text):
    1. VOID
    2. ProbeForWrite (
    3.     IN PVOID Address,
    4.     IN ULONG Length,
    5.     IN ULONG Alignment
    6.     )
    7. {
    8.  
    9.     ULONG_PTR EndAddress;
    10.     ULONG_PTR StartAddress;
    11. . . .
    12.         StartAddress = (ULONG_PTR)Address;
    13.         if ((StartAddress & (Alignment - 1)) == 0) {
    14.  
    15.             EndAddress = StartAddress + Length - 1;
    16.  
    17.             if ((StartAddress <= EndAddress) &&
    18. <font color="red]                (EndAddress < MM_USER_PROBE_ADDRESS)) {</font><!--color-->
    19.  
    20. . . .
    21.                 return;
    22.  
    23.             } else {
    24. <font color="red]                ExRaiseAccessViolation();</font><!--color-->
    25.             }
    26. . . .
    27.         }
    28.     }
    29.  
    30.     return;
    31. }




    <font color="gray][ cresta</font><!--color--><font color="gray]: Если причина неполучения доступа в этом, то как можно поправить это дело? ]</font><!--color-->



    PreviousMode временно поменять на ядерный. Насколько это безопасно - хз. Можно в отледьном WorkItem'е все получить и скопировать юзеру. Либо передевать из юзера буфер.
     
  12. cresta

    cresta Active Member

    Публикаций:
    0
    Регистрация:
    13 июн 2004
    Сообщения:
    2.257
    Four-F

    Если менять PreviousMode небезопасно, то наверное лучше для call XXXXXXXX сделать отдельный тред со своим чисто ядерным буфером, и в нем клацать таймером, заполняя буфер. А в DispatchControl если тред в данный момент не работает с буфером, скопировать буфер в переданную из юзера память. Если тред занят заполнением своего буфера, то взвести KeInitializeTimerEx и зациклить его, пока тред не отработает. Эта схема как, правильная или не очень?
     
  13. Four-F

    Four-F New Member

    Публикаций:
    0
    Регистрация:
    31 авг 2002
    Сообщения:
    1.237
    Проще поручить эту работу системному рабочему потоку. Примерно так:


    Код (Text):
    1. typedef struct _WORKER_CONTEXT {
    2.     PIO_WORKITEM pWorkItem;
    3.     PVOID        pBuffer;
    4.     ULONG        cbBuffer;
    5.     KEVENT       Event;
    6.     BOOL         bDataValid;
    7. } WORKER_CONTEXT, *PWORKER_CONTEXT;
    8.  
    9. WORKER_CONTEXT Context;
    10.  
    11. Context.pWorkItem = IoAllocateWorkItem( pDeviceObject );
    12.  
    13. if ( Context.pWorkItem ) {
    14.  
    15.     KeInitializeEvent( &Context.Event, NotificationEvent, FALSE );
    16.  
    17.     Context.pBuffer  = <указатель на буфер>;
    18.     Context.cbBuffer = <размер буфера>;
    19.  
    20.     Context.bDataValid = FALSE;
    21.  
    22.     IoQueueWorkItem( Context.pWorkItem, Worker, DelayedWorkQueue, &Context );
    23.  
    24.     KeWaitForSingleObject( &Context.Event, UserRequest, UserMode, FALSE, NULL );
    25.  
    26.     if ( Context.bDataValid ) {
    27.  
    28.         //
    29.         //  Context.pBuffer содержит нужные данные
    30.         //
    31.  
    32.     }
    33. }
    34.  
    35. void
    36.   Worker (
    37.     IN PDEVICE_OBJECT   pDeviceObject,
    38.     IN PWORKER_CONTEXT  pContext
    39.     )
    40. {
    41.     PAGED_CODE ();
    42.  
    43.     //
    44.     //  Находясь в контексте системного процесса и на IRQL == PASSIVE_LEVEL,
    45.     //  заполнить pContext->pBuffer нужными данными
    46.     //
    47.  
    48.     if ( OK ) {
    49.  
    50.         pContext->bDataValid = TRUE;
    51.     }
    52.  
    53.     IoFreeWorkItem ( pContext->pWorkItem );
    54.     KeSetEvent( &pContext->Event, IO_NO_INCREMENT, FALSE );
    55. }




    Тут могут быть нюансы. Например, буфер может выделять сама процедура Worker, т.к. заранее может быть не известен его размер, как в твоём случае. А освобождать его будет основной код.
     
  14. cresta

    cresta Active Member

    Публикаций:
    0
    Регистрация:
    13 июн 2004
    Сообщения:
    2.257
    Спасибо, попробую этот вариант.

    Единственно, непонятно, что за PAGED_CODE(); такая?

    На асме это как, а то у меня нет си, чтобы посмотреть на код этой штуки.
     
  15. Four-F

    Four-F New Member

    Публикаций:
    0
    Регистрация:
    31 авг 2002
    Сообщения:
    1.237
    Код (Text):
    1. #if DBG
    2. #define PAGED_CODE() \
    3.     { if (KeGetCurrentIrql() > APC_LEVEL) { \
    4.           KdPrint(( "EX: Pageable code called at IRQL %d\n", KeGetCurrentIrql() )); \
    5.           ASSERT(FALSE); \
    6.        } \
    7.     }
    8. #else
    9. #define PAGED_CODE() NOP_FUNCTION;
    10. #endif




    PAGED_CODE - это макрос. Используется на этапе разработки драйвера. Если функция, в которой он встретился будет вызвана на IRQL > APC_LEVEL, то ASSERT позовёт дебаггер.



    На асме что-то вроде этого:
    Код (Text):
    1. invoke KeGetCurrentIrql
    2.  
    3. .if eax > APC_LEVEL
    4.     invoke DbgPrint, $CTA0("Pageable code called at IRQL %d\n"), eax
    5.     int 3
    6. .endif




    Повторяю: только для дебага. Можешь просто забить на него. Worker Routines всегда вызываются на PASSIVE_LEVEL.



    Пример работы с системным рабочим потоком есть в KmdKit\examples\basic\WorkItem
     
  16. cresta

    cresta Active Member

    Публикаций:
    0
    Регистрация:
    13 июн 2004
    Сообщения:
    2.257
    Да я уже попробовал без этого макроса.

    Все работает. Замаппировал секцию, и прямо её адрес в ядро передал.


    Код (Text):
    1. Worker proc uses ebx pDeviceObject:PDEVICE_OBJECT, pContext: PTR WORKER_CONTEXT
    2.     LOCAL hSection              :HANDLE
    3.     LOCAL oa                    :OBJECT_ATTRIBUTES
    4.     LOCAL ptrSection            :PVOID
    5.     LOCAL ViewSize              :LARGE_INTEGER
    6.     LOCAL status                :DWORD
    7.  
    8.     mov     ebx,pContext
    9.     assume  ebx : ptr WORKER_CONTEXT
    10.     mov     status, STATUS_INVALID_DEVICE_REQUEST
    11.     lea     ecx, oa
    12.     InitializeObjectAttributes ecx, offset g_usProcListSection, OBJ_CASE_INSENSITIVE, NULL, NULL
    13.     invoke  ZwOpenSection, addr hSection, SECTION_MAP_WRITE or SECTION_MAP_READ, addr oa
    14.     .if     (eax == STATUS_SUCCESS)
    15.         mov     ptrSection, NULL
    16.         mov     ViewSize.HighPart, 0
    17.         mov     ViewSize.LowPart, 0
    18.         invoke  ZwMapViewOfSection, hSection, NtCurrentProcess, addr ptrSection, 0, 10000000,
    19.                                 NULL, addr ViewSize, ViewShare, 0, PAGE_READWRITE
    20.         .if     (eax == STATUS_SUCCESS)
    21.             mov     eax,ptrSection
    22.             mov     g_hMemProcThread,eax
    23.             mov     g_ProcThreadMemSize,10000000
    24.             call    GetProcessList
    25.             mov     [ebx].bDataValid, TRUE
    26.             mov     status, STATUS_SUCCESS
    27.             invoke  ZwUnmapViewOfSection, NtCurrentProcess, ptrSection
    28.         .endif
    29.         invoke ZwClose, hSection
    30.     .endif
    31.     invoke  IoFreeWorkItem, [ebx].pWorkItem
    32.     invoke  KeSetEvent, addr [ebx].Event, IO_NO_INCREMENT, FALSE
    33.     assume  ebx : nothing
    34.     mov     eax,status
    35.     ret
    36. Worker endp




    Спасибо за помощь :)
     
  17. Four-F

    Four-F New Member

    Публикаций:
    0
    Регистрация:
    31 авг 2002
    Сообщения:
    1.237
    Забыл спросить. Чему у тебя равен ptrSection. Я когда статью писАл, маленько поэкспериментировал с ZwMapViewOfSection. У меня получалось, что сколько раз её не зови, она всё время возвращает новый адрес, но только он кажет в юзерные адреса. Т.е. если находится в контексте юзерного процесса, то секция переотображается в юзера же и приходится ставить SEH. Интересно как ведёт себя ZwMapViewOfSection в контексте системного процесса.



    ЗЫ: 10000000... Хм. 2500 страниц... не многовато ли?
     
  18. cresta

    cresta Active Member

    Публикаций:
    0
    Регистрация:
    13 июн 2004
    Сообщения:
    2.257
    Для юзера вызов ZwMapViewOfSection всегда выдает адрес AA0000h.

    Для кернела в течение одной загрузки windows ZwMapViewOfSection выдаёт одинаковые адреса, например такие: 170000h, или 150000h. Перезапуск самого драйвера в течение одной загрузки windows адрес не меняет.



    10000000... Хм. 2500 страниц... не многовато ли?

    Я даже думаю мало, надо бы до 20 мег увеличить :)

    В обычных условиях хватает памяти порядка 32-128 кБайт.

    Но у меня тут живет hxdef100 :), и если действовать неосторожно, то он просекает, что кто-то пытается открыть его процесс, и каким-то образом влияет на размер памяти, запрашиваемой ZwQuerySystemInformation - небходимый для успешного вызова размер возрастает до 16 Мег.

    Чтобы не перевыделять память 10 раз подряд, сразу выделяю достаточное кол-во.
     
  19. cresta

    cresta Active Member

    Публикаций:
    0
    Регистрация:
    13 июн 2004
    Сообщения:
    2.257
    В продолжение темы...

    по ссылке от 90210 в исходниках есть такая процедура : getKiServiceTableAddr. И в комментариях к ней указано, что это "a more stable way to find the address of KiServiceTable". Если автор процедуры заглянет в топик, то к нему вопрос:

    чем это процедура стабильней "стандартного" способа с пересчетом, использованного, например в SDTrestore? "Стандартный" способ может дать неправильный результат?

    Или может кто-то ещё разбирался с этим вопросом, просветите пожалуйста.
     
  20. Saint German

    Saint German New Member

    Публикаций:
    0
    Регистрация:
    13 сен 2003
    Сообщения:
    222
    cresta

    а ты с касперским юзал этот китайский метод?