Создать дамп памяти ядра Ntoskrnl.exe

Тема в разделе "WASM.NT.KERNEL", создана пользователем Marylin, 15 мар 2024.

Метки:
  1. Marylin

    Marylin Active Member

    Публикаций:
    0
    Регистрация:
    17 фев 2023
    Сообщения:
    128
    Всем привет!
    Возникла необходимость из драйвера сдампить процесс Ntoskrnl, чтобы получить значения неэкспортирумых им переменных. Возможно-ли это в современных системах х64, ведь начиная вроде с Win8 появились сторожа типа KVAS (Kernel Virtual Address Shadow, затенение памяти ядра), и прочие. Так-же слышал, что ядро закрывает к себе доступ через мутанта (мьютекс), и чтобы сбросить его, нужно найти хэндл этого мутанта. Надеюсь не всё так плохо, и варианты получить дамп всё-же имеются? Мой скилл по драйверам на уровне "Hello World", но если кто поможет определиться лишь с функциями, то дальше я сам. На данный момент план такой:

    1. Из юзера чз DeviceIoControl() посылаю запрос. Какой из режимов обмена выбрать? Говорится, что "METHOD_BUFFERED" простой, но подходит для небольших объёмов данных. Однако не указывается, насколько небольших? Я пробовал дампить ядро в WinDbg командой .dump /m d:\krnl.dmp, и получил файл ~300Kb (miniDump). Подходит-ли он под определение "небольших"? Если-же использовать обмен через DMA с флагом "METHOD_IN_DIRECT", то вообще непонятно, куда помещать буфер с данными.

    2. Дров находит базу образа Ntoskrnl.exe в памяти (с этим проблем нет), и выделяет память PsAllocatePoolWighTag() с флагом "NonPagedPool".

    3. Теперь как произвести непосредственно копирование данных? MmCopyVirtualMemory() это то, что первое приходит в голову.

    Какие ещё есть нюансы, и на что в первую очередь нужно обратить внимание?
    Если есть готовые примеры, будет вообще здорово. А так, буду благодарен и просто за список функций в порядке их вызова.

    PS:\\ Сейчас в качестве экпериментов я написал юм.приложение, которое парсит уже готовый файл из дира c:\Windows\Minidump\*.dmp. Как оказалось, в нём тоже имеется образ Ntoskrnl, с нужной мне заполненной структурой "KDDEBUGGER_DATA64", где лежат адреса неэкспортуемых переменных ядра. Но система создала этот дамп хз когда, в результате чего ASLR делает адреса не валидными на текущий момент. Для справки, все имеется 140 переменных в ядре, из которых кернел выдаёт на экспорт лишь одну PsInitialSystemProcess. Вот результат в консоли, где переменные со-значением нуль я пропускаю:

    Код (Text):
    1.   OS version:  Windows 6.1.7601. Service Pack 1
    2.  
    3. ====== KERNEL INFO ================
    4.        Image:  xNtKrnl.exe
    5.      License:  Hacked for activation!
    6.         Base:  fffff800`02c4a000
    7.   EntryPoint:  fffff800`02efc6f0
    8.     Sections:  24
    9.  
    10. ====== CRASH-DUMP =================
    11.    File name:  C:\Windows\minidump\011823-36707-01.dmp
    12.    File size:  277072 byte
    13.        Magic:  PAGEDU64
    14.  
    15. ====== KDDEBUGGER_DATA64 ==========
    16.       Offset:  00008858h
    17.       Length:  832 byte
    18.  
    19. List of variable address..
    20.  
    21.    fffff800`02c8f730  PsLoadedModuleList
    22.    fffff800`02c71420  PsActiveProcessHead
    23.    fffff800`02c71468  PspCidTable
    24.    fffff800`02c66450  ExpSystemResourcesList
    25.    fffff800`02cf9a30  ExpPagedPoolDescriptor
    26.    fffff800`02cf9078  ExpNumberOfPagedPools
    27.    fffff800`02cf9094  KeTimeIncrement
    28.    fffff800`02cc23a0  KeBugCheckCallbackListHead
    29.    fffff800`02cbc220  KiBugcheckData
    30.    fffff800`02cc6990  IopErrorLogListHead
    31.    fffff800`02c72070  ObpRootDirectoryObject
    32.    fffff800`02c72088  ObpTypeObjectType
    33.    fffff800`02cfcb00  MmSystemCacheWs
    34.    fffff800`02cf9280  MmPfnDatabase
    35.    fffff800`02cf9a00  MmSubsectionBase
    36.    fffff800`02c78dc4  MmNumberOfPagingFiles
    37.    fffff800`02cf9300  MmLowestPhysicalPage
    38.    fffff800`02cf9108  MmHighestPhysicalPage
    39.    fffff800`02cf9060  MmNumberOfPhysicalPages
    40.    fffff800`02cf92e8  MmMaximumNonPagedPoolInBytes
    41.    fffff800`02cf90b8  MmNonPagedPoolStart
    42.    fffff800`02cf91a0  MmPagedPoolEnd
    43.    fffff800`02cf9940  MmPagedPoolInformation
    44.    00000000`00001000  MmPageSize
    45.    fffff800`02cf9498  MmSizeOfPagedPoolInBytes
    46.    fffff800`02c77db8  MmTotalCommitLimit
    47.    fffff800`02c77dc0  MmTotalCommittedPages
    48.    fffff800`02c622d0  MmSharedCommit
    49.    fffff800`02c8f7c8  MmDriverCommit
    50.    fffff800`02c77d90  MmProcessCommit
    51.    fffff800`02c77e00  MmPagedPoolCommit
    52.    fffff800`02cfc500  MmZeroedPageListHead
    53.    fffff800`02cfcac0  MmFreePageListHead
    54.    fffff800`02cfcbc0  MmStandbyPageListHead
    55.    fffff800`02cfc2c0  MmModifiedPageListHead
    56.    fffff800`02c39cd0  MmModifiedNoWritePageListHead
    57.    fffff800`02c44bc0  MmAvailablePages
    58.    fffff800`02c44c00  MmResidentAvailablePages
    59.    fffff800`02c66a68  PoolTrackTable
    60.    fffff800`02c50580  NonPagedPoolDescriptor
    61.    fffff800`02cf9010  MmHighestUserAddress
    62.    fffff800`02cf9100  MmSystemRangeStart
    63.    fffff800`02cf9000  MmUserProbeAddress
    64.    fffff800`02c51f70  KdPrintCircularBuffer
    65.    fffff800`02c52f70  KdPrintCircularBufferEnd
    66.    fffff800`02c3a028  KdPrintWritePointer
    67.    fffff800`02c52f70  KdPrintRolloverCount
    68.    fffff800`02c8ff90  MmLoadedUserImageList
    69.    fffff800`02bffb00  NtBuildLab
    70.    fffff800`02cf9c40  KiProcessorBlock
    71.    fffff800`02cf91f0  MmUnloadedDrivers
    72.    fffff800`02c8f728  MmLastUnloadedDriver
    73.    fffff800`02c6fb48  MmTriageActionTaken
    74.    fffff800`02cf90dc  MmSpecialPoolTag
    75.    fffff800`02c64ed8  KernelVerifier
    76.    fffff800`02c8fb80  MmVerifierData
    77.    fffff800`02c792b0  MmAllocatedNonPagedPool
    78.    fffff800`02c77df0  MmPeakCommitment
    79.    fffff800`02c77dc8  MmTotalCommitLimitMaximum
    80.    fffff800`02cc8c3c  CmNtCSDVersion
    81.    fffff800`02cf9038  MmPhysicalMemoryBlock
    82.                 007c  OffsetKThreadNextProcessor
    83.                 00b8  OffsetKThreadTeb
    84.                 0038  OffsetKThreadKernelStack
    85.                 0028  OffsetKThreadInitialStack
    86.                 0070  OffsetKThreadApcProcess
    87.                 0164  OffsetKThreadState
    88.                 04e8  SizeEProcess
    89.                 0338  OffsetEprocessPeb
    90.                 0290  OffsetEprocessParentCID
    91.                 0028  OffsetEprocessDirTableBase
    92.                 4d00  SizePrcb
    93.                 21da  OffsetPrcbDpcRoutine
    94.                 0008  OffsetPrcbCurrentThread
    95.                 05f4  OffsetPrcbMhz
    96.                 05f0  OffsetPrcbCpuType
    97.                 4bb8  OffsetPrcbVendorString
    98.                 0120  OffsetPrcbProcStateContext
    99.                 0024  OffsetPrcbNumber
    100.                 04a8  SizeEThread
    101.    fffff800`02c3a030  KdPrintCircularBufferPtr
    102.    fffff800`02c3a038  KdPrintBufferSize
    103.    fffff800`02cc33e0  KeLoaderBlock
    104.                 4e80  SizePcr
    105.                 0018  OffsetPcrSelfPcr
    106.                 0020  OffsetPcrCurrentPrcb
    107.                 0180  OffsetPcrContainedPrcb
    108.                 0040  OffsetPrcbProcStateSpecialReg
    109.                 0010  GdtR0Code
    110.                 0018  GdtR0Data
    111.                 0033  GdtR3Code
    112.                 002b  GdtR3Data
    113.                 0040  GdtTss
    114.                 0023  Gdt64R3CmCode
    115.                 0053  Gdt64R3CmTeb
    116.    fffff800`02cc64a0  IopNumTriageDumpDataBlocks
    117.    fffff800`02cc60a0  IopTriageDumpDataBlocks
    118.    fffff800`02c779e8  MmBadPagesDetected
    119.    fffff800`02c779e4  MmZeroedPageSingleBitErrors
    120.    fffff800`02bfb770  EtwpDebuggerData
    121.  
    122. --------------------------
    123. Total found:  100 variables
    124.  
     
  2. HoShiMin

    HoShiMin Well-Known Member

    Публикаций:
    5
    Регистрация:
    17 дек 2016
    Сообщения:
    1.430
    Адрес:
    Россия, Нижний Новгород
    Требуется сделать именно крашдамп dmp, который можно открыть в WinDbg, или просто прочитать что-то из ядерной памяти?
    Никаких проблем с чтением нет, никакими мутантами ядро не закрывается.
    Как только попал в ядро, можешь читать его память обычным разыменовыванием указателей или через memcpy.
    Важный момент: ты должен убедиться, что память, которую пытаешься прочитать, существует. В отличие от юзермодной памяти, в ядре нельзя через __try..__except обработать Access Violation на обращении к ядерной памяти - система упадёт.
    Чтобы убедиться, что память существует, можешь построить отображение через IoAllocateMdl -> MmProbeAndLockPages -> MmMapLockedPagesSpecifyCache - и читать из этого отображения.
    Разница не в том, какой из методов "простой" или "сложный", а в том, как переданные буферы будут передаваться в ядро.
    METHOD_BUFFERED выделяет в ядре буфер, выбирая в качестве размера больший из размеров входного и выходного буферов - т.е., max(inSize, outSize). Затем копирует в этот буфер данные из входного юзермодного буфера, и в обработчике IRP ты работаешь с этим ядерным буфером. Затем (на возврате в юзермод) система копирует содержимое этого буфера в выходной юзермодный буфер. Валидацию адресов система берёт на себя. Минус этого метода в накладных расходах на выделение буфера в ядре.
    METHOD_NEITHER - напротив - передаёт в ядро сырые юзермодные адреса буферов, не производя никакой валидации. Накладных расходов нет, доп. память не выделяется, проверок нет - предполагается или что адреса доверенные, или что ты провалидируешь их сам.
    METHOD_IN_DIRECT/METHOD_OUT_DIRECT - система формирует Mdl для буфера (но не отображает его) и проверяет, что буфер доступен на чтение или запись (в зависимости от метода).
    Детально можно прочитать здесь: https://learn.microsoft.com/en-us/w...nel/buffer-descriptions-for-i-o-control-codes
    Обычно используют или METHOD_BUFFERED, или METHOD_NEITHER (например, я у себя в драйверах использую NEITHER) - можешь выбирать любой из них, тебе подойдут оба.
    Скопировать можно через обычный memcpy. Вопрос лишь в том, сколько копировать. Для этого придётся распарсить PE-заголовок ядра, чтобы понять, где какие секции лежат и какие у них границы.
    Важный момент, что в ядре есть Discardable-секции, память которых освобождается после инициализации. Обычно в таких секциях лежит код или данные, которые нужны только единожды при инициализации ядра/драйвера.
    Иными словами, память ntoskrnl не непрерывная - в нём будут дыры. Поэтому нельзя просто сделать один memcpy от ImageBase до ImageBase+SizeOfImage - надо полноценно разбирать список секций и копировать только те, которые не Discardable.

    Но возможно, ты пытаешься сложным способом решить простую проблему: присмотрись к LiveKd или к Live Kernel-Mode Debugging - возможно, вручную дампить ядро тебе и не понадобится.
     
    Marylin нравится это.
  3. Marylin

    Marylin Active Member

    Публикаций:
    0
    Регистрация:
    17 фев 2023
    Сообщения:
    128
    Нет, мне просто нужно сдампить память модуля Ntoskrnl.exe, а WinDbg я просто использовал в своих тестах. LiveKd у меня установлен, но на другой машине его может и не быть, поэтому последний вариант отпадает, хотя нужно копнуть штатные либы винды DbgEng/DbgHelp.dll - в них тоже вроде были com-методы для создания дампов (правда юзер или кернел, хз).

    Да я в курсе, и планировал чекать её через MmIsAddressValid(), но оказывается есть и другой (более надёжный) способ через MmProbeAndLockPages(), хотя здесь его тоже критикуют. Ну да ладно.. с этим разберусь.

    Как правило, выгружаемые в ядре это секция INIT и ресурсов, но я уже знаю, что нужные мне данные находятся в одной из секций-данных, поэтому в РЕ-заголовке буду искать только их. Спасибо, что определил мне фронт работы.
     
  4. HoShiMin

    HoShiMin Well-Known Member

    Публикаций:
    5
    Регистрация:
    17 дек 2016
    Сообщения:
    1.430
    Адрес:
    Россия, Нижний Новгород
    Если предполагается что-то искать у пользователей, то зачем нужен дамп? Почему бы не найти нужные адреса сразу в ядре по сигнатурам?
    Или вообще скачать символы под пользовательское ядро и парсить через DbgHelp или DIA. Тогда и эвристика не нужна, и можешь получить адрес любой неэкспортирующейся функции или переменной.
    Это и самый простой, и самый надёжный способ.
    --- Сообщение объединено, 16 мар 2024 ---
    Кстати, второй способ, который он предложил (MmGetPhysicalAddress + MmMapIoSpace), хоть и защитит от крашей, но может делать не то, что ожидается.
    Он прочитает физическую память, но нет гарантии, что это будет та память, на которую смотрит виртуальный адрес.
    Например, страничка может уйти в своп, и мы будем читать мусор (или даже чужую память, которая выделится на этой освободившейся физической).
    Или, если читаем больше одной странички или переходим через границу страниц, может оказаться, что непрерывная виртуальная память раскидана по рандомным физическим адресам (и читать через memcpy сразу весь буфер нельзя).
    А MmCopyMemory - в принципе, да, хорошее решение.
     
    Marylin нравится это.
  5. Marylin

    Marylin Active Member

    Публикаций:
    0
    Регистрация:
    17 фев 2023
    Сообщения:
    128
    Спасибо за зоопарк идей, только я не уверен, что это будет самый надёжный способ.
    • Во-первых код должен воркать на всех системах Win7/8/10/11, и т.к. ядра у них отличаются,
    то придётся таскать с собой все 4 пакета символов, что совсем не вдохновляет.
    • Во-вторых, начиная с Win8 нужный мне "блок отладчика" шифруется как на диске, так и в памяти. Хоть алго шифрования примитивен и всем известен, всё-же это накладные расходы. Зато в дампы система сбрасывает уже декрипнутый образ, а потому парсить его легче.

    Я тут обратил внимание на DbgEng.dll - она входит в состав всех систем 7..11 даже без установленного WinDbg, а значит можно будет получить кроссплатформенность. В её интерфейсе "IDebugClient" есть уже готовый метод WriteDumpFile(), что позволит создавать мини-дампы как юзер, так и кернел спэйс. И всё это прямо под юзером. Так автоматом снимается прабла с подписью драйвера, и код будет работать на всех платформах. Потискаю этот вариант, может что и получится.
     
  6. HoShiMin

    HoShiMin Well-Known Member

    Публикаций:
    5
    Регистрация:
    17 дек 2016
    Сообщения:
    1.430
    Адрес:
    Россия, Нижний Новгород
    Нет, не таскать с собой, а скачивать на месте под конкретное пользовательское ядро, поскольку символы подходят только под конкретную сборку и меняются при каждом обновлении системы.
    Ядро постоянно меняется, и смещения, которые работают сейчас, через неделю могут сломаться после первого же обновления, поэтому единственный надёжный вариант - только работать с символами.

    В первом посте ты писал, что тебе нужен KDDEBUGGER_DATA64 с адресами.
    Если работаешь с символами, эту структуру уже искать не нужно, поскольку адрес любой неэкспортирующейся функции ты можешь получить напрямую.
     
    Marylin нравится это.
  7. alex_dz

    alex_dz Active Member

    Публикаций:
    0
    Регистрация:
    26 июл 2006
    Сообщения:
    362
    Советую почитать книги Дмитрия Востокова
    upload_2024-3-16_11-44-54.png
     
    Marylin нравится это.
  8. Marylin

    Marylin Active Member

    Публикаций:
    0
    Регистрация:
    17 фев 2023
    Сообщения:
    128
    Конечно могут, только не в случае со-структурой "KDDEBUGGER_DATA64".
    Если обнова добавляет в ядро переменные, то они ложатся в конец уже имеющихся, о чём явно говорится в хидере "wdbgexts.h".

    Код (C++):
    1. //
    2. // DO NOT ADD OR REMOVE FIELDS FROM THE MIDDLE OF THIS STRUCTURE!!!
    3. //
    4. // If you remove a field, replace it with an "unused" placeholder.
    5. // Do not reuse fields until there has been enough time for old debuggers and extensions to age out.
    6. //
    7. typedef struct _KDDEBUGGER_DATA64 {
    8.     DBGKD_DEBUG_DATA_HEADER64 Header;
    9.     //
    10.     // Base address of kernel image
    11.     //
    12.     ULONG64   KernBase;
    13. //..........
    14.  
    Вообще о каких символах идёт речь? Если ты имеешь в виду отладочные *.pdb, то в них вообще нет адресов, а только имена переменных, хотя адреса могут быть зашифрованы. Да и геморно качать каждый раз с инета до запуска мелкой софтины, тем более, что сервак мелкомягких не всегда доступен (например в нашем регионе). Или мы о разном говорим?
     
  9. HoShiMin

    HoShiMin Well-Known Member

    Публикаций:
    5
    Регистрация:
    17 дек 2016
    Сообщения:
    1.430
    Адрес:
    Россия, Нижний Новгород
    Да, про pdb. В них не адреса, а смещения относительно ImageBase.
    DbgHelp может сам пересчитывать их в адреса, если укажешь верный ImageBase при открытии pdb, или можешь пересчитать смещения в оффсеты вручную, просто прибавив к ним ImageBase.
    Пример работы с символами можно посмотреть здесь: https://github.com/HoShiMin/formatPE/tree/main/formatPE/Pdb
    Здесь же есть и скачивалка символов.
    Скачивать каждый раз не обязательно: если символы под ядро уже скачаны и совпадают с билдом самого ядра - незачем перекачивать их снова.
     
  10. k3rnl

    k3rnl Member

    Публикаций:
    0
    Регистрация:
    28 янв 2021
    Сообщения:
    39
    Windows 8 и все её защиты это детские забавы.
    Изоляция ядра Windows 10 и Windows 11 вот где уже другой уровень Включение целостности памяти | Microsoft Learn
    Куча самописных драйверов полегли под VBS. И если гипервизор включен в 10 или 11, то добраться до ядра через драйвер (без определённой подписи) невозможно.
     
  11. UbIvItS

    UbIvItS Well-Known Member

    Публикаций:
    0
    Регистрация:
    5 янв 2007
    Сообщения:
    6.124
    скоро они самописки вне закона объявят - хошЪ чой-нть накропать, покупай лицуху иль делай заказ сертификатам (прогерам с лицухой). :)
     
  12. HoShiMin

    HoShiMin Well-Known Member

    Публикаций:
    5
    Регистрация:
    17 дек 2016
    Сообщения:
    1.430
    Адрес:
    Россия, Нижний Новгород
    VBS вводит совсем не много ограничений, разработчики драйверов их обычно даже не замечают.
    Нельзя выделять исполняемую память, нельзя исполняемую память делать записываемой, нельзя менять ядерные структурки, типа IDT или GDT.
    Драйверам и до VBS ничто из этого не было нужно - если драйвер не делает ничего зловредного, ему VBS никак не помешает.


    Никаких проблем нет: если ты попал в ядро - можешь читать память. Пока не захотел что-то пропатчить, что патчить нельзя - проблем нет (в принципе, требования те же, которые выдвигает PatchGuard).
    С сертификатами сложнее, но не из-за VBS, а вообще: для загрузки с SecureBoot теперь нужен EV, который выпускают только на юрлица, и подписывать требуется на портале MS.
    Если для покупки EV можно за пару дней оформить ИП, то с личным кабинетом проблема: MS, следуя санкциям, заблокировал все учётки из РФ и Беларуси, поэтому компании из этих стран подписывать драйвера не смогут. Только если оформят юрлица в других странах.
     
    k3rnl нравится это.
  13. alex_dz

    alex_dz Active Member

    Публикаций:
    0
    Регистрация:
    26 июл 2006
    Сообщения:
    362
  14. HoShiMin

    HoShiMin Well-Known Member

    Публикаций:
    5
    Регистрация:
    17 дек 2016
    Сообщения:
    1.430
    Адрес:
    Россия, Нижний Новгород
    8 Gen же. 11е только для 21H2, а для 22H2 и более новых достаточно восьмого.
     
  15. Marylin

    Marylin Active Member

    Публикаций:
    0
    Регистрация:
    17 фев 2023
    Сообщения:
    128
    Чёт не хочет делать дампы IDebugClient::WriteDumpFile, при этом LastError утверждает "Операция успешна", но в файле дампа пусто с размером нуль. Может криво вызываю её, а толковых примеров в гугле не нашёл. Но как оказалось, удовлетворяющее мои запросы решение лежит на поверхности, осталось только протестить на разных системах от 7 до 11.

    Проблема моя была в том, чтобы получить валидные на данный момент адреса переменных. Но если вскрыть минидамп, то в заголовке его структуры "KDDEBUGGER_DATA64" есть уже база, от которой и пляшут зашитые в дамп адреса. Тогда получается, что если взять из дампа адрес переменной и отнять от неё лежащую рядом (в заголовке) базу, то получим смещение RVA переменной. Остаётся вызвать NtQuerySystemInformation() с классом "SysModuleInfo=11", и к вернувшейся базе Ntoskrnl прибавить этот RVA.

    Код (ASM):
    1. struct KDDEBUGGER_DATA64
    2.     Header            DBGKD_DEBUG_DATA_HEADER64
    3.     KernBase          dq  0    ;//<------------ База Ntoskrnl в дампе!
    4.     BpWithStatus      dq  0
    5.     SavedContext      dq  0
    6. ;//...........
    7.     lodsq    ;//<--------------;// Берём переменную из дампа
    8.     sub    rax,[dumpKrnlBase]  ;// оставляем в ней только смещение
    9.     add    rax,[realKrnlBase]  ;// + база на текущий момент.
    Я протестировал на своих машинах Win7/10, и если командой dps PspCidTable L1 запросить у отладчика WinDbg переменную (любую), то всё совпадает. В скрепку цепляю файл в надежде, что кто-нибудь проверит его так-же на своей Win11.

    kVar.png
     

    Вложения:

    • KrnlVarList.zip
      Размер файла:
      5,2 КБ
      Просмотров:
      66
  16. k3rnl

    k3rnl Member

    Публикаций:
    0
    Регистрация:
    28 янв 2021
    Сообщения:
    39
    Речь о специфических драйверах, которые точно не делают ничего зловредного. К примеру просто восстанавливают некоторый функционал в системном драйвере, который был вырезан майкрософом.


    Здесь соглашусь. Но вероятность споткнуться об уровни VTL при патчинге памяти всё эе есть.
    Может кому интересно будет https://connormcgarr.github.io/hvci/
     
    Последнее редактирование: 18 мар 2024
  17. Marylin

    Marylin Active Member

    Публикаций:
    0
    Регистрация:
    17 фев 2023
    Сообщения:
    128
    Раз уж создал тему, немного объясню, что здесь к чему..
    Зачем вообще искать переменные ядра, ведь в WDK всё и так расписано?
    Проблема в том, что формат ядерных структур не статичен для разных версий ОС. Если мы хотим написать кроссплатформенный дров Win7+, придётся динамически вычислять нужные поля в структурах, а поскольку они плавают, то тривиальная задача превращается в проблему. Именно поэтому для каждой системы мягкие выпускают свой WDK, в хидерах которых описываются структуры конкретно взятой ОС, ..например WDK-7 для Win7, WDK-10 для бесятки, и т.д.

    Такая картина приводит к тому, что скомпилированный на Win10 драйвер может вызвать сбой на Win7/11, т.к. оффсеты уже съехали с насиженных мест, и указывают теперь в космос. Здесь и приходят на помощь зашитые в кернел неэкспортируемые наружу переменные. Да, в ядре есть функции для поиска структур типа PsInitialSystemProcess() (линк на EPROCESS системы), но они возвращают только базу в памяти, а смещение полей приходится указывать явно, например: Eprocess->VadRoot. При этом компиль вместо VadRoot подставит взятое из хидера Win7 смещение, превратив запрос в Eprocess + 0x440, в то время как на Win10 это смещение будет уже иным.

    Динамический поиск переменных в памяти ядра можно осуществить двумя способами: (1)через структуру процессора "PCR" (Processor Control Region, область управления процессором), и (2)поиском сигнатуры "KDBG" в секции-данных модуля Ntoskrnl.exe. Рассмотрим оба варианта для 64-битных систем Windows.

    1. Поиск через цепочку структур

    Для начала нужно найти адрес структуры "PCR" в озу (у каждого ядра cpu она своя). Получить этот адрес в отладчике WinDbg можно запросом !pcr, а в реальных условиях на него указывает сегментный регистр(GS). Здесь нужно отметить, что в х64 значение GS хранится в двух MSR-регистрах - это IA32_GS_BASE=0xC0000101 (под юзером всегда указывает на TEB потока), а также IA32_KERNEL_GS_BASE=0xC0000102 (хранит линк на PCR). Но при входе процессора в Ring(0) по syscall, значения этих MSR меняются местами, а при выходе sysret опять восстанавливаются. Раньше на х32 для этих целей юзалась таблица GDT с дескриптором FS, сейчас-же отказались от чтения памяти GDT, в пользу регистров MSR. Мой WinDbg почему-то игнорит команду rdmsr, поэтому приложу скрин "Read & Write Utility":

    pcr.png

    Теперь имеем адрес PCR (в ядре числится как kpcr), и можем её прочитать.
    Начиная с Vista, в этой структуре появилось новое поле "KdVersionBlock", которое в ХР лежало в резерве. Если верить кодокопателям, в нём должен быть прописан указатель на ещё одну структуру "DBGKD_GET_VERSION64", и только она (в свою очередь) хранит уже линк на нужную нам "KDDEBUGGER_DATA64". Походу сильно Microsoft пыталась скрыть её, что закатала в такую матрёшку:

    Код (Text):
    1. 0: kd> dt _kpcr fffff800`02defd00
    2. ntdll!_KPCR
    3.    +0x000  NtTib           : _NT_TIB
    4.    +0x000  GdtBase         : 0xfffff800`03ffb000  _KGDTENTRY64
    5.    +0x008  TssBase         : 0xfffff800`03ffc080  _KTSS64
    6.    +0x010  UserRsp         : 0xad848
    7.    +0x018  Self            : 0xfffff800`02defd00  _KPCR
    8.    +0x020  CurrentPrcb     : 0xfffff800`02defe80  _KPRCB
    9.    +0x028  LockArray       : 0xfffff800`02df04f0  _KSPIN_LOCK_QUEUE
    10.    +0x030  Used_Self       : 0x000007ff`fffde000   Void
    11.    +0x038  IdtBase         : 0xfffff800`03ffb080  _KIDTENTRY64
    12. ;//...................
    13.    +0x100  Unused2         : 0
    14.    +0x108  KdVersionBlock  : (null)  ;//<--- указатель на "DBGKD_GET_VERSION64"
    15.    +0x110  Unused3         : (null)
    16.    +0x118  PcrAlign1       : [24] 0
    17.    +0x180  Prcb            : _KPRCB
    18. 0: kd>
    Но я провожу тесты на Win7 и здесь видно, что в поле "KdVersionBlock" лежит зеро, т.е. никакого указатель уже в нём нет - осталась лишь вложенная структура планировщика "KPRCB" (Kernel Processor Control Block), которая интереса для нас не представляет. Тогда как ядро находит базу "KDDEBUGGER_DATA64" в памяти? Для этого в функцию кернел KdSystemDebugControl() был добавлен новый функционал, и теперь легальным способом можно найти структуры отладчика только чз неё (кстати кастрированный вариант этой функи есть и в юзерской Ntddl.dll). Более подробно здесь.

    Код (Text):
    1. 0: kd> dt _DBGKD_GET_VERSION64
    2. nt!_DBGKD_GET_VERSION64
    3.    +0x000  MajorVersion       : Uint2B
    4.    +0x002  MinorVersion       : Uint2B
    5.    +0x004  ProtocolVersion    : UChar
    6.    +0x005  KdSecondaryVersion : UChar
    7.    +0x006  Flags              : Uint2B
    8.    +0x008  MachineType        : Uint2B
    9.    +0x00a  MaxPacketType      : UChar
    10.    +0x00b  MaxStateChange     : UChar
    11.    +0x00c  MaxManipulate      : UChar
    12.    +0x00d  Simulation         : UChar
    13.    +0x00e  Unused             : Uint2B
    14.    +0x010  KernBase           : Uint8B
    15.    +0x018  PsLoadedModuleList : Uint8B
    16.    +0x020  DebuggerDataList   : Uint8B  ;//<---- указатель на "KDDEBUGGER_DATA64"
    17. 0: kd>

    2. Поиск сигнатуры "KDBG" в секции-данных Ntoskrnl.exe

    Задача - найти базовый адрес загрузки модуля Ntoskrnl в память, и прочитать из его РЕ-заголовка смещение/размер секции ".data". Базу ядра можно получить разными способами, но самый простой - пнуть Nt/ZwQuerySystemInformation() с параметром "SysModuleInfo=11". Здесь-же я приведу порядок трёх команд в отладчике - это: lmn (load module name), !dh (display header), и s-b (search byte) для поиска сигнатуры "KDBG".

    Код (Text):
    1. 0: kd> lmn
    2. Start              End                 Module   Name
    3. fffff800`02c11000  fffff800`031f7000   nt       ntkrnlmp.exe  ;//<--- модуль Krnl
    4. fffff800`031f7000  fffff800`03240000   hal      hal.dll
    5. fffff800`00b9b000  fffff800`00ba5000   kdcom    kdcom.dll
    6.  
    7. ;//---------------------------------------------------------
    8. ;//---- Запрашиваем РЕ-Header: ключ(s) = только секции  ----
    9. ;//---------------------------------------------------------
    10. 0: kd> !dh -s  fffff800`02c11000
    11. SECTION HEADER #7
    12.    .data  name
    13.    93D48  virtual size
    14.   1E8000  virtual address
    15. C8000040  flags:  Data, NotPaged, Read/Write
    16.  
    17. ;//----------------------------------------------------------------------------------
    18. ;//---- VA секции: fffff800`02c11000 + 1e8000 = fffff800`02df9000  ------------------
    19. ;//---- Аргументом в "s-b" передаём диапазон поиска: VA --> VA + 93d48  -------------
    20. ;//---- Сигнатуру/патерн "KDBG" указываем в виде кодов ASCII = 4b 44 42 47  ---------
    21. ;//----------------------------------------------------------------------------------
    22. 0: kd> s-b  fffff80002df9000  fffff80002df9000 + 0x93d48  4b 44 42 47
    23. fffff800`02dfe120   4b 44 42 47 40 03 00 00-00 10 c1 02 00 f8 ff ff  KDBG@...........
    Упс - нашли! Открываем хидер "wdbgexts.h" где лежит описание структуры "KDDEBUGGER_DATA64".
    Значит сигнатура "KDBG" прописывается в поле "OwnerTag", а предшествует ей связанный список "LIST_ENTRY", размером в два qword'a. То-есть реальный указатель на структуру будет: fffff800`02dfe110. После тэга следует sizeof всего блока 0x340, база Ntoskrnl 0xfffff800`02c11000 и прочие поля. Что касается непосредственно переменных ядра, они начинаются сразу после данного заголовка:

    Код (Text):
    1. struct KDDEBUGGER_DATA64
    2.     List              dq  0,0  ;// LIST_ENTRY64
    3.     OwnerTag          dd  0    ;// тег в виде строки "KDBG"
    4.     Size              dd  0    ;// размер "KDDEBUGGER_DATA64"
    5.     KernBase          dq  0
    6.     BpWithStatus      dq  0
    7.     SavedContext      dq  0
    8.     ThCallbackStack   dw  0
    9.     NextCallback      dw  0
    10.     FramePointer      dw  0
    11.     PaeEnabled        dw  0
    12.     KiCallUserMode    dq  0    ;// kernel routine
    13.     KeUserCbDispatch  dq  0    ;// address in ntdll
    14. ;//.........
    15. ends
    Теперь если командой dqs (qword symbol) запросить в отладчике дамп этого адреса памяти, получим лог ниже, который можно представить в качестве пруфов POC. Поскольку размер всей структуры 0x340 (а шаг отображения размером qword), указываем L340/8:

    Код (Text):
    1. 0: kd> dqs  fffff800`02dfe110 L340/8
    2. fffff800`02dfe110  fffff800`02e1a0f0  nt!KdpDebuggerDataListHead  ;// ListEntry
    3. fffff800`02dfe118  fffff800`02e1a0f0  nt!KdpDebuggerDataListHead
    4. fffff800`02dfe120  00000340`4742444b         ;//<-------------------- Tag & Size
    5. fffff800`02dfe128  fffff800`02c11000  nt!KiSelectNextThread
    6. fffff800`02dfe130  fffff800`02c78fb0  nt!RtlpBreakWithStatusInstruction
    7. fffff800`02dfe138  00000000`00000000
    8. fffff800`02dfe140  00010000`00d801e8
    9. fffff800`02dfe148  fffff800`02c787d0  nt!KiCallUserMode
    10. fffff800`02dfe150  00000000`00000000
    11. fffff800`02dfe158  fffff800`02e53730  nt!PsLoadedModuleList       ;// start Variable List
    12. fffff800`02dfe160  fffff800`02e35420  nt!PsActiveProcessHead
    13. fffff800`02dfe168  fffff800`02e35468  nt!PspCidTable
    14. fffff800`02dfe170  fffff800`02e2a450  nt!ExpSystemResourcesList
    15. fffff800`02dfe178  fffff800`02ebda30  nt!ExpPagedPoolDescriptor
    16. fffff800`02dfe180  fffff800`02ebd078  nt!ExpNumberOfPagedPools
    17. fffff800`02dfe188  fffff800`02ebd094  nt!KeTimeIncrement
    18. fffff800`02dfe190  fffff800`02e863a0  nt!KeBugCheckCallbackListHead
    19. fffff800`02dfe198  fffff800`02e80220  nt!KiBugCheckData
    20. fffff800`02dfe1a0  fffff800`02e8a990  nt!IopErrorLogListHead
    21. fffff800`02dfe1a8  fffff800`02e36070  nt!ObpRootDirectoryObject
    22. fffff800`02dfe1b0  fffff800`02e36088  nt!ObpTypeObjectType
    23. fffff800`02dfe1b8  00000000`00000000
    24. fffff800`02dfe1c0  00000000`00000000
    25. fffff800`02dfe1c8  fffff800`02ec0b00  nt!MmSystemCacheWs
    26. fffff800`02dfe1d0  fffff800`02ebd280  nt!MmPfnDatabase
    27. fffff800`02dfe1d8  00000000`00000000
    28. fffff800`02dfe1e0  00000000`00000000
    29. fffff800`02dfe1e8  fffff800`02ebda00  nt!MmSubsectionBase
    30. fffff800`02dfe1f0  fffff800`02e3cdc4  nt!MmNumberOfPagingFiles
    31. fffff800`02dfe1f8  fffff800`02ebd300  nt!MmLowestPhysicalPage
    32. fffff800`02dfe200  fffff800`02ebd108  nt!MmHighestPhysicalPage
    33. fffff800`02dfe208  fffff800`02ebd060  nt!MmNumberOfPhysicalPages
    34. fffff800`02dfe210  fffff800`02ebd2e8  nt!MmMaximumNonPagedPoolInBytes
    35. fffff800`02dfe218  00000000`00000000
    36. fffff800`02dfe220  fffff800`02ebd0b8  nt!MmNonPagedPoolStart
    37. fffff800`02dfe228  00000000`00000000
    38. fffff800`02dfe230  00000000`00000000
    39. fffff800`02dfe238  fffff800`02ebd1a0  nt!MmPagedPoolEnd
    40. fffff800`02dfe240  fffff800`02ebd940  nt!MmPagedPoolInfo
    41. fffff800`02dfe248  00000000`00001000  nt!MmPagedSize
    42. fffff800`02dfe250  fffff800`02ebd498  nt!MmSizeOfPagedPoolInBytes
    43. fffff800`02dfe258  fffff800`02e3bdb8  nt!MmTotalCommitLimit
    44. fffff800`02dfe260  fffff800`02e3bdc0  nt!MmTotalCommittedPages
    45. fffff800`02dfe268  fffff800`02e262d0  nt!MmSharedCommit
    46. fffff800`02dfe270  fffff800`02e537c8  nt!MmDriverCommit
    47. fffff800`02dfe278  fffff800`02e3bd90  nt!MmProcessCommit
    48. fffff800`02dfe280  fffff800`02e3be00  nt!MmPagedPoolCommit
    49. fffff800`02dfe288  00000000`00000000
    50. fffff800`02dfe290  fffff800`02ec0500  nt!MmZeroedPageListHead
    51. fffff800`02dfe298  fffff800`02ec0ac0  nt!MmFreePageListHead
    52. fffff800`02dfe2a0  fffff800`02ec0bc0  nt!MmStandbyPageListHead
    53. fffff800`02dfe2a8  fffff800`02ec02c0  nt!MmModifiedPageListHead
    54. fffff800`02dfe2b0  fffff800`02dfdcd0  nt!MmModifiedNoWritePageListHead
    55. fffff800`02dfe2b8  fffff800`02e08bc0  nt!MmAvailablePages
    56. fffff800`02dfe2c0  fffff800`02e08c00  nt!MmResidentAvailablePages
    57. fffff800`02dfe2c8  fffff800`02e2aa68  nt!PoolTrackTable
    58. fffff800`02dfe2d0  fffff800`02e14580  nt!NonPagedPoolDescriptor
    59. fffff800`02dfe2d8  fffff800`02ebd010  nt!MmHighestUserAddress
    60. fffff800`02dfe2e0  fffff800`02ebd100  nt!MmSystemRangeStart
    61. fffff800`02dfe2e8  fffff800`02ebd000  nt!MmUserProbeAddress
    62. fffff800`02dfe2f0  fffff800`02e15f70  nt!KdPrintDefaultCircularBuffer
    63. fffff800`02dfe2f8  fffff800`02e16f70  nt!KdPrintRolloverCount
    64. fffff800`02dfe300  fffff800`02dfe028  nt!KdPrintWritePointer
    65. fffff800`02dfe308  fffff800`02e16f70  nt!KdPrintRolloverCount
    66. fffff800`02dfe310  fffff800`02e53f90  nt!MmLoadedUserImageList
    67. fffff800`02dfe318  fffff800`02dc3b00  nt!NtBuildLabEx
    68. fffff800`02dfe320  00000000`00000000
    69. fffff800`02dfe328  fffff800`02ebdc40  nt!KiProcessorBlock
    70. fffff800`02dfe330  fffff800`02ebd1f0  nt!MmUnloadedDrivers
    71. fffff800`02dfe338  fffff800`02e53728  nt!MmLastUnloadedDriver
    72. fffff800`02dfe340  fffff800`02e33b48  nt!VerifierTriageActionTaken
    73. fffff800`02dfe348  fffff800`02ebd0dc  nt!MmSpecialPoolTag
    74. fffff800`02dfe350  fffff800`02e28ed8  nt!KernelVerifier
    75. fffff800`02dfe358  fffff800`02e53b80  nt!MmVerifierData
    76. fffff800`02dfe360  fffff800`02e3d2b0  nt!MmAllocatedNonPagedPool
    77. fffff800`02dfe368  fffff800`02e3bdf0  nt!MmPeakCommitment
    78. fffff800`02dfe370  fffff800`02e3bdc8  nt!MmTotalCommitLimitMaximum
    79. fffff800`02dfe378  fffff800`02e8cc3c  nt!CmNtCSDVersion
    80. fffff800`02dfe380  fffff800`02ebd038  nt!MmPhysicalMemoryBlock
    81. fffff800`02dfe388  00000000`00000000
    82. fffff800`02dfe390  00000000`00000000
    83. fffff800`02dfe398  00000000`00000000
    84. fffff800`02dfe3a0  00000000`00000000
    85. fffff800`02dfe3a8  00280038`00b8007c
    86. fffff800`02dfe3b0  00000000`01640070
    87. fffff800`02dfe3b8  00280290`033804e8
    88. fffff800`02dfe3c0  05f40008`21da4d00
    89. fffff800`02dfe3c8  00240120`4bb805f0
    90. fffff800`02dfe3d0  00000000`000004a8
    91. fffff800`02dfe3d8  fffff800`02dfe030  nt!KdPrintCircularBuffer
    92. fffff800`02dfe3e0  fffff800`02dfe038  nt!KdPrintBufferSize
    93. fffff800`02dfe3e8  fffff800`02e873e0  nt!KdpLoaderDebuggerBlock
    94. fffff800`02dfe3f0  01800020`00184e80
    95. fffff800`02dfe3f8  00000000`00000000
    96. fffff800`02dfe400  00180010`00400000
    97. fffff800`02dfe408  00000028`00300000
    98. fffff800`02dfe410  00500020`00400000
    99. fffff800`02dfe418  fffff800`02e8a4a0  nt!IopNumTriageDumpDataBlocks
    100. fffff800`02dfe420  fffff800`02e8a0a0  nt!IopTriageDumpDataBlocks
    101. fffff800`02dfe428  00000000`00000000
    102. fffff800`02dfe430  fffff800`02e3b9e8  nt!MmBadPagesDetected
    103. fffff800`02dfe438  fffff800`02e3b9e4  nt!MmZeroedPageSingleBitErrorsDetected
    104. fffff800`02dfe440  fffff800`02dbf770  nt!EtwpDebuggerData
    105. fffff800`02dfe448  00000000`00004bd8
    106. 0: kd>

    А что если не ограничивать размер дампа, и запросить инфу после блока "KDDEBUGGER_DATA64", указав например длину L100 ? Как оказалось, далее ядро заселяет к себе какие-то маски устройств, ..что это и зачем - хз. Да и вообще имеет смысл сдампить таким макаром всю секцию ".data", тем-более что адрес её чердака и подвала мы уже нашли.

    3. Разбор алгоритма шифрования блока на системах Win8+


    Начиная с Win8 блок "KDDEBUGGER_DATA64" шифруется в любой версии ОС х64, хотя сама сигнатура "KDBG" лежит по прежнему в открытом виде. Расшифровать его можно посмотрев на участвующую в этом процессе функцию KdCopyDataBlock(). В ней фигурируют 2 переменные "KiWaitNever" и "KiWaitAlways", которые устанавливаются на основе значения счётчика RDTSC при загрузке ОС (кстати эти-же значения участвуют и в обфускации указателя для PatchGuard).

    Проблема в том, что в модуле ядра можно найти не одну, а несколько сигнатур "KDBG" (на своей Win10 я насчитал ровно 30 штук), а потому первое попадание может оказаться ложным. Чтобы из 30-ти вытащить джекпот, нужно будет на основе экспортируемой переменной "PsLoadedModulesList" определять для каждого выстрела кол-во процессов и загруженных модулей. Если попадание ложно, число процессов будет 0, иначе - это наш клиент.

    Чтобы представить весь процесс наглядно, командой uf KdCopyDataBlock запросим у отладчика дизасм этой функи:

    Код (Text):
    1. 0: kd> uf KdCopyDataBlock
    2. nt!KdCopyDataBlock:
    3. fffff800`02d16020  4883ec28        sub     rsp,28h
    4. fffff800`02d16024  803d540f100000  cmp     byte ptr [nt!KdpDataBlockEncoded (fffff800`02e16f7f)],0
    5. fffff800`02d1602b  488bd1          mov     rdx,rcx
    6. fffff800`02d1602e  7447            je      nt!KdCopyDataBlock+0x57 (fffff800`02d16077)
    7.  
    8. ;//-----------------------------------------------------------------------
    9. ;// Сразу на входе видим тест блока "Encoded" на нуль - проверим, его:
    10. 0: kd> dc  nt!KdpDataBlockEncoded
    11. fffff800`02e16f7f  00000000 00000000 00000000 00000000  ................
    12. fffff800`02e16f8f  00000000 00000000 00000000 00000000  ................
    13. fffff800`02e16f9f  00000000 00000000 00000000 00000000  ................
    14. fffff800`02e16faf  00000000 00000000 00000000 00000000  ................
    15.  
    16. ;// Угу.. зеро,  значит прыгаем вниз,
    17. ;// где идёт перемещение блока "KDDEBUGGER_DATA64" размером 0x340 байт,
    18. ;// (передаётся в функцию как аргумент в RCX), в эту пустую область "KdpDataBlockEncoded".
    19. ;// Интересно, что размер блока указывается в R8d в виде константы,
    20. ;// а не вычисляется динамически, что потверждает нашу теорию.
    21. ;//-----------------------------------------------------------------------
    22. nt!KdCopyDataBlock+0x57:
    23. fffff800`02d16077  488d1592800e00  lea     rdx,[nt!KdDebuggerDataBlock (fffff800`02dfe110)]
    24. fffff800`02d1607e  41b840030000    mov     r8d,340h
    25. fffff800`02d16084  e8c71af6ff      call    nt!memmove (fffff800`02c77b50)
    26.  
    27. ;//-------------------------------------------------------------------------
    28. ;// Если-же при первом вызове функции блок уже был перемещён,
    29. ;// то код приступает к шифрованию, алго которого основан на:
    30. ;// ** ключах из переменных KiWaitNever/Always --> xor --> rol --> bswap **
    31. ;// Здесь можно вытащить и значения самих ключей:
    32.  
    33. 0: kd> dqs nt!KiWaitNever L1
    34. fffff800`02ebd120   5a0f9a8b`01e16dfc
    35.  
    36. 0: kd> dqs nt!KiWaitAlways L1
    37. fffff800`02ebd218   001e16d0`3e0f9ab0
    38.  
    39. ;//-------------------------------------------------------------------------
    40. nt!KdCopyDataBlock+0x10:
    41. fffff800`02d16030  4c8b15e9701a00  mov     r10,qword ptr [nt!KiWaitNever (fffff800`02ebd120)]
    42. fffff800`02d16037  4c8d05d2800e00  lea     r8,[nt!KdDebuggerDataBlock    (fffff800`02dfe110)]
    43. fffff800`02d1603e  41b968000000    mov     r9d,68h
    44. fffff800`02d16044  492bd0          sub     rdx,r8
    45.  
    46. nt!KdCopyDataBlock+0x27:
    47. fffff800`02d16047  498b00          mov     rax,qword ptr [r8]
    48. fffff800`02d1604a  418bca          mov     ecx,r10d
    49. fffff800`02d1604d  4933c2          xor     rax,r10
    50. fffff800`02d16050  48d3c0          rol     rax,cl
    51. fffff800`02d16053  488d0d250f1000  lea     rcx,[nt!KdpDataBlockEncoded    (fffff800`02e16f7f)]
    52. fffff800`02d1605a  4833c1          xor     rax,rcx
    53. fffff800`02d1605d  480fc8          bswap   rax
    54. fffff800`02d16060  483305b1711a00  xor     rax,qword ptr [nt!KiWaitAlways (fffff800`02ebd218)]
    55. fffff800`02d16067  4a890402        mov     qword ptr [rdx+r8],rax
    56. fffff800`02d1606b  4983c008        add     r8,8
    57. fffff800`02d1606f  4183c1ff        add     r9d,0FFFFFFFFh
    58. fffff800`02d16073  75d2            jne     nt!KdCopyDataBlock+0x27  (fffff800`02d16047)
    59.  
    60. nt!KdCopyDataBlock+0x55:
    61. fffff800`02d16075  eb12            jmp     nt!KdCopyDataBlock+0x69  (fffff800`02d16089)
    62. nt!KdCopyDataBlock+0x69:
    63. fffff800`02d16089  4883c428        add     rsp,28h
    64. fffff800`02d1608d  c3              ret
    65. 0: kd>
    Как видим, всё здесь прозрачно. Интересен тот факт, что когда возникает BSOD, его процедуры опять вызывают функцию KdCopyDataBlock(), но на этот раз уже для декрипта "KDDEBUGGER_DATA64" по такому-же алго. В результате, в мини-дампы блок отладчика всегда сбрасывается в уже расшифрованном виде, чего не скажешь о создании полного дампа памяти при сбоях, куда отправляются в том числе и текущие значения переменных "KiWaitNever/Always". Ну вроде это всё, что я накопал по данной теме. Медаль тем, кто дочитал до конца!
     
    Последнее редактирование: 19 мар 2024
  18. HoShiMin

    HoShiMin Well-Known Member

    Публикаций:
    5
    Регистрация:
    17 дек 2016
    Сообщения:
    1.430
    Адрес:
    Россия, Нижний Новгород
    Marylin, но всё-таки, не кажется ли тебе, что ты усложняешь? Искать недокументированные функции и данные через такие же недокументированные функции и данные способами, основанными на реверсе?
    Ну возьми ты символы, не нужны здесь никакие костыли. Посчитай в юзермоде адреса всех нужных функций и данных, отправь их в драйвер - и не нужны эти карусели с расшифровкой KDDEBUGGER_DATA64 и сигнатурным поиском.
    Интернет есть у всех, скачивание символов уже лет 18 как перестало быть проблемой.
    Обновится винда, поменяют шифрование или что-то сломается в поиске через PsLoadedModulesList - ну и что? Всё, минус драйвер. В чём тогда был смысл таких извращений?
     
  19. Marylin

    Marylin Active Member

    Публикаций:
    0
    Регистрация:
    17 фев 2023
    Сообщения:
    128
    HoShiMin, во-первых просто дёргать за готовые функции как минимум не интересно, здесь ведь фишка в исследовании памяти напрямую. Во-вторых есть фрейворк питона "Volatility", который писали далеко не глупые люди. Тогда почему они не используют твой трюк с символами, а юзают именно описанные мной методы (которые я кстати вычитал у них-же). Да и вообще я не встречал нигде даже упоминания о парсинге pdb для этих целей, хотя идея в корне хорошая.
     
  20. HoShiMin

    HoShiMin Well-Known Member

    Публикаций:
    5
    Регистрация:
    17 дек 2016
    Сообщения:
    1.430
    Адрес:
    Россия, Нижний Новгород
    Это у них надо спрашивать, почему они решили, что миллиард профилей под каждый билд системы - это нормальное решение.
    Windows:
    * 32-bit Windows XP Service Pack 2 and 3
    * 32-bit Windows 2003 Server Service Pack 0, 1, 2
    * 32-bit Windows Vista Service Pack 0, 1, 2
    * 32-bit Windows 2008 Server Service Pack 1, 2 (there is no SP0)
    * 32-bit Windows 7 Service Pack 0, 1
    * 32-bit Windows 8, 8.1, and 8.1 Update 1
    * 32-bit Windows 10 (initial support)
    * 64-bit Windows XP Service Pack 1 and 2 (there is no SP0)
    * 64-bit Windows 2003 Server Service Pack 1 and 2 (there is no SP0)
    * 64-bit Windows Vista Service Pack 0, 1, 2
    * 64-bit Windows 2008 Server Service Pack 1 and 2 (there is no SP0)
    * 64-bit Windows 2008 R2 Server Service Pack 0 and 1
    * 64-bit Windows 7 Service Pack 0 and 1
    * 64-bit Windows 8, 8.1, and 8.1 Update 1
    * 64-bit Windows Server 2012 and 2012 R2
    * 64-bit Windows 10 (including at least 10.0.19041)
    * 64-bit Windows Server 2016 (including at least 10.0.19041)

    Profiles
    --------
    VistaSP0x64 - A Profile for Windows Vista SP0 x64
    VistaSP0x86 - A Profile for Windows Vista SP0 x86
    VistaSP1x64 - A Profile for Windows Vista SP1 x64
    VistaSP1x86 - A Profile for Windows Vista SP1 x86
    VistaSP2x64 - A Profile for Windows Vista SP2 x64
    VistaSP2x86 - A Profile for Windows Vista SP2 x86
    Win10x64 - A Profile for Windows 10 x64
    Win10x64_10586 - A Profile for Windows 10 x64 (10.0.10586.306 / 2016-04-23)
    Win10x64_14393 - A Profile for Windows 10 x64 (10.0.14393.0 / 2016-07-16)
    Win10x86 - A Profile for Windows 10 x86
    Win10x86_10586 - A Profile for Windows 10 x86 (10.0.10586.420 / 2016-05-28)
    Win10x86_14393 - A Profile for Windows 10 x86 (10.0.14393.0 / 2016-07-16)
    Win2003SP0x86 - A Profile for Windows 2003 SP0 x86
    Win2003SP1x64 - A Profile for Windows 2003 SP1 x64
    Win2003SP1x86 - A Profile for Windows 2003 SP1 x86
    Win2003SP2x64 - A Profile for Windows 2003 SP2 x64
    Win2003SP2x86 - A Profile for Windows 2003 SP2 x86
    Win2008R2SP0x64 - A Profile for Windows 2008 R2 SP0 x64
    Win2008R2SP1x64 - A Profile for Windows 2008 R2 SP1 x64
    Win2008R2SP1x64_23418 - A Profile for Windows 2008 R2 SP1 x64 (6.1.7601.23418 / 2016-04-09)
    Win2008SP1x64 - A Profile for Windows 2008 SP1 x64
    Win2008SP1x86 - A Profile for Windows 2008 SP1 x86
    Win2008SP2x64 - A Profile for Windows 2008 SP2 x64
    Win2008SP2x86 - A Profile for Windows 2008 SP2 x86
    Win2012R2x64 - A Profile for Windows Server 2012 R2 x64
    Win2012R2x64_18340 - A Profile for Windows Server 2012 R2 x64 (6.3.9600.18340 / 2016-05-13)
    Win2012x64 - A Profile for Windows Server 2012 x64
    Win2016x64_14393 - A Profile for Windows Server 2016 x64 (10.0.14393.0 / 2016-07-16)
    Win7SP0x64 - A Profile for Windows 7 SP0 x64
    Win7SP0x86 - A Profile for Windows 7 SP0 x86
    Win7SP1x64 - A Profile for Windows 7 SP1 x64
    Win7SP1x64_23418 - A Profile for Windows 7 SP1 x64 (6.1.7601.23418 / 2016-04-09)
    Win7SP1x86 - A Profile for Windows 7 SP1 x86
    Win7SP1x86_23418 - A Profile for Windows 7 SP1 x86 (6.1.7601.23418 / 2016-04-09)
    Win81U1x64 - A Profile for Windows 8.1 Update 1 x64
    Win81U1x86 - A Profile for Windows 8.1 Update 1 x86
    Win8SP0x64 - A Profile for Windows 8 x64
    Win8SP0x86 - A Profile for Windows 8 x86
    Win8SP1x64 - A Profile for Windows 8.1 x64
    Win8SP1x64_18340 - A Profile for Windows 8.1 x64 (6.3.9600.18340 / 2016-05-13)
    Win8SP1x86 - A Profile for Windows 8.1 x86
    WinXPSP1x64 - A Profile for Windows XP SP1 x64
    WinXPSP2x64 - A Profile for Windows XP SP2 x64
    WinXPSP2x86 - A Profile for Windows XP SP2 x86
    WinXPSP3x86 - A Profile for Windows XP SP3 x86
    Видимо, их фреймворк кроссплатформенный, а на линуксе и маке нет DbgHelp, который умеет парсить pdb'шки, а писать парсер вручную не хотят.
    Хотя вон в этих ваших растах взяли и просто сделали: https://crates.io/crates/pdb - значит, не такой уж и рокетсаенс.
    Гм... А как тогда ты в IDA Pro и в WinDbg смотришь структурки и неэкспортирующиеся функции? Они буквально это и делают - скачивают и парсят символы.

    Разумеется, из-за отличий в самом содержании структур и из-за различий в функциях под разные версии системы у тебя будет разный код, работающий с ними, но символы хотя бы снимают с тебя необходимость ещё и поиск этих функций затачивать под каждый билд.
     
    Последнее редактирование: 19 мар 2024