По слухам некогда исходники утилит 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/
Добавить комментарий