Сам себе Руссинович: перезагрузка

от автора


ctypes Среди утилит SysInternals есть те, что не обновляются годами, а их повседневное использование сомнительно. И все же назвать их абсолютно бесполезными язык не поворачивается. Внутреннее устройство таких утилит довольно просто, разбирать которое на Python весьма занимательно; не то, чтобы разбирать, скорее писать аналоги, не используя при этом сторонних расширений.

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

Первое препятствие на пути — с чего начать? Понятно, что с головой в омут бросаться не стоит и начинать лучше с самого простого, а еще прежде нужно позаботиться о внештатных ситуациях, способных возникнуть во время работы сценария Python. Под определения API’шных функций и иже с ними выделим отдельный модуль с незатейливым названием russinovich.py, в котором напишем следующее:

from ctypes import (    byref, c_ulong, c_void_p, c_wchar_p, windll )  FORMAT_MESSAGE_ALLOCATE_BUFFER = 0x00000100 FORMAT_MESSAGE_IGNORE_INSERTS  = 0x00000200 FORMAT_MESSAGE_FROM_SYSTEM     = 0x00001000 LANG_NEUTRAL                   = 0x00000000 SUBLANG_DEFAULT                = 0x00000001  FormatMessage         = windll.kernel32.FormatMessageW GetLastError          = windll.kernel32.GetLastError LocalFree             = windll.kernel32.LocalFree RtlNtStatusToDosError = windll.ntdll.RtlNtStatusToDosError  def printerror(err):    def MAKELANGID(p, s):       return c_ulong((s << 10) | p)    msg = c_void_p()    err = RtlNtStatusToDosError(err) if err != 0 else GetLastError()    FormatMessage(       FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_IGNORE_INSERTS | FORMAT_MESSAGE_FROM_SYSTEM,       None,       err,       MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),       byref(msg),       0,       None    )    print(c_wchar_p(msg.value).value.strip())    LocalFree(msg) 

Эдакая функция по Фрейду Рихтеру, правда здесь мы пошли на одну хитрость: ненулевое значение переданное функции printerror будет трактоваться как NTSTATUS, в противном случае обрабатывается значение возвращаемое GetLastError.

Так как clockres — самое простое, что есть в SysInternals Suite, с него и начнем. Добавим в russinovich.py определение функции NtQueryTimerResolution:

... NtQueryTimerResolution = windll.ntdll.NtQueryTimerResolution ... 

Создаем файл clockres.py:

from russinovich import printerror, NtQueryTimerResolution from ctypes      import byref, c_ulong from sys         import exit  if __name__ == '__main__':    _max, _min, _cur = c_ulong(), c_ulong(), c_ulong()     ntstatus = NtQueryTimerResolution(byref(_max), byref(_min), byref(_cur))    if ntstatus != 0:       printerror(ntstatus)       exit(1)    print('Maximum timer interval: %.3f ms' % (_max.value / 10000))    print('Minimum timer interval: %.3f ms' % (_min.value / 10000))    print('Current timer interval: %.3f ms' % (_cur.value / 10000)) 

А откуда, собственно, взята NtQueryTimerResolution? Естественным путем:

>>> from os.path import abspath >>> with open(abspath('clockres.exe'), 'rb') as f: ...     raw = str(f.read(), 'utf7', 'replace') ... >>> from re import compile, findall >>> for i in compile('[\x20-\x7E]{13,}').findall(raw): print(i) ... !This program cannot be run in DOS mode. tM<it-<ot)<ut%<xt!<Xt <dty<itu<otq<utm<xti<Xte Current timer interval: %.03f ms Minimum timer interval: %.03f ms Maximum timer interval: %.03f ms NtQueryTimerResolution ... >>> 

Можно и дизассемблером — кому как хочется.

Clockres — слишком просто, стоит попробовать что-то посложнее. Например, pipelist, тем паче, что на официальной странице утилиты Руссинович не стал ходить вокруг да около, а в открытую заявил, мол, использовал NtQueryDirectoryFile, описание которой (правда с приставкой Zw) можно найти либо в MSDN, либо в заголовочном файле ntifs.h:

... #if (NTDDI_VERSION >= NTDDI_WIN2K) __drv_maxIRQL(PASSIVE_LEVEL) NTSYSAPI NTSTATUS NTAPI ZwQueryDirectoryFile(     __in HANDLE FileHandle,     __in_opt HANDLE Event,     __in_opt PIO_APC_ROUTINE ApcRoutine,     __in_opt PVOID ApcContext,     __out PIO_STATUS_BLOCK IoStatusBlock,     __out_bcount(Length) PVOID FileInformation,     __in ULONG Length,     __in FILE_INFORMATION_CLASS FileInformationClass,     __in BOOLEAN ReturnSingleEntry,     __in_opt PUNICODE_STRING FileName,     __in BOOLEAN RestartScan     ); #endif ... 

Объявляем NtQueryDirectoryFile в russinovich.py:

... NtQueryDirectoryFile = windll.ntdll.NtQueryDirectoryFile ... 

Там же объявляем прочие типы, которые нам понадобятся:

from ctypes import (    ..., Structure, Union, c_long, c_longlong, addressof, c_wchar, sizeof )  ... class LARGE_INTEGER_UNION(Structure):    _fields_ = [       ('LowPart',  c_ulong),       ('HighPart', c_ulong),    ]  class LARGE_INTEGER(Union):    _fields_ = [       ('liu1',     LARGE_INTEGER_UNION),       ('liu2',     LARGE_INTEGER_UNION),       ('QuadPart', c_longlong),    ]  class FILE_DIRECTORY_INFORMATION(Structure):    _fields_ = [       ('NextEntryOffset', c_ulong),       ('FileIndex',       c_ulong),       ('CreationTime',    LARGE_INTEGER),       ('LastAccessTime',  LARGE_INTEGER),       ('LastWriteTime',   LARGE_INTEGER),       ('ChangeTime',      LARGE_INTEGER),       ('EndOfFile',       LARGE_INTEGER),       ('AllocationSize',  LARGE_INTEGER),       ('FileAttributes',  c_ulong),       ('FileNameLength',  c_ulong),       ('_FileName',       c_wchar * 1),    ]    @property    def FileName(self):       addr = addressof(self) + type(self)._FileName.offset       name = c_wchar * (self.FileNameLength // sizeof(c_wchar))       return name.from_address(addr).value ... 

Так как поле _FileName в структуре FILE_DIRECTORY_INFORMATION описывает лишь первый символ имени, мы дополнили структуру свойством FileName, извлекающее имя целиком. Пара штрихов к russinovich.py:

... GENERIC_READ             = 0x80000000 FILE_SHARE_READ          = 0x00000001 OPEN_EXISTING            = 0x00000003 INVALID_HANDLE_VALUE     = -1 FileDirectoryInformation = 1 ... CloseHandle = windll.kernel32.CloseHandle CreateFile  = windll.kernel32.CreateFileW ... 

И можно создавать pipelist.py:

from russinovich import (    CloseHandle, CreateFile, NtQueryDirectoryFile, GENERIC_READ,    FILE_SHARE_READ, OPEN_EXISTING, INVALID_HANDLE_VALUE, printerror,    IO_STATUS_BLOCK, FILE_DIRECTORY_INFORMATION, FileDirectoryInformation ) from ctypes      import (    POINTER, addressof, byref, cast, create_string_buffer ) from sys         import exit  def NT_SUCCESS(ntstatus): return True if ntstatus >= 0 else False  if __name__ == '__main__':    pipes = None        try:       isb = IO_STATUS_BLOCK()       dir_inf = cast(create_string_buffer(1024), POINTER(FILE_DIRECTORY_INFORMATION))       query = True              pipes = CreateFile('\\\\.\\pipe\\', GENERIC_READ, FILE_SHARE_READ, None, OPEN_EXISTING, 0, None)              if pipes == INVALID_HANDLE_VALUE:          printerror(0)          exit(1)                 print("%-40s%14s%20s" % ('Pipe Name', 'Instances', 'Max Instances'))       print("%-40s%14s%20s" % ('-' * 9, '-' * 9, '-' * 13))              while(1):          ntstatus = NtQueryDirectoryFile(             pipes,             None,             None,             0,             byref(isb),             dir_inf,             1024,             FileDirectoryInformation,             False,             None,             query          )                    if not NT_SUCCESS(ntstatus): break                    cur_inf = dir_inf          while(1):             cur = cur_inf.contents             print("%-40s%14s%20s" % (cur.FileName, cur.EndOfFile.liu1.LowPart, cur.AllocationSize.liu1.LowPart))             if cur.NextEntryOffset == 0: break             cur_inf = cast(int(addressof(cur)) + cur.NextEntryOffset, POINTER(FILE_DIRECTORY_INFORMATION))          query = False    except Exception as e:       print(e)    finally:       CloseHandle(pipes) 

Вид таблицы максимально подогнан под тот, что выводит pipelist Руссиновича, так что, называется, найдите различия. А если различий нет, какая разница что использовать?

Утилит SysInternals достаточно, так что не исключено, что продолжение все же последует…

ссылка на оригинал статьи http://habrahabr.ru/post/256161/


Комментарии

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *