• 15 апреля стартует «Курс «SQL-injection Master» ©» от команды The Codeby

    За 3 месяца вы пройдете путь от начальных навыков работы с SQL-запросами к базам данных до продвинутых техник. Научитесь находить уязвимости связанные с базами данных, и внедрять произвольный SQL-код в уязвимые приложения.

    На последнюю неделю приходится экзамен, где нужно будет показать свои навыки, взломав ряд уязвимых учебных сайтов, и добыть флаги. Успешно сдавшие экзамен получат сертификат.

    Запись на курс до 25 апреля. Получить промодоступ ...

Статья Кража открытых файлов Win

Одним из основных понятий в биологических дисциплинах является захват ареала обитания (области распространения таксонов). На подсознательном уровне все живые организмы пытаются занять свою нишу в общей сфере, любыми путями оберегая доступы к ней из вне. Аналогичной политики придерживаются и операционные системы, всё больше наделяемые интеллектом их разработчиками. Так, например, запущенный на исполнение процесс блокирует доступ ко-всем своим файлам и на запрос их копирования, система дипломатично посылает нас на материк:

SAM.png


Здесь и далее, в качестве кролика я выбрал системный файл SAM (Security Account Manager) – некий сейф с базой-данных учётных записей Win. Он хранит хэши паролей и приватную информацию о всех пользователях системы, а сами данные оформлены в виде древа структур настроек безопасности. На физическом уровне, база живёт в одноимённой ветке реестра HKLM\SAM, доступ к которой закрыт для всех, включая администратора. Система хранит жалкую копию валидного SAM-файла в своём дире ..\Windows\Repair, только создаётся он про запас в момент установки системы и в своей тушке не содержит ничего интересного (поэтому системный протекторат и позволяет его открывать).

Можно-ли программным способом утянуть реальный SAM-файл из под носа работающего мастдая? В документации Microsoft чёрным по-белому написано, что этим файлом оперировать нельзя – он доступен только ядру. Однако на заборе тоже написано, ..и если нельзя, но сильно хочется, то скопировать его всё-таки можно и данная статья подскажет как.

В сети встречаются много вариантов решения данной проблемы и все в унисон твердят, мол "загрузись с Live-CD, или линуха", т.е. советуют тупо остановить систему, после чего нажать F5 во-внешней оболочке сможет даже обезьяна. Но как быть, если кражей должен заниматься попавший на чужбину шелл-код? Он сам верхом на винде и не может рубить сук, на котором сидит. Вот об этом и поговорим..
-----------------------------------------------------

Системные объекты и их дескрипторы (handle)

Начнём с того, что в глазах систем класса Win-NT, мир существует в виде объектов. Объектно-ориентированный подход удобен тем, что при необходимости позволяет добавлять новые ресурсы, не затрагивая при этом код самого ядра Ntoskrnl.exe. На логическом уровне, объект – это просто структура данных, так-что если в обозримом будущем в системе появится какой-нибудь иноземный ресурс, под него достаточно будет выделить структуру и всё. Здесь нужно отдать должное инженерам, в пытливых умах которых нет-нет да и проскакивают гениальные идеи.

Под определение "ресурс" попадает всё-что шевелится, однако только разделяемым ресурсам выпала честь носить громкое имя "объект". На рисунке ниже, утилита "Object Explorer" от знаменитого хакера Four-F (прикреплена в скрепке). Когда-то он тусовался на васме и внёс большой вклад в область программирования драйверов. Утилита предоставляет исчерпывающую информацию о всех системных объектах, свойства которых можно просмотреть двойным кликом.

Мастдай обращается к объектам по их типу Type, и каждый тип имеет своё числовое значение. Например на семёрке и XP, объект типа "File" имеет значение 0х1С (28), хотя не факт, что на других системах оно будет совпадать:

WinObj.png


Когда мы запускаем процесс на исполнение, система создаёт для него окружение и сохраняет всю подноготную в своей ядерной структуре "EPROCESS" (у каждого процесса она своя). Внутри этой структуры нашлось место и для таблицы-объектов текущего процесса "ObjectTable", где хранятся дескрипторы Handle всех используемых нашим приложением объектов. Открывая объект по имени (например файл или устройство), процесс получает взамен его дескриптор, при помощи которого можно будет в дальнейшем ссылаться на этот объект. Ссылка по указателю работает быстрее чем по имени, т.к. при этом диспетчер может сразу найти его, не перебирая огромный список имён. Если запросить у отладчика WinDbg формат структуры _eprocess, то можно обнаружить в ней указатель на таблицу-объектов:

eprocess_srtuct.png


Самое обычное приложение типа "Hello World" может иметь до 50 объектов, большая часть из которых работает в фоне. Отметим, что объекты могут быть как именованные (тогда они находятся в пространстве имён), так и безымянные – например создаваемые нами объекты синхронизации типа Event, Semapfore и прочие (у них нет имени, но есть дескриптор). В демонстрационном примере я создал минимальное приложение и открыл его в утилите М.Руссиновича "Process Explorer". Она отображает тип объекта (жалко без числового значения), привязанный к нему дескриптор, флаги доступа к объекту Access, и адрес объекта в пространстве ядра:

C-подобный:
include  'win32ax.inc'
.data
capt    db  'Тест',0
msg     db  'Привет мир!',0
;//-------
.code
start:  invoke  MessageBox,0,msg,capt,0
        invoke  ExitProcess,0
.end start

ProcessExp.png


На рисунке выше нас интересуют флаги доступа к объекту (столбец Access). Система имеет пул-объектов в виде готовых шаблонов, на которые слой-за-слоем композиционным образом (логическое сложение OR) накладываются различные атрибуты. Теперь система возвращает нам дескриптор, который представляет из-себя указатель на элемент в таблице-объектов.

Очевидно, что если к файлу нет доступа (в данном случае к SAM), значит он занят каким-то процессом и в своих закромах система должна хранить соответствующий флаг. Этот флаг как-раз и припрятан в значении столбца Access. Вот если-бы мы могли подобраться к нему и пропатчить доступ под себя, тогда можно было скопировать файл без всяких проблем. Но в столбце ObjectAddress к сожалению мы видим значение, которое указывает на область адресов ядра выше 0х80000000, значит нужно будет писать драйвер, что нас совсем не вдохновляет. Соответственно способ с правкой атрибутов для доступа к файлу SAM нам не подходит и нужно искать обходные пути.


Программируем вывод таблицы-объектов

Что можно сделать с пользовательского уровня, так это просто перечислить все активные объекты. Практической пользы от этого мало, но для повышения скила можно написать подобную утилиту, где соло будет играть всего одна функция NtQuerySystemInformation(). Если её аргументу "Info-Class" вскормить значение SystemHandleInformation, то за один выстрел она вернёт дескрипторы всех объектов, которые открыты на текущий момент в системе. В приёмном буфере, каждая из описывающих дескрипторы структур будет иметь такой формат, а в первом дворде буфера мы получим их кол-тво. Эти две структуры известны как SYSTEM_HANDLE_INFORMATION и SYSTEM_HANDLE:

C-подобный:
struct SYSTEM_HANDLE_INFORMATION
    uCount         dd  0             ;// кол-во структур 'SYSTEM_HANDLE' в буфере
    aSH            SYSTEM_HANDLE     ;// массив структур..
ends

struct SYSTEM_HANDLE       ;//<--- Size=16 (описывает один дескриптор)
    uIdProcess     dd  0       ;// PID процесса, которому принадлежит данный Handle
    ObjectType     db  0       ;// тип объекта (числовое значение)
    Flags          db  0       ;// флаги свойств дескриптора
    Handle         dw  0       ;// значение дескриптора
    pAddress       dd  0       ;// адрес объекта в пространстве ядра
    GrantedAccess  dd  0       ;// флаги доступа к объекту!!!
ends

NtQuery_buff.png


Как видим, выхлоп данной функции достаточно прозрачный, и нам остаётся лишь через VirtualAlloc() выделить память под буфер, и в цикле пропарсить все структуры SYSTEM_HANDLE. Чтобы узнать размер требуемого буфера (величина не постоянная и зависит от числа активных процессов), нужно первый раз вызвать NtQuerySystemInformation() с заведомо кривым аргументом, и тогда функция вернёт требуемый размер. Вот код и результат его работы:

C-подобный:
format   PE console
include  'win32ax.inc'
include  'ddk\ntdll.inc'    ;//<-- смотри инклуд в скрепке
entry    start
;//-------
.data
capt      db  13,10,' OBJECT INFORMATION'
          db  13,10,' =========================================='
          db  13,10,' Current process PID.....: %04d'
          db  13,10,' Total open object.......: %d',10
          db  13,10,' PID   Type  Handle  Obj_Addr    Access'
          db  13,10,' ==========================================',0
info      db  13,10,' %04d  0x%02X  0x%04X  0x%08X  0x%08X',0
total     db  13,10,' ========================='
          db  13,10,' Total object found: %d',0
pid       dd  0
found     dd  0
count     dd  0
frmt      db  '%s',0
align     16
buffSize  dd  0
buffAddr  dd  0
keyb      db  0
;//-------
.code
start:
;//==== Заполняем буфер открытыми дескрипторами ==
;// первый выстрел холостой, чтобы получить требуемый размер буфа
        invoke  NtQuerySystemInformation,\
                SystemHandleInformation,buffAddr,1024*2,buffSize

        invoke  VirtualAlloc,0,[buffSize],0x3000,4  ;// выделяем память под буф!
        mov     [buffAddr],eax                      ;// запомнить его адрес..

;// второй выстрел дробью, и в буфере получили все дескрипторы
        invoke  NtQuerySystemInformation,\
                SystemHandleInformation,[buffAddr],[buffSize],0

;//==== Выводим шапку и общую информацию =========
        mov     eax,[buffAddr]   ;// указатель на начало
        mov     eax,[eax]        ;// берём первый дворд от туда
        mov     [count],eax      ;// получили всего структур в буфере!
        push    eax              ;//
        invoke  GetCurrentProcessId
        mov     [pid],eax        ;// PID нашего процесса
        pop     ebx              ;//
       cinvoke  printf,capt,eax,ebx   ;// вывести инфу на консоль!!!

;//==== Полезная нагрузка Payload ================
        mov     esi,[buffAddr]   ;// указатель на начало буфера
        add     esi,4            ;// пропускаем счётчик структур 'uCount'
        mov     ecx,[count]      ;// возьмём этот счётчик в ECX для LOOP

@find:  push    esi ecx          ;// запомнить!
        and     ebx,0            ;// три способа очистки регистров
        xor     ecx,ecx          ;//   ..^^^
        sub     edx,edx          ;//      ..^^^
;//---- парсим очередную структуру 'SYSTEM_HANDLE'
        mov     eax,[esi]        ;// PID родителя
        mov      bl,[esi+4]      ;// ObjectType
        mov      cx,[esi+6]      ;// Handle
        mov     edx,[esi+8]      ;// ObjTableAddr
        mov     edi,[esi+12]     ;// Access
;//----
        inc     [found]          ;// найдено +1
       cinvoke  printf,info,eax,ebx,ecx,edx,edi
        pop     ecx esi          ;// восстановить счётчик и указатель
        add     esi,16           ;// переходим к следующей 'SYSTEM_HANDLE'
        loop    @find            ;// промотать ECX-раз..

       cinvoke  printf,total,[found]  ;// всего обработано структур

@exit: cinvoke  scanf,frmt,keyb  ;// ждём клаву..
       cinvoke  exit,0           ;//  ..и на выход!
;//-------
section '.idata' import data readable
library  msvcrt,'msvcrt.dll',kernel32,'kernel32.dll',ntdll,'ntdll.dll'
import   msvcrt, printf,'printf',scanf,'scanf',exit,'exit'
import   ntdll,  NtQuerySystemInformation,'NtQuerySystemInformation'
include 'api\kernel32.inc'

SysHndl.png


Можно модернизировать этот код, например под вывод объектов, которые принадлежат только нашему процессу по его PID, или-же вывести из общей кухни только объекты типа "File". Для этого достаточно внести условие, и переходить по нему. Вот пример фильтрации вывода по идентификатору процесса PID:

C-подобный:
@find:  push    esi ecx          ;// запомнить!
        and     ebx,0            ;// три способа очистки регистров
        xor     ecx,ecx          ;//   ..^^^
        sub     edx,edx          ;//      ..^^^
;// парсим очередную структуру 'SYSTEM_HANDLE'
        mov     eax,[esi]        ;// PID родителя
        mov      bl,[esi+4]      ;// ObjectType
        mov      cx,[esi+6]      ;// Handle
        mov     edx,[esi+8]      ;// ObjTableAddr
        mov     edi,[esi+12]     ;// Access
        cmp     eax,[pid]        ;// -----> сравнить PID с нашим
        jne     @fuck            ;// -----> если не равно..
        inc     [found]          ;// иначе: найдено +1
       cinvoke  printf,info,eax,ebx,ecx,edx,edi
@fuck:  pop     ecx esi          ;// восстановить счётчик и указатель
        add     esi,16           ;// переходим к следующей 'SYSTEM_HANDLE'
        loop    @find            ;// промотать ECX-раз..

А вот с фильтрацией вывода только файлов может возникнуть проблема. Как упоминалось выше, объект типа-файл имеет разные константы на различных версия Win, поэтому нужно будет вычислять эту константу динамически. Например можно таскать с собой какой-нибудь файл и открыв, искать его тип в этом буфере по хэндлу. Другой вариант – открыть системное устройство NUL, которое представляет собой виртуальный файл. Этот файл поддерживает операцию Write, но при чтении всегда будет возвращать EOF (End of File), т.е. данные он не сохраняет. В любом случае, нужно перебирать тип по хэндлу.

Родственными к NtQuerySystemInformation() являются функции NtQueryObject() и NtQueryInformationFile(). Обе эти функции вплоть до структур описываются в бестцеллере Гарри Небета "Native_API_Reference". Судя по документации, они должны возвращать соответственно имя объекта, и имя файла по дескрипторам, однако у меня эти функции дают осечку (видят только имена файлов, которые открыл мой процесс), поэтому примеры их здесь я не привожу.


Объект типа "FILE"

Система создаёт данный объект, когда мы вызываем функцию CreateFile().
Посмотрим на её прототип:

C-подобный:
handle CreateFile (
   lpFileName              ;// указатель на имя файла, или устройства,
   dwDesiredAccess         ;// тип доступа к объекту – наш клиент!!!
   dwShareMode             ;// расшаривание объекта (см.ниже),
   lpSecurityAttributes    ;// опционально (наследование),
   dwCreationDisposition   ;// открыть или создать объект?
   dwFlagsAndAttributes    ;// опционально (атрибуты),
   hTemplateFile );        ;// опционально (шаблон).
;//---------------------------
;// Если dwShareMode = 0, объект НЕ доступен никому, кроме родителя.
;// Чтобы создать разделяемый объект, используйте комбинацию следующих флагов:
;//---------------------------
   FILE_SHARE_DELETE - доступ закрыт для всех, кроме открывшего.
   FILE_SHARE_READ   - доступ на чтение.
   FILE_SHARE_WRITE  - доступ на запись.

   dwDesiredAccess   Подразумевает
   ------------------------------------------
   GENERIC_EXECUTE   FILE_EXECUTE
                     FILE_READ_ATTRIBUTES
                     STANDARD_RIGHTS_EXECUTE
                     SYNCHRONIZE

   GENERIC_READ      FILE_READ_ATTRIBUTES
                     FILE_READ_DATA
                     FILE_READ_EA
                     STANDARD_RIGHTS_READ
                     SYNCHRONIZE

   GENERIC_WRITE     FILE_APPEND_DATA
                     FILE_WRITE_ATTRIBUTES
                     FILE_WRITE_DATA
                     FILE_WRITE_EA
                     STANDARD_RIGHTS_WRITE
                     SYNCHRONIZE

Судя по описанию, для самовыражения здесь места достаточно. У аргументов этой функции наблюдается явный реверанс в сторону политики безопасности. Зомбируя нас кодилом, она предлагает кучу вариаций различных атрибутов, которые можно логически складывать посредством OR. Но что особенно важно, мы можем и наоборот выбрать только один атрибут из всех и это распахивает перед нами все двери.

Например, если посмотреть на столбец "Подразумевает", он предлагает атрибут доступа FILE_READ_ATTRIBUTES. Выставив в запросе только его, мы можем обмануть систему и получить таким образом долгожданный дескриптор закрытого объекта. Запросы с таким флагом позволяют открывать буквально любые системные файлы, кроме файла подкачки Pagefile.sys. А вот если мы укажем GENERIC_READ (в составе которого лежит и FILE_READ_ATTRIBUTES), то система обламает нас по-полной, т.к. примет запрос за попытку чтения-данных.

Посмотрим, что представляет из-себя объект типа File под именем SAM. Здесь видно, что флаги доступа к нему (в столбце Access) имеют значение 0х00000003, т.е. почти все сброшены. Причём таким-же флагом наделены все файлы в папке system32\config, а значит вся эта братия одного поля ягоды. Я не искал константы атрибутов доступа Access, но видимо флаг(3) определяет полное эмбарго на ввод-вывод, разрешая читать только файловые атрибуты через FILE_READ_ATTRIBUTES:

SAM_info.png


Теперь посмотрим на структуру FILE_OBJECT, которую любезно предоставил WinDbg. После того-как функция CreateFile() вернёт нам дескриптор открываемого (создаваемого) файла, этот дескриптор всегда будет указывать на данную структуру. Не будем ворошить это осиное гнездо, а рассмотрим лишь те его поля, которые имеют прямое отношение к проблеме чтения SAM-файла.

C-подобный:
lkd> dt _file_object
nt!_FILE_OBJECT
   +0x000 Type              : Int2B
   +0x002 Size              : Int2B
   +0x004 DeviceObject      : Ptr32 _DEVICE_OBJECT
   +0x008 Vpb               : Ptr32 _VPB
   +0x00c FsContext         : Ptr32 Void
   +0x010 FsContext2        : Ptr32 Void
   +0x014 SectionObjPointer : Ptr32 _SECTION_OBJECT_POINTERS
   +0x018 PrivateCacheMap   : Ptr32 Void
   +0x01c FinalStatus       : Int4B
   +0x020 RelatedFileObject : Ptr32 _FILE_OBJECT
   +0x024 LockOperation     : UChar
   +0x025 DeletePending     : UChar
   +0x026 ReadAccess        : UChar
   +0x027 WriteAccess       : UChar
   +0x028 DeleteAccess      : UChar
   +0x029 SharedRead        : UChar
   +0x02a SharedWrite       : UChar
   +0x02b SharedDelete      : UChar
   +0x02c Flags             : Uint4B
   +0x030 FileName          : _UNICODE_STRING
   +0x038 CurrentByteOffset : _LARGE_INTEGER
   +0x040 Waiters           : Uint4B
   +0x044 Busy              : Uint4B
   +0x048 LastLock          : Ptr32 Void
   +0x04c Lock              : _KEVENT
   +0x05c Event             : _KEVENT
   +0x06c CompletionContext : Ptr32 _IO_COMPLETION_CONTEXT

Значит со-смещения 0х024 лежит массив байт UChar, где зарыты атрибуты доступа к данному объекту. Например в байте по смещению 0х026 "ReadAccess" хранится 8-битная маска, в которой перечислены возможные операции из столбца "Подразумевает" (см.прототип функции CreateFile() выше). В свою очередь со-смещения 0х029 начинаются атрибуты шары и т.д. Кроме того, этим полям вторит и поле "LockOperation" – запрещённые операции.

Но на что стоит обратить особое внимание, так это на 8-байтный (64-бит) указатель позиции в файле "CurrentByteOffset". Он определён как LARGE_INTEGER, что означает два двойных слова dword, или одно 64-битное слово QuadPart (в зависимости от разрядности операционной системы). Этим полем оперирует функции перемещения указателя в файле типа SetFilePointer() или fseek():

C-подобный:
lkd> dt _large_integer
nt!_LARGE_INTEGER
   +0x000 LowPart          : Uint4B    ;// младший dword позиции,
   +0x004 HighPart         : Int4B     ;// старший dword.
   +0x000 u                : __unnamed
   +0x000 QuadPart         : Int8B     ;// если система х64
;//------------------
;// Функция SetFilePointer() перемещает файловый указатель открытого файла.
;//------------------
SetFilePointer
    hFile                 dd  0   ;// дескриптор файла или устройства
    lDistanceToMove       dd  0   ;// мл.слово указателя
    lpDistanceToMoveHigh  dd  0   ;// ст.слово
    dwMoveMethod          dd  0   ;// отправная точка:
                                  ;//  ..начало, текущая позиция, или конец файла

К сожалению как и всё возвышенное, структура FILE_OBJECT находится в пространстве ядра и без драйвера мы не сможем программно подобраться к ней. Иначе можно было найти дескриптор файла SAM в таблице-объектов функцией NtQuerySystemInformation(), и напильником заточить его FILE_OBJECT под себя. Другими словами, из пользовательского режима возиться с атрибутами открытого файла – идея заранее провальная, и нужно применить смекалку с тяжёлой артиллерией.


Прямое чтение секторов диска

Теперь будем мыслить так..
Любой файл (в данном случае SAM) изначально хранится на диске и запускается с него только по требованию. А что если через драйвер файловой системы NTFS попытаться считать его, обратившись напрямую к кластерам диска? В этом случаем получим чтение не из памяти, а с носителя, и обойдя атрибуты-доступа наша лампада засияет ярче - этот вариант никогда не даёт осечек. Чтобы понять идею, рассмотрим поверхностно базовые принципы работы подсистемы ввода-вывода..

Операционная система, представляет устройства в виде файлов. В качестве устройства в нашем случае выступает диск. Как и обычный файл, диск можно открыть функцией CreateFile(), перемещать по его блинам файловый указатель и производить операции чтения-записи. Таким образом, при открытии девайсов создаётся такая-же структура FILE_OBJECT, только к ней привязывается дочерняя структура DEVICE_OBJECT, представляющая конкретный объект DEVICE. В этом объекте хранится ссылка на управляющий драйвер, а функции этого драйвера описываются во-вложенном в DEVICE_OBJECT объекте типа DRIVER_OBJECT. Пройдясь в отладчике по цепочке этих структур можно выстроить визуальную картину:

io.png


C-подобный:
lkd> dt _file_object
nt!_FILE_OBJECT
   +0x000 Type              : Int2B
   +0x002 Size              : Int2B
   +0x004 DeviceObject      : Ptr32 _DEVICE_OBJECT --+
   +0x008 Vpb               : Ptr32 _VPB             |
   +0x00c FsContext         : Ptr32 Void             V
   ......                                            |
;//----------------                                  |
lkd> dt _device_object    ;//<--------<<-------------+
nt!_DEVICE_OBJECT
   +0x000 Type               : Int2B
   +0x002 Size               : Uint2B
   +0x004 ReferenceCount     : Int4B
   +0x008 DriverObject       : Ptr32 _DRIVER_OBJECT -+
   +0x00c NextDevice         : Ptr32 _DEVICE_OBJECT  |
   +0x010 AttachedDevice     : Ptr32 _DEVICE_OBJECT  |
   +0x014 CurrentIrp         : Ptr32 _IRP            |
   +0x018 Timer              : Ptr32 _IO_TIMER       |
   ......                                            |
;//----------------                                  |
lkd> dt _DRIVER_OBJECT    ;//<--------<<-------------+
nt!_DRIVER_OBJECT
   +0x000 Type             : Int2B
   +0x002 Size             : Int2B
   +0x004 DeviceObject     : Ptr32 _DEVICE_OBJECT
   +0x008 Flags            : Uint4B
   +0x00c DriverStart      : Ptr32 Void
   +0x010 DriverSize       : Uint4B
   +0x014 DriverSection    : Ptr32 Void
   +0x018 DriverExtension  : Ptr32 _DRIVER_EXTENSION
   +0x01c DriverName       : _UNICODE_STRING        ;//<--------//
   +0x024 HardwareDatabase : Ptr32 _UNICODE_STRING
   +0x028 FastIoDispatch   : Ptr32 _FAST_IO_DISPATCH
   +0x02c DriverInit       : Ptr32     long
   +0x030 DriverStartIo    : Ptr32     void
   +0x034 DriverUnload     : Ptr32     void
   +0x038 MajorFunction    : [28] Ptr32     long    ;//<--------//

Для взаимодействия с драйверами из пользовательского режима, система имеет вполне легитимную функцию DeviceIoControl(), которая в своём аргументе ожидает код запрашиваемой операции IOCTL_xx. Кодов этих – как звёзд на небе, и среди них имеется FSCTL_GET_RETRIEVAL_POINTERS = 0x00090073 (коды файловой системы начинаются с приставки FS). Завернув его в пакет-запроса IRP (Interrupt_Request_Packet, см.структуру device_object) функция DeviceIoControl() передаёт его драйверу FS.

Обработав поступивший запрос, через этот-же DeviceIoControl() драйвер вернёт нам структуру RETRIEVAL_POINTERS_BUFFER, с полной картой кластеров, которые отведены на диске под запрашиваемый файл – нам остаётся только считать их. Структура STARTING_VCN_INPUT_BUFFER со-сброшенным в нуль полем "StartingVcn" так-же оборачивается в пакет-запроса, и характеризует номер начального виртуального кластера "Vcn":

C-подобный:
BOOL DeviceIoControl (
   HANDLE hDevice            ;// имя или дескриптор устройство
   DWORD dwIoControlCode     ;// код операции для выполнения
   LPVOID lpInBuffer         ;// STARTING_VCN_INPUT_BUFFER (входные данные)
   DWORD nInBufferSize       ;//   ..размер входного буфера = 8
   LPVOID lpOutBuffer        ;// RETRIEVAL_POINTERS_BUFFER (выходные данные)
   DWORD nOutBufferSize      ;//   ..размер выходного буфера = ???
   LPDWORD lpBytesReturned   ;// переменная для получения кол-ва выходных байт
   LPOVERLAPPED lpOverlapped ;// структура для асинхронной операции
);

struct STARTING_VCN_INPUT_BUFFER    ;// Size = 8
   StartingVcn    dq  0       ;// Virtual Cluster Number
ends

struct RETRIEVAL_POINTERS_BUFFER    ;// Size = 32
   ExtentCount    dd  0       ;// кол-во элементов 'Extents' в массиве
   StartingVcn    dq  0       ;// стартовый номер первой цепочки кластеров
  struct                 ;// <---- массив элементов 'Extents'
      NextVcn     dq  0       ;// кол-во кластеров в цепочке
      Lcn         dq  0       ;// номер первого кластера в данной цепочке
  ends
   Extents        dd  0       ;//
ends

Я не буду приводить здесь операцию чтения, т.к. код относится к классу вредоностных, но выведу только информацию о местоположении файла. Дальше функцией ReadFile() нужно будет считать эти кластеры и в буфере мы получим защищённый файл – вот код:

C-подобный:
format   PE console
include  'win32ax.inc'
include  'ddk\ntdll.inc'
entry    start
;//-------
.data
FSCTL_GET_RETRIEVAL_POINTERS = 0x00090073
struct STARTING_VCN_INPUT_BUFFER
   StartingVcn    dq  0   ; VCN = Virtual Cluster Number
ends
struct RETRIEVAL_POINTERS_BUFFER
   ExtentCount    dd  0    ;
   StartingVcn    dq  0    ;
     struct
        NextVcn   dd  0,0  ;
        Lcn       dd  0,0  ;
     ends
   Extents        dd  0    ;
ends
;buffPointer  RETRIEVAL_POINTERS_BUFFER

capt      db  13,10,' RAW-READ file: %s'
          db  13,10,' ============================================='
          db  13,10,' * Disk geometry'
          db  13,10,'     Sector  size.....: %d'
          db  13,10,'     Cluster size.....: %d',10,0
fileInfo  db  13,10,' * File info'
          db  13,10,'     Size.............: %d byte'
          db  13,10,'     Total fragments..: %d',10
          db  13,10,' * Read address',0
fragment  db  13,10,'   Fragment #: %d'
          db  13,10,'     Start cluster....: %d'
          db  13,10,'     Data length......: %d clusters',10,0

fName     db  'C:\WINDOWS\system32\config\SAM',0
Hndl      dd  0
frmt      db  '%s',0

SecCls    dd  0     ;// Число секторов на кластер.
BytesSec  dd  0     ;// байт в секторе
FreeCls   dd  0     ;// свободно кластеров
TotalClus dd  0     ;// всего кластеров
clsSize   dd  0     ;// размер кластера

inpBuff   dq  0     ;// передаваемые данные IRP
number    dd  1     ;// счётчик фрагментов
outSize   dd  0     ;// кол-во выходных байт
align     16
rpb       RETRIEVAL_POINTERS_BUFFER  ;// приёмный буфер
;//-------

.code
start:
;//==== Запрашиваем размеры кластера и сектора =====
        invoke  GetDiskFreeSpace,0,SecCls,BytesSec,FreeCls,TotalClus
        mov     eax,[SecCls]      ;// секторов в кластере
        mov     ebx,[BytesSec]    ;// байт в секторе
        mul     ebx               ;// EAX = размер кластера!!!
        mov     [clsSize],eax     ;// запомнить.
       cinvoke  printf,capt,fName,ebx,eax

;//==== Открываем SAM-файл на чтение атрибутов =====
        invoke  CreateFile,fName,FILE_READ_ATTRIBUTES,\
                           FILE_SHARE_READ,0,OPEN_EXISTING,0,0
        mov     [Hndl],eax        ;// запомнить его дескриптор
;//==== Запрашиваем размер файла ===================
        invoke  GetFileSize,eax,0
        push    eax               ;//
;//====
        invoke  DeviceIoControl,[Hndl],FSCTL_GET_RETRIEVAL_POINTERS,\
                                inpBuff,8,rpb,32,outSize,0
;//====
        pop     eax                     ;//
        mov     ebx,[rpb.ExtentCount]   ;// число структур в буфере
       cinvoke  printf,fileInfo,eax,ebx

        mov     ecx,[rpb.ExtentCount]   ;// ^^^^^
@findOffset:                            ;// цикл поиска карты кластеров..
        push    ecx                     ;//
        mov     eax,[rpb.Lcn+4]         ;// номер первой цепочки кластеров файла
        mov     ebx,[rpb.NextVcn+4]     ;// кол-во элементов Extents в структуре
       cinvoke  printf,fragment,[number],eax,ebx
        pop     ecx                     ;//
        loop    @findOffset             ;// промотать по ECX..

@exit: cinvoke  scanf,frmt,frmt    ;//
       cinvoke  exit,0             ;// Game-Over!!!
;//-------
section '.idata' import data readable
library  msvcrt,'msvcrt.dll',kernel32,'kernel32.dll'
import   msvcrt, printf,'printf',scanf,'scanf',exit,'exit'
include 'api\kernel32.inc'

dio.png


Здесь видно, что мой SAM на диске не фрагментирован и живёт в одном фрагменте. Можете в переменную "fName" подставить другой путь и посмотреть, из скольки фрагментов он состоит. Защититься от чтения сырых кластеров диска невозможно – диск всегда открыт за R\W, не уязвимость-ли это?
 

Вложения

  • WinObjEx.zip
    111,9 КБ · Просмотры: 574
  • NTDLL.zip
    9,7 КБ · Просмотры: 584

tupoy

One Level
27.11.2019
4
2
BIT
0
Последнее редактирование модератором:
  • Нравится
Реакции: Hardreversengineer
09.11.2017
258
261
BIT
5
Автор молодец нечего сказать а так можно попробовать сделать есть чем вечером заняться
 
  • Нравится
Реакции: ha1me
Мы в соцсетях:

Обучение наступательной кибербезопасности в игровой форме. Начать игру!