Фундаментальные основы хакерства

       

Неточности, недоговорки


1) "Почти все функции, создающие объекты ярда, принимают указатель на структуру SECURITY_ATTRIBUTES" как аргумент… Большинство приложений вместо этого аргумента передают NULL и создают объект с защитой по умолчанию. Такая защита подразумевает, что администратор и создатель объекта получают к нему полный доступ, а все прочие к объекту не допускаются" стр. 9.

Гм-гм, выходит, если к объекту необходимо допускать всех остальных, как часто и бывает, придется явно инициализировать SECURITY_ATTRIBUTES? Конечно же, нет! По умолчанию допускаются все пользователи со всеми полномочиями – будь то запись, чтение или еще что. Проверьте – создайте новый файл вызовом CreateFile, передав вместо атрибутов секретности NULL, и попытайтесь открыть его, войдя в систему под другим пользователем. Открывается? Вот и славненько!

2) "…если Вы создаете диалоговое окно, какой смысл формировать список одним потоком, а кнопку другим" стр. 53.

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

3) "Как узнать, например, чьим объектом – User или ядра – является данный значок? ...проанализировать Win32 функцию, создающую объект. Практически у всех функций, создающих объекты ядра, есть параметр, позволяющий указать атрибуты защиты". стр. 9

Не очень-то надежный способ! Вот, у функции HINSTANCE LoadLibrary(LPCTSTR lpLibFileName) нет никаких атрибутов секретности, но описатель HINSTANCE принадлежит ядру. Почему? Да хотя бы уже потому, что ядро ее и экспортирует, о чем и рассказывается в SDK. Если под рукой нет SDK, на помощь приходит тот факт, что функция содержится в библиотеке kernel32.lib и, стало быть, – "ядреная".

4) "…по завершении процесса операционная система гарантированно освобождает все ресурсы, принадлежащие эту процессу" стр. 12.

…если только процесс не вызвал исключение, вызывающие его аварийное завершение.
Именно поэтому приходится перегружать машину после очередного "зависания" того же Word-а, – иначе при попытке открытия последнего редактируемого файла, будет выдано сообщение – файл уже открыт другим процессом и работать с ним невозможно. Хороший программист должен предусмотреть такую ситуацию и принять адекватные меры по ее устранению.

5) "Имейте ввиду: описатели

объектов наследуются, но сами объекты нет (курсив Рихтера)" там же

Ух, ты! килограмм не длиннее литра, да еще курсивом! Объекты ядра принадлежат ядру ОС, но не породившему их процессу, которому остается довольствоваться только описателями (дескрипторами) этих объектов. Поэтому о наследовании объектов ядра другими процессами говорить просто некорректно.

6) "Первый и третий параметр функции DuplicateHandle представляют собой описатели объектов ярда, специфичные для вызывающего процесса" стр. 18

Брр… ничего не понял! А вы, читатель? На самом деле, эти параметры описатели процессов – процесса-источника и процесса-приемника (точнее, выражаясь терминологией самого же Рихтера – псвевдоописатели)

7) "Граница между двумя типами приложений [консольных и графических –KK] весьма условна. Можно, например, создать консольное приложение, способное отображать диалоговые окна…" стр. 25



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

8) "Завершение потока" стр. 60.

К трем перечисленным Рихтером способам завершения потока (ExitThread; TerminateThread; завершение процесса, породившего поток) необходимо добавить и четвертый (кстати, самый популярный и простой из всех) – return. Т.е. возврат управления главной функции потока.

9) "В Windows 95 все четыре описанные функции не предусмотрены.


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

Да, но только если она загружает экспортирующую их DLL неявной компоновкой. Поэтому, очень важно объяснить читателю, что API функции, отсутствующие в Windows 95, настоятельно рекомендуется вызывать, явно загружая соответствующие им библиотеки и самостоятельно обрабатывая ситуации с отсутствием функций.

Вообще же, в отношении функциональности Windows 95, Рихтер очень туманен и выражается то "приложение, использующее такие-то функции не будет работать в Windows 95", то "приложение, использующее такие-то функции вообще не удастся загрузить в Windows 95". Очень важно отличать отсутствие функций и отсутствие их реализаций. Первых – в Windows 95 вообще нет, вторые как будто-то есть, но при попытке вызова всегда возвращают ошибку. Рихтер, увы, различает эти два случая не всегда.

10) "Любой поток может вызвать эту функцию [SuspendThread – KK] и приостановить выполнение другого потока. Хоть об этом нигде и не говорится (но я все равно скажу!), приостановить свое выполнение поток способен сам, а возобновить без посторонней помощи – нет… Поток допустимо задерживать не более чем M " стр. 72

В этом маленьком абзаце сразу три ошибки. Первое – для приостановки другого потока его надо открыть с флагом THREAD_SUSPEND_RESUME, на что не у всех остальных потоков хватит прав, так приостановить выполнение системных потоков очень проблематично (точнее, не прибегая к недокументированным секретам – невозможно).

Второе – приостановка осуществляется не только SuspendThread, но и массой функций таких как: Sleep, WaitFor…

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

11) "Если система почему-либо не свяжет EXE-файл с необходимыми ему DLL-модулями, на экране появится соответствующее сообщение, а адресное пространство процесса и объект "процесс" освобождаются" стр. 164



Не могу удержаться, чтобы не заметить, что в Windows 2000 при запуске процесса из консольного приложения сообщение о неудачной загрузке DLL не появляется и процесс тихо "кончает", оставляя пользователя в недоумении – почему он не работает?!

Поэтому, теперь программисту недопустимо игнорировать результат успешности завершения CreateProcess и необходимо самостоятельно вызывать GetLastError для донесения до пользователя причины ошибки. Не стоит надеяться на операционную систему – отныне она это уже не делает.

12) "Семейство Interlocked функций" стр. 312

Описывая эти синхронизующие функции, Рихтер упустил одно немаловажное обстоятельство – большинство компиляторов в большинстве случав для приращения (уменьшения) значения переменной на единицу, используют ассемблерные команды inc [var] и dec[var] соответственно. Они не могут быть прерванными на середине операции и заботится об их синхронизации незачем.

Не прерываются и операции сложения (вычитания) 32-разрядной переменной с 32-разрядной константой, а так же все аналогичные битовые операции[4].

Исключения:

а) 64-разрядные переменные;

б) известная "болезнь" ранних компиляторов от Borland – выполнение всех операций с переменными как минимум в три этапа: mov reg,[var]\ ops reg,const\ mov [var], reg;

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

13) "Если бы этот код выполнялся в Win32 приложении без блока try-finally и оно завершилось бы из-за неправильного доступа к памяти в Funcinator, семафор остался бы занят и не освободился – соответственно и ожидающие его потоки не получили бы процессорного времени" стр. 522

Постой, постой. Какие потоки? Если потоки самого процесса – так ведь они тихо скончались вместе с самим приложением, а если потоки других процессов – так ведь после завершения процесса семафор будет освобожден операционной системой.


Так что принудительное освобождение семафора в этом случае – очевидное излишество.

14) "Это простейший способ внедрения DLL [добавления внедряемой DLL в ключ реестра HKEY_LOCAL_MACHINE\Software\Microsoft\Windows NT\ CurrentVersion\Windows\APPINIT_DLLS – KK].. Однако здесь есть ряд недостатков… Ваша DLL будет спроецирована лишь тех процессов, на которые отображен и USER32. А последнее делается только в GUI-приложениях, т.е. данные способ не подходит для программ консольного типа, - например, компиляторов или компоновщиков." стр. 602

В документации от Microsoft и в технических статьях сторонних авторов, содержащихся в том же MSDN, утверждается, что этот способ срабатывает для всех

процессов системы. Сейчас проверил на Windows 2000 – действительно, внедряемая DLL послушно проецируется даже на консольные приложения.

Потом, неверно утверждение, что консольные приложения не используют USER32. Используют, да еще как! Чаще всего он им необходим для подачи сигналов вызовом функции MessageBeep, экспортируемой USER32.


Содержание раздела