Драйверы режима ядра: Часть 11 : Базовая техника: Каталоги и файлы

Дата публикации 22 янв 2004

Драйверы режима ядра: Часть 11 : Базовая техника: Каталоги и файлы — Архив WASM.RU



Обеспечение возможности работы с файлами является важнейшей задачей любой операционной системы. Посмотрим, какие возможности предоставляют нам операционные системы семейства NT.


11.1 Таблица описателей ядра

Прежде чем мы перейдем непосредственно к теме статьи, обсудим один важный момент, которому прежде не было уделено должного внимания. Для получения описателя объекта требуется заполнить структуру OBJECT_ATTRIBUTES - это мы делали уже много раз примерно таким образом:

Код (Text):
  1.  
  2.  
  3.  InitializeObjectAttributes addr oa, addr g_usName, OBJ_CASE_INSENSITIVE, NULL, NULL
  4.  
  5.  

Инициализировав структуру OBJECT_ATTRIBUTES, мы вызывали функцию создания/открытия объекта и получали его описатель (hande). Но описатель этот попадал в таблицу описателей того процесса, в контексте которого был получен. Т.к. таблица описателей специфична для процесса, то использовать такой описатель можно только в контексте того же самого процесса. Например, попытка закрыть этот описатель в контексте другого процесса, в лучшем случае, завершится неудачно. А в худшем, если в таблице описателей этого процесса окажется описатель с таким же значением, ведь описатель это просто 32-битное число (точнее битовая структура), возможно, будет закрыт описатель совершенно другого объекта. Даже если описатель получен драйвером, но в контексте пользовательского процесса, он попадет в таблицу описателей этого процесса и по нему можно будет обратиться к объекту из режима пользователя, преднамеренно или случайно. Бывают ситуации, когда именно это и требуется, но в общем случае это не то, что вы хотите. Именно поэтому компоненты ядра, и драйверы в частности, не "любят" пользоваться описателями, а предпочитают использовать ссылки на объекты (reference to object), которые являются простыми указателями на структуру объекта в памяти ядра. Для учета предоставленных ссылок на объект в заголовке каждого объекта хранится счетчик ссылок (reference count). Если же для доступа к объекту нужен именно его описатель, как в этом и предыдущем примере, и вы планируете обращаться к нему из разных контекстов, нужно заставить систему поместить описатель в так называемую таблицу описателей ядра (kernel handle table).

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

Даже если получить описатель в контексте процесса System, например, в процедуре DriverEntry, то обратиться по нему к объекту в контексте пользовательского процесса нельзя. Т.е. процесс System использует свою собственную таблицу описателей, отличную от таблицы описателей ядра.

Для того чтобы описатель попал в таблицу описателей ядра, нужно явно указать флаг OBJ_KERNEL_HANDLE в вызове макроса InitializeObjectAttributes таким образом:

Код (Text):
  1.  
  2.  
  3.  InitializeObjectAttributes addr oa, addr g_usName, OBJ_KERNEL_HANDLE, NULL, NULL
  4.  
  5.  

11.2 Исходный текст драйвера FileWorks

Также как и в предыдущем примере, код драйвера состоит из нескольких автономных процедур: CreateDirectory, CreateFile, WriteFile, MarkAsReadOnly, ReadFile, UnmarkAsReadOnly, AppendFile, TruncateFile, DeleteFile, DeleteDirectory и EnumerateFiles. Все они роботают почти независимо друг от друга.

Код (Text):
  1.  
  2.  
  3.  ;@echo off
  4.  ;goto make
  5.  
  6.  ;:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
  7.  ;
  8.  ;  FileWorks - Пример различных операций с файлами.
  9.  ;
  10.  ;:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
  11.  
  12.  .386
  13.  .model flat, stdcall
  14.  option casemap:none
  15.  
  16.  ;:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
  17.  ;                               В К Л Ю Ч А Е М Ы Е    Ф А Й Л Ы
  18.  ;:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
  19.  
  20.  include \masm32\include\w2k\ntstatus.inc
  21.  include \masm32\include\w2k\ntifs.inc
  22.  include \masm32\include\w2k\ntoskrnl.inc
  23.  
  24.  includelib \masm32\lib\w2k\ntoskrnl.lib
  25.  
  26.  include \masm32\Macros\Strings.mac
  27.  
  28.  ;:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
  29.  ;                             Н Е И З М Е Н Я Е М Ы Е    Д А Н Н Ы Е                                
  30.  ;:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
  31.  
  32.  .const
  33.  
  34.  CCOUNTED_UNICODE_STRING "\\??\\c:\\FileWorks\\test.txt", g_usFileName, 4
  35.  CCOUNTED_UNICODE_STRING "\\??\\c:\\FileWorks", g_usDirName, 4
  36.  
  37.  ;:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
  38.  ;                                           К О Д                                                  
  39.  ;:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
  40.  
  41.  .code
  42.  
  43.  ;:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
  44.  ;                                      CreateDirectory                                              
  45.  ;:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
  46.  
  47.  CreateDirectory proc
  48.  
  49.  local oa:OBJECT_ATTRIBUTES
  50.  local iosb:IO_STATUS_BLOCK
  51.  local hDirectory:HANDLE
  52.  
  53.      ; Помни, что коды форматирования Unicode (%C, %S, %lc, %ls, %wc, %ws, %wZ), передаваемые в
  54.      ; функцию DbgPrint могут быть использованы только на IRQL = PASSIVE_LEVEL!
  55.      invoke DbgPrint, $CTA0("\nFileWorks: Creating %ws directory\n"), g_usDirName.Buffer
  56.  
  57.      InitializeObjectAttributes addr oa, addr g_usDirName, \
  58.                          OBJ_CASE_INSENSITIVE + OBJ_KERNEL_HANDLE, NULL, NULL
  59.  
  60.      invoke ZwCreateFile, addr hDirectory, SYNCHRONIZE, addr oa, addr iosb, 0, FILE_ATTRIBUTE_NORMAL, \
  61.                          0, FILE_OPEN_IF, FILE_DIRECTORY_FILE + FILE_SYNCHRONOUS_IO_NONALERT, NULL, 0
  62.      .if eax == STATUS_SUCCESS
  63.          .if iosb.Information == FILE_CREATED
  64.              invoke DbgPrint, $CTA0("FileWorks: Directory created\n")
  65.          .elseif iosb.Information == FILE_OPENED
  66.              invoke DbgPrint, $CTA0("FileWorks: Directory exists and was opened\n")
  67.          .endif
  68.          invoke ZwClose, hDirectory
  69.      .else
  70.          invoke DbgPrint, $CTA0("FileWorks: Can't create directory. Status: %08X\n"), eax
  71.      .endif
  72.    
  73.      ret
  74.  
  75.  CreateDirectory endp
  76.  
  77.  ;:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
  78.  ;                                        CreateFile                                                
  79.  ;:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
  80.  
  81.  CreateFile proc
  82.  
  83.  local oa:OBJECT_ATTRIBUTES
  84.  local iosb:IO_STATUS_BLOCK
  85.  local hFile:HANDLE
  86.  
  87.      ; Помни, что коды форматирования Unicode (%C, %S, %lc, %ls, %wc, %ws, %wZ), передаваемые в
  88.      ; функцию DbgPrint могут быть использованы только на IRQL = PASSIVE_LEVEL!
  89.      invoke DbgPrint, $CTA0("\nFileWorks: Creating %ws file\n"), g_usFileName.Buffer
  90.  
  91.      InitializeObjectAttributes addr oa, addr g_usFileName, \
  92.                          OBJ_CASE_INSENSITIVE + OBJ_KERNEL_HANDLE, NULL, NULL
  93.  
  94.      invoke ZwCreateFile, addr hFile, SYNCHRONIZE, addr oa, addr iosb, 0, FILE_ATTRIBUTE_NORMAL, \
  95.                          0, FILE_CREATE, FILE_SYNCHRONOUS_IO_NONALERT, NULL, 0
  96.      .if eax == STATUS_SUCCESS
  97.  
  98.          invoke DbgPrint, $CTA0("FileWorks: File created\n")
  99.          invoke ZwClose, hFile
  100.      .else
  101.          invoke DbgPrint, $CTA0("FileWorks: Can't create file. Status: %08X\n"), eax
  102.      .endif
  103.    
  104.      ret
  105.  
  106.  CreateFile endp
  107.  
  108.  ;:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
  109.  ;                                            WriteFile                                              
  110.  ;:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
  111.  
  112.  WriteFile proc
  113.  
  114.  local oa:OBJECT_ATTRIBUTES
  115.  local iosb:IO_STATUS_BLOCK
  116.  local hFile:HANDLE
  117.  
  118.      invoke DbgPrint, $CTA0("\nFileWorks: Opening file for writing\n")
  119.  
  120.      InitializeObjectAttributes addr oa, addr g_usFileName, \
  121.                          OBJ_CASE_INSENSITIVE + OBJ_KERNEL_HANDLE, NULL, NULL
  122.    
  123.      invoke ZwCreateFile, addr hFile, FILE_WRITE_DATA + SYNCHRONIZE, addr oa, addr iosb, \
  124.                          0, 0, FILE_SHARE_READ, FILE_OPEN, FILE_SYNCHRONOUS_IO_NONALERT, NULL, 0
  125.      .if eax == STATUS_SUCCESS
  126.          invoke DbgPrint, $CTA0("FileWorks: File openeded\n")
  127.  
  128.          CTA0 "Data can be written to an open file", g_szData, 4
  129.  
  130.          invoke ZwWriteFile, hFile, 0, NULL, NULL, addr iosb, \
  131.                          addr g_szData, sizeof g_szData - 1, NULL, NULL
  132.          .if eax == STATUS_SUCCESS
  133.              invoke DbgPrint, $CTA0("FileWorks: File was written\n")
  134.          .else
  135.              invoke DbgPrint, $CTA0("FileWorks: Can't write to the file. Status: %08X\n"), eax
  136.          .endif
  137.  
  138.          invoke ZwClose, hFile
  139.      .else
  140.          invoke DbgPrint, $CTA0("FileWorks: Can't open file. Status: %08X\n"), eax
  141.      .endif
  142.  
  143.      ret
  144.  
  145.  WriteFile endp
  146.  
  147.  ;:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
  148.  ;                                        MarkAsReadOnly                                            
  149.  ;:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
  150.  
  151.  MarkAsReadOnly proc
  152.  
  153.  local oa:OBJECT_ATTRIBUTES
  154.  local iosb:IO_STATUS_BLOCK
  155.  local hFile:HANDLE
  156.  local fbi:FILE_BASIC_INFORMATION
  157.  
  158.      invoke DbgPrint, $CTA0("\nFileWorks: Opening file for changing attributes\n")
  159.  
  160.      InitializeObjectAttributes addr oa, addr g_usFileName, \
  161.                          OBJ_CASE_INSENSITIVE + OBJ_KERNEL_HANDLE, NULL, NULL
  162.    
  163.      invoke ZwCreateFile, addr hFile, FILE_READ_ATTRIBUTES + FILE_WRITE_ATTRIBUTES + SYNCHRONIZE, \
  164.                          addr oa, addr iosb, 0, 0, FILE_SHARE_READ, \
  165.                          FILE_OPEN, FILE_SYNCHRONOUS_IO_NONALERT, NULL, 0
  166.      .if eax == STATUS_SUCCESS
  167.          invoke DbgPrint, $CTA0("FileWorks: File openeded\n")
  168.  
  169.          invoke ZwQueryInformationFile, hFile, addr iosb, addr fbi, sizeof fbi, FileBasicInformation
  170.          .if eax == STATUS_SUCCESS
  171.              invoke DbgPrint, $CTA0("FileWorks: File attributes were: %08X\n"), fbi.FileAttributes
  172.              or fbi.FileAttributes, FILE_ATTRIBUTE_READONLY
  173.              invoke ZwSetInformationFile, hFile, addr iosb, addr fbi, sizeof fbi, FileBasicInformation
  174.              .if eax == STATUS_SUCCESS
  175.                  invoke DbgPrint, $CTA0("FileWorks: Now file marked as read-only\n")
  176.              .else
  177.                  invoke DbgPrint, $CTA0("FileWorks: Can't change file attributes. Status: %08X\n"), eax
  178.              .endif
  179.          .else
  180.              invoke DbgPrint, $CTA0("FileWorks: Can't query file attributes. Status: %08X\n"), eax
  181.          .endif
  182.  
  183.          invoke ZwClose, hFile
  184.      .else
  185.          invoke DbgPrint, $CTA0("FileWorks: Can't open file. Status: %08X\n"), eax
  186.      .endif
  187.  
  188.      ret
  189.  
  190.  MarkAsReadOnly endp
  191.  
  192.  ;:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
  193.  ;                                          ReadFile                                                
  194.  ;:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
  195.  
  196.  ReadFile proc
  197.  
  198.  local oa:OBJECT_ATTRIBUTES
  199.  local iosb:IO_STATUS_BLOCK
  200.  local hFile:HANDLE
  201.  local p:PVOID
  202.  local cb:DWORD
  203.  local fsi:FILE_STANDARD_INFORMATION
  204.  
  205.      invoke DbgPrint, $CTA0("\nFileWorks: Opening file for reading\n")
  206.  
  207.      InitializeObjectAttributes addr oa, addr g_usFileName, \
  208.                          OBJ_CASE_INSENSITIVE + OBJ_KERNEL_HANDLE, NULL, NULL
  209.  
  210.      invoke ZwOpenFile, addr hFile, FILE_READ_DATA + SYNCHRONIZE, addr oa, addr iosb, \
  211.                  FILE_SHARE_READ + FILE_SHARE_WRITE + FILE_SHARE_DELETE, FILE_SYNCHRONOUS_IO_NONALERT
  212.      .if eax == STATUS_SUCCESS
  213.  
  214.          invoke DbgPrint, $CTA0("FileWorks: File openeded\n")
  215.  
  216.          invoke ZwQueryInformationFile, hFile, addr iosb, addr fsi, sizeof fsi, FileStandardInformation
  217.          .if eax == STATUS_SUCCESS
  218.  
  219.              mov eax, fsi.EndOfFile.LowPart
  220.              inc eax
  221.              mov cb, eax
  222.  
  223.              invoke ExAllocatePool, PagedPool, cb
  224.              .if eax != NULL
  225.                  mov p, eax
  226.  
  227.                  invoke RtlZeroMemory, p, cb
  228.  
  229.                  invoke ZwReadFile, hFile, 0, NULL, NULL, addr iosb, p, cb, 0, NULL
  230.                  .if eax == STATUS_SUCCESS
  231.                      invoke DbgPrint, $CTA0("FileWorks: File content: \=%s\=\n"), p
  232.                  .else
  233.                      invoke DbgPrint, $CTA0("FileWorks: Can't read from the file. Status: %08X\n"), eax
  234.                  .endif
  235.  
  236.                  invoke ExFreePool, p
  237.  
  238.              .else
  239.                  invoke DbgPrint, $CTA0("FileWorks: Can't allocate memory. Status: %08X\n"), eax
  240.              .endif
  241.          .else
  242.              invoke DbgPrint, $CTA0("FileWorks: Can't query file size. Status: %08X\n"), eax
  243.          .endif
  244.  
  245.          invoke ZwClose, hFile
  246.  
  247.      .else
  248.          invoke DbgPrint, $CTA0("FileWorks: Can't open file. Status: %08X\n"), eax
  249.      .endif
  250.  
  251.      ret
  252.  
  253.  ReadFile endp
  254.  
  255.  ;:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
  256.  ;                                        UnmarkAsReadOnly                                          
  257.  ;:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
  258.  
  259.  UnmarkAsReadOnly proc
  260.  
  261.  local oa:OBJECT_ATTRIBUTES
  262.  local iosb:IO_STATUS_BLOCK
  263.  local hFile:HANDLE
  264.  local fbi:FILE_BASIC_INFORMATION
  265.  
  266.      invoke DbgPrint, $CTA0("\nFileWorks: Opening file for changing attributes\n")
  267.  
  268.      InitializeObjectAttributes addr oa, addr g_usFileName, \
  269.                          OBJ_CASE_INSENSITIVE + OBJ_KERNEL_HANDLE, NULL, NULL
  270.    
  271.      invoke ZwCreateFile, addr hFile, FILE_READ_ATTRIBUTES + FILE_WRITE_ATTRIBUTES + SYNCHRONIZE, \
  272.                          addr oa, addr iosb, 0, 0, FILE_SHARE_READ, FILE_OPEN, \
  273.                          FILE_SYNCHRONOUS_IO_NONALERT, NULL, 0
  274.      .if eax == STATUS_SUCCESS
  275.          invoke DbgPrint, $CTA0("FileWorks: File openeded\n")
  276.  
  277.          invoke ZwQueryInformationFile, hFile, addr iosb, addr fbi, sizeof fbi, FileBasicInformation
  278.          .if eax == STATUS_SUCCESS
  279.              invoke DbgPrint, $CTA0("FileWorks: File attributes were: %08X\n"), fbi.FileAttributes
  280.              and fbi.FileAttributes, not FILE_ATTRIBUTE_READONLY
  281.              invoke ZwSetInformationFile, hFile, addr iosb, addr fbi, sizeof fbi, FileBasicInformation
  282.              .if eax == STATUS_SUCCESS
  283.                  invoke DbgPrint, $CTA0("FileWorks: Now file can be written or deleted\n")
  284.              .else
  285.                  invoke DbgPrint, $CTA0("FileWorks: Can't change file attributes. Status: %08X\n"), eax
  286.              .endif
  287.          .else
  288.              invoke DbgPrint, $CTA0("FileWorks: Can't query file attributes. Status: %08X\n"), eax
  289.          .endif
  290.  
  291.          invoke ZwClose, hFile
  292.      .else
  293.          invoke DbgPrint, $CTA0("FileWorks: Can't open file. Status: %08X\n"), eax
  294.      .endif
  295.  
  296.      ret
  297.  
  298.  UnmarkAsReadOnly endp
  299.  
  300.  ;:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
  301.  ;                                         AppendFile                                                
  302.  ;:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
  303.  
  304.  AppendFile proc
  305.  
  306.  local oa:OBJECT_ATTRIBUTES
  307.  local iosb:IO_STATUS_BLOCK
  308.  local hFile:HANDLE
  309.  
  310.      invoke DbgPrint, $CTA0("\nFileWorks: Opening file to append data\n")
  311.  
  312.      InitializeObjectAttributes addr oa, addr g_usFileName, \
  313.                          OBJ_CASE_INSENSITIVE + OBJ_KERNEL_HANDLE, NULL, NULL
  314.  
  315.      invoke ZwOpenFile, addr hFile, FILE_APPEND_DATA + SYNCHRONIZE, addr oa, addr iosb, \
  316.                                      FILE_SHARE_READ, FILE_SYNCHRONOUS_IO_NONALERT
  317.      .if eax == STATUS_SUCCESS
  318.          invoke DbgPrint, $CTA0("FileWorks: File openeded\n")
  319.  
  320.          CTA0 " using ZwWriteFile", g_szDataToAppend, 4
  321.  
  322.          invoke ZwWriteFile, hFile, 0, NULL, NULL, addr iosb, \
  323.                          addr g_szDataToAppend, sizeof g_szDataToAppend - 1, NULL, NULL
  324.          .if eax == STATUS_SUCCESS
  325.              invoke DbgPrint, $CTA0("FileWorks: Data appended to the file\n")
  326.          .else
  327.              invoke DbgPrint, $CTA0("FileWorks: Can't append data to file. Status: %08X\n"), eax
  328.          .endif
  329.  
  330.          invoke ZwClose, hFile
  331.     .else
  332.          invoke DbgPrint, $CTA0("FileWorks: Can't open file. Status: %08X\n"), eax
  333.      .endif
  334.  
  335.      ret
  336.  
  337.  AppendFile endp
  338.  
  339.  ;:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
  340.  ;                                        TruncateFile                                              
  341.  ;:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
  342.  
  343.  TruncateFile proc
  344.  
  345.  local oa:OBJECT_ATTRIBUTES
  346.  local iosb:IO_STATUS_BLOCK
  347.  local hFile:HANDLE
  348.  local fsi:FILE_STANDARD_INFORMATION
  349.  local feofi:FILE_END_OF_FILE_INFORMATION
  350.  
  351.      invoke DbgPrint, $CTA0("\nFileWorks: Opening file to truncate\n")
  352.  
  353.      InitializeObjectAttributes addr oa, addr g_usFileName, \
  354.                          OBJ_CASE_INSENSITIVE + OBJ_KERNEL_HANDLE, NULL, NULL
  355.  
  356.      invoke ZwOpenFile, addr hFile, FILE_WRITE_DATA + SYNCHRONIZE, addr oa, addr iosb, \
  357.                          FILE_SHARE_READ, FILE_SYNCHRONOUS_IO_NONALERT
  358.      .if eax == STATUS_SUCCESS
  359.          invoke DbgPrint, $CTA0("FileWorks: File openeded\n")
  360.  
  361.          invoke ZwQueryInformationFile, hFile, addr iosb, \
  362.                          addr fsi, sizeof fsi, FileStandardInformation
  363.          .if eax == STATUS_SUCCESS
  364.  
  365.              invoke DbgPrint, $CTA0("FileWorks: EOF was: %08X\n"), fsi.EndOfFile.LowPart
  366.  
  367.              and feofi.EndOfFile.HighPart, 0
  368.              mov eax, fsi.EndOfFile.LowPart
  369.              shr eax, 1
  370.              mov feofi.EndOfFile.LowPart, eax
  371.              invoke ZwSetInformationFile, hFile, addr iosb, \
  372.                          addr feofi, sizeof feofi, FileEndOfFileInformation
  373.              .if eax == STATUS_SUCCESS
  374.                  invoke DbgPrint, $CTA0("FileWorks: File truncated to its half size\n")
  375.              .else
  376.                  invoke DbgPrint, $CTA0("FileWorks: Can't truncate file. Status: %08X\n"), eax      
  377.              .endif
  378.  
  379.          .else
  380.              invoke DbgPrint, $CTA0("FileWorks: Can't query file info. Status: %08X\n"), eax
  381.          .endif
  382.  
  383.          invoke ZwClose, hFile
  384.      .else
  385.          invoke DbgPrint, $CTA0("FileWorks: Can't open file. Status: %08X\n"), eax
  386.      .endif
  387.  
  388.      ret
  389.  
  390.  TruncateFile endp
  391.  
  392.  ;:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
  393.  ;                                         DeleteFile                                                
  394.  ;:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
  395.  
  396.  DeleteFile proc
  397.  
  398.  local oa:OBJECT_ATTRIBUTES
  399.  local iosb:IO_STATUS_BLOCK
  400.  local hFile:HANDLE
  401.  local fdi:FILE_DISPOSITION_INFORMATION
  402.  
  403.      invoke DbgPrint, $CTA0("\nFileWorks: Opening file for deletion")
  404.  
  405.      InitializeObjectAttributes addr oa, addr g_usFileName, \
  406.                          OBJ_CASE_INSENSITIVE + OBJ_KERNEL_HANDLE, NULL, NULL
  407.  
  408.      invoke ZwCreateFile, addr hFile, DELETE + SYNCHRONIZE, addr oa, addr iosb, \
  409.                          0, 0, FILE_SHARE_DELETE, FILE_OPEN, FILE_SYNCHRONOUS_IO_NONALERT, NULL, 0
  410.      .if eax == STATUS_SUCCESS
  411.          invoke DbgPrint, $CTA0("FileWorks: File openeded\n")
  412.  
  413.          mov fdi.DeleteFile, TRUE
  414.          invoke ZwSetInformationFile, hFile, addr iosb, addr fdi, sizeof fdi, FileDispositionInformation
  415.          .if eax == STATUS_SUCCESS
  416.              ; The file has been marked for deletion. Do nothing with the file handle except closing it.
  417.              invoke DbgPrint, $CTA0("FileWorks: File has been marked for deletion\n")
  418.              invoke DbgPrint, $CTA0("FileWorks: It should be deleted when the last open handle is closed\n")
  419.          .else
  420.              invoke DbgPrint, $CTA0("FileWorks: Can't mark file for deletion. Status: %08X\n"), eax
  421.          .endif
  422.  
  423.          invoke ZwClose, hFile
  424.      .else
  425.          invoke DbgPrint, $CTA0("FileWorks: Can't open file. Status: %08X\n"), eax
  426.      .endif
  427.  
  428.      ret
  429.  
  430.  DeleteFile endp
  431.  
  432.  ;:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
  433.  ;                                       DeleteDirectory                                            
  434.  ;:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
  435.  
  436.  DeleteDirectory proc
  437.  
  438.  local oa:OBJECT_ATTRIBUTES
  439.  local iosb:IO_STATUS_BLOCK
  440.  local hDirectory:HANDLE
  441.  
  442.      InitializeObjectAttributes addr oa, addr g_usDirName, \
  443.                          OBJ_CASE_INSENSITIVE + OBJ_KERNEL_HANDLE, NULL, NULL
  444.  
  445.      invoke ZwDeleteFile, addr oa
  446.      .if eax == STATUS_SUCCESS
  447.          invoke DbgPrint, $CTA0("\nFileWorks: Directory should be deleted\n")            
  448.      .else
  449.          invoke DbgPrint, $CTA0("\nFileWorks: Can't delete directory. Status: %08X\n"), eax
  450.      .endif
  451.  
  452.      ret
  453.  
  454.  DeleteDirectory endp
  455.  
  456.  ;:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
  457.  ;                                      EnumerateFiles                                              
  458.  ;:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
  459.  
  460.  EnumerateFiles proc uses esi
  461.  
  462.  local status:NTSTATUS
  463.  local oa:OBJECT_ATTRIBUTES
  464.  local hSystemRootDirectory:HANDLE
  465.  local hDriversDirectory:HANDLE
  466.  local as:ANSI_STRING
  467.  local us:UNICODE_STRING
  468.  local iosb:IO_STATUS_BLOCK
  469.  local tf:TIME_FIELDS
  470.  local cb:DWORD
  471.  local pfdi:PFILE_DIRECTORY_INFORMATION
  472.  
  473.      invoke DbgPrint, $CTA0("\nFileWorks: Opening directory to enumerate files\n")
  474.    
  475.      InitializeObjectAttributes addr oa, $CCOUNTED_UNICODE_STRING("\\SystemRoot"), \
  476.                                  OBJ_CASE_INSENSITIVE + OBJ_KERNEL_HANDLE, NULL, NULL
  477.  
  478.      invoke ZwOpenFile, addr hSystemRootDirectory, FILE_LIST_DIRECTORY + SYNCHRONIZE, addr oa, \
  479.                          addr iosb, FILE_SHARE_READ + FILE_SHARE_WRITE + FILE_SHARE_DELETE, \
  480.                          FILE_DIRECTORY_FILE + FILE_SYNCHRONOUS_IO_NONALERT
  481.      .if eax == STATUS_SUCCESS
  482.  
  483.          InitializeObjectAttributes addr oa, $CCOUNTED_UNICODE_STRING("system32\\drivers"), \
  484.                              OBJ_CASE_INSENSITIVE + OBJ_KERNEL_HANDLE, hSystemRootDirectory, NULL
  485.  
  486.          invoke ZwOpenFile, addr hDriversDirectory, FILE_LIST_DIRECTORY + SYNCHRONIZE, addr oa, \
  487.                              addr iosb, FILE_SHARE_READ + FILE_SHARE_WRITE + FILE_SHARE_DELETE, \
  488.                              FILE_DIRECTORY_FILE + FILE_SYNCHRONOUS_IO_NONALERT
  489.          .if eax == STATUS_SUCCESS
  490.  
  491.              mov cb, sizeof FILE_DIRECTORY_INFORMATION + 256
  492.  
  493.              invoke ExAllocatePool, PagedPool, cb
  494.              .if eax != NULL
  495.  
  496.                  mov pfdi, eax
  497.                  mov esi, eax
  498.                  assume esi:ptr FILE_DIRECTORY_INFORMATION
  499.  
  500.                  invoke DbgPrint, \
  501.                          $CTA0("\nFileWorks: ---------- Starting enumerate files ----------\n")
  502.  
  503.                  invoke ZwQueryDirectoryFile, hDriversDirectory, NULL, NULL, NULL, addr iosb, \
  504.                              esi, cb, FileDirectoryInformation, \
  505.                              TRUE, $CCOUNTED_UNICODE_STRING("c*"), TRUE
  506.  
  507.                  .while eax != STATUS_NO_MORE_FILES
  508.  
  509.                      .if ( eax == STATUS_SUCCESS )
  510.  
  511.                          mov eax, [esi].FileNameLength
  512.                          mov us._Length, ax
  513.                          mov us.MaximumLength, ax
  514.                          lea eax, [esi].FileName
  515.                          mov us.Buffer, eax
  516.                          invoke RtlUnicodeStringToAnsiString, addr as, addr us, TRUE
  517.                          .if eax == STATUS_SUCCESS
  518.  
  519.                              invoke RtlTimeToTimeFields, addr [esi].CreationTime, addr tf
  520.                              movzx eax, tf.Day
  521.                              movzx ecx, tf.Month
  522.                              movzx edx, tf.Year
  523.  
  524.                              invoke DbgPrint, $CTA0("    %s   size=%d   created on %d.%02d.%04d\n"), \
  525.                                          as.Buffer, [esi].EndOfFile.LowPart, eax, ecx, edx
  526.  
  527.                              invoke RtlFreeAnsiString, addr as
  528.                          .endif
  529.  
  530.                      .endif
  531.  
  532.                      invoke ZwQueryDirectoryFile, hDriversDirectory, NULL, NULL, NULL, addr iosb, \
  533.                                  esi, cb, FileDirectoryInformation, \
  534.                                  TRUE, NULL, FALSE
  535.                  .endw
  536.                  invoke DbgPrint, \
  537.                      $CTA0("FileWorks: ------------------------------------------------\n")
  538.  
  539.                  assume esi:nothing
  540.                  invoke ExFreePool, pfdi
  541.              .endif
  542.              invoke ZwClose, hDriversDirectory
  543.          .else
  544.              invoke DbgPrint, $CTA0("FileWorks: Can't open drivers directory. Status: %08X\n"), eax
  545.          .endif
  546.          invoke ZwClose, hSystemRootDirectory
  547.      .else
  548.          invoke DbgPrint, $CTA0("FileWorks: Can't open system root directory. Status: %08X\n"), eax
  549.      .endif
  550.  
  551.      ret
  552.  
  553.  EnumerateFiles endp
  554.  
  555.  ;:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
  556.  ;                                       DriverEntry                                                
  557.  ;:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
  558.  
  559.  DriverEntry proc pDriverObject:PDRIVER_OBJECT, pusRegistryPath:PUNICODE_STRING
  560.  
  561.      invoke DbgPrint, $CTA0("\nFileWorks: Entering DriverEntry\n")
  562.  
  563.      invoke CreateDirectory
  564.      invoke CreateFile
  565.      invoke WriteFile
  566.      invoke MarkAsReadOnly
  567.      invoke ReadFile
  568.      invoke UnmarkAsReadOnly
  569.      invoke AppendFile
  570.      invoke ReadFile
  571.      invoke TruncateFile
  572.      invoke ReadFile
  573.      invoke DeleteFile
  574.      invoke DeleteDirectory
  575.      invoke EnumerateFiles
  576.  
  577.      invoke DbgPrint, $CTA0("\nFileWorks: Leaving DriverEntry\n")
  578.  
  579.      mov eax, STATUS_DEVICE_CONFIGURATION_ERROR
  580.      ret
  581.  
  582.  DriverEntry endp
  583.  
  584.  ;:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
  585.  ;                                                                                                  
  586.  ;:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
  587.  
  588.  end DriverEntry
  589.  
  590.  :make
  591.  
  592.  set drv=FileWorks
  593.  
  594.  \masm32\bin\ml /nologo /c /coff %drv%.bat
  595.  \masm32\bin\link /nologo /driver /base:0x10000 /align:32 /out:%drv%.sys /subsystem:native %drv%.obj
  596.  
  597.  del %drv%.obj
  598.  
  599.  echo.
  600.  pause
  601.  
  602.  

В ntddk.inc не входят некоторые нужные нам константы и структуры.

Код (Text):
  1.  
  2.  
  3.  include \masm32\include\w2k\ntifs.inc
  4.  
  5.  

В Installable File System (IFS) Kit, предназначенный для разработки драйверов файловых систем, входит заголовочный файл ntifs.h, аналогом которого и является включаемый файл ntifs.inc. Этот файл входит в состав KmdKit начиная с версии 1.5.


11.3 Создание каталога и файла

Код (Text):
  1.  
  2.  
  3.      InitializeObjectAttributes addr oa, addr g_usDirName, \
  4.                          OBJ_CASE_INSENSITIVE + OBJ_KERNEL_HANDLE, NULL, NULL
  5.  
  6.  

Заполняем структуру OBJECT_ATTRIBUTES, не забывая про флаг OBJ_KERNEL_HANDLE. Хочу подчеркнуть, что в данном примере это необязательно, т.к. мы не собираемся использовать ни один из описателей в контексте какого-либо другого процесса. Но поскольку процедуры драйвера FileWorks легко могут быть использованы в других проектах в контексте любого процесса, я решил сделать именно так. Если в ваши задачи входит совместное использование описателя драйвером и пользовательским процессом, то флаг OBJ_KERNEL_HANDLE использовать не следует. Если обращения к объекту будут происходить в контексте одного и того же процесса, флаг OBJ_KERNEL_HANDLE не нужен.

Для создания, как каталога, так и файла используется функция ZwCreateFile. С точки зрения системы, каталоги являются теми же файлами, поэтому принципиальных различий в процедуре создания файла и каталога нет. Поэтому, все сказанное ниже в отношении создания каталога в равной степени относится и к файлам, за исключением флага FILE_DIRECTORY_FILE.

Код (Text):
  1.  
  2.  
  3.      invoke ZwCreateFile, addr hDirectory, SYNCHRONIZE, addr oa, addr iosb, 0, FILE_ATTRIBUTE_NORMAL, \
  4.                          0, FILE_OPEN_IF, FILE_DIRECTORY_FILE + FILE_SYNCHRONOUS_IO_NONALERT, NULL, 0
  5.  
  6.  

У функции ZwCreateFile довольно много параметров. Поэтому, я приведу её протопип. И вынужден сделать это, используя синтаксис языка си, чтобы были видны входные (IN) выходные (OUT) и необязательные (OPTIONAL) параметры.

Код (Text):
  1.  
  2.  
  3. NTSTATUS  
  4.   ZwCreateFile(
  5.     OUT PHANDLE             FileHandle,
  6.     IN  ACCESS_MASK         DesiredAccess,
  7.     IN  POBJECT_ATTRIBUTES  ObjectAttributes,
  8.     OUT PIO_STATUS_BLOCK    IoStatusBlock,
  9.     IN  PLARGE_INTEGER      AllocationSize  OPTIONAL,
  10.     IN  ULONG               FileAttributes,
  11.     IN  ULONG               ShareAccess,
  12.     IN  ULONG               CreateDisposition,
  13.     IN  ULONG               CreateOptions,
  14.     IN  PVOID               EaBuffer        OPTIONAL,
  15.     IN  ULONG               EaLength
  16.     );
  17.  
  18.  

При успешном завершении функции, параметр FileHandle получает описатель созданного каталога. DesiredAccess определяет тип запрашиваемого доступа к создаваемому каталогу. Мы передаем только флаг SYNCHRONIZE, значение которого станет ясно чуть позже. Что такое ObjectAttributes вы уже знаете. После завершения функции из параметра IoStatusBlock, являющегося указателем на структуру IO_STATUS_BLOCK можно извлечь кое-какую дополнительную информацию. Параметр FileAttributes определяет атрибуты (только для чтения, скрытый и т.п.) создаваемого каталога. Мы используем FILE_ATTRIBUTE_NORMAL, т.к. придавать какие-то особые свойства каталогу, в данном случае, не требуется. FILE_ATTRIBUTE_NORMAL используется по умолчанию, поэтому, можно просто передать в этом параметре 0. Нулевое значение в необязательном параметре AllocationSize определяет, что будет создан файл нулевого размера. В случае каталога это вполне естественно. Параметр ShareAccess определяет, может ли какой-то другой код открыть описатель каталога и с какими правами доступа. В данном случае, мы запрещаем кому-либо доступ к каталогу, установив этот параметр в 0. Параметр CreateDisposition определяет действия системы, в случае если такой каталог уже существует или наоборот - если такого файла нет. Мы используем флаг FILE_OPEN_IF, который означает, что если такой каталог уже существует, он будет открыт. В параметре CreateOptions передаем комбинацию флагов FILE_DIRECTORY_FILE и FILE_SYNCHRONOUS_IO_NONALERT. Первый в особых пояснениях не нуждается и означает, что будет создан каталог, а не файл. FILE_SYNCHRONOUS_IO_NONALERT определяет (не уверен, кстати, что в случае каталога этот флаг имеет значение), что все операции с файлом будут проводиться синхронно, т.е. например, вызов ZwReadFile не вернет управление до тех пор, пока данные из файла не будут фактически прочитаны. При этом диспетчер ввода-вывода поддерживает для файла так называемую текущую позицию указателя файла (file position context). Если флаг FILE_SYNCHRONOUS_IO_NONALERT установлен, то в параметре DesiredAccess так же должен быть определен флаг SYNCHRONIZE. Последние два параметра не используются драйверами.

Код (Text):
  1.  
  2.  
  3.      .if eax == STATUS_SUCCESS
  4.          .if iosb.Information == FILE_CREATED
  5.          .elseif iosb.Information == FILE_OPENED
  6.          .endif
  7.  
  8.  

Как я только что сказал, в структуре IO_STATUS_BLOCK будет находиться дополнительная информация.

Вспомните, как мы завершаем обработку запроса ввода-вывода (см. предыдущие части). Например, в драйвере SharingMemory (часть 9) обработка IRP_MJ_CREATE и IRP_MJ_CLOSE завершается так:

Код (Text):
  1.  
  2.  
  3.  mov eax, pIrp
  4.  mov (_IRP PTR [eax]).IoStatus.Status, STATUS_SUCCESS
  5.  and (_IRP PTR [eax]).IoStatus.Information, 0
  6.  fastcall IofCompleteRequest, pIrp, IO_NO_INCREMENT
  7.  
  8.  

Примерно то же самое делает драйвер, завершающий запрос создания файла. Только значения, помещаемые в поля структуры IO_STATUS_BLOCK, будут зависеть от типа запроса.

Т.к. мы определили флаг FILE_OPEN_IF, система может поступить двояко. По значению iosb.Information мы узнаем, что же произошло: был ли создан новый каталог или такой каталог уже существовал и поэтому был открыт.

Код (Text):
  1.  
  2.  
  3.          invoke ZwClose, hDirectory
  4.      .endif
  5.  
  6.  

Лишний раз повторю, что в ядре необходимо явно закрывать все открытые описатели.

Код (Text):
  1.  
  2.  
  3.      invoke ZwCreateFile, addr hFile, SYNCHRONIZE, addr oa, addr iosb, 0, FILE_ATTRIBUTE_NORMAL, \
  4.                          0, FILE_CREATE, FILE_SYNCHRONOUS_IO_NONALERT, NULL, 0
  5.  
  6.  

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


11.4 Объект "файл"

Каждому открытому описателю файла соответствует объект "файл" (file object), представляющийся в памяти ядра структурой FILE_OBJECT.

Код (Text):
  1.  
  2.  
  3.  FILE_OBJECT STRUCT                              ; sizeof = 070h
  4.      _Type                   SWORD       ?       ; 0000h  IO_TYPE_FILE
  5.      _Size                   SWORD       ?       ; 0002h
  6.      DeviceObject            PVOID       ?       ; 0004h  PTR DEVICE_OBJECT
  7.      Vpb                     PVOID       ?       ; 0008h  PTR VPB
  8.      FsContext               PVOID       ?       ; 000Ch
  9.      FsContext2              PVOID       ?       ; 0010h
  10.      SectionObjectPointer    PVOID       ?       ; 0014h  PTR SECTION_OBJECT_POINTERS
  11.      PrivateCacheMap         PVOID       ?       ; 0018h
  12.      FinalStatus             SDWORD      ?       ; 001Ch
  13.      RelatedFileObject       PVOID       ?       ; 0020h  PTR FILE_OBJECT
  14.      LockOperation           BYTE        ?       ; 0024h  BOOLEAN
  15.      DeletePending           BYTE        ?       ; 0025h  BOOLEAN
  16.      ReadAccess              BYTE        ?       ; 0026h  BOOLEAN
  17.      WriteAccess             BYTE        ?       ; 0027h  BOOLEAN
  18.      DeleteAccess            BYTE        ?       ; 0028h  BOOLEAN
  19.      SharedRead              BYTE        ?       ; 0029h  BOOLEAN
  20.      SharedWrite             BYTE        ?       ; 002Ah  BOOLEAN
  21.      SharedDelete            BYTE        ?       ; 002Bh  BOOLEAN
  22.      Flags                   DWORD       ?       ; 002Ch
  23.      FileName                UNICODE_STRING    ; 0030h
  24.      CurrentByteOffset       LARGE_INTEGER     ; 0038h
  25.      Waiters                 DWORD       ?       ; 0040h
  26.      Busy                    DWORD       ?       ; 0044h
  27.      LastLock                PVOID       ?       ; 0048h
  28.      _Lock                   KEVENT            ; 004Ch
  29.      Event                   KEVENT            ; 005Ch
  30.      CompletionContext       PVOID       ?       ; 006Ch  PTR IO_COMPLETION_CONTEXT
  31.  FILE_OBJECT ENDS
  32.  PFILE_OBJECT typedef ptr FILE_OBJECT
  33.  
  34.  

Например, мы можем два раза открыть один и тот же файл, но запросить разные права доступа: чтение (FILE_READ_DATA) в первом случае и запись (FILE_WRITE_DATA) - во втором. В результате в ядре будет создано две структуры FILE_OBJECT, каждая из которых будет соответствовать своему описателю файла. Но оба описателя и соответственно обе структуры соответствуют одному и тому же файлу на диске. В первой структуре FILE_OBJECT будет установлено поле ReadAccess, во второй - WriteAccess. Например, при попытке записи в файл по первому описателю, система увидит, что поле WriteAccess = FALSE и запрос будет отклонен.

Поле DeviceObject будет содержать указатель на объект "устройство" \Device\HarddiskVolume1, т.к. именно на это устройство ссылается символьная ссылка \??\c:, которую мы используем в имени создаваемого файла.

Можете после прочтения статьи поэкспериментировать с файлами и посмотреть, как система заполняет и управляет структурой FILE_OBJECT. Если возникнут проблемы с локализацией её в памяти, можно использовать функцию ObReferenceObjectByHandle примерно таким образом:

Код (Text):
  1.  
  2.  
  3.  local pFileObject:PFILE_OBJECT
  4.  . . .
  5.  invoke ObReferenceObjectByHandle, hFile, FILE_READ_DATA, NULL, KernelMode, addr pFileObject, NULL
  6.  .if eax == STATUS_SUCCESS
  7.      ; pFileObject указывает на структуру FILE_OBJECT соответствующую описателю hFile
  8.      fastcall ObfDereferenceObject, pFileObject
  9.  .endif
  10.  
  11.  

ObReferenceObjectByHandle вернет в переменной pFileObject указатель на структуру FILE_OBJECT, соответствующую описателю файла. При этом счетчик ссылок будет увеличен на единицу. Вызовом ObfDereferenceObject необходимо вернуть ему старое значение.


11.5 Запись в файл

К этому моменту у нас уже есть каталог и файл нулевого размера. Пора в него что-нибудь записать - это будет строка символов.

Код (Text):
  1.  
  2.  
  3.      invoke ZwCreateFile, addr hFile, FILE_WRITE_DATA + SYNCHRONIZE, addr oa, addr iosb, \
  4.                          0, 0, FILE_SHARE_READ, FILE_OPEN, FILE_SYNCHRONOUS_IO_NONALERT, NULL, 0
  5.  
  6.  

С помощью функции ZwCreateFile можно не только создавать файлы, но и открывать существующие (и, кстати, не только файлы, а и некоторые другие объекты). Для этого надо указать флаг FILE_OPEN.

Для записи в файл нужен соответствующий доступ - используем флаг FILE_WRITE_DATA.

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

Код (Text):
  1.  
  2.  
  3.  FILE_GENERIC_WRITE equ (STANDARD_RIGHTS_WRITE or FILE_WRITE_DATA or FILE_WRITE_ATTRIBUTES or FILE_WRITE_EA or FILE_APPEND_DATA or SYNCHRONIZE)
  4.  
  5.  

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

Очевидно, что пока мы будем писать в файл никто другой не должен делать того же. Используем флаг FILE_SHARE_READ - больше никто не сможет открыть наш файл ни для записи, ни для удаления, а сможет только получить доступ на чтение из файла.

Код (Text):
  1.  
  2.  
  3.      .if eax == STATUS_SUCCESS
  4.  
  5.          CTA0 "Data can be written to an open file", g_szData, 4
  6.  
  7.          invoke ZwWriteFile, hFile, 0, NULL, NULL, addr iosb, \
  8.                          addr g_szData, sizeof g_szData - 1, NULL, NULL
  9.  
  10.  

Название функции ZwWriteFile говорит само за себя.

Код (Text):
  1.  
  2.  
  3.  NTSTATUS
  4.    ZwWriteFile(
  5.      IN  HANDLE            FileHandle,
  6.      IN  HANDLE            Event       OPTIONAL,
  7.      IN  PIO_APC_ROUTINE   ApcRoutine  OPTIONAL,
  8.      IN  PVOID             ApcContext  OPTIONAL,
  9.      OUT PIO_STATUS_BLOCK  IoStatusBlock,
  10.      IN  PVOID             Buffer,
  11.      IN  ULONG             Length,
  12.      IN  PLARGE_INTEGER    ByteOffset  OPTIONAL,
  13.      IN  PULONG            Key  OPTIONAL
  14.      );
  15.  
  16.  

Минимально необходимым набором параметров для функции ZwWriteFile являются описатель файла, указатель на структуру IO_STATUS_BLOCK, в которую будет помещена дополнительная информация (в частности, количество фактически записанных в файл байт), указатель на данные, которые требуется записать в файл и их размер.


11.6 Изменение свойств файла

Предположим, нам потребовалось предотвратить удаление файла.

Код (Text):
  1.  
  2.  
  3.      invoke ZwCreateFile, addr hFile, FILE_READ_ATTRIBUTES + FILE_WRITE_ATTRIBUTES + SYNCHRONIZE, \
  4.                          addr oa, addr iosb, 0, 0, FILE_SHARE_READ, \
  5.                          FILE_OPEN, FILE_SYNCHRONOUS_IO_NONALERT, NULL, 0
  6.  
  7.  

Флаги FILE_READ_ATTRIBUTES и FILE_WRITE_ATTRIBUTES требуются для получения и изменения свойств файла соответственно.

Код (Text):
  1.  
  2.  
  3.      .if eax == STATUS_SUCCESS
  4.          invoke ZwQueryInformationFile, hFile, addr iosb, addr fbi, sizeof fbi, FileBasicInformation
  5.          .if eax == STATUS_SUCCESS
  6.  
  7.  

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

Код (Text):
  1.  
  2.  
  3.              or fbi.FileAttributes, FILE_ATTRIBUTE_READONLY
  4.              invoke ZwSetInformationFile, hFile, addr iosb, addr fbi, sizeof fbi, FileBasicInformation
  5.  
  6.  

Добавляем к атрибутам файла необходимый нам флаг. Когда понадобиться произвести изменения в файле мы сбросим этот атрибут в процедуре UnmarkAsReadOnly так:

Код (Text):
  1.  
  2.  
  3.              and fbi.FileAttributes, not FILE_ATTRIBUTE_READONLY
  4.  
  5.  

11.7 Чтение из файла

Теперь, для разнообразия, откроем файл для чтения с помощью функции ZwOpenFile, специально предназначенной для этих целей. Назначение параметров совпадает с аналогичными параметрами функции ZwCreateFile.

Код (Text):
  1.  
  2.  
  3.  NTSTATUS
  4.    ZwOpenFile(
  5.      OUT PHANDLE             FileHandle,
  6.      IN  ACCESS_MASK         DesiredAccess,
  7.      IN  POBJECT_ATTRIBUTES  ObjectAttributes,
  8.      OUT PIO_STATUS_BLOCK    IoStatusBlock,
  9.      IN  ULONG               ShareAccess,
  10.      IN  ULONG               OpenOptions
  11.      );
  12.  
  13.  

Код (Text):
  1.  
  2.  
  3.      invoke ZwOpenFile, addr hFile, FILE_READ_DATA + SYNCHRONIZE, addr oa, addr iosb, \
  4.                  FILE_SHARE_READ + FILE_SHARE_WRITE + FILE_SHARE_DELETE, FILE_SYNCHRONOUS_IO_NONALERT
  5.  
  6.  

FILE_READ_DATA - в данный момент нам будет достаточно только доступа для чтения данных из файла. Комбинация флагов FILE_SHARE_READ, FILE_SHARE_WRITE и FILE_SHARE_DELETE позволит всем остальным получать полный доступ к файлу.

Код (Text):
  1.  
  2.  
  3.      .if eax == STATUS_SUCCESS
  4.          invoke ZwQueryInformationFile, hFile, addr iosb, addr fsi, sizeof fsi, FileStandardInformation
  5.          .if eax == STATUS_SUCCESS
  6.  
  7.  

Мы собираемся прочитать все содержимое файла, но для этого требуется буфер, размер которого зависит от размера файла. Размер файла можно узнать вызвав функцию ZwQueryInformationFile с информационным классом FileStandardInformation, передав ей указатель на структуру FILE_BASIC_INFORMATION.

Код (Text):
  1.  
  2.  
  3.              mov eax, fsi.EndOfFile.LowPart
  4.              inc eax
  5.              mov cb, eax
  6.  
  7.  

Добавим один байт на завершающий ноль.

Код (Text):
  1.  
  2.  
  3.              invoke ExAllocatePool, PagedPool, cb
  4.              .if eax != NULL
  5.                  mov p, eax
  6.  
  7.                  invoke RtlZeroMemory, p, cb
  8.  
  9.                  invoke ZwReadFile, hFile, 0, NULL, NULL, addr iosb, p, cb, 0, NULL
  10.                  .if eax == STATUS_SUCCESS
  11.                      invoke DbgPrint, $CTA0("FileWorks: File content: \=%s\=\n"), p
  12.                  .endif
  13.  
  14.                  invoke ExFreePool, p
  15.              .endif
  16.  
  17.  

Выделим буфер нужного размера, обнулим его и передадим в функцию ZwReadFile. Прототип этой функции совпадает с прототипом функции ZwWriteFile, только буфер является не источником данных, а приемником. Т.к. перед использованием мы обнулили буфер и его размер на один байт больше строки, содержащейся в файле, то никаких проблем с выводом его содержимого в отладочном сообщении у нас не возникнет.


11.8 Добавление данных в файл

Есть несколько путей добавления данных в файл. Можно открыть его с флагом FILE_WRITE_DATA, установить текущую позицию указателя файла на его конец, передав смещение в параметре ByteOffset функции ZwWriteFile, и писать данные. Текущая позиция указателя файла, кстати, храниться в FILE_OBJECT.CurrentByteOffset. А можно открыть файл в флагом FILE_APPEND_DATA и текущая позиция указателя файла будет автоматически установлена на его конец. Так мы и поступим.

Код (Text):
  1.  
  2.  
  3.      invoke ZwOpenFile, addr hFile, FILE_APPEND_DATA + SYNCHRONIZE, addr oa, addr iosb, \
  4.                                      FILE_SHARE_READ, FILE_SYNCHRONOUS_IO_NONALERT
  5.      .if eax == STATUS_SUCCESS
  6.  
  7.          CTA0 " using ZwWriteFile", g_szDataToAppend, 4
  8.  
  9.          invoke ZwWriteFile, hFile, 0, NULL, NULL, addr iosb, \
  10.                          addr g_szDataToAppend, sizeof g_szDataToAppend - 1, NULL, NULL
  11.  
  12.  

Размер файла будет автоматически увеличен в соответствии с размером добавленных данных.


11.9 Усечение файла

Допустим, нам нужно уменьшить размер файла, отбросив ненужные данные. В данном случае, я, для простоты, уменьшаю размер файла в два раза.

Код (Text):
  1.  
  2.  
  3.      invoke ZwOpenFile, addr hFile, FILE_WRITE_DATA + SYNCHRONIZE, addr oa, addr iosb, \
  4.                          FILE_SHARE_READ, FILE_SYNCHRONOUS_IO_NONALERT
  5.  
  6.  

Открываем файл с доступом на запись данных.

Код (Text):
  1.  
  2.  
  3.      .if eax == STATUS_SUCCESS
  4.          invoke ZwQueryInformationFile, hFile, addr iosb, \
  5.                          addr fsi, sizeof fsi, FileStandardInformation
  6.          .if eax == STATUS_SUCCESS
  7.  
  8.  

Получаем текущий размер файла в составе структуры FILE_STANDARD_INFORMATION.

Код (Text):
  1.  
  2.  
  3.              and feofi.EndOfFile.HighPart, 0
  4.              mov eax, fsi.EndOfFile.LowPart
  5.              shr eax, 1
  6.              mov feofi.EndOfFile.LowPart, eax
  7.              invoke ZwSetInformationFile, hFile, addr iosb, \
  8.                          addr feofi, sizeof feofi, FileEndOfFileInformation
  9.  
  10.  

Устанавливаем новый размер, равный половине текущего, пользуясь информационным классом FileEndOfFileInformation.


11.10 Удаление файла и каталога

Дело за малым - привести диск c: в первоначальное состояние. Как это ни странно, но 2000 DDK умалчивает о существовании функции ZwDeleteFile. В XP DDK говорится, что эта функция все таки существует в системе, но начиная с Windows XP. Но это не так. ZwDeleteFile есть и Windows 2000 и даже в Windows NT4. Но для удаления файла мы воспользуемся несколько более сложным способом, а вот каталог удалим с помощью ZwDeleteFile.

Код (Text):
  1.  
  2.  
  3.      invoke ZwCreateFile, addr hFile, DELETE + SYNCHRONIZE, addr oa, addr iosb, \
  4.                          0, 0, FILE_SHARE_DELETE, FILE_OPEN, FILE_SYNCHRONOUS_IO_NONALERT, NULL, 0
  5.  
  6.  

Открываем файл для удаления. При этом всем остальным также разрешаем только удалять его.

Код (Text):
  1.  
  2.  
  3.      .if eax == STATUS_SUCCESS
  4.  
  5.          mov fdi.DeleteFile, TRUE
  6.          invoke ZwSetInformationFile, hFile, addr iosb, addr fdi, sizeof fdi, FileDispositionInformation
  7.  
  8.  

Воспользуемся информационным классом FileDispositionInformation и установим признак удаления файла. При этом в структуре FILE_OBJECT поле DeletePending изменит свое значение с FALSE на TRUE. Это будет означать, что файл отмечен для удаления. До тех пор, пока хотя бы один открытый описатель такого файла существует, он не будет удален.

Код (Text):
  1.  
  2.  
  3.          invoke ZwClose, hFile
  4.  
  5.  

Теперь единственный описатель файла закрыт и файл удален.

Код (Text):
  1.  
  2.  
  3.      invoke ZwDeleteFile, addr oa
  4.  
  5.  

Удаление каталога с помощью ZwDeleteFile проходит несколько проще и пояснений не требует.


11.11 Перечисление содержимого каталога

Существует два альтернативных способа определения имени создаваемого/открываемого объекта вообще и файла в частности. Доступ к именованному объекту можно получить либо используя полный путь, либо относительный. До сих пор мы использовали только абсолютные пути. Например, путь \??\c:\FileWorks\test.txt является абсолютным. В этом случае заполнение структуры OBJECT_ATTRIBUTES с помощью макроса InitializeObjectAttributes выглядит примерно так:

Код (Text):
  1.  
  2.  
  3.  InitializeObjectAttributes addr oa, $CCOUNTED_UNICODE_STRING("\??\c:\FileWorks\test.txt"), \
  4.                             OBJ_CASE_INSENSITIVE, NULL, NULL
  5.  
  6.  

Предпоследний параметр RootDirectory равен NULL. Параметр RootDirectory макроса InitializeObjectAttributes и одноименное поле структуры OBJECT_ATTRIBUTES, которую этот макрос заполняет, определяет описатель каталога-контейнера объекта.

Код (Text):
  1.  
  2.  
  3.  OBJECT_ATTRIBUTES STRUCT
  4.  . . .
  5.      RootDirectory               HANDLE          ?
  6.  . . .
  7.  OBJECT_ATTRIBUTES ENDS
  8.  
  9.  

Если каталог-контейнер объекта уже открыт, т.е. имеется его описатель, то к объекту можно обратиться по относительному к каталогу-контейнеру пути. При этом описатель каталога-контейнера должен быть помещен в поле RootDirectory. Например, если мы уже открыли каталог \??\c:\FileWorks\ и поместили его описатель в переменную hDirectory, то можем использовать относительный путь файла test.txt таким образом:

Код (Text):
  1.  
  2.  
  3.  InitializeObjectAttributes addr oa, $CCOUNTED_UNICODE_STRING("test.txt"), OBJ_CASE_INSENSITIVE, hDirectory, NULL
  4.  
  5.  

Под каталогом-контейнером подразумевается не только каталог на диске, но и каталог в пространстве имен диспетчера объектов.

Для перечисления содержимого системного каталога \%SystemRoot%\System32\Drivers\ мы как раз и будем использовать относительный путь.

Код (Text):
  1.  
  2.  
  3.      InitializeObjectAttributes addr oa, $CCOUNTED_UNICODE_STRING("\\SystemRoot"), \
  4.                                  OBJ_CASE_INSENSITIVE + OBJ_KERNEL_HANDLE, NULL, NULL
  5.  
  6.  

Заполняем структуру OBJECT_ATTRIBUTES, используя символьную ссылку \SystemRoot - этот путь абсолютный.

Код (Text):
  1.  
  2.  
  3.      invoke ZwOpenFile, addr hSystemRootDirectory, FILE_LIST_DIRECTORY + SYNCHRONIZE, addr oa, \
  4.                          addr iosb, FILE_SHARE_READ + FILE_SHARE_WRITE + FILE_SHARE_DELETE, \
  5.                          FILE_DIRECTORY_FILE + FILE_SYNCHRONOUS_IO_NONALERT
  6.  
  7.  

Открываем каталог \%SystemRoot%\. Флаг FILE_LIST_DIRECTORY позволит перечислять содержимое каталога.

Код (Text):
  1.  
  2.  
  3.      .if eax == STATUS_SUCCESS
  4.  
  5.          InitializeObjectAttributes addr oa, $CCOUNTED_UNICODE_STRING("system32\\drivers"), \
  6.                              OBJ_CASE_INSENSITIVE + OBJ_KERNEL_HANDLE, hSystemRootDirectory, NULL
  7.  
  8.  

Если каталог успешно открыт, то в переменной hSystemRootDirectory получим его описатель, который и будем использовать в качестве описателя каталога-контейнера. Заполняем структуру OBJECT_ATTRIBUTES, используя относительный путь "system32\drivers".

Код (Text):
  1.  
  2.  
  3.          invoke ZwOpenFile, addr hDriversDirectory, FILE_LIST_DIRECTORY + SYNCHRONIZE, addr oa, \
  4.                              addr iosb, FILE_SHARE_READ + FILE_SHARE_WRITE + FILE_SHARE_DELETE, \
  5.                              FILE_DIRECTORY_FILE + FILE_SYNCHRONOUS_IO_NONALERT
  6.          .if eax == STATUS_SUCCESS
  7.  
  8.  

Открываем каталог \%SystemRoot%\System32\Drivers\ по относительному пути.

Код (Text):
  1.  
  2.  
  3.              mov cb, sizeof FILE_DIRECTORY_INFORMATION + 256
  4.  
  5.              invoke ExAllocatePool, PagedPool, cb
  6.              .if eax != NULL
  7.  
  8.  

Выделяем небольшой буфер, в который должна поместиться структура FILE_DIRECTORY_INFORMATION и имя файла.

Код (Text):
  1.  
  2.  
  3.                  mov pfdi, eax
  4.                  mov esi, eax
  5.                  assume esi:ptr FILE_DIRECTORY_INFORMATION
  6.  
  7.                  invoke ZwQueryDirectoryFile, hDriversDirectory, NULL, NULL, NULL, addr iosb, \
  8.                              esi, cb, FileDirectoryInformation, \
  9.                              TRUE, $CCOUNTED_UNICODE_STRING("c*"), TRUE
  10.  
  11.  

Начинаем перечислять файлы каталога, используя информационный класс FileDirectoryInformation. Прототип функции ZwQueryDirectoryFile, пожалуй, будет не лишним.

Код (Text):
  1.  
  2.  
  3.  NTSTATUS
  4.    ZwQueryDirectoryFile(
  5.      IN  HANDLE                  FileHandle,
  6.      IN  HANDLE                  Event       OPTIONAL,
  7.      IN  PIO_APC_ROUTINE         ApcRoutine  OPTIONAL,
  8.      IN  PVOID                   ApcContext  OPTIONAL,
  9.      OUT PIO_STATUS_BLOCK        IoStatusBlock,
  10.      OUT PVOID                   FileInformation,
  11.      IN  ULONG                   Length,
  12.      IN  FILE_INFORMATION_CLASS  FileInformationClass,
  13.      IN  BOOLEAN                 ReturnSingleEntry,
  14.      IN  PUNICODE_STRING         FileName    OPTIONAL,
  15.      IN  BOOLEAN                 RestartScan
  16.      );
  17.  
  18.  

И о существовании этой функции 2000 DDK тоже умалчивает. В XP DDK говорится, что эта функция существует в системе, начиная с Windows XP. И это тоже неправда.

Параметр ReturnSingleEntry устанавливаем равным TRUE, что заставляет функцию ZwQueryDirectoryFile вернуть информацию только об одном файле, причем первом. Параметр FileName указывает на строку с условием поиска "c*", т.е. будут перечисляться только файлы, имена которых начинаются с символа "c". Это позволит сократить объем выводимой через отладочные сообщения информации. При первом вызове ZwQueryDirectoryFile мы должны установить параметр RestartScan в TRUE. Это заставит функцию ZwQueryDirectoryFile начать просмотр содержимого каталога.

Код (Text):
  1.  
  2.  
  3.                  .while eax != STATUS_NO_MORE_FILES
  4.  
  5.  

Запускаем цикл, который крутится до тех пор, пока ZwQueryDirectoryFile не вернет STATUS_NO_MORE_FILES, т.е. пока все файлы в каталоге не будут перечислены.

Код (Text):
  1.  
  2.  
  3.                      .if ( eax == STATUS_SUCCESS )
  4.  
  5.  

Если вдруг в просматриваемом нами каталоге окажется файл, длина имени которого превысит 256 байт (что, кстати, практически невероятно, т.к. имена драйверов не превышают 8 символов), ZwQueryDirectoryFile вернет код ошибки отличный от STATUS_NO_MORE_FILES. В этом случае мы просто пропускаем такой файл.

Код (Text):
  1.  
  2.  
  3.                          mov eax, [esi].FileNameLength
  4.                          mov us._Length, ax
  5.                          mov us.MaximumLength, ax
  6.                          lea eax, [esi].FileName
  7.                          mov us.Buffer, eax
  8.                          invoke RtlUnicodeStringToAnsiString, addr as, addr us, TRUE
  9.                          .if eax == STATUS_SUCCESS
  10.  
  11.                              invoke RtlTimeToTimeFields, addr [esi].CreationTime, addr tf
  12.                              movzx eax, tf.Day
  13.                              movzx ecx, tf.Month
  14.                              movzx edx, tf.Year
  15.  
  16.                              invoke DbgPrint, $CTA0("    %s   size=%d   created on %d.%02d.%04d\n"), \
  17.                                          as.Buffer, [esi].EndOfFile.LowPart, eax, ecx, edx
  18.  
  19.                              invoke RtlFreeAnsiString, addr as
  20.                          .endif
  21.  
  22.  

Форматируем полученную информацию, выводя имя файла, его размер и дату создания.

Код (Text):
  1.  
  2.  
  3.                      .endif
  4.  
  5.                      invoke ZwQueryDirectoryFile, hDriversDirectory, NULL, NULL, NULL, addr iosb, \
  6.                                  esi, cb, FileDirectoryInformation, \
  7.                                  TRUE, NULL, FALSE
  8.                  .endw
  9.  
  10.  

В цикле вызываем ZwQueryDirectoryFile, но параметры ReturnSingleEntry, FileName и RestartScan равны TRUE, NULL и FALSE соответственно. Это заставит функцию ZwQueryDirectoryFile продолжить перечисление файлов.

Код (Text):
  1.  
  2.  
  3.                  invoke ExFreePool, pfdi
  4.              .endif
  5.              invoke ZwClose, hDriversDirectory
  6.          .endif
  7.          invoke ZwClose, hSystemRootDirectory
  8.      .endif
  9.  
  10.  

Отдаем все занятые ресурсы назад.

Исходный код драйвера в архиве. Для компиляции требуется версия KmdKit не ниже 1.5 - берите на сайте.

© Four-F

0 1.442
archive

archive
New Member

Регистрация:
27 фев 2017
Публикаций:
532