Драйверы режима ядра: Часть 14: Базовая техника. Синхронизация: Использование объекта

Дата публикации 3 авг 2004

Драйверы режима ядра: Часть 14: Базовая техника. Синхронизация: Использование объекта — Архив WASM.RU



Предположим, что у нас есть драйвер, собирающий какую-то статистическую информацию и программа управления, получающая и обрабатывающая эту информацию. Как организовать их взаимодействие? В самом простейшем случае надо создать в программе управления таймер и с некой периодичностью (скажем, раз в секунду) забирать у драйвера статистику через DeviceIoControl. За примерами далеко ходить не надо: RegMon, FileMon и т.д. Но что если события, которые отслеживает драйвер (например, создание процесса) происходят относительно редко? Большую часть времени мы будем гонять DeviceIoControl впустую. Увеличение интервала срабатывания таймера (скажем, до 10 секунд), приведет к раздражающим задержкам в работе программы управления. Очевидно, что в данном сценарии инициатором передачи очередной порции информации должен быть драйвер, т.к. о том, что отслеживаемое событие (события) произошло, узнает именно он. Значит, он должен каким-то образом извещать свою программу управления о том, что имеются свежие данные.

Есть два базовых и относительно хорошо документированных способа. Один заключается в асинхронном вызове DeviceIoControl. Пользовательский поток создает объект "событие" и соответствующим образом заполняет структуру OVERLAPPED, указатель на которую передается в последнем параметре. Описатель устройства должен быть открыт с флагом FILE_FLAG_OVERLAPPED. Получив такой запрос, драйвер откладывает его завершение до тех пор, пока не произойдет ожидаемое событие и возвращает STATUS_PENDING. На стороне режима пользователя вызов DeviceIoControl возвращает ошибку ERROR_IO_PENDING и поток должен ждать на объекте "событие". Когда происходит ожидаемое событие, драйвер завершает IRP и сигнализирует об этом пользовательскому потоку, освобождая объект "событие". При этом драйвер должен быть готов к тому, что ему придется обрабатывать несколько таких IRP. Т.е. он должен организовать очередь запросов ввода-вывода ожидающих завершения. Подробно разбирать этот способ мы не будем (см. DDK и MSDN). Второй способ несколько проще и заключается в том, что драйвер и его клиент режима пользователя совместно используют объект "событие". Клиент ждет на объекте и если оно переходит в сигнальное состояние (по указанию драйвера) синхронно вызывает DeviceIoControl, получая от драйвера необходимую информацию.

Итак, мы должны совместно использовать объект "событие". Тут есть несколько вариантов. Можно использовать именованный объект и обращаться к нему по имени. Вспомните, как мы использовали именованный объект "раздел" в Части 8 "Базовая техника: Работа с памятью. Совместно используемый раздел". Очевидный недостаток - этот объект будет виден всем. Поэтому лучше использовать безымянный объект. Обычной и официально документированной практикой здесь является создание объекта "событие" клиентом в режиме пользователя и передача его описателя драйверу через DeviceIoControl. Этим методом мы и воспользуемся. В этом примере мы будем отслеживать создание/удаление процессов.

Дополнительную информацию по теме можно почерпнуть из следующих источников:

  • MSDN Q228785 "OpenEvent Fails in a Non-Administrator Account"
  • MSDN Q176415 "Event.exe Shows How to Share and Signal an Event Object"
  • Пример в DDK \src\general\event\
  • Журнал "The NT Insider" за 2002 год, "Sharing is Caring - Sharing Events Between Kernel and User Mode"


14.1 Общие данные

Начнем с разбора содержимого файла common.inc.

Код (Text):
  1.  
  2.  
  3.  IOCTL_SET_NOTIFY        equ CTL_CODE(FILE_DEVICE_UNKNOWN, 800h, METHOD_BUFFERED, FILE_WRITE_ACCESS)
  4.  IOCTL_REMOVE_NOTIFY     equ CTL_CODE(FILE_DEVICE_UNKNOWN, 801h, 0, 0)
  5.  IOCTL_GET_PROCESS_DATA  equ CTL_CODE(FILE_DEVICE_UNKNOWN, 802h, METHOD_BUFFERED, FILE_READ_ACCESS)
  6.  
  7.  IMAGE_FILE_PATH_LEN equ 512
  8.  
  9.  PROCESS_DATA STRUCT
  10.      bCreate             BOOL        ?
  11.  
  12.      dwProcessId         DWORD       ?
  13.      szProcessPath       CHAR    IMAGE_FILE_PATH_LEN dup(?)
  14.  
  15.  PROCESS_DATA ENDS
  16.  
  17.  

Имеем три управляющих кода: IOCTL_SET_NOTIFY заставляет драйвер начать слежение за созданием/удалением процессов; IOCTL_REMOVE_NOTIFY, соответственно, делает обратное; IOCTL_GET_PROCESS_DATA возвращает в структуре PROCESS_DATA информацию о процессе. Эта информация состоит из идентификатора процесса, флага, определяющего, был ли процесс создан или удален, и полного пути к образу, создавшему процесс.



14.2 Исходный текст программы управления драйвером ProcessMon

Код (Text):
  1.  
  2.  
  3.  ;@echo off
  4.  ;goto make
  5.  
  6.  ;:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
  7.  ;
  8.  ;  Программа управления драйвером ProcessMon
  9.  ;
  10.  ;:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
  11.  
  12.  .386
  13.  .model flat, stdcall
  14.  option casemap:none
  15.  
  16.  ;:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
  17.  ;                               В К Л Ю Ч А Е М Ы Е    Ф А Й Л Ы
  18.  ;:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
  19.  
  20.  include \masm32\include\windows.inc
  21.  
  22.  include \masm32\include\kernel32.inc
  23.  include \masm32\include\user32.inc
  24.  include \masm32\include\advapi32.inc
  25.  include \masm32\include\comctl32.inc
  26.  
  27.  includelib \masm32\lib\kernel32.lib
  28.  includelib \masm32\lib\user32.lib
  29.  includelib \masm32\lib\advapi32.lib
  30.  includelib \masm32\lib\comctl32.lib
  31.  
  32.  include \masm32\include\winioctl.inc
  33.  
  34.  include cocomac\ListView.mac
  35.  include \masm32\Macros\Strings.mac
  36.  
  37.  include ..\common.inc
  38.  
  39.  ;:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
  40.  ;                                      К О Н С Т А Н Т Ы                                          
  41.  ;:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
  42.  
  43.  IDD_MAIN        equ 1000
  44.  IDC_LISTVIEW    equ 1001
  45.  IDI_ICON        equ 1002
  46.  
  47.  ;:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
  48.  ;                     Н Е И Н И Ц И А Л И З И Р О В А Н Н Ы Е    Д А Н Н Ы Е                        
  49.  ;:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
  50.  
  51.  .data?
  52.  
  53.  g_hInstance     HINSTANCE   ?
  54.  g_hwndDlg       HWND        ?
  55.  g_hwndListView  HWND        ?
  56.  
  57.  g_hSCManager    HANDLE      ?
  58.  g_hService      HANDLE      ?
  59.  g_hEvent        HANDLE      ?
  60.  
  61.  g_hDevice       HANDLE      ?
  62.  
  63.  g_fbExitNow     BOOL        ?
  64.  
  65.  ;:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
  66.  ;                                           К О Д                                                  
  67.  ;:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
  68.  
  69.  .code
  70.  
  71.  ;:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
  72.  ;                             MyUnhandledExceptionFilter                                            
  73.  ;:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
  74.  
  75.  MyUnhandledExceptionFilter proc lpExceptionInfo:PTR EXCEPTION_POINTERS
  76.  
  77.  local dwBytesReturned:DWORD
  78.  local _ss:SERVICE_STATUS
  79.  
  80.      invoke DeviceIoControl, g_hDevice, IOCTL_REMOVE_NOTIFY, \
  81.                      NULL, 0, NULL, 0, addr dwBytesReturned, NULL
  82.  
  83.      mov g_fbExitNow, TRUE
  84.      invoke SetEvent, g_hEvent
  85.  
  86.      invoke Sleep, 100
  87.  
  88.      invoke CloseHandle, g_hEvent
  89.      invoke CloseHandle, g_hDevice
  90.  
  91.      invoke ControlService, g_hService, SERVICE_CONTROL_STOP, addr _ss
  92.      invoke DeleteService, g_hService
  93.  
  94.      invoke CloseServiceHandle, g_hService
  95.      invoke CloseServiceHandle, g_hSCManager
  96.  
  97.      mov eax, EXCEPTION_EXECUTE_HANDLER
  98.      ret
  99.  
  100.  MyUnhandledExceptionFilter endp
  101.  
  102.  ;:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
  103.  ;                                     ListViewInsertColumn                                          
  104.  ;:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
  105.  
  106.  ListViewInsertColumn proc
  107.  
  108.  local lvc:LV_COLUMN
  109.  
  110.      mov lvc.imask, LVCF_TEXT + LVCF_WIDTH
  111.      mov lvc.pszText, $CTA0("Process")
  112.      mov lvc.lx, 354
  113.      ListView_InsertColumn g_hwndListView, 0, addr lvc
  114.  
  115.      mov lvc.pszText, $CTA0("PID")
  116.      or lvc.imask, LVCF_FMT
  117.      mov lvc.fmt, LVCFMT_RIGHT
  118.      mov lvc.lx, 40
  119.      ListView_InsertColumn g_hwndListView, 1, addr lvc
  120.  
  121.      mov lvc.fmt, LVCFMT_LEFT
  122.      mov lvc.lx, 80
  123.      mov lvc.pszText, $CTA0("State")
  124.      ListView_InsertColumn g_hwndListView, 2, addr lvc
  125.  
  126.      ret
  127.  
  128.  ListViewInsertColumn endp
  129.  
  130.  ;:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
  131.  ;                                        FillProcessInfo                                            
  132.  ;:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
  133.  
  134.  FillProcessInfo proc uses esi pProcessData:PTR PROCESS_DATA
  135.  
  136.  local lvi:LV_ITEM
  137.  local buffer[1024]:CHAR
  138.  
  139.      mov esi, pProcessData
  140.      assume esi:ptr PROCESS_DATA
  141.  
  142.      mov lvi.imask, LVIF_TEXT
  143.  
  144.      ListView_GetItemCount g_hwndListView
  145.      mov lvi.iItem, eax
  146.  
  147.      invoke GetLongPathName, addr [esi].szProcessName, addr buffer, sizeof buffer
  148.      .if ( eax == 0 ) || ( eax >= sizeof buffer )
  149.          lea ecx, [esi].szProcessName
  150.      .else
  151.          lea ecx, buffer
  152.      .endif
  153.  
  154.      and lvi.iSubItem, 0
  155.      mov lvi.pszText, ecx
  156.      ListView_InsertItem g_hwndListView, addr lvi
  157.  
  158.      inc lvi.iSubItem
  159.      invoke wsprintf, addr buffer, $CTA0("%X"), [esi].dwProcessId
  160.      lea eax, buffer
  161.      mov lvi.pszText, eax
  162.      ListView_SetItem g_hwndListView, addr lvi
  163.  
  164.      inc lvi.iSubItem
  165.      .if [esi].bCreate
  166.          mov lvi.pszText, $CTA0("Created")
  167.      .else
  168.          mov lvi.pszText, $CTA0("Destroyed")
  169.      .endif
  170.      ListView_SetItem g_hwndListView, addr lvi
  171.  
  172.      assume esi:nothing
  173.  
  174.      ret
  175.  
  176.  FillProcessInfo endp
  177.  
  178.  ;:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
  179.  ;                                     WaitForProcessData                                            
  180.  ;:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
  181.  
  182.  WaitForProcessData proc hEvent:HANDLE
  183.  
  184.  local ProcessData:PROCESS_DATA
  185.  local dwBytesReturned:DWORD
  186.  
  187.      invoke GetCurrentThread
  188.      invoke SetThreadPriority, eax, THREAD_PRIORITY_HIGHEST  
  189.  
  190.      .while TRUE
  191.          invoke WaitForSingleObject, hEvent, INFINITE
  192.          .if eax != WAIT_FAILED
  193.  
  194.              .break .if g_fbExitNow == TRUE
  195.  
  196.              invoke DeviceIoControl, g_hDevice, IOCTL_GET_PROCESS_DATA, NULL, 0, \
  197.                          addr ProcessData, sizeof ProcessData, addr dwBytesReturned, NULL
  198.  
  199.              .if eax != 0
  200.                  invoke FillProcessInfo, addr ProcessData
  201.              .endif
  202.  
  203.          .else
  204.              invoke MessageBox, g_hwndDlg, \
  205.                  $CTA0("Wait for event failed. Thread now exits. Restart application."), \
  206.                  NULL, MB_ICONERROR
  207.              .break
  208.          .endif
  209.      .endw
  210.  
  211.      invoke ExitThread, 0
  212.      ret                         ; Never executed.
  213.  
  214.  WaitForProcessData endp
  215.      
  216.  ;:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
  217.  ;                            Д И А Л О Г О В А Я    П Р О Ц Е Д У Р А                              
  218.  ;:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
  219.  
  220.  DlgProc proc hDlg:HWND, uMsg:UINT, wParam:WPARAM, lParam:LPARAM
  221.  
  222.  local rect:RECT
  223.  
  224.      mov eax, uMsg
  225.      .if eax == WM_INITDIALOG
  226.  
  227.          push hDlg
  228.          pop g_hwndDlg
  229.  
  230.          invoke LoadIcon, g_hInstance, IDI_ICON
  231.          invoke SendMessage, hDlg, WM_SETICON, ICON_BIG, eax
  232.  
  233.          invoke GetDlgItem, hDlg, IDC_LISTVIEW
  234.          mov g_hwndListView, eax
  235.          invoke SetFocus, g_hwndListView
  236.  
  237.          invoke GetClientRect, hDlg, addr rect
  238.          invoke MoveWindow, g_hwndListView, rect.left, rect.top, rect.right, rect.bottom, FALSE
  239.  
  240.          ListView_SetExtendedListViewStyle g_hwndListView, LVS_EX_GRIDLINES + LVS_EX_FULLROWSELECT
  241.  
  242.          invoke ListViewInsertColumn
  243.  
  244.      .elseif eax == WM_SIZE
  245.  
  246.          mov eax, lParam
  247.          mov ecx, eax
  248.          and eax, 0FFFFh
  249.          shr ecx, 16
  250.          invoke MoveWindow, g_hwndListView, 0, 0, eax, ecx, TRUE
  251.  
  252.      .elseif eax == WM_COMMAND
  253.  
  254.          mov eax, wParam
  255.          and eax, 0FFFFh
  256.          .if eax == IDCANCEL
  257.              invoke MessageBox, hDlg, $CTA0("Sure want to exit?"), \
  258.                      $CTA0("Exit Confirmation"), MB_YESNO + MB_ICONQUESTION + MB_DEFBUTTON1
  259.              .if eax == IDYES
  260.                  invoke EndDialog, hDlg, 0
  261.              .endif
  262.          .endif
  263.  
  264.      .elseif uMsg == WM_GETMINMAXINFO
  265.  
  266.          mov ecx, lParam
  267.          mov (MINMAXINFO PTR [ecx]).ptMinTrackSize.x, 380
  268.          mov (MINMAXINFO PTR [ecx]).ptMinTrackSize.y, 150
  269.  
  270.      .else
  271.  
  272.          xor eax, eax
  273.          ret
  274.      
  275.      .endif
  276.  
  277.      xor eax, eax
  278.      inc eax
  279.      ret
  280.      
  281.  DlgProc endp
  282.  
  283.  ;:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
  284.  ;                                         start                                                    
  285.  ;:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
  286.  
  287.  start proc
  288.  
  289.  local acModulePath[MAX_PATH]:CHAR
  290.  local _ss:SERVICE_STATUS
  291.  local dwBytesReturned:DWORD
  292.  
  293.      CTA  "This program was tested on Windows 2000+sp2/sp3/sp4,\n", szExecutionConfirmation
  294.      CTA  "Windows XP no sp, Windows Server 2003 Std and\n"
  295.      CTA  "seems to be workable. But it uses undocumented\n"
  296.      CTA  "tricks in kernel mode and may crash your system\:\n"
  297.      CTA  "\n"
  298.      CTA0 "Are your shure you want to run it?\n"
  299.  
  300.      invoke MessageBox, NULL, addr szExecutionConfirmation, \
  301.          $CTA0("Execution Confirmation"), MB_YESNO + MB_ICONQUESTION + MB_DEFBUTTON2
  302.      .if eax == IDNO
  303.          invoke ExitProcess, 0
  304.      .endif
  305.      
  306.      invoke SetUnhandledExceptionFilter, MyUnhandledExceptionFilter
  307.  
  308.      invoke OpenSCManager, NULL, NULL, SC_MANAGER_ALL_ACCESS
  309.      .if eax != NULL
  310.          mov g_hSCManager, eax
  311.  
  312.          push eax
  313.          invoke GetFullPathName, $CTA0("ProcessMon.sys"), sizeof acModulePath, addr acModulePath, esp
  314.          pop eax
  315.  
  316.          invoke CreateService, g_hSCManager, $CTA0("ProcessMon"), \
  317.              $CTA0("Process creation/destruction monitor"), \
  318.              SERVICE_START + SERVICE_STOP + DELETE, SERVICE_KERNEL_DRIVER, SERVICE_DEMAND_START, \
  319.              SERVICE_ERROR_IGNORE, addr acModulePath, NULL, NULL, NULL, NULL, NULL
  320.  
  321.          .if eax != NULL
  322.              mov g_hService, eax
  323.  
  324.              invoke StartService, g_hService, 0, NULL
  325.              .if eax != 0
  326.  
  327.                  invoke CreateFile, $CTA0("\\\\.\\ProcessMon"), \
  328.                          GENERIC_READ + GENERIC_WRITE, 0, NULL, OPEN_EXISTING, 0, NULL
  329.  
  330.                  .if eax != INVALID_HANDLE_VALUE
  331.                      mov g_hDevice, eax
  332.  
  333.                      invoke DeleteService, g_hService
  334.  
  335.                      invoke CreateEvent, NULL, FALSE, FALSE, NULL
  336.                      mov g_hEvent, eax
  337.  
  338.                      and g_fbExitNow, FALSE
  339.  
  340.                      push eax
  341.                      invoke CreateThread, NULL, 0, offset WaitForProcessData, g_hEvent, 0, esp
  342.                      pop ecx
  343.                      .if eax != NULL
  344.                      
  345.                          invoke CloseHandle, eax                            
  346.  
  347.                          ;:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
  348.  
  349.                          invoke DeviceIoControl, g_hDevice, IOCTL_SET_NOTIFY, \
  350.                                  addr g_hEvent, sizeof g_hEvent, NULL, 0, addr dwBytesReturned, NULL
  351.  
  352.                          .if eax != 0
  353.  
  354.                              invoke GetModuleHandle, NULL
  355.                              mov g_hInstance, eax
  356.                              invoke DialogBoxParam, g_hInstance, IDD_MAIN, NULL, addr DlgProc, 0
  357.  
  358.                              invoke DeviceIoControl, g_hDevice, IOCTL_REMOVE_NOTIFY, \
  359.                                          NULL, 0, NULL, 0, addr dwBytesReturned, NULL
  360.                          .else
  361.                              invoke MessageBox, NULL, \
  362.                                      $CTA0("Can't set notify."), NULL, MB_ICONSTOP
  363.                          .endif
  364.  
  365.                          ;:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
  366.  
  367.                          mov g_fbExitNow, TRUE
  368.                          invoke SetEvent, g_hEvent
  369.                      
  370.                          invoke Sleep, 100
  371.                      
  372.                      .else
  373.                          invoke MessageBox, NULL, $CTA0("Can't create thread."), NULL, MB_ICONSTOP
  374.                      .endif
  375.  
  376.                      invoke CloseHandle, g_hEvent
  377.                      invoke CloseHandle, g_hDevice
  378.                  .else
  379.                      invoke MessageBox, NULL, $CTA0("Can't open device."), NULL, MB_ICONSTOP
  380.                  .endif
  381.                  invoke ControlService, g_hService, SERVICE_CONTROL_STOP, addr _ss
  382.              .else
  383.                  invoke MessageBox, NULL, $CTA0("Can't start driver."), NULL, MB_ICONSTOP
  384.              .endif
  385.  
  386.              invoke DeleteService, g_hService
  387.              invoke CloseServiceHandle, g_hService
  388.  
  389.          .else
  390.              invoke MessageBox, NULL, $CTA0("Can't register driver."), NULL, MB_ICONSTOP
  391.          .endif
  392.          invoke CloseServiceHandle, g_hSCManager
  393.      .else
  394.          invoke MessageBox, NULL, \
  395.              $CTA0("Can't connect to SCM."), NULL, MB_ICONSTOP
  396.      .endif
  397.  
  398.      invoke ExitProcess, 0
  399.      invoke InitCommonControls
  400.      ret
  401.  
  402.  start endp
  403.  
  404.  ;:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
  405.  ;                                                                                                  
  406.  ;:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
  407.  
  408.  end start
  409.  
  410.  :make
  411.  
  412.  set exe=ProcessMon
  413.  
  414.  :makerc
  415.  if exist rsrc.obj goto final
  416.      \masm32\bin\rc /v rsrc.rc
  417.      \masm32\bin\cvtres /machine:ix86 rsrc.res
  418.      if errorlevel 0 goto final
  419.          echo.
  420.          pause
  421.          exit
  422.  
  423.  :final
  424.  
  425.  if exist rsrc.res del rsrc.res
  426.  if exist ..\%exe%.exe del ..\%exe%.exe
  427.  
  428.  \masm32\bin\ml /nologo /c /coff %exe%.bat
  429.  \masm32\bin\link /nologo /subsystem:windows %exe%.obj rsrc.obj
  430.  
  431.  del %exe%.obj
  432.  move %exe%.exe ..
  433.  if exist %exe%.exe del %exe%.exe
  434.  
  435.  echo.
  436.  pause
  437.  
  438.  


14.3 Разбираем программу управления драйвером

Код (Text):
  1.  
  2.  
  3.      invoke SetUnhandledExceptionFilter, MyUnhandledExceptionFilter
  4.  
  5.  

Устанавливаем обработчик исключений, покрывающий все потоки процесса (подробнее см. Часть 9). При возникновении исключения в любом из потоков процесса наш обработчик попытается привести систему в состояние, предшествовавшее запуску программы.

В Части 9 "Базовая техника: Работа с памятью. Разделяемая память" в определении функции MyUnhandledExceptionFilter была допущена ошибка: функция не принимала параметров, хотя система передает обработчику один параметр - указатель на структуру EXCEPTION_POINTERS. В результате функция не возвращала стек в исходное состояние. Если бы действительно произошло исключение, то по выходе из обработчика из-за некорректного состояния стека произошло бы новое исключение, что повлекло бы за собой повторный его вызов и так до бесконечности. Так что, обратите внимание: функция MyUnhandledExceptionFilter принимает указатель на структуру EXCEPTION_POINTERS.

Код (Text):
  1.  
  2.  
  3.                      invoke CreateEvent, NULL, FALSE, FALSE, NULL
  4.                      mov g_hEvent, eax
  5.  
  6.  

После регистрации и запуска драйвера создаем безымянный объект "событие" с автоматическим сбросом и в не сигнальном состоянии (nonsignaled). Именно этот объект и будет совместно использоваться.

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

Код (Text):
  1.  
  2.  
  3.                      and g_fbExitNow, FALSE
  4.  
  5.  

Явно сбрасываем глобальный флаг, который потребуется для того, чтобы сообщить рабочему потоку о прекращении работы.

Код (Text):
  1.  
  2.  
  3.                      push eax
  4.                      invoke CreateThread, NULL, 0, offset WaitForProcessData, g_hEvent, 0, esp
  5.                      pop ecx
  6.  
  7.  

Создаем рабочий поток. Этот поток будет ждать на описателе g_hEvent и по сигналу забирать у драйвера статистику.

Код (Text):
  1.  
  2.  
  3.                          invoke DeviceIoControl, g_hDevice, IOCTL_SET_NOTIFY, \
  4.                                  addr g_hEvent, sizeof g_hEvent, NULL, 0, addr dwBytesReturned, NULL
  5.  
  6.  

Шлем драйверу управляющий код IOCTL_SET_NOTIFY, передавая ему описатель нашего объекта "событие". Драйвер должен начать слежение за созданием/удалением процессов. При создании или удалении какого-либо процесса, драйвер будет устанавливать объект "событие" в сигнальное состояние.

Код (Text):
  1.  
  2.  
  3.                          .if eax != 0
  4.  
  5.                              invoke GetModuleHandle, NULL
  6.                              mov g_hInstance, eax
  7.                              invoke DialogBoxParam, g_hInstance, IDD_MAIN, NULL, addr DlgProc, 0
  8.  
  9.  

Если слежение установлено, запускаем диалог. В самой диалоговой процедуре нет ничего интересного.

Код (Text):
  1.  
  2.  
  3.                              invoke DeviceIoControl, g_hDevice, IOCTL_REMOVE_NOTIFY, \
  4.                                          NULL, 0, NULL, 0, addr dwBytesReturned, NULL
  5.  
  6.  

После закрытия диалога заставляем драйвер прекратить слежение.

Код (Text):
  1.  
  2.  
  3.                          mov g_fbExitNow, TRUE
  4.                          invoke SetEvent, g_hEvent
  5.  
  6.  

Устанавливаем флаг g_fbExitNow и переводим объект "событие" в сигнальное состояние. Это пробудит рабочий поток, он увидит установленный флаг и прекратит работу.

Код (Text):
  1.  
  2.  
  3.                          invoke Sleep, 100
  4.  
  5.  

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



14.4 Процедура рабочего потока

Посмотрим теперь на процедуру рабочего потока.

Код (Text):
  1.  
  2.  
  3.      invoke GetCurrentThread
  4.      invoke SetThreadPriority, eax, THREAD_PRIORITY_HIGHEST
  5.  
  6.  

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

Код (Text):
  1.  
  2.  
  3.      .while TRUE
  4.          invoke WaitForSingleObject, hEvent, INFINITE
  5.          .if eax != WAIT_FAILED
  6.  
  7.  

В бесконечном цикле ждем на объекте "событие".

Код (Text):
  1.  
  2.  
  3.              .break .if g_fbExitNow == TRUE
  4.  
  5.  

Если пора выходить, прерываем цикл.

Код (Text):
  1.  
  2.  
  3.              invoke DeviceIoControl, g_hDevice, IOCTL_GET_PROCESS_DATA, NULL, 0, \
  4.                          addr ProcessData, sizeof ProcessData, addr dwBytesReturned, NULL
  5.  
  6.  

Забираем у драйвера свежие данные.

Код (Text):
  1.  
  2.  
  3.              .if eax != 0
  4.                  invoke FillProcessInfo, addr ProcessData
  5.              .endif
  6.  
  7.  

Выводим, полученную от драйвера информацию.



14.5 Функция FillProcessInfo

Случается, что драйвер возвращает "короткий" путь. Например, "C:\PROGRA~1\WinZip\WinZip32.EXE". Детально в причинах этого явления я не разбирался. Видимо, если при создании процесса его родитель запускает его (точнее его исполнимый образ) по "короткому" пути, то этот путь и попадает в соответствующее поле FILE_OBJECT. Мы просто вызовем функцию GetLongPathName, которая всё сделает сама. Если буфера размером 1024 байт, вдруг окажется недостаточно, то просто покажем тот путь, который вернул драйвер. Для простоты, я не стал выделять дополнительную память и вызывать GetLongPathName снова.

Код (Text):
  1.  
  2.  
  3.  local buffer[1024]:CHAR
  4.  
  5.  . . .
  6.  
  7.      invoke GetLongPathName, addr [esi].szProcessName, addr buffer, sizeof buffer
  8.      .if ( eax == 0 ) || ( eax >= sizeof buffer )
  9.          lea ecx, [esi].szProcessName
  10.      .else
  11.          lea ecx, buffer
  12.      .endif
  13.  
  14.      and lvi.iSubItem, 0
  15.      mov lvi.pszText, ecx
  16.      ListView_InsertItem g_hwndListView, addr lvi
  17.  
  18.  


14.6 Исходный текст драйвера ProcessMon

Модуль ProcessMon.bat

Код (Text):
  1.  
  2.  
  3.  ;@echo off
  4.  ;goto make
  5.  
  6.  ;:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
  7.  ;
  8.  ;  ProcessMon - Пример того, как драйвер может сообщить
  9.  ;                  режиму пользователя о наступлении интересующего его события и не только это.
  10.  ;
  11.  ;:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
  12.  
  13.  .386
  14.  .model flat, stdcall
  15.  option casemap:none
  16.  
  17.  ;:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
  18.  ;                               В К Л Ю Ч А Е М Ы Е    Ф А Й Л Ы
  19.  ;:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
  20.  
  21.  include \masm32\include\w2k\ntstatus.inc
  22.  include \masm32\include\w2k\ntddk.inc
  23.  include \masm32\include\w2k\ntoskrnl.inc
  24.  
  25.  includelib \masm32\lib\w2k\ntoskrnl.lib
  26.  
  27.  include \masm32\Macros\Strings.mac
  28.  
  29.  include ..\common.inc
  30.  include ProcPath.asm
  31.  
  32.  ;:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
  33.  ;                             Н Е И З М Е Н Я Е М Ы Е    Д А Н Н Ы Е                                
  34.  ;:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
  35.  
  36.  .const
  37.  
  38.  CCOUNTED_UNICODE_STRING "\\Device\\ProcessMon", g_usDeviceName, 4
  39.  CCOUNTED_UNICODE_STRING "\\DosDevices\\ProcessMon", g_usSymbolicLinkName, 4
  40.  
  41.  ;:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
  42.  ;                     Н Е И Н И Ц И А Л И З И Р О В А Н Н Ы Е    Д А Н Н Ы Е                        
  43.  ;:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
  44.  
  45.  .data?
  46.  
  47.  g_pkEventObject         PKEVENT         ?
  48.  g_dwProcessNameOffset   DWORD           ?
  49.  g_fbNotifyRoutineSet    BOOL            ?
  50.  
  51.  g_ProcessData           PROCESS_DATA    
  52.  
  53.  ;:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
  54.  ;                                           К О Д                                                  
  55.  ;:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
  56.  
  57.  .code
  58.  
  59.  ;:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
  60.  ;                                   DispatchCreateClose                                            
  61.  ;:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
  62.  
  63.  DispatchCreateClose proc pDeviceObject:PDEVICE_OBJECT, pIrp:PIRP
  64.  
  65.      mov ecx, pIrp
  66.      mov (_IRP PTR [ecx]).IoStatus.Status, STATUS_SUCCESS
  67.      and (_IRP PTR [ecx]).IoStatus.Information, 0
  68.  
  69.      fastcall IofCompleteRequest, ecx, IO_NO_INCREMENT
  70.  
  71.      mov eax, STATUS_SUCCESS
  72.      ret
  73.  
  74.  DispatchCreateClose endp
  75.  
  76.  ;:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
  77.  ;                                     ProcessNotifyRoutine                                          
  78.  ;:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
  79.  
  80.  ProcessNotifyRoutine proc dwParentId:DWORD, dwProcessId:DWORD, bCreate:BOOL ; BOOLEAN
  81.  
  82.  local peProcess:PVOID               ; PEPROCESS
  83.  local fbDereference:BOOL
  84.  local us:UNICODE_STRING
  85.  local as:ANSI_STRING
  86.  
  87.      push eax
  88.      invoke PsLookupProcessByProcessId, dwProcessId, esp
  89.      pop peProcess
  90.      .if eax == STATUS_SUCCESS
  91.          mov fbDereference, TRUE
  92.      .else
  93.          invoke IoGetCurrentProcess
  94.          mov peProcess, eax
  95.          and fbDereference, FALSE
  96.      .endif
  97.  
  98.      mov eax, dwProcessId
  99.      mov g_ProcessData.dwProcessId, eax
  100.  
  101.      mov eax, bCreate
  102.      mov g_ProcessData.bCreate, eax
  103.  
  104.      invoke memset, addr g_ProcessData.szProcessName, 0, IMAGE_FILE_PATH_LEN
  105.  
  106.      invoke GetImageFilePath, peProcess, addr us
  107.      .if eax == STATUS_SUCCESS
  108.  
  109.          lea eax, g_ProcessData.szProcessName
  110.          mov as.Buffer,          eax
  111.          mov as.MaximumLength,   IMAGE_FILE_PATH_LEN
  112.          and as._Length,         0
  113.  
  114.          invoke RtlUnicodeStringToAnsiString, addr as, addr us, FALSE
  115.  
  116.          invoke ExFreePool, us.Buffer
  117.      .else
  118.  
  119.          mov eax, g_dwImageFileNameOffset
  120.          .if eax != 0
  121.              add eax, peProcess
  122.              invoke memcpy, addr g_ProcessData.szProcessName, eax, 16
  123.          .endif
  124.  
  125.      .endif
  126.  
  127.      .if fbDereference
  128.          fastcall ObfDereferenceObject, peProcess
  129.      .endif
  130.  
  131.      invoke KeSetEvent, g_pkEventObject, 0, FALSE
  132.  
  133.      ret
  134.  
  135.  ProcessNotifyRoutine endp
  136.  
  137.  ;:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
  138.  ;                                     DispatchControl                                              
  139.  ;:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
  140.  
  141.  DispatchControl proc uses esi edi pDeviceObject:PDEVICE_OBJECT, pIrp:PIRP
  142.  
  143.  local liDelayTime:LARGE_INTEGER
  144.  
  145.      mov esi, pIrp
  146.      assume esi:ptr _IRP
  147.  
  148.      mov [esi].IoStatus.Status, STATUS_UNSUCCESSFUL
  149.      and [esi].IoStatus.Information, 0
  150.  
  151.      IoGetCurrentIrpStackLocation esi
  152.      mov edi, eax
  153.      assume edi:ptr IO_STACK_LOCATION
  154.  
  155.      .if [edi].Parameters.DeviceIoControl.IoControlCode == IOCTL_SET_NOTIFY
  156.          .if [edi].Parameters.DeviceIoControl.InputBufferLength >= sizeof HANDLE
  157.  
  158.              .if g_fbNotifyRoutineSet == FALSE
  159.  
  160.                  mov edx, [esi].AssociatedIrp.SystemBuffer
  161.                  mov edx, [edx]
  162.  
  163.                  mov ecx, ExEventObjectType
  164.                  mov ecx, [ecx]
  165.                  mov ecx, [ecx]
  166.  
  167.                  invoke ObReferenceObjectByHandle, edx, EVENT_MODIFY_STATE, ecx, \
  168.                                          UserMode, addr g_pkEventObject, NULL
  169.                  .if eax == STATUS_SUCCESS
  170.  
  171.                      invoke PsSetCreateProcessNotifyRoutine, ProcessNotifyRoutine, FALSE
  172.                      mov [esi].IoStatus.Status, eax
  173.  
  174.                      .if eax == STATUS_SUCCESS
  175.  
  176.                          mov g_fbNotifyRoutineSet, TRUE
  177.  
  178.                          invoke DbgPrint, \
  179.                                  $CTA0("ProcessMon: Notification was set\n")
  180.      
  181.                          mov eax, pDeviceObject
  182.                          mov eax, (DEVICE_OBJECT PTR [eax]).DriverObject
  183.                          and (DRIVER_OBJECT PTR [eax]).DriverUnload, NULL
  184.  
  185.                      .else
  186.                          invoke DbgPrint, \
  187.                          $CTA0("ProcessMon: Couldn't set notification\n")
  188.                      .endif
  189.  
  190.                  .else
  191.                      mov [esi].IoStatus.Status, eax
  192.                      invoke DbgPrint, \
  193.                      $CTA0("ProcessMon: Couldn't reference user event object. Status: %08X\n"), \
  194.                      eax
  195.                  .endif
  196.              .endif
  197.          .else
  198.              mov [esi].IoStatus.Status, STATUS_BUFFER_TOO_SMALL
  199.          .endif
  200.  
  201.      .elseif [edi].Parameters.DeviceIoControl.IoControlCode == IOCTL_REMOVE_NOTIFY
  202.  
  203.          .if g_fbNotifyRoutineSet == TRUE
  204.  
  205.              invoke PsSetCreateProcessNotifyRoutine, ProcessNotifyRoutine, TRUE
  206.              mov [esi].IoStatus.Status, eax
  207.  
  208.              .if eax == STATUS_SUCCESS
  209.  
  210.                  and g_fbNotifyRoutineSet, FALSE
  211.  
  212.                  invoke DbgPrint, $CTA0("ProcessMon: Notification was removed\n")
  213.  
  214.                  or liDelayTime.HighPart, -1
  215.                  mov liDelayTime.LowPart, -500000
  216.      
  217.                  invoke KeDelayExecutionThread, KernelMode, FALSE, addr liDelayTime
  218.  
  219.                  mov eax, pDeviceObject
  220.                  mov eax, (DEVICE_OBJECT PTR [eax]).DriverObject
  221.                  mov (DRIVER_OBJECT PTR [eax]).DriverUnload, offset DriverUnload
  222.  
  223.                  .if g_pkEventObject != NULL
  224.                      invoke ObDereferenceObject, g_pkEventObject
  225.                      and g_pkEventObject, NULL
  226.                  .endif
  227.              .else
  228.                  invoke DbgPrint, \
  229.                  $CTA0("ProcessMon: Couldn't remove notification\n")
  230.              .endif
  231.              
  232.          .endif
  233.  
  234.      .elseif [edi].Parameters.DeviceIoControl.IoControlCode == IOCTL_GET_PROCESS_DATA
  235.          .if [edi].Parameters.DeviceIoControl.OutputBufferLength >= sizeof PROCESS_DATA
  236.  
  237.              mov eax, [esi].AssociatedIrp.SystemBuffer
  238.              invoke memcpy, eax, offset g_ProcessData, sizeof g_ProcessData
  239.      
  240.              mov [esi].IoStatus.Status, STATUS_SUCCESS
  241.              mov [esi].IoStatus.Information, sizeof g_ProcessData
  242.  
  243.          .else
  244.              mov [esi].IoStatus.Status, STATUS_BUFFER_TOO_SMALL
  245.          .endif
  246.  
  247.      .else
  248.          mov [esi].IoStatus.Status, STATUS_INVALID_DEVICE_REQUEST
  249.      .endif
  250.  
  251.      push [esi].IoStatus.Status
  252.      
  253.      assume edi:nothing
  254.      assume esi:nothing
  255.  
  256.      fastcall IofCompleteRequest, esi, IO_NO_INCREMENT
  257.  
  258.      pop eax
  259.      ret
  260.  
  261.  DispatchControl endp
  262.  
  263.  ;:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
  264.  ;                                       DriverUnload                                                
  265.  ;:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
  266.  
  267.  DriverUnload proc pDriverObject:PDRIVER_OBJECT
  268.  
  269.      invoke IoDeleteSymbolicLink, addr g_usSymbolicLinkName
  270.  
  271.      mov eax, pDriverObject
  272.      invoke IoDeleteDevice, (DRIVER_OBJECT PTR [eax]).DeviceObject
  273.  
  274.      ret
  275.  
  276.  DriverUnload endp
  277.  
  278.  ;:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
  279.  ;                              D I S C A R D A B L E   C O D E                                      
  280.  ;:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
  281.  
  282.  .code INIT
  283.  
  284.  ;:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
  285.  ;                                    GetImageFileNameOffset                                        
  286.  ;:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
  287.  
  288.  GetImageFileNameOffset proc uses esi ebx
  289.  
  290.      invoke IoGetCurrentProcess
  291.      mov esi, eax
  292.  
  293.      xor ebx, ebx
  294.      .while ebx < 1000h
  295.          lea eax, [esi+ebx]
  296.          invoke _strnicmp, eax, $CTA0("system"), 6
  297.          .break .if eax == 0
  298.          inc ebx
  299.      .endw
  300.  
  301.      .if eax == 0
  302.          mov eax, ebx
  303.      .else
  304.          xor eax, eax
  305.      .endif
  306.  
  307.      ret
  308.  
  309.  GetImageFileNameOffset endp
  310.  
  311.  ;:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
  312.  ;                                       DriverEntry                                                
  313.  ;:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
  314.  
  315.  DriverEntry proc pDriverObject:PDRIVER_OBJECT, pusRegistryPath:PUNICODE_STRING
  316.  
  317.  local status:NTSTATUS
  318.  local pDeviceObject:PDEVICE_OBJECT
  319.  
  320.      mov status, STATUS_DEVICE_CONFIGURATION_ERROR
  321.  
  322.      invoke IoCreateDevice, pDriverObject, 0, addr g_usDeviceName, \
  323.                  FILE_DEVICE_UNKNOWN, 0, TRUE, addr pDeviceObject
  324.      .if eax == STATUS_SUCCESS
  325.          invoke IoCreateSymbolicLink, addr g_usSymbolicLinkName, addr g_usDeviceName
  326.          .if eax == STATUS_SUCCESS
  327.              mov eax, pDriverObject
  328.              assume eax:ptr DRIVER_OBJECT
  329.              mov [eax].MajorFunction[IRP_MJ_CREATE*(sizeof PVOID)],          offset DispatchCreateClose
  330.              mov [eax].MajorFunction[IRP_MJ_CLOSE*(sizeof PVOID)],           offset DispatchCreateClose
  331.              mov [eax].MajorFunction[IRP_MJ_DEVICE_CONTROL*(sizeof PVOID)],  offset DispatchControl
  332.              mov [eax].DriverUnload,                                         offset DriverUnload
  333.              assume eax:nothing
  334.  
  335.              and g_fbNotifyRoutineSet, FALSE
  336.              invoke memset, addr g_ProcessData, 0, sizeof g_ProcessData
  337.          
  338.              invoke GetImageFileNameOffset
  339.              mov g_dwImageFileNameOffset, eax
  340.  
  341.              mov status, STATUS_SUCCESS
  342.          .else
  343.              invoke IoDeleteDevice, pDeviceObject
  344.          .endif
  345.      .endif
  346.  
  347.      mov eax, status
  348.      ret
  349.  
  350.  DriverEntry endp
  351.  
  352.  ;:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
  353.  ;                                                                                                  
  354.  ;:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
  355.  
  356.  end DriverEntry
  357.  
  358.  :make
  359.  
  360.  set drv=ProcessMon
  361.  
  362.  \masm32\bin\ml /nologo /c /coff %drv%.bat
  363.  \masm32\bin\link /nologo /driver /base:0x10000 /align:32 /out:%drv%.sys /subsystem:native /ignore:4078 %drv%.obj
  364.  
  365.  del %drv%.obj
  366.  move %drv%.sys ..
  367.  
  368.  echo.
  369.  pause
  370.  
  371.  

Модуль ProcPath.asm

Код (Text):
  1.  
  2.  
  3.  ;:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
  4.  ;                                      С Т Р У К Т У Р Ы                                            
  5.  ;:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
  6.  
  7.  OBJECT_HEADER STRUCT                        ; sizeof = 018h
  8.      PointerCount            SDWORD      ?   ; 0000h
  9.      union
  10.          HandleCount         SDWORD      ?   ; 0004h
  11.          SEntry              PVOID       ?   ; 0004h PTR SINGLE_LIST_ENTRY
  12.      ends
  13.      _Type                   PVOID       ?   ; 0008h PTR OBJECT_TYPE
  14.      NameInfoOffset          BYTE        ?   ; 000Ch
  15.      HandleInfoOffset        BYTE        ?   ; 000Dh
  16.      QuotaInfoOffset         BYTE        ?   ; 000Eh
  17.      Flags                   BYTE        ?   ; 000Fh
  18.      union
  19.          ObjectCreateInfo    PVOID       ?   ; 0010h PTR OBJECT_CREATE_INFORMATION
  20.          QuotaBlockCharged   PVOID       ?   ; 0010h
  21.      ends
  22.      SecurityDescriptor      PVOID       ?   ; 0014h
  23.  ;   Body                    QUAD          ; 0018h
  24.  OBJECT_HEADER ENDS
  25.  
  26.  _SEGMENT STRUCT                             ; sizeof = 40h
  27.      ControlArea             PVOID       ?   ; 000 PTR CONTROL_AREA
  28.      SegmentBaseAddress      PVOID       ?   ; 004
  29.      TotalNumberOfPtes       DWORD       ?   ; 008
  30.      NonExtendedPtes         DWORD       ?   ; 00C
  31.      SizeOfSegment           QWORD       ?   ; 010 ULONG64
  32.      ImageCommitment         DWORD       ?   ; 018
  33.      ImageInformation        PVOID       ?   ; 01C PTR SECTION_IMAGE_INFORMATION
  34.      SystemImageBase         PVOID       ?   ; 020
  35.      NumberOfCommittedPages  DWORD       ?   ; 024
  36.      SegmentPteTemplate      DWORD       ?   ; 028 MMPTE
  37.      BasedAddress            PVOID       ?   ; 02C
  38.      ExtendInfo              PVOID       ?   ; 030 PTR MMEXTEND_INFO
  39.      PrototypePte            PVOID       ?   ; 034 PTR MMPTE
  40.      ThePtes                 DWORD 1 dup(?)  ; 038 array of MMPTE
  41.  _SEGMENT ENDS
  42.  
  43.  CONTROL_AREA STRUCT                             ; sizeof = 38h
  44.      _Segment                    PVOID       ?   ; 000 PTR _SEGMENT
  45.      DereferenceList             LIST_ENTRY    ; 004
  46.      NumberOfSectionReferences   DWORD       ?   ; 00C
  47.      NumberOfPfnReferences       DWORD       ?   ; 010
  48.      NumberOfMappedViews         DWORD       ?   ; 014
  49.      NumberOfSubsections         WORD        ?   ; 018
  50.      FlushInProgressCount        WORD        ?   ; 01A
  51.      NumberOfUserReferences      DWORD       ?   ; 01C
  52.      union u
  53.          LongFlags               DWORD       ?   ; 020
  54.          Flags                   DWORD       ?   ; 020 MMSECTION_FLAGS
  55.      ends
  56.      FilePointer                 PVOID       ?   ; 024 PTR FILE_OBJECT
  57.      WaitingForDeletion          PVOID       ?   ; 028 PTR EVENT_COUNTER
  58.      ModifiedWriteCount          WORD        ?   ; 02C
  59.      NumberOfSystemCacheViews    WORD        ?   ; 02E
  60.      PagedPoolUsage              DWORD       ?   ; 030
  61.      NonPagedPoolUsage           DWORD       ?   ; 034
  62.  CONTROL_AREA ENDS
  63.  
  64.  MMADDRESS_NODE STRUCT                           ; sizeof = 14h
  65.      StartingVpn                 DWORD       ?   ; 00 ULONG_PTR
  66.      EndingVpn                   DWORD       ?   ; 04 ULONG_PTR
  67.      Parent                      PVOID       ?   ; 08 PTR MMADDRESS_NODE
  68.      LeftChild                   PVOID       ?   ; 0C PTR MMADDRESS_NODE
  69.      RightChild                  PVOID       ?   ; 10 PTR MMADDRESS_NODE
  70.  MMADDRESS_NODE ENDS
  71.  PMMADDRESS_NODE typedef ptr MMADDRESS_NODE
  72.  
  73.  SECTION STRUCT                                      ; sizeof = 28h
  74.      Address                     MMADDRESS_NODE    ; 00
  75.      _Segment                    PVOID           ?   ; 14 PTR _SEGMENT
  76.      SizeOfSection               LARGE_INTEGER     ; 18
  77.      union u
  78.          LongFlags               DWORD           ?   ; 20
  79.          Flags                   DWORD           ?   ; 20 MMSECTION_FLAGS
  80.      ends
  81.      InitialPageProtection       DWORD           ?   ; 24
  82.  SECTION ENDS
  83.  PSECTION typedef ptr SECTION
  84.  
  85.  ;:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
  86.  ;                                      К О Н С Т А Н Т Ы                                            
  87.  ;:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
  88.  
  89.  WINVER_UNINITIALIZED    equ -1
  90.  WINVER_2K               equ 0
  91.  WINVER_XP_OR_HIGHER     equ 1
  92.  
  93.  ;:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
  94.  ;                 И Н И Ц И А Л И З И Р О В А Н Н Ы Е    Д А Н Н Н Ы Е                              
  95.  ;:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
  96.  
  97.  .data
  98.  
  99.  g_dwWinVer  DWORD   WINVER_UNINITIALIZED
  100.  
  101.  ;:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
  102.  ;                                           К О Д                                                  
  103.  ;:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
  104.  
  105.  .code
  106.  
  107.  ;:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
  108.  ;                                  IsAddressInPoolRanges                                            
  109.  ;:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
  110.  
  111.  IsAddressInPoolRanges proc uses ebx pAddress:PVOID
  112.  
  113.  local fOk:BOOL
  114.  
  115.      and fOk, FALSE
  116.  
  117.      mov eax, MmSystemRangeStart
  118.      mov eax, [eax]
  119.      mov eax, [eax]
  120.  
  121.      .if eax == 80000000h
  122.              
  123.          mov ebx, pAddress
  124.  
  125.          xor ecx, ecx
  126.          xor edx, edx
  127.  
  128.          .if ( ebx > 80000000h ) && ( ebx < 0A0000000h )
  129.              inc ecx
  130.          .endif
  131.  
  132.          .if ( ebx > 0E1000000h ) && ( ebx < 0FFBE0000h )
  133.              inc edx
  134.          .endif
  135.  
  136.          or ecx, edx
  137.          .if !ZERO?
  138.              mov fOk, TRUE   ; OK
  139.          .endif
  140.  
  141.      .endif
  142.  
  143.      mov eax, fOk
  144.      ret
  145.  
  146.  IsAddressInPoolRanges endp
  147.  
  148.  ;:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
  149.  ;                                     IsLikeObjectPointer                                          
  150.  ;:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
  151.  
  152.  IsLikeObjectPointer proc uses esi pObject:PVOID
  153.  
  154.  local fOk:BOOL
  155.  
  156.      and fOk, FALSE
  157.  
  158.      mov esi, pObject
  159.  
  160.      invoke IsAddressInPoolRanges, esi
  161.      .if eax == TRUE
  162.  
  163.          mov eax, esi
  164.          and eax, (8 - 1)
  165.          .if eax == 0
  166.                  
  167.              invoke MmIsAddressValid, esi
  168.              .if al
  169.  
  170.                  mov eax, esi
  171.                  and eax, (PAGE_SIZE-1)
  172.                  .if eax < sizeof OBJECT_HEADER
  173.  
  174.                      sub esi, sizeof OBJECT_HEADER
  175.      
  176.                      invoke MmIsAddressValid, esi
  177.                      .if al
  178.  
  179.                          mov fOk, TRUE
  180.  
  181.                      .endif
  182.  
  183.                  .else
  184.                  
  185.                      mov fOk, TRUE
  186.  
  187.                  .endif
  188.              .endif
  189.          .endif
  190.      .endif
  191.  
  192.      mov eax, fOk
  193.      ret
  194.  
  195.  IsLikeObjectPointer endp
  196.  
  197.  ;:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
  198.  ;                                     GetImageFilePath                                              
  199.  ;:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
  200.  
  201.  GetImageFilePath proc uses ebx esi edi peProcess:PVOID, pusImageFilePath:PUNICODE_STRING
  202.  
  203.  local status:NTSTATUS
  204.  local pSection:PVOID            ; PTR SECTION
  205.  local usDosName:UNICODE_STRING
  206.  
  207.      mov status, STATUS_UNSUCCESSFUL
  208.  
  209.      PROCESS_QUERY_INFORMATION equ 400h  ; winnt.inc
  210.  
  211.      mov ecx, PsProcessType
  212.      mov ecx, [ecx]
  213.      mov ecx, [ecx]
  214.  
  215.      invoke ObReferenceObjectByPointer, peProcess, PROCESS_QUERY_INFORMATION, ecx, UserMode
  216.      .if eax == STATUS_SUCCESS
  217.  
  218.          .if g_dwWinVer == WINVER_UNINITIALIZED
  219.  
  220.              invoke IoIsWdmVersionAvailable, 1, 20h
  221.              .if al
  222.                  mov g_dwWinVer, WINVER_XP_OR_HIGHER
  223.              .else
  224.                  mov g_dwWinVer, WINVER_2K
  225.              .endif
  226.  
  227.          .endif
  228.  
  229.          .if g_dwWinVer == WINVER_XP_OR_HIGHER
  230.  
  231.              mov esi, peProcess
  232.              mov ebx, 80h            ; Start at offset 80h
  233.              .while ebx < 204h
  234.  
  235.                  ; Filter unreasonable candidates
  236.  
  237.                  mov edi, [esi][ebx]
  238.                  invoke IsLikeObjectPointer, edi
  239.                  .if eax == TRUE
  240.  
  241.                      mov eax, edi
  242.                      sub eax, sizeof OBJECT_HEADER
  243.  
  244.                      .if ([OBJECT_HEADER PTR [eax]].PointerCount <= 4)
  245.                      .if ([OBJECT_HEADER PTR [eax]].HandleCount <= 1)
  246.  
  247.                          mov ecx, MmSectionObjectType
  248.                          mov ecx, [ecx]
  249.                          mov ecx, [ecx]
  250.  
  251.                          invoke ObReferenceObjectByPointer, edi, SECTION_QUERY, ecx, UserMode
  252.                          .if eax == STATUS_SUCCESS
  253.  
  254.                              mov status, eax
  255.                              mov pSection, edi
  256.  
  257.                               .break
  258.                          .endif
  259.                      .endif
  260.                      .endif
  261.                  .endif
  262.  
  263.                  add ebx, 4
  264.  
  265.              .endw
  266.  
  267.          .else
  268.  
  269.              xor ebx, ebx
  270.              mov edi, 4
  271.              .while ebx < 3
  272.  
  273.                  invoke IoGetCurrentProcess
  274.                  .if eax == peProcess
  275.                      
  276.                      mov ecx, MmSectionObjectType
  277.                      mov ecx, [ecx]
  278.                      mov ecx, [ecx]  ; PTR OBJECT_TYPE
  279.                  
  280.                      invoke ObReferenceObjectByHandle, edi, SECTION_QUERY, ecx, KernelMode, addr pSection, NULL
  281.                      mov status, eax
  282.  
  283.                  .else
  284.  
  285.                      invoke KeAttachProcess, peProcess
  286.  
  287.                      mov ecx, MmSectionObjectType
  288.                      mov ecx, [ecx]
  289.                      mov ecx, [ecx]
  290.  
  291.                      invoke ObReferenceObjectByHandle, edi, SECTION_QUERY, ecx, KernelMode, addr pSection, NULL
  292.                      mov status, eax
  293.  
  294.                      invoke KeDetachProcess
  295.  
  296.                  .endif
  297.  
  298.                  .break .if status == STATUS_SUCCESS
  299.                      
  300.                  .if ebx == 0
  301.                      
  302.                      mov edi, 03F8h  ; Try 03F8h handle.
  303.                          
  304.                  .elseif ebx == 1
  305.                      
  306.                      mov eax, peProcess
  307.                      add eax, 01ACh
  308.                      mov eax, [eax]
  309.                      mov edi, eax
  310.  
  311.                      and eax, (4 - 1)
  312.                      .break .if ( eax != 0 ) || ( edi >= 800h )
  313.                          
  314.                  .endif
  315.                  
  316.                  inc ebx
  317.              .endw
  318.  
  319.          .endif
  320.  
  321.          .if status == STATUS_SUCCESS
  322.  
  323.              mov status, STATUS_UNSUCCESSFUL
  324.  
  325.              mov ebx, pSection
  326.              mov ebx, (SECTION PTR [ebx])._Segment
  327.  
  328.              invoke IsAddressInPoolRanges, ebx
  329.              push eax
  330.              invoke MmIsAddressValid, ebx
  331.              pop ecx
  332.              .if al && ( ecx == TRUE )
  333.  
  334.                  mov esi, ebx
  335.  
  336.                  mov ebx, (_SEGMENT PTR [ebx]).ControlArea
  337.  
  338.                  invoke IsAddressInPoolRanges, ebx
  339.                  push eax
  340.                  invoke MmIsAddressValid, ebx
  341.                  pop ecx
  342.                  .if al && ( ecx == TRUE ) && ([CONTROL_AREA PTR [ebx]]._Segment == esi )
  343.  
  344.                      mov ebx, (CONTROL_AREA PTR [ebx]).FilePointer
  345.  
  346.                      invoke IsLikeObjectPointer, ebx
  347.                      .if eax == TRUE
  348.  
  349.                          mov ecx, IoFileObjectType
  350.                          mov ecx, [ecx]
  351.                          mov ecx, [ecx]          ; PTR OBJECT_TYPE
  352.  
  353.                          invoke ObReferenceObjectByPointer, ebx, FILE_READ_ATTRIBUTES, ecx, UserMode
  354.                          .if eax == STATUS_SUCCESS
  355.  
  356.                              invoke ExAllocatePool, PagedPool, (IMAGE_FILE_PATH_LEN+1) * sizeof WCHAR
  357.                              .if eax != NULL
  358.  
  359.                                  mov edi, pusImageFilePath
  360.                                  assume edi:ptr UNICODE_STRING
  361.  
  362.                                  mov [edi].Buffer, eax
  363.  
  364.                                  invoke memset, eax, 0, (IMAGE_FILE_PATH_LEN+1) * sizeof WCHAR
  365.  
  366.                                  mov [edi].MaximumLength,    IMAGE_FILE_PATH_LEN * sizeof WCHAR
  367.                                  and [edi]._Length,          0
  368.  
  369.                                  invoke RtlVolumeDeviceToDosName, \
  370.                                          (FILE_OBJECT PTR [ebx]).DeviceObject, addr usDosName
  371.                                  .if eax == STATUS_SUCCESS
  372.  
  373.                                      invoke RtlCopyUnicodeString, edi, addr usDosName
  374.                                      invoke ExFreePool, usDosName.Buffer
  375.  
  376.                                  .endif
  377.  
  378.                                  invoke RtlAppendUnicodeStringToString, edi, \
  379.                                                  addr (FILE_OBJECT PTR [ebx]).FileName
  380.  
  381.                                  assume edi:nothing
  382.  
  383.                                  mov status, STATUS_SUCCESS
  384.  
  385.                              .endif
  386.  
  387.                              invoke ObDereferenceObject, ebx
  388.                          .endif
  389.                      .endif
  390.                  .endif
  391.              .endif
  392.  
  393.              invoke ObDereferenceObject, pSection
  394.          .endif
  395.  
  396.          invoke ObDereferenceObject, peProcess
  397.      .endif
  398.  
  399.      mov eax, status
  400.      ret
  401.  
  402.  GetImageFilePath endp
  403.  
  404.  


14.7 Процедура DriverEntry

Кроме всего прочего в процедуре DriverEntry нам надо получить смещение поля ImageFileName структуры EPROCESS.

Код (Text):
  1.  
  2.  
  3.  EPROCESS STRUCT
  4.  . . .
  5.      ImageFileName BYTE 16 dup(?)
  6.  . . .
  7.  EPROCESS ENDS
  8.  
  9.  

ImageFileName хранит имя образа, создавшего процесс. Как видите, это поле позволяет хранить всего 16 символов. Более длинные имена усекаются до 16 символов и, в этом случае, завершающий ноль не используется. Содержимое этого поля мы будем копировать в нашу структуру PROCESS_DATA, но только в том случае, если не удастся получить полный путь к образу. Положение этого поля в структуре EPROCESS отличается на разных версиях системы (для w2k, wxp и w2k3 оно равно 01FCh, 0174h и 0154h, соответственно), но есть очень простой и хорошо известный способ его найти. Для этого надо просканировать структуру EPROCESS процесса System и найти там строку "System" - это и будет поле ImageFileName. Разумеется, этот трюк будет работать только в том случае, если это поле преднамеренно кем-то не изменено. В последнее время стало модно залезать в ядро и вытворять там всякие глупости. Будем считать, что наша система девственно чиста. В противном случае придется изобретать способ похитрее.

Итак, сейчас мы в процедуре DriverEntry, т.е. в контексте процесса System.

Код (Text):
  1.  
  2.  
  3.              invoke GetImageFileNameOffset
  4.              mov g_dwImageFileNameOffset, eax
  5.  
  6.  

Вызываем процедуру GetImageFileNameOffset, которая вернет нам смещение поля ImageFileName от начала структуры EPROCESS или ноль, если не сможет его найти. Процедура GetImageFileNameOffset проста и делает следующее.

Код (Text):
  1.  
  2.  
  3.      invoke IoGetCurrentProcess
  4.      mov esi, eax
  5.  
  6.  

Получаем указатель на структуры EPROCESS текущего процесса, т.е. процесса System.

Код (Text):
  1.  
  2.  
  3.      xor ebx, ebx
  4.      .while ebx < 1000h
  5.          lea eax, [esi+ebx]
  6.          invoke _strnicmp, eax, $CTA0("system"), 6
  7.          .break .if eax == 0
  8.          inc ebx
  9.      .endw
  10.  
  11.  

Ищем в первой странице от начала строку "system", причем используем функцию _strnicmp, которая сравнивает строго определенное количество символов (6 в данном случае) и не учитывает их регистр.

Код (Text):
  1.  
  2.  
  3.      .if eax == 0
  4.          mov eax, ebx
  5.      .else
  6.          xor eax, eax
  7.      .endif
  8.  
  9.      ret
  10.  
  11.  

Если такая строка найдена, то это поле ImageFileName - возвращаем его смещение или ноль в случае неудачи.



14.8 Обрабатываем IOCTL_SET_NOTIFY

Получив управляющий код IOCTL_SET_NOTIFY, мы должны установить слежение за созданием/удалением процессов.

Код (Text):
  1.  
  2.  
  3.              .if g_fbNotifyRoutineSet == FALSE
  4.  
  5.  

На всякий случай, проверим флаг g_fbNotifyRoutineSet - возможно слежение уже установлено.

Код (Text):
  1.  
  2.  
  3.                  mov edx, [esi].AssociatedIrp.SystemBuffer
  4.                  mov edx, [edx]
  5.  
  6.  

Извлекаем, переданный нам из режима пользователя описатель объекта "событие".

В дальнейшем мы будем оперировать указателем на объект, а не описателем, как и полагается драйверам. Поэтому мы должны этот указатель получить и заодно проверить, действительно ли переданное нам число является описателем объекта "событие". Проверка любых данных полученных из режима пользователя - это обязательное условие для стабильной работы любого драйвера. Обе эти задачи, в данном случае, можно решить одним вызовом функции ObReferenceObjectByHandle. Для этого нам потребуется указатель на структуру OBJECT_TYPE, описывающую объект "тип" (object type) (в данном случае он типизирует объекты "событие"). Чтобы понять, что это такое сделаем небольшое лирическое отступление в сторону мира объектов Windows (более подробно см. книгу Свена Шрайбера "Недокументированные возможности Windows 2000").



14.9 Объект "тип"

Итак, что такое объект "тип"? Этот объект описывает общие характеристики для всех объектов какого-то типа. Всего в Windows 2000 существует 27 типов объектов (в xp и w2k3 их уже 29). Запустив WinObjEx (см. в разделе "ИНСТРУМЕНТЫ > Уголок NT+" wasm.ru) и раскрыв каталог \ObjectTypes мы увидим следующую картину:

Рис. 14-1. Объекты "тип" в пространстве имен диспетчера объектов

Это типы объектов, существующие в системе. Щелкнув на любом из них можно получить кое-какую дополнительную информацию. Каждому такому объекту соответствует структура OBJECT_TYPE.

Код (Text):
  1.  
  2.  
  3.  OBJECT_TYPE_INITIALIZER STRUCT                       ; sizeof = 04Ch
  4.       _Length                     WORD        ?       ; 0000h
  5.       UseDefaultObject            BYTE        ?       ; 0002h
  6.       Reserved                    BYTE        ?       ; 0003h
  7.       InvalidAttributes           DWORD       ?       ; 0004h
  8.       GenericMapping              GENERIC_MAPPING   ; 0008h
  9.       ValidAccessMask             DWORD       ?       ; 0018h
  10.       SecurityRequired            BYTE        ?       ; 001Ch
  11.       MaintainHandleCount         BYTE        ?       ; 001Dh
  12.       MaintainTypeList            BYTE        ?       ; 001Eh
  13.                                   db 1 dup(?)         ; padding
  14.       PoolType                    SDWORD      ?       ; 0020h
  15.       DefaultPagedPoolCharge      DWORD       ?       ; 0024h
  16.       DefaultNonPagedPoolCharge   DWORD       ?       ; 0028h
  17.       . . .
  18.   OBJECT_TYPE_INITIALIZER ENDS
  19.  
  20.   OBJECT_TYPE STRUCT                                  ; sizeof = 0B0h
  21.       Mutex                       ERESOURCE         ; 0000h
  22.       TypeList                    LIST_ENTRY        ; 0038h
  23.       _Name                       UNICODE_STRING    ; 0040h
  24.       DefaultObject               PVOID           ?   ; 0048h
  25.       Index                       DWORD           ?   ; 004Ch
  26.       TotalNumberOfObjects        DWORD           ?   ; 0050h
  27.       TotalNumberOfHandles        DWORD           ?   ; 0054h
  28.       HighWaterNumberOfObjects    DWORD           ?   ; 0058h
  29.       HighWaterNumberOfHandles    DWORD           ?   ; 005Ch
  30.       TypeInfo                    OBJECT_TYPE_INITIALIZER   ; 0060h
  31.       Key                         DWORD           ?   ; 00ACh
  32.   OBJECT_TYPE ENDS
  33.  
  34.  

Когда системе требуется создать новый объект, она обращается к структуре, соответствующей этому типу объектов. Адреса структур некоторых объектов "тип" экспортируются. Например, для объектов типа WindowStation адрес структуры OBJECT_TYPE, описывающей все объекты этого типа, хранится в экспортируемой переменной ядра ExWindowStationObjectType, а для объектов Event - ExEventObjectType.

Код (Text):
  1.  
  2.  
  3.                  pushad
  4.                  mov ecx, ExEventObjectType
  5.                  mov ecx, [ecx]
  6.                  mov ecx, [ecx]
  7.                  invoke DbgPrint, $CTA0("\nExEventObjectType: %08X\n"), ecx
  8.                  popad
  9.  
  10.  

Добавив в драйвер вышеприведенный код, можно получить адрес структуры OBJECT_TYPE для объектов "событие". Для меня этот адрес оказался равным 8188C200h. Зная, что на машинах с объемом оперативной памяти 128 и более (для xp 256 и более, хотя это не точные данные) мегабайт ядро отображается в большие (по 4Мб) страницы, и зная, что на виртуальные адреса 80000000h - 9FFFFFFFh отображается физическая память по адресам 00000000h - 01FFFFFFFh мы можем воспользоваться услугами PhysMemBrowser (входит в состав KmdKit). Я довольно часто им пользуюсь, т.к. иногда это значительно быстрее и удобнее чем использовать отладчик.

Итак, на виртуальный адрес 8188C200h отображен физический адрес 0188C200h. Вводим это значение в PhysMemBrowser и получаем дамп структуры OBJECT_TYPE для объектов типа "событие":

Рис. 14-2. Дамп объекта "тип"

По адресу E1007C48h можно обнаружить строку "Event", т.е. имя объекта "тип". Значения TotalNumberOfObjects (общее кол-во объектов типа "событие" в системе), TotalNumberOfHandles (общее кол-во открытых описателей объектов типа "событие" в системе), HighWaterNumberOfObjects (пиковое кол-во объектов типа "событие", существовавших в системе), HighWaterNumberOfHandles (пиковое кол-во открытых описателей объектов типа "событие" существовавших в системе) равны (выделены) 646h, 67Eh, 66Bh, 6A1h соответственно. Если щелкнуть на объекте "тип" Event в окне WinObjEx, то можно увидеть эти же самые значения, только в десятичной форме.

Рис. 14-3. Свойства объекта "тип"

Имейте в виду то, что количество объектов постоянно меняется. Я просто сделал всё в нужном порядке, поэтому у меня значения точно совпадают.

Pool Type соответствует полю PoolType, Paged Pool Usage - полю DefaultPagedPoolCharge, NonPaged Pool Usage - полю DefaultNonPagedPoolCharge, а Maintain Handle Database соответствует полю MaintainHandleCount.

WinObjEx также покажет в удобной форме содержимое полей InvalidAttributes, GenericMapping и ValidAccessMask. Сравните с дампом и увидите, что все значения совпадают.

Надеюсь, что с объектом "тип" кое-что прояснилось. Вернемся к коду драйвера.



14.10 Продолжаем обрабатывать IOCTL_SET_NOTIFY

Код (Text):
  1.  
  2.  
  3.                  mov ecx, ExEventObjectType
  4.                  mov ecx, [ecx]
  5.                  mov ecx, [ecx]
  6.  
  7.                  invoke ObReferenceObjectByHandle, edx, EVENT_MODIFY_STATE, ecx, \
  8.                                          UserMode, addr g_pkEventObject, NULL
  9.  
  10.  

Вызываем ObReferenceObjectByHandle. Первый параметр - описатель объекта "событие", который мы получили от программы управления. Второй параметр - требуемый тип доступа. Третий - указатель на структуру OBJECT_TYPE для объектов типа "событие". Используя именно этот указатель, система будет проверять, а действительно ли описатель соответствует объекту "событие" и если да, то вернет нам в переменной g_pkEventObject ссылку на этот объект. Четвертый параметр определяет, в какой таблице описателей следует искать объект. Если это KernelMode, то будет просматриваться таблица описателей ядра (подробнее см. часть 11), а это совсем не то, что нам нужно. Если это UserMode, то система будет исследовать таблицу описателей текущего процесса, что нам и нужно. Надеюсь, вы помните, что при обработке IRP_MJ_DEVICE_CONTROL мы как одноуровневый драйвер находимся в контексте вызывающего процесса. Последний параметр функции ObReferenceObjectByHandle - это указатель на структуру OBJECT_HANDLE_INFORMATION. В DDK написано, что нам следует всегда выставлять его в NULL. Ну, вы знаете, как относиться к подобным категоричным заявлениям :smile3: В данном случае, нам не требуется эта информация.

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

Если в таблице описателей текущего процесса есть такой описатель, и он соответствует объекту "событие" и запрашиваемый тип доступа может быть предоставлен, мы получим в переменной g_pkEventObject необходимый нам указатель на объект.

Код (Text):
  1.  
  2.  
  3.                      invoke PsSetCreateProcessNotifyRoutine, ProcessNotifyRoutine, FALSE
  4.                      mov [esi].IoStatus.Status, eax
  5.  
  6.  

Вызов функции PsSetCreateProcessNotifyRoutine заставляет систему поместить/удалить адрес нашей процедуры ProcessNotifyRoutine в/из список процедур, которые она (система) будет вызывать при создании или удалении процесса. К сожалению, этот список ограничивается восьмью членами. Если второй параметр FALSE, процедура добавляется в список, если TRUE - удаляется из списка. В DDK написано, что если драйвер успешно зарегистрировал свою процедуру, он должен оставаться в памяти до выключения системы. Для аналогичных функций PsSetCreateThreadNotifyRoutine и PsSetLoadImageNotifyRoutine это действительно так (у них вообще нет параметра Remove), но для PsSetCreateProcessNotifyRoutine нет. После снятия зарегистрированной процедуры, драйвер может быть выгружен.

Код (Text):
  1.  
  2.  
  3.                      .if eax == STATUS_SUCCESS
  4.  
  5.                          mov g_fbNotifyRoutineSet, TRUE
  6.  
  7.  

Если мы удачно зарегистрировались, выставляем глобальный флаг.

Код (Text):
  1.  
  2.  
  3.                          mov eax, pDeviceObject
  4.                          mov eax, (DEVICE_OBJECT PTR [eax]).DriverObject
  5.                          and (DRIVER_OBJECT PTR [eax]).DriverUnload, NULL
  6.  
  7.  

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

Итак, всё прошло успешно и мы ждем момента, когда количество процессов в системе изменится. Как только это произойдет, будет вызвана наша ProcessNotifyRoutine.



14.11 Процедура ProcessNotifyRoutine

Код (Text):
  1.  
  2.  
  3.      push eax
  4.      invoke PsLookupProcessByProcessId, dwProcessId, esp
  5.      pop peProcess
  6.      .if eax == STATUS_SUCCESS
  7.          mov fbDereference, TRUE
  8.      .else
  9.          invoke IoGetCurrentProcess
  10.          mov peProcess, eax
  11.          and fbDereference, FALSE
  12.      .endif
  13.  
  14.  

В параметре dwProcessId система передает нам идентификатор создаваемого/удаляемого процесса, а нам нужен указатель на объект "процесс". Для начала вызываем PsLookupProcessByProcessId, и если она вернет ошибку - IoGetCurrentProcess. Дело в том, что в Windows 2000 PsLookupProcessByProcessId не работает, если вызвана в контексте процесса, идентификатор которого ей передан (возможно, это так только на этапе удаления процесса - я этого не проверял). Это будет происходить при удалении процесса. При создании мы окажемся в контексте процесса-родителя. Т.о. тем или иным способом мы всё же получаем указатель на интересующий нас объект. Поскольку PsLookupProcessByProcessId увеличивает счетчик ссылок на объект, а IoGetCurrentProcess нет, используем флаг fbDereference.

Код (Text):
  1.  
  2.  
  3.      mov eax, dwProcessId
  4.      mov g_ProcessData.dwProcessId, eax
  5.  
  6.      mov eax, bCreate
  7.      mov g_ProcessData.bCreate, eax
  8.  
  9.  

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

Код (Text):
  1.  
  2.  
  3.      invoke memset, addr g_ProcessData.szProcessPath, 0, IMAGE_FILE_PATH_LEN
  4.  
  5.  

Готовим место для пути к образу.

Код (Text):
  1.  
  2.  
  3.      invoke GetImageFilePath, peProcess, addr us
  4.      .if eax == STATUS_SUCCESS
  5.  
  6.          lea eax, g_ProcessData.szProcessPath
  7.          mov as.Buffer,          eax
  8.          mov as.MaximumLength,   IMAGE_FILE_PATH_LEN
  9.          and as._Length,         0
  10.  
  11.          invoke RtlUnicodeStringToAnsiString, addr as, addr us, FALSE
  12.  
  13.          invoke ExFreePool, us.Buffer
  14.  
  15.  

Удачный вызов GetImageFilePath вернет нам в us.Buffer unicode-строку с полным путем к образу создавшему процесс. Преобразуем её в ansi-строку и запишем по адресу g_ProcessData.szProcessPath одним вызовом функции RtlUnicodeStringToAnsiString. Т.к. GetImageFilePath сама выделяет буфер, то после удачного её вызова этот буфер необходимо освободить.

Код (Text):
  1.  
  2.  
  3.      .else
  4.  
  5.          mov eax, g_dwImageFileNameOffset
  6.          .if eax != 0
  7.              add eax, peProcess
  8.              invoke memcpy, addr g_ProcessData.szProcessPath, eax, 16
  9.          .endif
  10.  
  11.      .endif
  12.  
  13.  

Если нам не удается получить полный путь, то будем довольствоваться только именем процесса, извлекаемым из структуры EPROCESS. Напоминаю о том, что если длина этого имени превышает 16 символов (ansi), то оно усекается и, в этом случае, не завершается нулем. Поэтому копируем ровно 16 байт.

Код (Text):
  1.  
  2.  
  3.      .if fbDereference
  4.          fastcall ObfDereferenceObject, peProcess
  5.      .endif
  6.  
  7.  

Если флаг fbDereference установлен, уменьшаем счетчик ссылок на объект.

Код (Text):
  1.  
  2.  
  3.      invoke KeSetEvent, g_pkEventObject, 0, FALSE
  4.  
  5.  

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



14.12 Обрабатываем IOCTL_REMOVE_NOTIFY

Тут всё то же самое, что мы делали при обработке IOCTL_SET_NOTIFY, но наоборот.

Код (Text):
  1.  
  2.  
  3.             invoke PsSetCreateProcessNotifyRoutine, ProcessNotifyRoutine, TRUE
  4.             mov [esi].IoStatus.Status, eax
  5.  
  6.  

Уведомления системы о создании/удалении процессов нам больше не нужны. Как я уже сказал, DDK нас обманывает, и снять уведомление всё же можно.

Код (Text):
  1.  
  2.  
  3.                 or liDelayTime.HighPart, -1
  4.                 mov liDelayTime.LowPart, -1000000
  5.    
  6.                 invoke KeDelayExecutionThread, KernelMode, FALSE, addr liDelayTime
  7.  
  8.  

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

Код (Text):
  1.  
  2.  
  3.                 mov eax, pDeviceObject
  4.                 mov eax, (DEVICE_OBJECT PTR [eax]).DriverObject
  5.                 mov (DRIVER_OBJECT PTR [eax]).DriverUnload, offset DriverUnload
  6.  
  7.                 .if g_pkEventObject != NULL
  8.                     invoke ObDereferenceObject, g_pkEventObject
  9.                     and g_pkEventObject, NULL
  10.                 .endif
  11.  
  12.  

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

Таким образом, объект "событие" используется драйвером для взаимодействия с программой управления. Всё, что мы обсудим ниже, носит факультативный характер и прямого отношения к теме статьи не имеет.



14.13 Ищем полный путь

Хорошо, что в структуре EPROCESS присутствует имя процесса. Достать его оттуда не представляет большого труда, но было бы неплохо получить полный путь к образу файла, создавшему процесс. Сделать это гораздо сложнее. Если коротко, и я не открываю здесь Америку, то необходимо пройти по следующему маршруту: SECTION.Segment -> SEGMENT.ControlArea -> CONTROL_AREA.FilePointer -> FILE_OBJECT.FileName -> UNICODE_STRING.Buffer. Некоторые взаимосвязи этих структур показаны на рисунке 14-4.

Рис. 14-4. Некоторые взаимосвязи структур SECTION, SEGMENT, CONTROL_AREA,FILE_OBJECT и SECTION_OBJECT_POINTERS

Некоторые из этих структур документированы, некоторые нет. Но все они существуют без изменений (по крайней мере, интересующие нас поля) в Windows 2000/XP/2003. И это очень хорошо, т.к. код будет единым (почти).

Для начала нам нужен указатель на структуру SECTION, описывающую объект "секция". На эту секцию отображается исполняемый файл процесса. Будем называть этот объект базовой секцией процесса. Это самый первый объект, создаваемый при запуске процесса. В Windows 2000 (и NT4, наверное, тоже) описатель этого объекта помещается в EPROCESS.SectionHandle и его значение всегда равно 4 (с одним исключением в SP4, о чем мы поговорим позже). Мы даже не будем его там искать. На этом факте, кстати, основан хорошо известный трюк, придуманный Гари Неббеттом, когда exe-модуль может сам себя удалить с диска, вызвав в нужной последовательность несколько самых обычных API, ключевой из которых является CloseHandle(4). Как вы, наверное, догадываетесь, в Windows XP этот фокус уже не проходит, ибо описателя за номером 4 теперь нет, а значит добраться до базовой секции процесса из режима пользователя невозможно. Взамен в структуре EPROCESS появилось поле SectionObject хранящее указатель на базовую секцию процесса. В Windows Server 2003 эта традиция продолжена, но смещение поля SectionObject естественно изменилось. Нам нужно будет его найти.

Имея указатель на базовую секцию процесса, и пройдя по связям изображенным на рис. 14-4, мы можем добраться до полного пути к модулю, создавшему процесс. Для начала я проделал это путем с помощью утилиты NTObjects ( http://www.smidgeonsoft.com/ ) рис. 14-5.

Рис. 14-5. Дампы структур SECTION, SEGMENT, CONTROL_AREA,FILE_OBJECT и SECTION_OBJECT_POINTERS

NTObjects позволяет в относительно удобной форме просмотреть все объекты, описатели которых попадают в таблицу описателей процесса. Поэтому воспользоваться её услугами для просмотра базовой секции процесса в Windows XP и выше не удастся и придется использовать какой-нибудь другой инструмент, например, SoftICE.



14.14 Процедура GetImageFilePath

Весь код, отвечающий за получение полного пути, помещен в отдельный файл ProcPath.asm. Я не настаиваю на том, что предлагаемый способ единственно возможный. В данном конкретном случае, можно было бы ограничится двумя жестко зашитыми смещениями (для XP и 2003). Но возможно, вам приходилось встречать неплохие инструменты, работающие в одной версии системы и не работающие в другой. Это результат порочной практики использования фиксированных смещений, адресов функций и т.п. Если есть малейшая возможность динамически найти какое-либо недокументированное поле структуры или не экспортируемую функцию, почему бы ни попробовать это сделать.

Код (Text):
  1.  
  2.  
  3.      PROCESS_QUERY_INFORMATION equ 400h
  4.  
  5.      mov ecx, PsProcessType
  6.      mov ecx, [ecx]
  7.      mov ecx, [ecx]
  8.  
  9.      invoke ObReferenceObjectByPointer, peProcess, PROCESS_QUERY_INFORMATION, ecx, UserMode
  10.      .if eax == STATUS_SUCCESS
  11.  
  12.  

Проверим, является ли адрес, переданный нам в переменной peProcess, указателем на объект "процесс". Т.к. мы собираемся "копаться" в самых недрах системы без её разрешения, придется двигаться с максимальной осторожностью.

Реальное поведение функции ObReferenceObjectByPointer весьма значительно отличается от того, что можно найти в DDK.

Во-первых, DDK заявляет, что второй параметр должен содержать запрашиваемые права доступа к объекту. Это не так. ObReferenceObjectByPointer вообще игнорирует этот параметр и в нем можно передать что угодно. Но мы всё равно будем передавать необходимую маску доступа, т.к. поведение функции ObReferenceObjectByPointer может измениться в будущем.

Во-вторых, DDK заявляет, что третий параметр может быть указателем только на два объекта "тип", соответствующие объектам "файл" и "событие". Эти указатели можно извлечь из экспортируемых переменных IoFileObjectType и ExEventObjectType, соответственно. Это тоже не так и передавать можно указатель на любой объект "тип". И это просто замечательно, т.к. позволит нам проверять случайные указатели "на вшивость". Из 27-ми типов объектов в Windows 2000 и 29-ти в Windows XP/2003 экспортируется около 15 указателей на объекты "тип". Среди экспортируемых имеются все нужные нам, а именно: IoDeviceObjectType, PsProcessType, MmSectionObjectType, IoFileObjectType. И это тоже здорово.

В-третьих, что-то совершенно невнятное сказано про последний параметр. Мне, лично, его присутствие вообще непонятно. Обычно при обращении к объектам режим доступа имеет смысл указывать при переходе от режима пользователя к режиму ядра. Т.е. при преобразовании описателя в указатель. Например, в функции ObReferenceObjectByHandle параметр AccessMode вполне логичен. Но если у нас уже есть указатель, то зачем указывать права доступа? А воспользоваться указателем в режиме пользователя всё равно невозможно (Во всяком случае, документированными средствами. Кстати, используя Physical Memory Browser мы обратились к объекту прямо из режима пользователя). Если вы не поленитесь и загляните в дизассемблировнный листинг функции ObReferenceObjectByPointer, то убедитесь в весьма странном её поведении, в случае если параметр AccessMode = KernelMode, а именно… Если в параметре Object передать указатель на любой кусок валидной памяти, то функция, ничего не проверяя, посчитает, что это валидный указатель на тело объекта. Вычтет из этого указателя размер структуры OBJECT_HEADER, т.е. получит указатель на заголовок объекта и увеличит на единицу поле PointerCount. Т.е. функция считает, что если AccessMode = KernelMode, то уже имеющийся у нас указатель на объект был получен где-то ранее и валиден. Т.о. если AccessMode = KernelMode, передавать случайный указатель ни в коем случае нельзя. А вот если AccessMode = UserMode, функция таки проверит, соответствует ли тип объекта заявленному. Это обстоятельство и позволит нам проверять случайные указатели. В любом случае всё вышесказанное, мягко говоря, немного отличается от того, что написано в DDK.

При хорошем знании структур и принципов организации объектов можно без особого труда написать свою функцию проверки. А пока воспользуемся услугами ObReferenceObjectByPointer.

Код (Text):
  1.  
  2.  
  3.          .if g_dwWinVer == WINVER_UNINITIALIZED
  4.          
  5.              invoke IoIsWdmVersionAvailable, 1, 20h
  6.              .if al
  7.                  mov g_dwWinVer, WINVER_XP_OR_HIGHER
  8.              .else
  9.                  mov g_dwWinVer, WINVER_2K
  10.              .endif
  11.  
  12.          .endif
  13.  
  14.  

Смотрим, в какой версии системы мы находимся. Чтобы не делать это каждый раз, используем переменную g_dwWinVer.

Код (Text):
  1.  
  2.  
  3.          .if g_dwWinVer == WINVER_XP_OR_HIGHER
  4.  
  5.              mov esi, peProcess
  6.              mov ebx, 80h
  7.              .while ebx < 204h
  8.  
  9.  

Если мы оказались в XP и выше, будем искать поле SectionObject в структуре EPROCESS в пределах 80h - 200h (надеюсь, что этого диапазона будет достаточно для всех обновлений).

Код (Text):
  1.  
  2.  
  3.                  mov edi, [esi][ebx]
  4.                  invoke IsLikeObjectPointer, edi
  5.                  .if eax == TRUE
  6.  
  7.  

Процедура IsLikeObjectPointer отсеивает "мусор". Если она вернула TRUE, то вполне вероятно, что в регистре edi содержится указатель на объект.

Код (Text):
  1.  
  2.  
  3.                      mov eax, edi
  4.                      sub eax, sizeof OBJECT_HEADER
  5.  
  6.                      .if ([OBJECT_HEADER PTR [eax]].PointerCount <= 4)
  7.                      .if ([OBJECT_HEADER PTR [eax]].HandleCount <= 1)
  8.  
  9.  

Если мы на стадии создания процесса, то счетчик ссылок базовой секции будет равен 3, а счетчик описателей 1. Если процесс удаляется, то 2 и 0, соответственно. Эти проверки позволят ещё сильнее ограничить число возможных кандидатов на объект "секция". Я накинул единичку к счетчику - на всякий случай. Как я уже говорил, при хорошем уровне знаний можно написать процедуру, практически однозначно идентифицирующую объект, но дабы не усложнять и без того непростой код мы идем легкими путями. Для того чтобы окончательно убедиться в том, что регистр edi содержит указатель на объект "секция", используем ObReferenceObjectByPointer.

Код (Text):
  1.  
  2.  
  3.                          mov ecx, MmSectionObjectType
  4.                          mov ecx, [ecx]
  5.                          mov ecx, [ecx]
  6.  
  7.                          invoke ObReferenceObjectByPointer, edi, SECTION_QUERY, ecx, UserMode
  8.                          .if eax == STATUS_SUCCESS
  9.  
  10.  

Здесь мы уже уверены, что нашли то, что искали.

Код (Text):
  1.  
  2.  
  3.                              mov status, eax
  4.                              mov pSection, edi
  5.  
  6.                              .break
  7.                          .endif
  8.                      .endif
  9.                      .endif
  10.                  .endif
  11.  
  12.                  add ebx, 4
  13.  
  14.              .endw
  15.  
  16.  

Если поле SectionObject всё ещё не найдено, продолжаем поиск. Указатели в структурах всегда выровнены по двойному слову. Поэтому двигаемся DWORD'ами.

Код (Text):
  1.  
  2.  
  3.          .else
  4.  
  5.  

Если мы в Windows 2000, то тут тоже не всё так просто, как хотелось бы. Точнее, сложности начинаются с сервис пака №4. До этого злосчастного SP4 всё просто. Достаточно "натравить" функцию ObReferenceObjectByHandle на цифру 4 и получить указатель на базовую секцию процесса. Но на SP4, в случае если процесс запускается из командной строки или bat-файлом, описатель базовой секции процесса, имеет значение 03E8h. Причем оно также фиксировано. По крайней мере, на моей машине и на ещё 5 машинах наших уважаемых коллег, любезно согласившихся протестировать этот пример (nerst, Noble Ghost, hGoblin, mokc0der, Vladimir), значение было именно таким. Откуда берется такое странное значение?

Описатель объекта представляет собой индекс в таблице описателей процесса. Таблица описателей реализована по трехуровневой схеме аналогично реализации механизма трансляции адресов в x86 системах. Младшие 24 бита описателя интерпретируются как три 8-битных индекса для каждого уровня. Первые два уровня состоят из массивов по 256 элементов, которые содержат указатели на массив следующего уровня. Массив самого нижнего уровня - это таблица вторичных описателей и содержит собственно элементы таблицы описателей, каждый из которых имеет размер в восемь байт (указатель на заголовок объекта и флаги). Самый последний элемент (256-ой) каждой незаполненной таблицы вторичных описателей инициализируется значением -1. Поскольку указатель имеет размер в четыре байта, то и описатели кратны четырем. Описатель со значением 0 (1-ый элемент в первой таблице вторичных описателей) не используется. Т.о. самый первый объект в процессе получит описатель 4 (2-ой элемент в первой таблице вторичных описателей), второй - 8 (3-ий элемент в первой таблице вторичных описателей) и т.д. до 3F8h (255-ый элемент в первой таблице вторичных описателей). Дойдя до описателя 3FCh (256-ой и последний элемент в первой таблице вторичных описателей) диспетчер объектов увидит -1, выделит при необходимости вторую таблицу вторичных описателей и заполнит 256-ой элемент первой (заместив -1). Последующие описатели (400h - 800h) будут попадать во вторую таблицу вторичных описателей и т.д.

Но на системах W2K+SP4 при запуске процесса из командной строки первая таблица вторичных описателей заполняется сверху вниз: 3F8h - 4. Затем диспетчеру объектов приходится таки заполнить последний элемент в первой таблице вторичных описателей 3FCh. И дальше всё продолжается как обычно: 400h - и дальше по возрастающей. Интереса ради, я посмотрел как это происходит на Windows XP: 7FCh - 4, 804h - и дальше по возрастающей. Куда подевался описатель 800h я не знаю.

Я прекрасно понимаю, что если вы не знакомы с организацией таблицы описателей, то вряд ли что-то поняли из этого объяснения. Читайте раздел "Описатели объектов и таблица описателей, принадлежащая процессу" в книге "Внутреннее устройство Microsoft Windows 2000", берите в руки SoftICE используйте эти структуры и многое прояснится. Смещение поля ObjectTable равно 0128h, 00C4h и 00C4h для Windows 2000, XP и Server 2003 соответственно. Поле Table хранит указатель на трехуровневую таблицу.

Код (Text):
  1.  
  2.  
  3.  EPROCESS STRUCT
  4.  . . .
  5.      ObjectTable    PVOID  ?   ; PTR HANDLE_TABLE
  6.  . . .
  7.  EPROCESS ENDS
  8.  
  9.  HANDLE_TABLE STRUCT
  10.      Flags         DWORD   ?
  11.      HandleCount   DWORD   ?
  12.      Table         PVOID   ?   ; PTR PTR PTR HANDLE_TABLE_ENTRY
  13.  . . .
  14.  HANDLE_TABLE ENDS
  15.  
  16.  

Ответ на вопрос, почему диспетчер объектов поступает именно так как описано выше, я оставляю за вами.

Код (Text):
  1.  
  2.  
  3.              xor ebx, ebx
  4.              mov edi, 4
  5.              .while ebx < 3
  6.  
  7.  

Делаем три попытки получить указатель на базовую секцию процесса. Сначала попробуем описатель со значением 4.

Код (Text):
  1.  
  2.  
  3.                  invoke IoGetCurrentProcess
  4.                  .if eax == peProcess
  5.                      
  6.                      mov ecx, MmSectionObjectType
  7.                      mov ecx, [ecx]
  8.                      mov ecx, [ecx]
  9.                  
  10.                      invoke ObReferenceObjectByHandle, edi, SECTION_QUERY, ecx, KernelMode, addr pSection, NULL
  11.                      mov status, eax
  12.  
  13.                  .else
  14.  
  15.                      invoke KeAttachProcess, peProcess
  16.  
  17.                      mov ecx, MmSectionObjectType
  18.                      mov ecx, [ecx]
  19.                      mov ecx, [ecx]
  20.  
  21.                      invoke ObReferenceObjectByHandle, edi, SECTION_QUERY, ecx, KernelMode, addr pSection, NULL
  22.                      mov status, eax
  23.  
  24.                      invoke KeDetachProcess
  25.  
  26.                  .endif
  27.  
  28.  

Если мы не в контексте создаваемого/удаляемого процесса, переключаемся на него вызовом KeAttachProcess.

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

Если попытка удалась, выходим из цикла.

Код (Text):
  1.  
  2.  
  3.                  .if ebx == 0
  4.                      
  5.                      mov edi, 03F8h  ; Try 03F8h handle.
  6.  
  7.  

Если нет - попробуем то же самое со значением 03F8h.

Код (Text):
  1.  
  2.  
  3.                  .elseif ebx == 1
  4.                      
  5.                      mov eax, peProcess
  6.                      add eax, 01ACh
  7.                      mov eax, [eax]
  8.                      mov edi, eax
  9.  
  10.  

Последняя попытка. Ничего другого не остается, как взять значение описателя прямо из поля EPROCESS.SectionHandle.

Код (Text):
  1.  
  2.  
  3.                      and eax, (4 - 1)
  4.                      .break .if ( eax != 0 ) || ( edi >= 800h )
  5.  
  6.  

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

Код (Text):
  1.  
  2.  
  3.                  .endif
  4.                  
  5.                  inc ebx
  6.              .endw
  7.  
  8.          .endif
  9.  
  10.          .if status == STATUS_SUCCESS
  11.  
  12.              mov status, STATUS_UNSUCCESSFUL
  13.  
  14.              mov ebx, pSection
  15.              mov ebx, (SECTION PTR [ebx])._Segment
  16.  
  17.              invoke IsAddressInPoolRanges, ebx
  18.              push eax
  19.              invoke MmIsAddressValid, ebx
  20.              pop ecx
  21.              .if al && ( ecx == TRUE )
  22.  
  23.                  mov esi, ebx
  24.  
  25.                  mov ebx, (_SEGMENT PTR [ebx]).ControlArea
  26.  
  27.                  invoke IsAddressInPoolRanges, ebx
  28.                  push eax
  29.                  invoke MmIsAddressValid, ebx
  30.                  pop ecx
  31.                  .if al && ( ecx == TRUE ) && ([CONTROL_AREA PTR [ebx]]._Segment == esi )
  32.  
  33.                      mov ebx, (CONTROL_AREA PTR [ebx]).FilePointer
  34.  
  35.                      invoke IsLikeObjectPointer, ebx
  36.                      .if eax == TRUE
  37.  
  38.                          mov ecx, IoFileObjectType
  39.                          mov ecx, [ecx]
  40.                          mov ecx, [ecx]          ; PTR OBJECT_TYPE
  41.  
  42.                          invoke ObReferenceObjectByPointer, ebx, FILE_READ_ATTRIBUTES, ecx, UserMode
  43.  
  44.  

Если мы поимели указатель на базовую секцию процесса, то по схеме на рис. 14-4 пытаемся получить указатель на соответствующий файловый объект. Здесь должно быть всё понятно и без пояснений. О функции IsAddressInPoolRanges чуть позже.

Код (Text):
  1.  
  2.  
  3.                          .if eax == STATUS_SUCCESS
  4.  
  5.                              invoke ExAllocatePool, PagedPool, (IMAGE_FILE_PATH_LEN+1) * sizeof WCHAR
  6.                              .if eax != NULL
  7.  
  8.                                  mov edi, pusImageFilePath
  9.                                  assume edi:ptr UNICODE_STRING
  10.  
  11.                                  mov [edi].Buffer, eax
  12.  
  13.                                  invoke memset, eax, 0, (IMAGE_FILE_PATH_LEN+1) * sizeof WCHAR
  14.  
  15.                                  mov [edi].MaximumLength, IMAGE_FILE_PATH_LEN * sizeof WCHAR
  16.                                  and [edi]._Length, 0
  17.  
  18.                                  invoke RtlVolumeDeviceToDosName, \
  19.                                          (FILE_OBJECT PTR [ebx]).DeviceObject, addr usDosName
  20.  
  21.  

Поле DeviceObject структуры FILE_OBJECT хранит указатель на объект "устройство", которому принадлежит файл. Из объекта "устройство" можно извлечь его имя. Но тогда мы получим путь к файлу относительно объекта "устройство". Например, "\Device\HarddiskVolume1\WINNT\system32\notepad.exe". С помощью функции RtlVolumeDeviceToDosName преобразуем его в более привычный "C:\ WINNT\system32\notepad.exe". DDK говорит, что начиная с XP мы должны использовать IoVolumeDeviceToDosName. Это не обязательно, т.к. для обратной совместимости обе функции имеют одну и ту же точку входа.

Код (Text):
  1.  
  2.  
  3.                                  .if eax == STATUS_SUCCESS
  4.  
  5.                                      invoke RtlCopyUnicodeString, edi, addr usDosName
  6.                                      invoke ExFreePool, usDosName.Buffer
  7.  
  8.                                  .endif
  9.  
  10.                                  invoke RtlAppendUnicodeStringToString, edi, \
  11.                                                  addr (FILE_OBJECT PTR [ebx]).FileName
  12.  
  13.                                  assume edi:nothing
  14.  
  15.                                  mov status, STATUS_SUCCESS
  16.  
  17.  

Составляем полный путь к образу. DeviceToDosName сама выделяет буфер для строки, поэтому, не забываем его освободить.

Теперь разберем остатки. "Разберем" - сильно сказано. Я уже довольно давно пишу эту статью, и желание закончить её поскорей растет во мне с каждой новой строчкой :smile3: Надеюсь, что вы в состоянии понять, что делают оставшиеся не разобранными функции. Я лишь скажу о каждой несколько слов. Также в исходном коде имеется достаточное количество комментариев.



14.15 Процедура IsAddressInPoolRanges

Процедура IsAddressInPoolRanges проста и проверяет, находится ли переданный ей адрес в границах системных пулов. Адреса начала и конца пулов хорошо известны. Подробности можно почитать в книге "Внутреннее устройство Microsoft Windows 2000". Добавлю только то, что объекты не являющиеся объектами синхронизации (каковым объект секция и является), помещаются в подкачиваемый пул. Процедура IsAddressInPoolRanges же проверяет как подкачиваемый, так и неподкачиваемый пулы. IsAddressInPoolRanges предполагает, что выполняется в системе с 2Гб системным адресным пространством. Для систем с поддержкой PAE (Physical Address Extension) и систем запущенных с ключом /3GB в boot.ini придется её несколько видоизменить.



14.16 Процедура IsLikeObjectPointer

Эта процедура делает обоснованное заключение о том, может ли переданный ей адрес являться указателем на объект. Обратите внимание: "может являться", но не "является". Это ещё надо проверить вызовом функции ObReferenceObjectByPointer. Адрес может являться указателем на объект, если…

  • находится в границах системных пулов;
  • кратен как минимум 8-ми байтам (подробнее см. комментарий в исходнике);
  • является действительным (память по этому адресу выделена);
  • <адрес - 18h>, т.е. заголовок предполагаемого объекта также находится в действительной памяти.

Рис. 14-6. Результат работы программы ProcessMon. Установка сервис паков, оказывается, довольно жадна до процессов.

Т.к. в примере используются недокументированные трюки, то, во-первых, я не гарантирую его стабильную работу, а во-вторых, мне интересно знать о любых проявлениях нестабильности. Поэтому, большая просьба сообщить о любых обнаруженных багах.

Исходный код драйвера в архиве.

© Four-F

0 1.421
archive

archive
New Member

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