Как увидеть скрытые процессы windows

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

Первым шагом для того, чтобы увидеть скрытые процессы, необходимо открыть Диспетчер задач. Для этого можно воспользоваться несколькими способами. Например, нажмите комбинацию клавиш Ctrl + Shift + Esc или кликните правой кнопкой мыши на панели задач и выберите пункт «Диспетчер задач».

Когда Диспетчер задач будет открыт, переключитесь на вкладку «Подробности». Здесь вы увидите список всех запущенных процессов. Однако, по умолчанию отображаются только процессы, которые работают от имени текущего пользователя. Чтобы увидеть скрытые процессы, необходимо включить отображение всех процессов.

Для этого щелкните правой кнопкой мыши на заголовке одной из колонок (например, «Имя образа») и выберите пункт «Выбрать столбцы». В появившемся окне установите флажок напротив «Root Path» и нажмите кнопку «OK». Теперь вы увидите новую колонку «Root Path», где будут отображаться пути к процессам. Среди них могут быть скрытые процессы.

Как найти скрытые процессы в Диспетчере задач Windows 10

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

  1. Откройте Диспетчер задач. Для этого можно нажать комбинацию клавиш Ctrl + Shift + Esc, нажать правой кнопкой мыши на панели задач и выбрать пункт «Диспетчер задач» или нажать комбинацию клавиш Ctrl + Alt + Delete и выбрать пункт «Диспетчер задач».
  2. В Диспетчере задач выберите вкладку «Подробности».
  3. Нажмите правой кнопкой мыши в любом месте на шапке таблицы процессов и выберите пункт «Выбрать столбцы».
  4. В открывшемся окне «Выбрать столбцы» найдите и выберите пункт «Путь файла» и нажмите кнопку «OK».
  5. Теперь вы увидите новый столбец «Путь файла» в таблице процессов. Пролистайте таблицу вниз и обратите внимание на процессы, у которых поле «Путь файла» оставлено пустым или содержит подозрительный путь. Это могут быть скрытые процессы, которые требуют дополнительного расследования.

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

Почему это важно

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

Кроме того, увидеть скрытые процессы в Диспетчере задач Windows 10 помогает вам осуществлять более эффективную работу на компьютере. Вы можете определить, какие программы или процессы потребляют большую часть ресурсов ПК, и выявить причины возможных проблем с производительностью. Это позволяет вам предпринять соответствующие меры для улучшения работы системы, закрыть ненужные программы или установить приоритеты для запущенных задач.

В итоге, знание и умение увидеть скрытые процессы в Диспетчере задач Windows 10 являются необходимыми навыками для эффективного управления вашей операционной системой, обеспечения безопасности компьютера и повышения производительности работы системы.

Где искать скрытые процессы

В поиске скрытых процессов в Диспетчере задач Windows 10 важно учитывать, что они могут быть расположены в различных местах системы.

Во-первых, стоит обратить внимание на раздел «Процессы» в Диспетчере задач. Здесь отображаются все запущенные процессы на компьютере. Однако некоторые процессы могут быть скрытыми и не отображаться сразу.

Для детального анализа процессов можно воспользоваться вкладкой «Подробно». Здесь можно найти более подробную информацию о каждом процессе, включая его имя, путь к файлу и использование ресурсов. Прокрутите список процессов вниз и обратите внимание на любые подозрительные или незнакомые процессы.

Также стоит обратить внимание на вкладку «Службы» в Диспетчере задач. Здесь отображаются все выполняющиеся службы на компьютере. Скрытые процессы могут быть замаскированы в виде служб, поэтому проверьте этот раздел на наличие подозрительных или неизвестных служб.

Еще одним местом, где можно найти скрытые процессы, является папка «Запуск». Для ее открытия нажмите комбинацию клавиш Win + R, введите «shell:startup» и нажмите «ОК». В этой папке хранятся ярлыки программ, которые автоматически запускаются при старте системы. Проверьте эту папку на наличие подозрительных или неизвестных ярлыков.

Наконец, можно провести анализ системного реестра. Для его открытия нажмите комбинацию клавиш Win + R, введите «regedit» и нажмите «ОК». В реестре можно найти различные настройки и параметры, связанные с запущенными процессами. Однако будьте осторожны при работе с реестром, так как неправильные изменения могут навредить системе.

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

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

Методы обнаружения скрытых процессов

Обнаружение скрытых процессов в операционной системе Windows 10 может быть непростой задачей. Однако существуют несколько методов, которые позволяют выявить подозрительные процессы и установить, скрываются ли они.

  • Использование антивирусного программного обеспечения: Одним из наиболее эффективных методов обнаружения скрытых процессов является использование специализированного антивирусного ПО. Антивирусные программы сканируют файлы и процессы системы, ищут подозрительную активность и могут помочь в выявлении скрытых процессов.
  • Использование программ для управления процессами: Существуют программы, которые специализируются на управлении и мониторинге процессов в операционной системе. Такие программы могут помочь в обнаружении скрытых процессов путем отображения всех процессов, включая те, которые скрываются.
  • Анализ потребления ресурсов: Если система работает медленно или выдает странные ошибки, это может быть признаком наличия скрытых процессов. Часто эти процессы потребляют большие объемы памяти или процессорного времени. Анализ потребления ресурсов может помочь в обнаружении подозрительной активности.
  • Использование инструментов командной строки: В операционной системе Windows 10 доступны различные инструменты командной строки, которые могут быть полезны при обнаружении скрытых процессов. Например, команда tasklist позволяет просмотреть список всех запущенных процессов, а команда taskkill позволяет завершить процесс.

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

Признаки скрытых процессов

Следующие признаки могут указывать на наличие скрытых процессов:

  1. Расход системных ресурсов: Скрытые процессы могут потреблять больше оперативной памяти или процессорного времени, чем обычные процессы. Это может привести к замедлению работы компьютера.
  2. Отсутствие информации: В списке активных процессов в Диспетчере задач может отображаться название процесса без дополнительной информации о его локации или разработчике. Это может быть признаком скрытого процесса.
  3. Неожиданное поведение: Если ваш компьютер ведет себя необычно, например, появляются новые окна или происходят автоматические перезагрузки, это может свидетельствовать о наличии скрытых процессов.
  4. Отсутствие видимых программ: Если в списке активных процессов не отображается та программа, которую вы запустили, это может быть признаком скрытого процесса.
  5. Создание подозрительных файлов: Если в системе появляются новые файлы, которые вы не создавали или не устанавливали, это может быть следствием работы скрытого процесса.
  6. Отсутствие автоответчика: Некоторые антивирусные программы могут блокировать скрытые процессы, поэтому их отсутствие может быть признаком их наличия.

Если вы обнаружили указанные признаки, рекомендуется просканировать систему антивирусной программой и проверить активные процессы с помощью специализированных инструментов, например, Process Explorer.

Как прекратить работу скрытых процессов

Когда вы обнаружите скрытый процесс в Диспетчере задач Windows 10, существует несколько способов прекратить его работу.

1. Используйте Диспетчер задач

Откройте Диспетчер задач, нажав комбинацию клавиш Ctrl + Shift + Esc или щелкнув правой кнопкой мыши на панели задач и выбрав «Диспетчер задач». Затем найдите скрытый процесс на вкладке «Процессы», щелкните на нем правой кнопкой мыши и выберите «Завершить задачу». Этот метод прекращает работу выбранного процесса, но не удаляет его с компьютера.

2. Используйте командную строку

Если вы знаете имя скрытого процесса, вы можете воспользоваться командной строкой, чтобы его прекратить. Чтобы открыть командную строку, нажмите комбинацию клавиш Win + R, введите «cmd» и нажмите Enter. Затем введите команду «taskkill /F /IM имя_процесса.exe» и нажмите Enter. Эта команда принудительно завершит указанный процесс.

3. Используйте антивирусное программное обеспечение

Если вам сложно найти или прекратить работу скрытого процесса, вы можете обратиться к антивирусному программному обеспечению. Многие антивирусные программы предлагают функцию сканирования и удаления скрытых процессов. Запустите программу, выполните полное сканирование вашей системы и следуйте инструкциям для удаления обнаруженных вредоносных программ.

Используя эти методы, вы сможете прекратить работу скрытых процессов в Диспетчере задач Windows 10, обеспечивая безопасность и надежность вашей системы.

Подводя итоги

В этой статье мы рассмотрели, как увидеть скрытые процессы в Диспетчере задач Windows 10. Мы узнали, что некоторые процессы могут быть скрытыми и не видными по умолчанию, но их все равно можно найти и подробно изучить.

Мы рассмотрели различные методы, которые позволяют нам увидеть скрытые процессы. В частности, мы изучили как использовать команду командной строки, как использовать Диспетчер задач Windows 10 и как использовать дополнительные программы для отслеживания и анализа процессов.

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

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

Надеюсь, что эта статья помогла вам получить полезную информацию о том, как увидеть скрытые процессы в Диспетчере задач Windows 10. Удачного анализа процессов!

Для начала нужно научиться обнаруживать стандартные процессы — знать что из них что делает, к чему относится, как себя ведет и как должен запускаться.

После этого уже можно искать нестандартные процессы.

Способы конечно есть, но объяснять их — значит научить человека администрированию windows на достаточно глубоком уровне. Это нельзя вместить в ответ на вопрос, а грубо говоря целый процесс обучения.

Пользуйтесь Comodo
Cleaning Essentials и KillSwitch, они как раз предназначены для анализа и идентификации скрытых системных процессов. Первая специализируется на поиске вирусов и руткитов, очистке системы, вторая улучшенный аналог Диспечера задач, не только отображает сетевую активность и все процессы в виде древа, но и сверяет все активные по электронной подписи, подсвечивая подозрительные. Ещё там есть Autorun Analyzer, он покажет все процессы и службы в автозапуске. Плюс хорошая мысль добавить отдельно VirusTotal Uploader, чтобы проверять всё подозрительные файлы более чем 50 известными антивирусами, чтоб наверняка.

Как выше сказано — гарантированных способов нет.
Но можно проверить систему DrWeb CureIt, Malwarebytes AntiMalware, AVZ, Kaspersky Free.
У этих программ достаточно сильна эвристика — и они теоретически могут подсказать, если какой-то процесс ведет себя не так, как ожидается, даже если сигнатуры вируса нет в их базах.

Начать с простого и посмотреть сетевой трафик утилитой currports, при обнаружении подозрительной активности изучить процессы тем же process explorer.

Проверяйте каждый процесс
1) Подпись
Нет подписи у экзешника это уже не профессиональное ПО.
2) Место запуска
Если запущено не из програм файлс, то повод задуматься. Иногда ставится скайп или дропбокс в профиль пользователя. Но нормальное ПО. Стоит только в програм файлз, а не темповских папках с замудреными именами

Инструмент AVZ, AnvirrTaskManager
шерстите автозагрузку, плагины браузеров, планировщик задач.
Сначала надо прибить все не системные / неизвестные процессы.
У системных посмотреть используемые библиотеки (эти тулзы показывают)

Есть простенькие программы типа Process Hacker, у него свой драйвер и он увидит скрытый процесс, но вы увидите ещё столько интересного, что без точного знания каждой строчки можете сделать ещё хуже.

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

Помню как три дня гонялся за полиморфным драйвером алкоголя. Когда поймал понял, для чего разрабы это сделали, много думал…

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

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

Для начала вам понадобится мощная утилита, которая позволит вам просмотреть все запущенные процессы и загрузки на вашем компьютере. Особенно полезным может быть поиск подозрительных или ненужных процессов, которые могут замедлять работу вашей системы или потенциально угрожать ее безопасности.

Программы, такие как Task Manager в операционной системе Windows или Activity Monitor в macOS, могут быть великолепными инструментами для отслеживания процессов и загрузки. Они предоставляют детальную информацию о каждом запущенном процессе, включая его имя, использование ресурсов и идентификатор процесса. При наличии таких сведений вы сможете определить, какие процессы являются необходимыми и безопасными, а какие могут быть подозрительными или вредоносными.

Как отследить скрытые процессы и загрузку на компьютере?

Скрытые процессы и загрузка на компьютере могут представлять угрозу для безопасности и работоспособности системы. Но есть способы обнаружить их наличие и принять меры по устранению проблем.

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

Кроме того, можно воспользоваться антивирусными программами и антишпионскими программами для обнаружения и удаления скрытого вредоносного программного обеспечения. Эти программы сканируют систему на наличие известных угроз и предупреждают пользователя о потенциальных проблемах.

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

Во-вторых, можно обратить внимание на активность жесткого диска и сетевую активность. Если диск или сеть постоянно используется, то это может быть признаком загрузки на компьютере. Для отслеживания дисковой активности можно воспользоваться программами мониторинга ресурсов, а для отслеживания сетевой активности — сетевыми утилитами.

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

Открыть диспетчер задач и увидеть активность

Чтобы открыть диспетчер задач, выполните следующие шаги:

  1. Нажмите комбинацию клавиш Ctrl+Shift+Esc. Это откроет диспетчер задач непосредственно.
  2. Или нажмите комбинацию клавиш Ctrl+Alt+Delete и выберите «Диспетчер задач» из меню.
  3. Также вы можете щелкнуть правой кнопкой мыши на панели задач и выбрать «Диспетчер задач» из контекстного меню.

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

Вкладка «Процессы» покажет список всех запущенных процессов со всей доступной информацией, такой как имя процесса, потребление ресурсов и ID процесса. Вы также можете завершить или приостановить процессы, если это необходимо.

Вкладка «Производительность» позволит вам отслеживать загрузку процессора, оперативной памяти, дискового пространства и сетевого трафика. Здесь вы можете увидеть текущие значения и графики для каждого ресурса.

Вкладка «Сеть» позволит вам увидеть активность сети, включая скорость загрузки и отсылки данных. Вы также можете узнать, какие приложения используют интернет.

Вкладка «Пользователи» отобразит информацию о текущих пользователях, включая имя пользователя и тип активности.

Вкладка «Службы» покажет список всех запущенных служб с их состоянием и типом запуска. Вы можете управлять службами, выбирая нужные операции из контекстного меню.

Открытие диспетчера задач позволяет вам легко отслеживать активность вашего компьютера и контролировать работу приложений и процессов. Это полезный инструмент для обнаружения скрытой активности и оптимизации загрузки системы.

Проверить скрытые процессы через командную строку

Чтобы найти скрытые процессы, можно воспользоваться командой tasklist /svc. Она покажет список процессов и сервисов, которые они используют. Важно обратить внимание на любые неизвестные или подозрительные сервисы, которые могут быть связаны с вредоносным программным обеспечением.

Для более подробной информации о каждом процессе, можно использовать команду tasklist /v. Она выведет дополнительные сведения, такие как имя пользователя, идентификатор процесса и использование памяти. Это может помочь определить, является ли процесс подозрительным или нет.

Если вы хотите просмотреть процессы, которые не отображаются в менеджере задач Windows, вам понадобится открыть командную строку от имени администратора. Для этого щелкните правой кнопкой мыши по значку командной строки и выберите «Запуск от имени администратора». Затем выполните нужную вам команду.

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

Использовать антивирусную программу для обнаружения загрузки

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

Антивирусная программа будет сканировать ваш компьютер в режиме реального времени и обнаруживать любые подозрительные файлы или процессы. Если обнаружена потенциально вредоносная активность, антивирусная программа может предложить удалить или поместить подозрительные файлы в карантин.

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

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

Использование антивирусной программы является важным шагом для обеспечения безопасности вашего компьютера и обнаружения любой скрытой активности или загрузки. Регулярные сканирования и обновления помогут вам сохранить ваш компьютер в безопасности и предотвратить любые угрозы вредоносных программ.

Проверить список запущенных служб на наличие скрытой активности

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

Чтобы выполнить это действие, можно воспользоваться встроенными средствами операционной системы или специализированными программами. Например, в операционной системе Windows можно воспользоваться программой «Утилита служб». С ее помощью можно просмотреть список всех запущенных служб и определить, есть ли среди них подозрительные или неизвестные службы.

При проверке списка запущенных служб необходимо обратить внимание на следующие аспекты:

  • Известные службы: проанализируйте список запущенных служб и убедитесь, что все службы, которые должны быть запущены, присутствуют в списке, их статус активен и они работают без ошибок.

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

  • Статус служб: обратите внимание на статус каждой службы в списке. Если вы обнаружили службу со статусом «неизвестно» или «остановлена», это может свидетельствовать о проблемах с работой службы или наличии вредоносного программного обеспечения. В таких случаях следует принять меры по исправлению или удалению проблемной службы.

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

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

Проанализировать список установленных программ и их активность

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

Для этого можно воспользоваться инструментами, позволяющими просмотреть список программ, установленных на компьютере, и получить информацию о их активности. Например, в операционной системе Windows можно воспользоваться функцией «Установленные программы» в разделе «Панель управления». Также можно воспользоваться специальными программами, предназначенными для анализа активности программ.

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

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

Проверить автозапуск файлов и программ

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

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

В Windows вы можете открыть диспетчер задач (нажать комбинацию клавиш Ctrl+Shift+Esc или правой кнопкой мыши кликнуть на панели задач и выбрать «Диспетчер задач»), перейти на вкладку «Автозапуск» и посмотреть список программ, которые запускаются при старте системы.

В macOS вы можете открыть настройки системы (нажать на значок «Приложения», затем выбрать «Системные настройки»), затем выбрать «Пользователи и группы» или «Учетные записи» и перейти во вкладку «Входные элементы». Здесь вы увидите список программ, которые запускаются при входе в систему.

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

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

Отследить сетевые соединения и загрузку данных

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

Существуют различные инструменты и программы, которые могут помочь отслеживать сетевые соединения. Например, командная строка в операционной системе Windows позволяет использовать команду netstat для просмотра всех активных соединений. Эта команда показывает IP-адреса, порты и состояние соединения для каждого процесса.

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

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

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

Отслеживание сетевых соединений и загрузки данных является важным инструментом для обнаружения скрытой активности на компьютере. Это позволяет обнаружить приложения или процессы, которые активно взаимодействуют с сетью и потенциально представляют угрозу для безопасности или нормальной работы системы.

Использование инструментов, таких как команда netstat или программы мониторинга сетевого трафика, позволяет увидеть, какие процессы открывают соединения, какие данные они отправляют и получают, и на какие адреса они обращаются. Это помогает анализировать использование ресурсов и идентифицировать потенциальные угрозы.

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

Проверить конфигурации браузера и расширения

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

Сначала рассмотрим настройки браузера. В отдельных случаях, внесение изменений в настройки браузера может привести к незапланированной загрузке файлов или скрытой активности. Проверьте настройки безопасности, сохраненные пароли и файлы cookie. Убедитесь, что у вас установлены правильные настройки безопасности, чтобы предотвратить незапланированный доступ к вашей системе.

Далее идет проверка расширений. Расширения, установленные в вашем браузере, могут вызывать скрытую активность и загрузку на вашем компьютере. Перейдите в меню расширений вашего браузера и проверьте все установленные расширения. Удалите любые ненужные, подозрительные или неизвестного происхождения расширения. Помните, что некоторые расширения могут получать доступ к вашей системе, данные или сети без вашего разрешения.

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

Использовать специальные утилиты для обнаружения скрытой активности и загрузки

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

Профессиональные утилиты, такие как Process Explorer, Autoruns и Procmon, предоставляют возможность просматривать активные процессы и отслеживать их действия в режиме реального времени. Эти программы позволяют увидеть все загруженные драйверы, службы и модули, а также отображают информацию о каждом процессе, включая его потребление ресурсов и связанные с ним файлы и ключи реестра.

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

Кроме того, существуют специализированные антивирусные программы, такие как Malwarebytes Anti-Malware и Norton Power Eraser, которые могут обнаруживать скрытые угрозы и нежелательные программы. Эти инструменты осуществляют сканирование системы на наличие вредоносных файлов и регистрационных записей, и помогают удалить их.

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

Использование специальных утилит для обнаружения скрытой активности и загрузки может быть ключевым шагом в обеспечении безопасности компьютерной системы и защите от потенциальных угроз.

Обнаружение скрытых процессов

Дата публикации 14 сен 2005

Обнаружение скрытых процессов — Архив WASM.RU

Многие пользователи привыкли к тому, что в Windows NT диспетчер задач
показывает все процессы, и многие считают, что скрыться от него вообще
невозможно. На самом деле, скрыть процесс черезвычайно просто. Для этого
существует множество методов, и их реализации доступны в исходниках. Остается
только удивляться, почему так редки трояны использующие эти методики? Их
буквально 1 на 1000 не умеющих скрываться. Я думаю, это объясняется тем, что
авторам троянов лень, ведь для этого необязательно писать что-то свое, всегда
можно взять готовый исходник и вставить в свою программу. Поэтому следует
ожидать, что скоро скрытие процессов будет применяться во всех
широкораспостраненных рядовых троянах.

Естественно, от этого нужно иметь защиту. Производители антивирусов и
фаерволлов отстали от жизни, так как их продукты не умеют обнаруживать скрытые
процессы. Для этого существует только несколько утилит, из которых единственной
бесплатной является Klister(работает только на Windows 2000), а за остальные
производители требуют немалых денег. Причем все эти утилиты довольно легко
обходятся.

Все имеющиеся сейчас программы для обнаружения скрытых процессов построены на
каком-то одном принципе, поэтому для их обхода можно придумать метод скрытия от
конкретного принципа обнаружения, либо привязываться к одной конкретной
программе, что гораздо проще в реализации. Пользователь купивший коммерческую
программу не может изменить ее, а поэтому привязка к конкретной программе будет
работать достаточно надежно, поэтому этот метод используется в коммерческих
руткитах (например hxdef Golden edition). Единственным выходом будет создание
бесплатной Opensource программы для обнаружения скрытых процессов в которой
будут применены несколько методов обнаружения, что позволит защититься от
фундаментальных принципов скрытия, а от привязки к конкретным программам может
защититься каждый пользователь, для этого нужно всего лишь взять исходники
программы и переделать ее под себя.

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

Обнаружение в User Mode

Для начала рассмотрим простые методы обнаружения, которые могут быть
применены в 3 кольце, без использования драйверов. Они основаны на том, что
каждый запущенный процесс порождает побочные проявления своей деятельности, по
которым его и можно обнаружить. Этими проявлениями могут быть открытые им
хэндлы, окна, созданные системные объекты. От подобных методик обнаружения
несложно скрыться, но для этого нужно учесть ВСЕ побочные проявления работы
процесса. Ни в одном из публичных руткитов это пока еще не сделано (приватные
версии к сожалению ко мне не попали). Юзермодные методы просты в реализации,
безопасны в применении, и могут дать положительный эффект, поэтому их
использованием не стоит пренебрегать.

Для начала определимся с форматом данных возвращаемых функциями поиска, пусть
это будут связанные списки:

  1.  TProcList = packed record
  2.    ProcName: array [0..MAX_PATH] of Char;

Получение списка процессов через ToolHelp API

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

  1.  Получение списка процессов через ToolHelp API.
  2. procedure GetToolHelpProcessList(var List: PListStruct);
  3.  Process: TPROCESSENTRY32;
  4.   Snap := CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
  5.   if Snap <> INVALID_HANDLE_VALUE then
  6.       Process.dwSize := SizeOf(TPROCESSENTRY32);
  7.       if Process32First(Snap, Process) then
  8.           GetMem(NewItem, SizeOf(TProcessRecord));
  9.           ZeroMemory(NewItem, SizeOf(TProcessRecord));
  10.           NewItem^.ProcessId  := Process.th32ProcessID;
  11.           NewItem^.ParrentPID := Process.th32ParentProcessID;
  12.           lstrcpy(@NewItem^.ProcessName, Process.szExeFile);
  13.          until not Process32Next(Snap, Process);

Очевидно, что любой скрытый процесс при таком перечислении найден не будет,
поэтому эта функция будет образцовой для отделения скрытых процессов от
нескрытых.

Получение списка процессов через Native API

Следующим уровнем проверки будет получение списка процессов через
ZwQuerySystemInformation (Native API). На этом уровне также врядли что-нибудь
обнаружиться, но проверить все-таки стоит.

  1.   Получение списка процессов через ZwQuerySystemInformation.
  2. procedure GetNativeProcessList(var List: PListStruct);
  3.   Info := GetInfoTable(SystemProcessesAndThreadsInformation);
  4.    GetMem(NewItem, SizeOf(TProcessRecord));
  5.    ZeroMemory(NewItem, SizeOf(TProcessRecord));
  6.    lstrcpy(@NewItem^.ProcessName,
  7.            PChar(WideCharToString(Info^.ProcessName.Buffer)));
  8.    NewItem^.ProcessId  := Info^.ProcessId;
  9.    NewItem^.ParrentPID := Info^.InheritedFromProcessId;
  10.    Info := pointer(dword(info) + info^.NextEntryDelta);
  11.   until Info^.NextEntryDelta = 0;
  12.   VirtualFree(Mem, 0, MEM_RELEASE);

Получение списка процессов по списку открытых хэндлов.

Многие программы скрывающие процесс, не скрывают открытые им хэндлы,
следовательно перечислив открытые хэндлы через ZwQuerySystemInformation мы можем
построить список процессов.

  1.   Получение списка процессов по списку открытых хэндлов.
  2.   Возвращает только ProcessId.
  3. procedure GetHandlesProcessList(var List: PListStruct);
  4.  Info: PSYSTEM_HANDLE_INFORMATION_EX;
  5.   Info := GetInfoTable(SystemHandleInformation);
  6.   for r := 0 to Info^.NumberOfHandles do
  7.     if Info^.Information[r].ProcessId <> OldPid then
  8.        OldPid := Info^.Information[r].ProcessId;
  9.        GetMem(NewItem, SizeOf(TProcessRecord));
  10.        ZeroMemory(NewItem, SizeOf(TProcessRecord));
  11.        NewItem^.ProcessId   := OldPid;
  12.   VirtualFree(Info, 0, MEM_RELEASE);

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

Получение списка процессов по списку открытых ими окон.

Получив список окон зарегистрированных в системе и вызвав для каждого
GetWindowThreadProcessId можно построить список процессов имеющих
окна.

  1.   Получение списка процессов по списку окон.
  2.   Возвращает только ProcessId.
  3. procedure GetWindowsProcessList(var List: PListStruct);
  4.  function EnumWindowsProc(hwnd: dword; PList: PPListStruct): bool; stdcall;
  5.   GetWindowThreadProcessId(hwnd, ProcId);
  6.    if not IsPidAdded(PList^, ProcId) then
  7.      GetMem(NewItem, SizeOf(TProcessRecord));
  8.      ZeroMemory(NewItem, SizeOf(TProcessRecord));
  9.      NewItem^.ProcessId   := ProcId;
  10.      AddItem(PList^, NewItem);
  11.  EnumWindows(@EnumWindowsProc, dword(@List));

Окна не скрывает почти никто, поэтому эта проверка также позволяет что-то
найти, но полагаться на нее тоже не стоит.

Получение списка процессов с помощью прямого системного
вызова.

Для скрытия процессов в User Mode обычно используется технология внедрения
своего кода в чужие процессы и перехвата функции ZwQuerySystemInformation из
ntdll.dll. Функции ntdll на самом деле являются переходниками к соответствующим
функциям ядра системы, и представляют из себя обращение к интерфейсу системных
вызовов (Int 2Eh в Windows 2000 или sysenter в XP), поэтому самым простым и
эффективным способом обнаружения процессов скрытых Usermode API перехватчиками
будет прямое обращение к интерфейсу системных вызовов минуя API.

Вариант функции заменяющей ZwQuerySystemInformation будет выглядеть для
Windows XP так:

  1.  Системный вызов ZwQuerySystemInformation для Windows XP.
  2. Function XpZwQuerySystemInfoCall(ASystemInformationClass: dword;
  3.                                  ASystemInformation: Pointer;
  4.                                  ASystemInformationLength: dword;
  5.                                  AReturnLength: pdword): dword; stdcall;

В связи с другим интерфейсом системных вызовов, для Windows 2000 этот код
будет выглядеть иначе.

  1.   Системный вызов ZwQuerySystemInformation для Windows 2000.
  2. Function Win2kZwQuerySystemInfoCall(ASystemInformationClass: dword;
  3.                                     ASystemInformation: Pointer;
  4.                                     ASystemInformationLength: dword;
  5.                                     AReturnLength: pdword): dword; stdcall;

Теперь остается перечислить процессы не с помощью функций из ntdll.dll, а с
помощью только что определенных функций. Вот код, который это делает:

  1.   Получение списка процессов через системный вызов
  2.   ZwQuerySystemInformation.
  3. procedure GetSyscallProcessList(var List: PListStruct);
  4.   St := ZwQuerySystemInfoCall(SystemProcessesAndThreadsInformation,
  5.   if St = STATUS_INFO_LENGTH_MISMATCH then
  6.  until St <> STATUS_INFO_LENGTH_MISMATCH;
  7.  if St = STATUS_SUCCESS then
  8.      GetMem(NewItem, SizeOf(TProcessRecord));
  9.      ZeroMemory(NewItem, SizeOf(TProcessRecord));
  10.      lstrcpy(@NewItem^.ProcessName,
  11.              PChar(WideCharToString(Info^.ProcessName.Buffer)));
  12.      NewItem^.ProcessId  := Info^.ProcessId;
  13.      NewItem^.ParrentPID := Info^.InheritedFromProcessId;
  14.      Info := pointer(dword(info) + info^.NextEntryDelta);
  15.     until Info^.NextEntryDelta = 0;

Этот метод практически 100% обнаруживает юзермодные руткиты, например все
версии hxdef (в том числе и Golden) им обнаруживаются.

Получение списка процессов путем анализа связанных с ним
хэндлов.

Также, можно применить еще один метод основанный на перечислении хэндлов. Его
суть состоит в том, чтобы найти не хэндлы открытые искомым процессом, а хэндлы
других процессов связанные с ним. Это могут быть хэндлы самого процесса либо его
потоков. При получении хэндла процесса, можно определить его PID с
ZwQueryInformationProcess. Для потока можно вызвать ZwQueryInformationThread и
получить Id его процесса. Все процессы существующие в системе были кем-то
запущены, следовательно родительские процессы будут иметь их хэндлы (если только
не успели их закрыть), также хэндлы всех работающих процессов имеются в сервере
подсистемы Win32 (csrss.exe). Также в Windows NT активно используются Job
объекты, которые позволяют обьединять процессы (например все процессы
определенного прользователя, или какие-либо службы), следовательно при
нахождении хэндла Job объекта, не стоит принебрегать возможностью получить Id
всех обьединенных им процессов. Делается это с помощью функции
QueryInformationJobObject с классом информации — JobObjectBasicProcessIdList.
Код производящий поиск процесов путем анализа открытых другими процессами
хэндлов будет выглядеть так:

{
 Получение списка процессов через проверку хэнжлов в других процессах.
}
procedure GetProcessesFromHandles(var List: PListStruct; Processes, Jobs, Threads: boolean);
var
 HandlesInfo: PSYSTEM_HANDLE_INFORMATION_EX;
 ProcessInfo: PROCESS_BASIC_INFORMATION;
 hProcess : dword;
 tHandle: dword;
 r, l     : integer;
 NewItem: PProcessRecord;
 Info: PJOBOBJECT_BASIC_PROCESS_ID_LIST;
 Size: dword;
 THRInfo: THREAD_BASIC_INFORMATION;
begin
 HandlesInfo := GetInfoTable(SystemHandleInformation);
 if HandlesInfo <> nil then
 for r := 0 to HandlesInfo^.NumberOfHandles do
   if HandlesInfo^.Information[r].ObjectTypeNumber in [OB_TYPE_PROCESS, OB_TYPE_JOB, OB_TYPE_THREAD] then
    begin
      hProcess  := OpenProcess(PROCESS_DUP_HANDLE, false,
                               HandlesInfo^.Information[r].ProcessId);
                               
      if DuplicateHandle(hProcess, HandlesInfo^.Information[r].Handle,
                         INVALID_HANDLE_VALUE, @tHandle, 0, false,
                         DUPLICATE_SAME_ACCESS) then
            begin
             case HandlesInfo^.Information[r].ObjectTypeNumber of
               OB_TYPE_PROCESS : begin
                     if Processes and (HandlesInfo^.Information[r].ProcessId = CsrPid) then
                     if ZwQueryInformationProcess(tHandle, ProcessBasicInformation,
                                            @ProcessInfo,
                                            SizeOf(PROCESS_BASIC_INFORMATION),
                                            nil) = STATUS_SUCCESS then
                     if not IsPidAdded(List, ProcessInfo.UniqueProcessId) then
                        begin
                        GetMem(NewItem, SizeOf(TProcessRecord));
                        ZeroMemory(NewItem, SizeOf(TProcessRecord));
                        NewItem^.ProcessId   := ProcessInfo.UniqueProcessId;
                        NewItem^.ParrentPID  := ProcessInfo.InheritedFromUniqueProcessId;
                        AddItem(List, NewItem);
                        end; 
                     end;

               OB_TYPE_JOB     : begin
                                  if Jobs then
                                   begin
                                    Size := SizeOf(JOBOBJECT_BASIC_PROCESS_ID_LIST) + 4 * 1000;
                                    GetMem(Info, Size);
                                    Info^.NumberOfAssignedProcesses := 1000;
                                    if QueryInformationJobObject(tHandle, JobObjectBasicProcessIdList,
                                                                 Info, Size, nil) then
                                       for l := 0 to Info^.NumberOfProcessIdsInList - 1 do
                                         if not IsPidAdded(List, Info^.ProcessIdList[l]) then
                                           begin
                                            GetMem(NewItem, SizeOf(TProcessRecord));
                                            ZeroMemory(NewItem, SizeOf(TProcessRecord));
                                            NewItem^.ProcessId   := Info^.ProcessIdList[l];
                                            AddItem(List, NewItem);
                                           end;
                                    FreeMem(Info);
                                   end;
                                  end;

               OB_TYPE_THREAD  : begin
                                  if Threads then
                                  if ZwQueryInformationThread(tHandle, THREAD_BASIC_INFO,
                                                              @THRInfo,
                                                              SizeOf(THREAD_BASIC_INFORMATION),
                                                              nil) = STATUS_SUCCESS then
                                    if not IsPidAdded(List, THRInfo.ClientId.UniqueProcess) then
                                     begin
                                       GetMem(NewItem, SizeOf(TProcessRecord));
                                       ZeroMemory(NewItem, SizeOf(TProcessRecord));
                                       NewItem^.ProcessId   := THRInfo.ClientId.UniqueProcess;
                                       AddItem(List, NewItem);
                                     end;
                                 end;

             end;
             CloseHandle(tHandle);
            end;
          CloseHandle(hProcess);
        end;
 VirtualFree(HandlesInfo, 0, MEM_RELEASE);
end;

К сожалению, некоторые из вышеприведенных методов позволяют определить только
ProcessId, но не имя процесса. Следовательно, нам нужно уметь получить имя
процесса по pid. ToolHelp API для этого использовать естественно не стоит, так
как процесс можкт быть скрытым, поэтому мы будем открывать память процесса на
чтение и читьть имя из его PEB. Адрес PEB в процессе можно определить с помощью
функции ZwQueryInformationProcess. А вот и код осуществляющий все это:

  1. function GetNameByPid(Pid: dword): string;
  2.  Info: PROCESS_BASIC_INFORMATION;
  3.  ProcessParametres: pointer;
  4.  ImagePath: TUnicodeString;
  5.  ImgPath: array[0..MAX_PATH] of WideChar;
  6.  ZeroMemory(@ImgPath, MAX_PATH * SizeOf(WideChar));
  7.  hProcess := OpenProcess(PROCESS_QUERY_INFORMATION or PROCESS_VM_READ, false, Pid);
  8.  if ZwQueryInformationProcess(hProcess, ProcessBasicInformation, @Info,
  9.                               SizeOf(PROCESS_BASIC_INFORMATION), nil) = STATUS_SUCCESS then
  10.    if ReadProcessMemory(hProcess, pointer(dword(Info.PebBaseAddress) + $10),
  11.                         @ProcessParametres, SizeOf(pointer), Bytes) and
  12.       ReadProcessMemory(hProcess, pointer(dword(ProcessParametres) + $38),
  13.                         @ImagePath, SizeOf(TUnicodeString), Bytes)  and
  14.       ReadProcessMemory(hProcess, ImagePath.Buffer, @ImgPath,
  15.                         ImagePath.Length, Bytes) then
  16.           Result := ExtractFileName(WideCharToString(ImgPath));

Естественно, юзермодные методы обнаружения на этом не заканчиваются. Если
приложить немного усилий, то можно придумать еще несколько новых (например
загрузку своей Dll в доступные процессы с помощью SetWindowsHookEx с последующим
анализом списка процессов, где наша Dll оказалась), но пока этих методов нам
хватит. Их достоинство в том, что они просты в программировании, но позволяют
обнаружить только процессы скрытые API перехватом в User Mode, либо плохо
скрытые из Kernel Mode. Для действительно надежного обнаружения скрытых
процессов нам придется писать драйвер и работать с внутренними структурами ядра
Windows.

Kernel Mode detection

Вот мы и дошли до методов обнаружения скрытых процессов в режиме ядра. От
юзермодных методов они отличаются в первую очередь тем, что списки процессов
можно построить не используя API, а работая напрямую с структурами планировщика.
Скрыться от таких методов обнаружения гораздо труднее, так как они основаны на
самых принципах работы системы, и удаление всех следов процесса из списков
планировщика приведет к невозможности его работы.

Что представляет из себя процесс изнутри? Каждый процесс имеет свое адресное
пространство, свои дескрипторы, потоки, и.т.д. С этими вещами связаны
соответствующие структуры ядра. Каждый процесс описывается структурой EPROCESS,
структуры всех процессов связаны в кольцевой двухсвязный список. Один из методов
скрытия процессов заключается в изменении указателей так, чтобы перечисление шло
в обход скрываемого процесса. Для работы процесса некритично, будет ли он
участвовать в перечислении или нет. Но структура EPROCESS всегда должна быть,
она необходима для работы процесса. Большинство методов обнаружения скрытых
процессов в Kernel Mode так или иначе связаны с обнаружением этой структуры.

Сначала определимся с форматом хранения полученной информации о процессах.
Формат этот должен быть удобен для передачи из драйвера в приложение. Пусть этим
форматом будет следующая структура:

  1. typedef struct _ProcessRecord
  2. } TProcessRecord, *PProcessRecord;

Пусть структуры располагаются в памяти по порядку, и у последней из них
сброшен флаг Present.

Получение списка процессов через ZwQuerySystemInformation в
ядре.

Начнем как всегда с простого, с получения образцового списка процессов через
ZwQuerySystemInformation:

  1. PVOID GetNativeProcessList(ULONG *MemSize)
  2.     PVOID Info = GetInfoTable(SystemProcessesAndThreadsInformation);
  3.     if (!Info) return NULL; else Proc = Info;
  4.         Proc = (PSYSTEM_PROCESSES)((ULONG)Proc + Proc-&gt;NextEntryDelta); 
  5.     } while (Proc-&gt;NextEntryDelta);
  6.     *MemSize = (PsCount + 1) * sizeof(TProcessRecord);
  7.     Mem = ExAllocatePool(PagedPool, *MemSize);
  8.     if (!Mem) return NULL; else Data = Mem;
  9.         Proc = (PSYSTEM_PROCESSES)((ULONG)Proc + Proc-&gt;NextEntryDelta);
  10.         wcstombs(Data-&gt;ProcessName, Proc-&gt;ProcessName.Buffer, 255);
  11.         Data-&gt;ProcessId  = Proc-&gt;ProcessId;
  12.         Data-&gt;ParrentPID = Proc-&gt;InheritedFromProcessId;
  13.         PsLookupProcessByProcessId((HANDLE)Proc-&gt;ProcessId, &amp;Data-&gt;pEPROCESS);
  14.         ObDereferenceObject(Data-&gt;pEPROCESS);
  15.     } while (Proc-&gt;NextEntryDelta);
  16.     Data-&gt;Present = FALSE;

Пусть эта функция будет образцовой, так как любой Kernel Mode метод скрытия
процесса не будет ею обнаружен. Но юзермодные руткиты типа hxdef будут здесь
обнаружены.

В этом коде применяеся функция GetInfoTable для простого получения
информации. Для того чтобы не возникало вопросов что это такое я приведу ее
здесь полностью:

  1.   Получение буфера с результатом ZwQuerySystemInformation.
  2. PVOID GetInfoTable(ULONG ATableType)
  3.         mPtr = ExAllocatePool(PagedPool, mSize);
  4.             St = ZwQuerySystemInformation(ATableType, mPtr, mSize, NULL);
  5.         if (St == STATUS_INFO_LENGTH_MISMATCH)
  6.     } while (St == STATUS_INFO_LENGTH_MISMATCH);
  7.     if (St == STATUS_SUCCESS) return mPtr;

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

Получение списка процессов из двусвязного списка структур
EPROCESS.

Итак, идем дальше. Следующим шагом будет получение списка процессов проходом
по двухсвязному списку структур EPROCESS. Список начинается с головы —
PsActiveProcessHead, поэтому для корректного перечисления процессов нам сначала
нужно найти этот неэкспортируемый символ. Для этого проще всего будет
воспользоваться тем свойством, что процесс System является первым в списке
процессов. Нам нужно находясь в DriverEntry получить указатель на текущий
процесс с помощью PsGetCurrentProcess (драйвера загруженные с помощью SC Manager
API или ZwLoadDriver всегда грузятся в контексте процесса System), и Blink по
смещению ActiveProcessLinks будет указывать на PsActiveProcessHead. Выглядит это
примерно так:

  1. PsActiveProcessHead = *(PVOID *)((PUCHAR)PsGetCurrentProcess + ActiveProcessLinksOffset + 4);

Теперь можно пройтись по двухсвязному списку и построить список
процессов:

  1. PVOID GetEprocessProcessList(ULONG *MemSize)
  2.     if (!PsActiveProcessHead) return NULL;
  3.     Process = PsActiveProcessHead-&gt;Flink;
  4.     while (Process != PsActiveProcessHead)
  5.         Process = Process-&gt;Flink;
  6.     *MemSize = PsCount * sizeof(TProcessRecord);
  7.     Mem = ExAllocatePool(PagedPool, *MemSize);
  8.     memset(Mem, 0, *MemSize);
  9.     if (!Mem) return NULL; else Data = Mem;
  10.     Process = PsActiveProcessHead-&gt;Flink;
  11.     while (Process != PsActiveProcessHead)
  12.         Data-&gt;ProcessId   = *(PULONG)((ULONG)Process — ActPsLink + pIdOffset);
  13.         Data-&gt;ParrentPID  = *(PULONG)((ULONG)Process — ActPsLink + ppIdOffset);
  14.         Data-&gt;SignalState = *(PULONG)((ULONG)Process — ActPsLink + 4);
  15.         Data-&gt;pEPROCESS   = (PEPROCESS)((ULONG)Process — ActPsLink);
  16.         strncpy(Data-&gt;ProcessName, (PVOID)((ULONG)Process — ActPsLink + NameOffset), 16);       
  17.         Process = Process-&gt;Flink;

Для получения имени процесса, его Process Id и ParrentProcessId используются
смещения данных полей в структуре EPROCESS (pIdOffset, ppIdOffset, NameOffset,
ActPsLink). Эти смещения различаются в различных версиях Windows, поэтому их
получение вынесено в отдельную функцию, которую вы можете увидеть в исходном
коде программы Process Hunter (в приложении к статье).

Любое скрытие процесса методом API перехвата будет обнаружено вышеприведенным
способом. Но если процесс скрыт с помощью метода DKOM (Direct Kernel Object
Manipulation), то этот способ не поможет, так как при этом процесс удаляется из
списка процессов.

Получение списка процессов по спискам потоков
планировщика.

Один из методов обнаружения такого скрытия состоит в получнии списка
процессов по списку потоков в планировщике. В Windows 2000 имеется три
двусвязных списка потоков: KiWaitInListHead, KiWaitOutListHead,
KiDispatcherReadyListHead. Первые два списка содержат потоки ожидающие
наступления какого-либо события, а третий содержит потоки готовые к исполнению.
Пройдясь по спискам и вычев смещение списка потоков в стуктуре ETHREAD мы
получим указатель на ETHREAD потока. Эта структура содержит несколько указателей
на процесс связанный с потоком, это struct _KPROCESS *Process (0x44, 0x150) и
sruct _EPROCESS *ThreadsProcess (0x22C, смещения указаны для Windows 2000).
Первые два указателя не оказывают никакого влияния на работу потока, поэтому
легко могут быть подменены в целях скрытия. А третий указатель используеся
планировщиком при переключении адресных пространств, поэтому подменен быть не
может. Его мы и будем использовать для определения процесса владеющего
потоком.

Этот метод обнаружения применяется в программе klister, главный недостаток
которой — работа только под Windows 2000 (и то не со всеми сервиспаками).
Обусловлен это недостаток тем, что в Klister жестко зашиты адреса списков
потоков, которые меняются почти с каждым сервиспаком системы.

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

Для начала попробуем найти KiWaitItListHead и KiWaitOutListHead в Windows
2000. Адреса этих списков используются в функции KeWaitForSingleObject в коде
следующего вида:

  1. .text:0042DE56                 mov     ecx, offset KiWaitInListHead
  2. .text:0042DE5B                 test    al, al
  3. .text:0042DE5D                 jz      short loc_42DE6E
  4. .text:0042DE5F                 cmp     byte ptr [esi+135h], 0
  5. .text:0042DE66                 jz      short loc_42DE6E
  6. .text:0042DE68                 cmp     byte ptr [esi+33h], 19h
  7. .text:0042DE6C                 jl      short loc_42DE73
  8. .text:0042DE6E                 mov     ecx, offset KiWaitOutListHead

Для получения адресов этих списков надо пройтись дизассемблером длин
инструкций (будем использовать мой LDasm) по KeWaitForSingleObject и когда
указатель (pOpcode) будет на команде mov ecx, KiWaitInListHead, то pOpcode + 5
будет указывать на test al, al, а pOpcode + 24 на mov ecx, KiWaitOutListHead.
После этого адреса KiWaitItListHead и KiWaitOutListHead извлекаются по
указателям pOpcode + 1 и pOpcode + 25 соответственно. Код поиска этих адресов
будет выглядеть так:

  1. void Win2KGetKiWaitInOutListHeads()
  2.     for (cPtr = (PUCHAR)KeWaitForSingleObject;
  3.          cPtr &lt; (PUCHAR)KeWaitForSingleObject + PAGE_SIZE;
  4.         Length = SizeOfCode(cPtr, &amp;pOpcode);
  5.         if (*pOpcode == 0xB9 &amp;&amp; *(pOpcode + 5) == 0x84 &amp;&amp; *(pOpcode + 24) == 0xB9)
  6.             KiWaitInListHead  = *(PLIST_ENTRY *)(pOpcode + 1);
  7.             KiWaitOutListHead = *(PLIST_ENTRY *)(pOpcode + 25);

KiDispatcherReadyListHead в Windows 2000 ищется аналогичным путем, с помощью
поиска в функции KeSetAffinityThread следующего кода:

  1. .text:0042FAAA                 lea     eax, KiDispatcherReadyListHead[ecx*8]
  2. .text:0042FAB1                 cmp     [eax], eax

А вот и функция ищущая KiDispatcherReadyListHead:

  1. void Win2KGetKiDispatcherReadyListHead()
  2.     for (cPtr = (PUCHAR)KeSetAffinityThread;
  3.          cPtr &lt; (PUCHAR)KeSetAffinityThread + PAGE_SIZE;
  4.         Length = SizeOfCode(cPtr, &amp;pOpcode);
  5.         if (*(PUSHORT)pOpcode == 0x048D &amp;&amp; *(pOpcode + 2) == 0xCD &amp;&amp; *(pOpcode + 7) == 0x39)
  6.             KiDispatcherReadyListHead = *(PVOID *)(pOpcode + 3);

К сожалению, в Windows XP ядро довольно сильно отличается от Windows 2000.
Планировщик в XP имеет не три, а только два списка потоков: KiWaitListHead и
KiDispatcherReadyListHead. KiWaitListHead можно найти сканированием функции
KeDelayExecutionThread на следующий код:

  1. .text:004055B5                 mov     dword ptr [ebx], offset KiWaitListHead
  2. .text:004055BB                 mov     [ebx+4], eax

Такой поиск осуществляется следующим кодом:

  1. void XPGetKiWaitListHead()
  2.     for (cPtr = (PUCHAR)KeDelayExecutionThread;
  3.          cPtr &lt; (PUCHAR)KeDelayExecutionThread + PAGE_SIZE;
  4.         Length = SizeOfCode(cPtr, &amp;pOpcode);
  5.         if (*(PUSHORT)cPtr == 0x03C7 &amp;&amp; *(PUSHORT)(pOpcode + 6) == 0x4389)
  6.             KiWaitInListHead = *(PLIST_ENTRY *)(pOpcode + 2);

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

  1. .text:00404E72                 mov     byte ptr [edi+50h], 1
  2. .text:00404E76                 call    sub_404C5A
  3. .text:00404E7D                 call    sub_404EB9

Первый call в этом участке кода указывает на функцию, в которой есть ссылка
на KiDispatcherReadyListHead, но поиск адреса осложняется тем, что нужное нам
место функции имеет различный вид в Winows XP SP1 и SP2. В SP2 оно выглядит
так:

  1. .text:00404CCD                 add     eax, 60h
  2. .text:00404CD0                 test    bl, bl
  3. .text:00404CD2                 lea     edx, KiDispatcherReadyListHead[ecx*8]
  4. .text:00404CD9                 jnz     loc_401F12
  5. .text:00404CDF                 mov     esi, [edx+4]

А в SP1 так:

  1. .text:004180FE                 add     eax, 60h
  2. .text:00418101                 cmp     [ebp+var_1], bl
  3. .text:00418104                 lea     edx, KiDispatcherReadyListHead[ecx*8]
  4. .text:0041810B                 jz      loc_418760
  5. .text:00418111                 mov     esi, [edx]

Искать только по одной инструкции lea ненадежно, поэтому мы будем также
проверять присутствие после lea команды с rel32 смещением (функция IsRelativeCmd
в LDasm). Полный код поиска KiDispatcherReadyListHead будет выглядеть
так:

  1. void XPGetKiDispatcherReadyListHead()
  2.     for (cPtr = (PUCHAR)KiDispatchInterrupt;
  3.          cPtr &lt; (PUCHAR)KiDispatchInterrupt + PAGE_SIZE;
  4.         Length = SizeOfCode(cPtr, &amp;pOpcode);
  5.         if (*pOpcode == 0xE8 &amp;&amp; *(PUSHORT)(pOpcode + 5) == 0x01B1)
  6.             CallAddr = (PUCHAR)(*(PULONG)(pOpcode + 1) + (ULONG)cPtr + Length);
  7.     if (!CallAddr || !MmIsAddressValid(CallAddr)) return;
  8.     for (cPtr = CallAddr; cPtr &lt; CallAddr + PAGE_SIZE; cPtr += Length)
  9.         Length = SizeOfCode(cPtr, &amp;pOpcode);
  10.         if (*(PUSHORT)pOpcode == 0x148D &amp;&amp; *(pOpcode + 2) == 0xCD &amp;&amp; IsRelativeCmd(pOpcode + 7))
  11.             KiDispatcherReadyListHead = *(PLIST_ENTRY *)(pOpcode + 3);

После нахождения адресов списков потоков, мы можем легко перечислить их
процессы с помощью следующей функции:

  1. void ProcessListHead(PLIST_ENTRY ListHead)
  2.         Item = ListHead-&gt;Flink;
  3.             CollectProcess(*(PEPROCESS *)((ULONG)Item + WaitProcOffset));

CollectProcess — это функция добавляющая процесс в список, если он еще не был
туда добавлен.

Получение списка процессов перехватом системных вызовов.

Любой работающий процесс взаимодействует с системой через API, и большинство
этих запросов превращаються в обращения к ядру системы через интерфейс системных
вызовов. Конечно, процесс может работать не вызывая API, но тогда никакой
полезной (или вредной) работы он выполнять не сможет. В общем идея состоит в
том, чтобы перехватить обращения к интерфейсу системных вызовов, а в обработчике
получать указатель на EPROCESS текущего процесса. Список указателей придется
собирать определенное время, и в него не войдут процессы ни разу не выполнявшие
системные вызовы за время сбора этой информации (например процессы, потоки
которых находятся в состоянии ожидания).

В windows 2000 для системного вызова используется прерывание 2Eh, поэтому для
перехвата системных вызовов нам нужно изменить дескриптор соответствующего
прерывания в idt. Для этого нам нужно сначала определить положение idt в памяти
с помощью команды sidt. Эта команда возвращает следующую структуру:

Код изменяющий вектор прерывания 2Eh будет выглядеть так:

Естественно, перед выгрузкой драйвера нужно все восстанавливать:

  1. void Win2kSyscallUnhook()

В Windows XP используется интерфейс системных вызовов построенный на основе
команды sysenter/sysexit которые появились в процессорах Pentium 2. Работой этих
команд управляют модельно-специфичные регистры (MSR). Адрес обработчика
системного вызова задается в MSR регистре SYSENTER_EIP_MSR (номер 0x176). Чтение
MSR регистра выполняется командой rdmsr, перед этим в ЕСХ должен быть помещен
номер читаемого регистра, а результат помещается в пару регистров EDX:EAX. В
нашем случае регистр SYSENTER_EIP_MSR 32 битный, поэтому в EDX будет 0, а в EAX
адрес обработчика системных вызовов. Аналогично, с помощью wrmsr выполняется
запись в MSR регистр. Но тут существует один подводный камень: при записи в 32
битный MSR регистр, EDX должен быть обнулен, иначе это вызовет исключений и
приведет к немедленному падению системы.

С учетом вышесказанного, код заменяющий обработчик системных вызовов будет
выглядеть так:

А восстанавливающий старый обработчик так:

Особенность Windows XP в том, что системный вызов может быть произведен как
через sysenter, так и через int 2Eh, поэтому нам нужно заменить оба обработчика
своими.

Новый обработчик системного вызова должен получить указатель на EPROCESS
текущего процесса, и если это новый процесс, добавить этот процесс в списки.

Соответственно, новый обработчик системного вызова будет выглядеть
так:

  1. void __declspec(naked) NewSyscall()

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

  1. (*PCREATE_PROCESS_NOTIFY_ROUTINE) (

Установка обработчика производится так:

  1. PsSetCreateProcessNotifyRoutine(NotifyRoutine, FALSE);

А удаление так:

  1. PsSetCreateProcessNotifyRoutine(NotifyRoutine, TRUE);

Здесь существует один неочевидный момент, Callback функция всегда вызывается
в контексте завершаемого процесса, следовательно нельзя удалять процесс из
списков прямо в ней. Для этого мы воспользуемся рабочими потоками системы,
сначала выделим память под рабочий поток с помощью IoAllocateWorkItem, а затем
поместим свое задание в очередь рабочего потока с помощью IoQueueWorkItem. В
самом обработчике будем не только удалять из списка завершившиеся процессы, но и
добавлять создающиеся. А вот и код самого обработчика:

  1. void WorkItemProc(PDEVICE_OBJECT DeviceObject, PWorkItemStruct Data)
  2.     KeWaitForSingleObject(Data-&gt;pEPROCESS, Executive, KernelMode, FALSE, NULL);
  3.     DelItem(&amp;wLastItem, Data-&gt;pEPROCESS);
  4.     ObDereferenceObject(Data-&gt;pEPROCESS);
  5.     IoFreeWorkItem(Data-&gt;IoWorkItem);
  6. void NotifyRoutine(IN HANDLE  ParentId,
  7.         PsLookupProcessByProcessId(ProcessId, &amp;process);
  8.         if (!IsAdded(wLastItem, process)) AddItem(&amp;wLastItem, process);
  9.         ObDereferenceObject(process);
  10.         process = PsGetCurrentProcess();
  11.         ObReferenceObject(process);
  12.         Data = ExAllocatePool(NonPagedPool, sizeof(TWorkItemStruct));
  13.         Data-&gt;IoWorkItem = IoAllocateWorkItem(deviceObject);
  14.         Data-&gt;pEPROCESS  = process;
  15.         IoQueueWorkItem(Data-&gt;IoWorkItem, WorkItemProc, DelayedWorkQueue, Data);

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

Обойти этот метод обнаружения при желании также несложно, для этого нужно
изменить метод выполнения системного вызова в скрываемых процессах (перестроить
на другое прерывание или на каллгейт в GDT). Особенно легко это сделать для
Windows XP, так как достаточно пропатчить KiFastSystemCall в ntdll.dll и создать
соответствующий шлюз для системного вызова. В Windows 2000 это сделать несколько
сложнее, так как там вызовы int 2E разбросаны по ntdll, но найти и пропатчить
все эти места также не очень сложно, поэтому полностью полагаться на результаты
этой проверки тоже нельзя.

Получение списка процессов просмотром списка таблиц
хэндлов.

Если вы когда-нибудь скрывали процесс методом его удаления из списка
PsActiveProcesses, то наверняка обратили внимание на то, что при перечислении
хэндлов с помощью ZwQuerySystemInformation хэндлы скрытого процесса участвуют в
перечислении, в том числе определяется его ProcessId. Происходит это потому, что
для удобства перечисления хэндлов, все таблицы хэндлов обьединены в двусвязный
список HandleTableList. Смещение этого списка в структуре HANDLE_TABLE для
Windows 2000 равно 0x054, а для Windows XP — 0x01C, начинается этот список с
HandleTableListHead. Структура HANDLE_TABLE содержит в себе указатель на
владеющий ей процесс (QuotaProcess), смещение этого указателя в Windows 2000
равно 0x00C, а в Windows XP — 0x004. Пройдя по списку таблиц хэндлов мы можем
построить по ним список процессов. Для начала нам нужно найти
HandleTableListHead. Дизассемблирование ядра показало, что ссылки на него
находятся глубоко во вложенных функциях, поэтому метод поиска путем
дизассемблирования кода, который мы применяли ранее, здесь совсем не подходит.
Но для поиска HandleTableListHead можно использовать то свойство, что
HandleTableListHead — это глобальная переменная ядра, и следовательно она
находится в одной из секций его PE файла, а остальные элементы HandleTableList
находятся в динамически выделяемой памяти, а следовательно всегда будут за его
пределами. Из этого следует, что нам нужно получить указатель на HandleTable
любого процесса, и двигаться по связаному списку до тех пор, пока его элемент не
окажется внутри PE файла ядра. Этот элемент и будет HandleTableListHead. Для
определения базы и размера файла ядра в памяти используем функцию
ZwQuerySystemInformation с классом SystemModuleInformation. Она возвратит нам
массив описателей загруженных модулей, в котором первым элементом всегда будет
ядро. С учетом всего вышесказаного, код поиска HandleTableListHead будет
выглядеть так:

  1. void GetHandleTableListHead()
  2.     PSYSTEM_MODULE_INFORMATION_EX Info = GetInfoTable(SystemModuleInformation);
  3.     ULONG NtoskrnlBase = (ULONG)Info-&gt;Modules[0].Base;
  4.     ULONG NtoskrnlSize = Info-&gt;Modules[0].Size;
  5.     PHANDLE_TABLE HandleTable = *(PHANDLE_TABLE *)((ULONG)PsGetCurrentProcess() + HandleTableOffset);
  6.     PLIST_ENTRY HandleTableList = (PLIST_ENTRY)((ULONG)HandleTable + HandleTableListOffset);
  7.     for (CurrTable = HandleTableList-&gt;Flink;
  8.          CurrTable != HandleTableList;
  9.          CurrTable = CurrTable-&gt;Flink)
  10.         if ((ULONG)CurrTable &gt; NtoskrnlBase &amp;&amp; (ULONG)CurrTable &lt; NtoskrnlBase + NtoskrnlSize)
  11.             HandleTableListHead = CurrTable;

Этот код весьма универсален, так как работает в любых версиях Windows NT, и к
тому же может применяться не только для поиска HandleTableListHead, но также и
любых других списков имеющих подобную структуру.

После получения адреса HandleTableListHead мы можем пройтись по таблицам
хэндлов и построить по ним список запущенных процессов:

  1. void ScanHandleTablesList()
  2.     for (CurrTable =  HandleTableListHead-&gt;Flink;
  3.          CurrTable != HandleTableListHead;
  4.          CurrTable =  CurrTable-&gt;Flink)
  5.         QuotaProcess = *(PEPROCESS *)((PUCHAR)CurrTable — HandleTableListOffset + QuotaProcessOffset);
  6.         if (QuotaProcess) CollectProcess(QuotaProcess);

Этот метод обнаружения скрытых процессов применяется в программах F-Secure
Black Light и в последней версии KProcCheck. Как его обойти, я думаю вы сами
догадаетесь.

Получение списка процессов путем сканирования PspCidTable.

Еще одна особенность скрытия процесса с помощью удаления его из
PsActiveProcesses состоит в том, что это никак не мешает открытию процесса с
помощью OpenProcess. На этой особенности построен метод обнаружения процессов
путем перебора их pid с попыткой открыть такой процесс. Этот метод я приводить
не стал, так как по моему мнению, он лишен каких либо достоинств, в общем, можно
сказать — черезжопный метод. Но сам факт его существования говорит о том, что в
системе существует еще какой-то список процессов помимо PsActiveProcesses, по
которому и происходит открытие процесса. При переборе ProcessId обнаруживается
еще одна особенность — один процесс может быть открыт по нескольким разным pid,
а это наводит на мысль о том, что второй список процессов представляет из себя
ни что иное, как HANDLE_TABLE. Для того, чтобы удостовериться в этом, заглянем в
функцию ZwOpenProces:

  1. PAGE:0049D59E ; NTSTATUS __stdcall NtOpenProcess(PHANDLE ProcessHandle, ACCESS_MASK DesiredAccess,
  2.                                                  POBJECT_ATTRIBUTES ObjectAttributes,PCLIENT_ID ClientId)
  3. PAGE:0049D59E                 public NtOpenProcess
  4. PAGE:0049D59E NtOpenProcess   proc near              
  5. PAGE:0049D59E ProcessHandle   = dword ptr  4
  6. PAGE:0049D59E DesiredAccess   = dword ptr  8
  7. PAGE:0049D59E ObjectAttributes= dword ptr  0Ch
  8. PAGE:0049D59E ClientId        = dword ptr  10h
  9. PAGE:0049D5A3                 push    offset dword_413560 ; int
  10. PAGE:0049D5A8                 call    sub_40BA92
  11. PAGE:0049D5AD                 xor     esi, esi
  12. PAGE:0049D5AF                 mov     [ebp-2Ch], esi
  13. PAGE:0049D5B2                 xor     eax, eax
  14. PAGE:0049D5B4                 lea     edi, [ebp-28h]
  15. PAGE:0049D5B8                 mov     eax, large fs:124h
  16. PAGE:0049D5BE                 mov     al, [eax+140h]
  17. PAGE:0049D5C4                 mov     [ebp-34h], al
  18. PAGE:0049D5C7                 test    al, al
  19. PAGE:0049D5C9                 jz      loc_4BE034
  20. PAGE:0049D5CF                 mov     [ebp-4], esi
  21. PAGE:0049D5D2                 mov     eax, MmUserProbeAddress
  22. PAGE:0049D5D7                 mov     ecx, [ebp+8]
  23. PAGE:0049D5DA                 cmp     ecx, eax
  24. PAGE:0049D5DC                 jnb     loc_520CDE
  25. PAGE:0049D5E2 loc_49D5E2:                            
  26. PAGE:0049D5E2                 mov     eax, [ecx]
  27. PAGE:0049D5E4                 mov     [ecx], eax
  28. PAGE:0049D5E6                 mov     ebx, [ebp+10h]
  29. PAGE:0049D5EC                 jnz     loc_520CE5
  30. PAGE:0049D5F2 loc_49D5F2:                          
  31. PAGE:0049D5F2                 mov     eax, MmUserProbeAddress
  32. PAGE:0049D5F7                 cmp     ebx, eax
  33. PAGE:0049D5F9                 jnb     loc_520CEF
  34. PAGE:0049D5FF loc_49D5FF:                            
  35. PAGE:0049D5FF                 cmp     [ebx+8], esi
  36. PAGE:0049D602                 setnz   byte ptr [ebp-1Ah]
  37. PAGE:0049D606                 mov     ecx, [ebx+0Ch]
  38. PAGE:0049D609                 mov     [ebp-38h], ecx
  39. PAGE:0049D60C                 mov     ecx, [ebp+14h]
  40. PAGE:0049D60F                 cmp     ecx, esi
  41. PAGE:0049D611                 jz      loc_4CCB88
  42. PAGE:0049D61A                 jnz     loc_520CFB
  43. PAGE:0049D620 loc_49D620:                            
  44. PAGE:0049D620                 cmp     ecx, eax
  45. PAGE:0049D622                 jnb     loc_520D0D
  46. PAGE:0049D628 loc_49D628:
  47. PAGE:0049D628                 mov     eax, [ecx]
  48. PAGE:0049D62A                 mov     [ebp-2Ch], eax
  49. PAGE:0049D62D                 mov     eax, [ecx+4]
  50. PAGE:0049D630                 mov     [ebp-28h], eax
  51. PAGE:0049D633                 mov     byte ptr [ebp-19h], 1
  52. PAGE:0049D637 loc_49D637:                      
  53. PAGE:0049D637                 or      dword ptr [ebp-4], 0FFFFFFFFh
  54. PAGE:0049D63B loc_49D63B:                    
  55. PAGE:0049D63B                 cmp     byte ptr [ebp-1Ah], 0
  56. PAGE:0049D63F                 jnz     loc_520D34
  57. PAGE:0049D645 loc_49D645:                            
  58. PAGE:0049D645                 mov     eax, PsProcessType
  59. PAGE:0049D64A                 add     eax, 68h
  60. PAGE:0049D64E                 push    dword ptr [ebp+0Ch]
  61. PAGE:0049D651                 lea     eax, [ebp-0D4h]
  62. PAGE:0049D658                 lea     eax, [ebp-0B8h]
  63. PAGE:0049D65F                 call    SeCreateAccessState
  64. PAGE:0049D664                 cmp     eax, esi
  65. PAGE:0049D666                 jl      loc_49D718
  66. PAGE:0049D66C                 push    dword ptr [ebp-34h] ; PreviousMode
  67. PAGE:0049D66F                 push    ds:stru_5B6978.HighPart
  68. PAGE:0049D675                 push    ds:stru_5B6978.LowPart ; PrivilegeValue
  69. PAGE:0049D67B                 call    SeSinglePrivilegeCheck
  70. PAGE:0049D680                 test    al, al
  71. PAGE:0049D682                 jnz     loc_4AA7DB
  72. PAGE:0049D688 loc_49D688:                          
  73. PAGE:0049D688                 cmp     byte ptr [ebp-1Ah], 0
  74. PAGE:0049D68C                 jnz     loc_520D52
  75. PAGE:0049D692                 cmp     byte ptr [ebp-19h], 0
  76. PAGE:0049D696                 jz      loc_4CCB9A
  77. PAGE:0049D69C                 mov     [ebp-30h], esi
  78. PAGE:0049D69F                 cmp     [ebp-28h], esi
  79. PAGE:0049D6A2                 jnz     loc_4C1301
  80. PAGE:0049D6A8                 lea     eax, [ebp-24h]
  81. PAGE:0049D6AC                 push    dword ptr [ebp-2Ch]
  82. PAGE:0049D6AF                 call    PsLookupProcessByProcessId
  83. PAGE:0049D6B4 loc_49D6B4:                            

Как вы видите, этот код безопасным образом копирует переданные указатели,
проверяя присутствие их в границах пользовательских адресов, проверяет права
доступа и наличие привилегии «SeDebugPrivilege», после чего извлекает ProcessId
из структуры CLIENT_ID и передает его функции PsLookupProcessByProcessId, задача
которой — получить по ProcessId указатель на EPROCESS. Дальнейшее продолжение
функции приводить не имеет смысла, поэтому заглянем теперь в
PsLookupProcessByProcessId:

  1. PAGE:0049D725                 public PsLookupProcessByProcessId
  2. PAGE:0049D725 PsLookupProcessByProcessId proc near    
  3. PAGE:0049D725 ProcessId       = dword ptr  8
  4. PAGE:0049D725 Process         = dword ptr  0Ch
  5. PAGE:0049D725                 mov     edi, edi
  6. PAGE:0049D728                 mov     ebp, esp
  7. PAGE:0049D72C                 mov     eax, large fs:124h
  8. PAGE:0049D732                 push    [ebp+ProcessId]
  9. PAGE:0049D735                 mov     esi, eax
  10. PAGE:0049D737                 dec     dword ptr [esi+0D4h]
  11. PAGE:0049D73D                 push    PspCidTable
  12. PAGE:0049D743                 call    ExMapHandleToPointer
  13. PAGE:0049D748                 mov     ebx, eax
  14. PAGE:0049D74A                 test    ebx, ebx
  15. PAGE:0049D74C                 mov     [ebp+ProcessId], STATUS_INVALID_PARAMETER
  16. PAGE:0049D753                 jz      short loc_49D787
  17. PAGE:0049D756                 mov     edi, [ebx]
  18. PAGE:0049D758                 cmp     byte ptr [edi], 3
  19. PAGE:0049D75B                 jnz     short loc_49D77A
  20. PAGE:0049D75D                 cmp     dword ptr [edi+1A4h], 0
  21. PAGE:0049D764                 jz      short loc_49D77A
  22. PAGE:0049D766                 mov     ecx, edi
  23. PAGE:0049D768                 call    sub_4134A9
  24. PAGE:0049D76D                 test    al, al
  25. PAGE:0049D76F                 jz      short loc_49D77A
  26. PAGE:0049D771                 mov     eax, [ebp+Process]
  27. PAGE:0049D774                 and     [ebp+ProcessId], 0
  28. PAGE:0049D778                 mov     [eax], edi
  29. PAGE:0049D77A loc_49D77A:                                                                
  30. PAGE:0049D77B                 push    PspCidTable
  31. PAGE:0049D781                 call    ExUnlockHandleTableEntry
  32. PAGE:0049D787 loc_49D787:                          
  33. PAGE:0049D787                 inc     dword ptr [esi+0D4h]
  34. PAGE:0049D78D                 jnz     short loc_49D79A
  35. PAGE:0049D78F                 lea     eax, [esi+34h]
  36. PAGE:0049D792                 cmp     [eax], eax
  37. PAGE:0049D794                 jnz     loc_52388A
  38. PAGE:0049D79A loc_49D79A:                                                            
  39. PAGE:0049D79A                 mov     eax, [ebp+ProcessId]

То что мы видим здесь, подтверждает наличие второй таблицы процессов,
организованной как HANDLE_TABLE. Сама таблица называется PspCidTable и хранит в
себе списки процессов и потоков, и используется еще в функциях
PsLookupProcessThreadByCid и PsLookupThreadByThreadId. Как мы видим, хэндл и
указатель на таблицу хэндлов передаются функции ExMapHandleToPointer, которая
(при валидности хэндла) возвращает указатель на элемент таблицы описывающий
данный хэндл — HANDLE_TABLE_ENTRY. Скормив файл ntoskrnl.pdb программе PDBdump и
порывшись в полученном логе, можно откопать следующее:

  1. struct _HANDLE_TABLE_ENTRY {
  2.  // static data ————————————
  3.  // non-static data ———————————
  4.   /*&lt;thisrel this+0x0&gt;*/ /*|0x4|*/ void* Object;
  5.   /*&lt;thisrel this+0x0&gt;*/ /*|0x4|*/ unsigned long ObAttributes;
  6.   /*&lt;thisrel this+0x0&gt;*/ /*|0x4|*/ struct _HANDLE_TABLE_ENTRY_INFO* InfoTable;
  7.   /*&lt;thisrel this+0x0&gt;*/ /*|0x4|*/ unsigned long Value;
  8.   /*&lt;thisrel this+0x4&gt;*/ /*|0x4|*/ unsigned long GrantedAccess;
  9.   /*&lt;thisrel this+0x4&gt;*/ /*|0x2|*/ unsigned short GrantedAccessIndex;
  10.   /*&lt;thisrel this+0x6&gt;*/ /*|0x2|*/ unsigned short CreatorBackTraceIndex;
  11.   /*&lt;thisrel this+0x4&gt;*/ /*|0x4|*/ long NextFreeTableEntry;

Из этого можно составить такую структуру HANDLE_TABLE_ENTRY:

  1. typedef struct _HANDLE_TABLE_ENTRY
  2.         PHANDLE_TABLE_ENTRY_INFO InfoTable;
  3.           ACCESS_MASK GrantedAccess;
  4.               USHORT GrantedAccessIndex;
  5.               USHORT CreatorBackTraceIndex;
  6. } HANDLE_TABLE_ENTRY, *PHANDLE_TABLE_ENTRY;

Что полезного мы можем из этого извлечь? Первым делом нас интересует
содержимое поля Object, которое является суммой указателя на описываемый хэндлом
объект и флага указывающего на занятость данного элемента таблицы (подробнее
этот момент мы рассмотрим немного позже). Весьма интересным является поле
GrantedAccess, которое указывает допустимые права доступа к объекту по этому
хэндлу. Нпример, можно открыть файл на чтение, поправить поле GrantedAccess и
писать в этот файл. Подобный метод можно использовать для чтения/записи файлов,
которых не удается открыть с требуемыми правами доступа (например занятых другим
процессом). Но вернемся к нашей задаче — получить список процессов путем
просмотра PspCidTable. Для этого нам нужно разобраться с форматом таблицы
хендлов, для того чтобы суметь их перечислить. С этого момента начинается
серьезная разница между Windows 2000 и Windows XP. Форматы их таблиц хэндлов
сильно отличаются, и нам придется разобраться с их форматом для каждой ОС
отдельно.

Для начала рассмотрим формат таблицы хэндлов в Windows 2000, так как там она
гораздо проще для понимания. Для начала заглянем в код функции
ExMapHandleToPointer:

  1. PAGE:00493285 ExMapHandleToPointer proc near          
  2. PAGE:00493285 HandleTable     = dword ptr  8
  3. PAGE:00493285 Handle          = dword ptr  0Ch
  4. PAGE:00493286                 push    [esp+Handle]
  5. PAGE:0049328A                 push    [esp+4+HandleTable]
  6. PAGE:0049328E                 call    ExpLookupHandleTableEntry
  7. PAGE:00493293                 mov     esi, eax
  8. PAGE:00493295                 test    esi, esi
  9. PAGE:00493297                 jz      short loc_4932A9
  10. PAGE:0049329A                 push    [esp+4+HandleTable]
  11. PAGE:0049329E                 call    ExLockHandleTableEntry
  12. PAGE:004932A5                 sbb     eax, eax
  13. PAGE:004932A7                 and     eax, esi
  14. PAGE:004932A9 loc_4932A9:                            
  15. PAGE:004932AA ExMapHandleToPointer endp

Здесь происходит вызов функции ExMapHandleToPointer которая производит сам
поиск по HANDLE_TABLE, и вызов ExLockHandleTableEntry которая устанавливает Lock
Bit. Для понимания работы таблицы хэндлов нам придется разобрать обе эти
функции. Начнем с ExpLookupHandleTableEntry:

  1. PAGE:00493545 ExpLookupHandleTableEntry proc near    
  2. PAGE:00493545 HandleTable     = dword ptr  0Ch
  3. PAGE:00493545 Handle          = dword ptr  10h
  4. PAGE:00493547                 mov     edi, [esp+Handle]
  5. PAGE:0049354B                 mov     eax, 0FFh
  6. PAGE:00493550                 mov     ecx, edi
  7. PAGE:00493552                 mov     edx, edi
  8. PAGE:00493554                 mov     esi, edi
  9. PAGE:00493556                 shr     ecx, 12h
  10. PAGE:00493559                 shr     edx, 0Ah
  11. PAGE:0049355F                 and     ecx, eax
  12. PAGE:00493561                 and     edx, eax
  13. PAGE:00493563                 and     esi, eax
  14. PAGE:00493565                 test    edi, 0FC000000h
  15. PAGE:0049356B                 jnz     short loc_49358A
  16. PAGE:0049356D                 mov     eax, [esp+HandleTable]
  17. PAGE:00493571                 mov     eax, [eax+8]
  18. PAGE:00493574                 mov     ecx, [eax+ecx*4]
  19. PAGE:00493577                 test    ecx, ecx
  20. PAGE:00493579                 jz      short loc_49358A
  21. PAGE:0049357B                 mov     ecx, [ecx+edx*4]
  22. PAGE:0049357E                 test    ecx, ecx
  23. PAGE:00493580                 jz      short loc_49358A
  24. PAGE:00493582                 lea     eax, [ecx+esi*8]
  25. PAGE:00493585 loc_493585:                            
  26. PAGE:0049358A loc_49358A:                                                      
  27. PAGE:0049358A                 xor     eax, eax
  28. PAGE:0049358C                 jmp     short loc_493585
  29. PAGE:0049358C ExpLookupHandleTableEntry endp

В дополнение к этому приведу структуру HANDLE_TABLE полученную из дампа
ntoskrnl.pdb:

  1.   // static data ————————————  
  2.   // non-static data ———————————
  3.   /*&lt;thisrel this+0x0&gt;*/ /*|0x4|*/ unsigned long Flags;
  4.   /*&lt;thisrel this+0x4&gt;*/ /*|0x4|*/ long HandleCount;
  5.   /*&lt;thisrel this+0x8&gt;*/ /*|0x4|*/ struct _HANDLE_TABLE_ENTRY*** Table;
  6.   /*&lt;thisrel this+0xc&gt;*/ /*|0x4|*/ struct _EPROCESS* QuotaProcess;
  7.   /*&lt;thisrel this+0x10&gt;*/ /*|0x4|*/ void* UniqueProcessId;
  8.   /*&lt;thisrel this+0x14&gt;*/ /*|0x4|*/ long FirstFreeTableEntry;
  9.   /*&lt;thisrel this+0x18&gt;*/ /*|0x4|*/ long NextIndexNeedingPool;
  10.   /*&lt;thisrel this+0x1c&gt;*/ /*|0x38|*/ struct _ERESOURCE HandleTableLock;  
  11.   /*&lt;thisrel this+0x54&gt;*/ /*|0x8|*/ struct _LIST_ENTRY HandleTableList;
  12.   /*&lt;thisrel this+0x5C&gt;*/ /*|0x10|*/ struct _KEVENT HandleContentionEvent;

По этим данным восстановим структуру таблицы хэндлов:

  1. typedef struct _WIN2K_HANDLE_TABLE
  2.     PHANDLE_TABLE_ENTRY **Table;
  3.     LONG                  FirstFreeTableEntry;
  4.     LONG                  NextIndexNeedingPool;
  5.     ERESOURCE             HandleTableLock;
  6.     LIST_ENTRY            HandleTableList;
  7.     KEVENT                HandleContentionEvent;
  8. } WIN2K_HANDLE_TABLE , *PWIN2K_HANDLE_TABLE ;

Из всего этого очевидно, что значение хэндла раскладывается на три части,
которые являются индексами в трехуровневой таблице объектов. Теперь посмотрим в
функцию ExLockHandleTableEntry:

  1. PAGE:00492E2B ExLockHandleTableEntry proc near        
  2. PAGE:00492E2B var_8           = dword ptr -8
  3. PAGE:00492E2B var_4           = dword ptr -4
  4. PAGE:00492E2B HandleTable     = dword ptr  8
  5. PAGE:00492E2B Entry           = dword ptr  0Ch
  6. PAGE:00492E2C                 mov     ebp, esp
  7. PAGE:00492E32                 xor     ebx, ebx
  8. PAGE:00492E34 loc_492E34:                                                                
  9. PAGE:00492E34                 mov     eax, [ebp+Entry]
  10. PAGE:00492E37                 mov     esi, [eax]
  11. PAGE:00492E39                 test    esi, esi
  12. PAGE:00492E3B                 mov     [ebp+var_8], esi
  13. PAGE:00492E3E                 jz      short loc_492E89
  14. PAGE:00492E40                 jle     short loc_492E64
  15. PAGE:00492E42                 mov     eax, esi
  16. PAGE:00492E44                 or      eax, 80000000h      // set WIN2K_TABLE_ENTRY_LOCK_BIT
  17. PAGE:00492E49                 mov     [ebp+var_4], eax
  18. PAGE:00492E4C                 mov     eax, [ebp+var_8]
  19. PAGE:00492E4F                 mov     ecx, [ebp+Entry]
  20. PAGE:00492E52                 mov     edx, [ebp+var_4]
  21. PAGE:00492E55                 cmpxchg [ecx], edx
  22. PAGE:00492E58                 cmp     eax, esi
  23. PAGE:00492E5A                 jnz     short loc_492E64
  24. PAGE:00492E5E loc_492E5E:                            
  25. PAGE:00492E64 loc_492E64:              
  26. PAGE:00492E64                 mov     eax, ebx
  27. PAGE:00492E6A                 jb      loc_4BC234
  28. PAGE:00492E70                 mov     eax, [ebp+HandleTable]
  29. PAGE:00492E73                 push    offset unk_46D240 ; Timeout
  30. PAGE:00492E78                 push    0               ; Alertable
  31. PAGE:00492E7A                 push    0               ; WaitMode
  32. PAGE:00492E7C                 add     eax, 5Ch
  33. PAGE:00492E7F                 push    0               ; WaitReason
  34. PAGE:00492E81                 push    eax             ; Object
  35. PAGE:00492E82                 call    KeWaitForSingleObject
  36. PAGE:00492E87                 jmp     short loc_492E34
  37. PAGE:00492E89 loc_492E89:                        
  38. PAGE:00492E8B                 jmp     short loc_492E5E
  39. PAGE:00492E8B ExLockHandleTableEntry endp

Смысл данного кода состоит в том, что он проверяет 31 бит в элементе Object
структуры HANDLE_TABLE_ENTRY, устанавливает его, а в случае, если он установлен
— ждет HandleContentionEvent в HANDLE_TABLE. Для нас важен лишь факт установки
TABLE_ENTRY_LOCK_BIT, так как он являтся частью адреса объекта, и при сброшенном
бите мы получим невалидный адрес. С форматом таблицы хэндлов мы вроде
разобрались, теперь можно написать код перебора объектов в таблице:

  1. void ScanWin2KHandleTable(PWIN2K_HANDLE_TABLE HandleTable)
  2.     PHANDLE_TABLE_ENTRY Entry;
  3.     for (i = 0; i &lt; 0x100; i++)
  4.         if (HandleTable-&gt;Table[i])
  5.             for (j = 0; j &lt; 0x100; j++)
  6.                 if (HandleTable-&gt;Table[i][j])
  7.                     for (k = 0; k &lt; 0x100; k++)
  8.                         Entry = &amp;HandleTable-&gt;Table[i][j][k];
  9.                           ProcessObject((PVOID)((ULONG)Entry-&gt;Object | WIN2K_TABLE_ENTRY_LOCK_BIT));

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

  1. void ProcessObject(PVOID Object)
  2.     POBJECT_HEADER ObjectHeader = OBJECT_TO_OBJECT_HEADER(Object);
  3.     if (ObjectHeader-&gt;Type == *PsProcessType) CollectProcess(Object);
  4.     if (ObjectHeader-&gt;Type == *PsThreadType)  ThreadCollect(Object);

Итак, с форматом таблицы объектов в Windows 2000 мы разобрались, теперь пора
приступить к ее разбору в Windows XP. Начнем с дизассемблирования функции
ExpLookupHandleTableEntry:

  1. PAGE:0048D3C1 ExpLookupHandleTableEntry proc near    
  2. PAGE:0048D3C1 HandleTable     = dword ptr  8
  3. PAGE:0048D3C1 Handle          = dword ptr  0Ch
  4. PAGE:0048D3C1                 mov     edi, edi
  5. PAGE:0048D3C4                 mov     ebp, esp
  6. PAGE:0048D3C6                 and     [ebp+Handle], 0FFFFFFFCh
  7. PAGE:0048D3CA                 mov     eax, [ebp+Handle]
  8. PAGE:0048D3CD                 mov     ecx, [ebp+HandleTable]
  9. PAGE:0048D3D0                 mov     edx, [ebp+Handle]
  10. PAGE:0048D3D6                 cmp     edx, [ecx+38h]
  11. PAGE:0048D3D9                 jnb     loc_4958D6
  12. PAGE:0048D3E0                 mov     esi, [ecx]
  13. PAGE:0048D3E2                 mov     ecx, esi
  14. PAGE:0048D3E4                 and     ecx, 3     // ecx — table level
  15. PAGE:0048D3E7                 and     esi, not 3 // esi — pointer to first table
  16. PAGE:0048D3ED                 jnz     loc_48DEA4
  17. PAGE:0048D3F3                 lea     eax, [esi+eax*8]
  18. PAGE:0048D3F6 loc_48D3F6:                            
  19. PAGE:0048D3F7 loc_48D3F7:                            
  20. PAGE:0048DEA4 loc_48DEA4:                            
  21. PAGE:0048DEA5                 mov     ecx, eax
  22. PAGE:0048DEA7                 jnz     loc_52F57A
  23. PAGE:0048DEB0                 mov     ecx, [esi+ecx*4]  
  24. PAGE:0048DEB3 loc_48DEB3:                            
  25. PAGE:0048DEB3                 and     eax, 1FFh
  26. PAGE:0048DEB8                 lea     eax, [ecx+eax*8]
  27. PAGE:0048DEBB                 jmp     loc_48D3F6
  28. PAGE:0052F57A loc_52F57A:                            
  29. PAGE:0052F57A                 shr     ecx, 13h
  30. PAGE:0052F57D                 mov     edx, ecx
  31. PAGE:0052F57F                 mov     ecx, [esi+ecx*4]
  32. PAGE:0052F582                 shl     edx, 13h
  33. PAGE:0052F585                 sub     eax, edx
  34. PAGE:0052F587                 mov     edx, eax
  35. PAGE:0052F58C                 mov     ecx, [ecx+edx*4]
  36. PAGE:0052F58F                 jmp     loc_48DEB3

Теперь посмотрим на структуру HANDLE_TABLE из дампа ntoskrnl.pdb:

  1.  // static data ————————————
  2.  // non-static data ———————————
  3.   /*&lt;thisrel this+0x0&gt;*/ /*|0x4|*/ unsigned long TableCode;
  4.   /*&lt;thisrel this+0x4&gt;*/ /*|0x4|*/ struct _EPROCESS* QuotaProcess;
  5.   /*&lt;thisrel this+0x8&gt;*/ /*|0x4|*/ void* UniqueProcessId;
  6.   /*&lt;thisrel this+0xc&gt;*/ /*|0x10|*/ struct _EX_PUSH_LOCK HandleTableLock[4];
  7.   /*&lt;thisrel this+0x1c&gt;*/ /*|0x8|*/ struct _LIST_ENTRY HandleTableList;
  8.   /*&lt;thisrel this+0x24&gt;*/ /*|0x4|*/ struct _EX_PUSH_LOCK HandleContentionEvent;
  9.   /*&lt;thisrel this+0x28&gt;*/ /*|0x4|*/ struct _HANDLE_TRACE_DEBUG_INFO* DebugInfo;
  10.   /*&lt;thisrel this+0x2c&gt;*/ /*|0x4|*/ long ExtraInfoPages;
  11.   /*&lt;thisrel this+0x30&gt;*/ /*|0x4|*/ unsigned long FirstFree;
  12.   /*&lt;thisrel this+0x34&gt;*/ /*|0x4|*/ unsigned long LastFree;
  13.   /*&lt;thisrel this+0x38&gt;*/ /*|0x4|*/ unsigned long NextHandleNeedingPool;
  14.   /*&lt;thisrel this+0x3c&gt;*/ /*|0x4|*/ long HandleCount;
  15.   /*&lt;thisrel this+0x40&gt;*/ /*|0x4|*/ unsigned long Flags;
  16.   /*&lt;bitfield this+0x40&gt;*/ /*|0x1|*/ unsigned char StrictFIFO:0:1;

И восстановим по нему описание структуры:

  1. typedef struct _XP_HANDLE_TABLE
  2.     EX_PUSH_LOCK             HandleTableLock[4];
  3.     LIST_ENTRY               HandleTableList;
  4.     EX_PUSH_LOCK             HandleContentionEvent;
  5.     PHANDLE_TRACE_DEBUG_INFO DebugInfo;
  6.     ULONG                    NextHandleNeedingPool;
  7. } XP_HANDLE_TABLE, *PXP_HANDLE_TABLE;

Из приведенного кода очевидно, что функция ExpLookupHandleTableEntry
извлекает значение TableCode из структуры HANDLE_TABLE и в зависимости от его
двух младших бит определяет число уровней таблицы. Оставшиеся биты представляют
из себя указатель на таблицу первого уровня. Следовательно HANDLE_TABLE в
Windows XP может иметь от одного до трех уровней вложенности, при этом размер
таблицы на любом уровне равен 1FFh. При увеличении количества записей в таблице,
система может автоматически увеличить уровень вложенности. Очевидно, что второй
уровень таблица будет иметь при количестве записей большем 0x200, а третий
уровень — при количестве большем 0x40000. Производит ли система уменьшение числа
уровней таблице при освобождении объектов мне неизвестно, во всяком случае я
такого не наблюдал.

Функция ExLockHandleTableEntry в Windows XP отсутствует, поэтому код
производящий блокировку элемента таблицы находится в функции
ExMapHandleToPointer. Дизассемблируем эту функцию и посмотрим что же она
делает:

  1. PAGE:0048F61E ExMapHandleToPointer proc near          
  2. PAGE:0048F61E var_8           = dword ptr -8
  3. PAGE:0048F61E var_4           = dword ptr -4
  4. PAGE:0048F61E HandleTable     = dword ptr  8
  5. PAGE:0048F61E Handle          = dword ptr  0Ch
  6. PAGE:0048F61E                 mov     edi, edi
  7. PAGE:0048F621                 mov     ebp, esp
  8. PAGE:0048F626                 mov     edi, [ebp+Handle]
  9. PAGE:0048F629                 test    di, 7FCh
  10. PAGE:0048F62E                 jz      loc_4A2A36
  11. PAGE:0048F637                 push    [ebp+HandleTable]
  12. PAGE:0048F63A                 call    ExpLookupHandleTableEntry
  13. PAGE:0048F63F                 mov     esi, eax
  14. PAGE:0048F641                 test    esi, esi
  15. PAGE:0048F643                 jz      loc_4A2711
  16. PAGE:0048F649                 mov     [ebp+var_4], esi
  17. PAGE:0048F64C loc_48F64C:                                                              
  18. PAGE:0048F64C                 mov     ebx, [esi]
  19. PAGE:0048F651                 mov     [ebp+var_8], ebx
  20. PAGE:0048F654                 jz      loc_508844
  21. PAGE:0048F65A                 lea     eax, [ebx-1]
  22. PAGE:0048F65D                 mov     [ebp+Handle], eax
  23. PAGE:0048F660                 mov     eax, [ebp+var_8]
  24. PAGE:0048F663                 mov     ecx, [ebp+var_4]
  25. PAGE:0048F666                 mov     edx, [ebp+Handle]
  26. PAGE:0048F669                 cmpxchg [ecx], edx
  27. PAGE:0048F66C                 cmp     eax, ebx
  28. PAGE:0048F66E                 jnz     loc_50884C
  29. PAGE:0048F674                 mov     eax, esi
  30. PAGE:0048F676 loc_48F676:                            
  31. PAGE:0048F678 loc_48F678:                          
  32. PAGE:0048F67A ExMapHandleToPointer endp

После того, как функция ExpLookupHandleTableEntry возвращает указатель на
HANDLE_TABLE_ENTRY, проверяется младший бит поля Object, и если он не
установлен, то он сбрасывается, а если не установлен, то происходит ожидание его
установки. Следовательно при извлечении адреса объекта нам надо не устанавливать
старший бит (как в Windows 2000), а сбрасывать младший. С учетом вышесказанного
составим код сканирующий таблицу объектов:

  1. void ScanXpHandleTable(PXP_HANDLE_TABLE HandleTable)
  2.     PHANDLE_TABLE_ENTRY Entry;
  3.     ULONG TableCode = HandleTable-&gt;TableCode &amp; ~TABLE_LEVEL_MASK;
  4.     switch (HandleTable-&gt;TableCode &amp; TABLE_LEVEL_MASK)
  5.           for (i = 0; i &lt; 0x200; i++)
  6.               Entry = &amp;((PHANDLE_TABLE_ENTRY)TableCode)[i];
  7.               if (Entry-&gt;Object) ProcessObject((PVOID)((ULONG)Entry-&gt;Object &amp; ~XP_TABLE_ENTRY_LOCK_BIT));
  8.           for (i = 0; i &lt; 0x200; i++)
  9.               if (((PVOID *)TableCode)[i])
  10.                   for (j = 0; j &lt; 0x200; j++)
  11.                       Entry = &amp;((PHANDLE_TABLE_ENTRY *)TableCode)[i][j];
  12.                       if (Entry-&gt;Object) ProcessObject((PVOID)((ULONG)Entry-&gt;Object &amp; ~XP_TABLE_ENTRY_LOCK_BIT));
  13.           for (i = 0; i &lt; 0x200; i++)
  14.               if (((PVOID *)TableCode)[i])
  15.                   for (j = 0; j &lt; 0x200; j++)
  16.                       if (((PVOID **)TableCode)[i][j])
  17.                           for (k = 0; k &lt; 0x200; k++)
  18.                               Entry = &amp;((PHANDLE_TABLE_ENTRY **)TableCode)[i][j][k];
  19.                                 ProcessObject((PVOID)((ULONG)Entry-&gt;Object &amp; ~XP_TABLE_ENTRY_LOCK_BIT));

Итак, с форматом таблиц объектов мы разобрались. Теперь для перечисления
процессов нам нужно найти сам адрес PspCidTable. Как вы уже догадались, искать
его мы будем дизассемблируя функцию PsLookupProcessByProcessId, в которой первый
call будет содержать адрес PspCidTable. А вот и код производящий
поиск:

  1.     for (cPtr = (PUCHAR)PsLookupProcessByProcessId;
  2.          cPtr &lt; (PUCHAR)PsLookupProcessByProcessId + PAGE_SIZE;
  3.         Length = SizeOfCode(cPtr, &amp;pOpcode);
  4.         if (*(PUSHORT)cPtr == 0x35FF &amp;&amp; *(pOpcode + 6) == 0xE8)
  5.             PspCidTable = **(PVOID **)(pOpcode + 2);

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

Получение списка процессов перехватом SwapContext.

Если в системе имеется процесс, то соотвестсвенно он будет иметь свои потоки.
Если нам удастся поймать переключение потоков, то можно будет по полученным
данным построить список процессов. Для начала кратко рассмотрим механизм
переключения потоков. Через определенные промежутки времени (10-15 мс) системный
таймер генерирует прерывание, которое вызывает планировщик и если квант времени
связанный с текущим потоком истек, то происходит переключение потоков. Само
переключение потоков выполняется неэкспортируемой функцией ядра SwapContext,
которую тем не менее можно найти в отладочных символах на ntoskrnl.exe. Эта
функция вызывается планировщиком при истечении кванта времени потока, либо при
ожидании потоком какого-либо события. В первом случае эта функция вызывается из
KiDispatchInterrupt, а во втором случае из неэкспортируемой функции находящейся
глубоко в недрах ядра, которая в свою очередь вызывается из
KeWaitForSingleObject, KeDelayExecutionThread и KeWaitForMultipleObjects.
Параметры функции SwapContext передаются в регистрах и имеют следующее
назначение: cl — определяет режим обработки APC, edi — указатель на поток
отдающий управление, esi — указатель на поток получающий управление, ebx —
указатель на PCR. Нас интересуют только указатели на переключающиеся потоки
передаваемые в регистрах esi и edi. Для начала нам нужно найти адрес функции
SwapContext. Для этого будем дизассемблировать KiDispatchInterrupt и искать код
следующего вида:

  1. .text:00404E76                 call    sub_404C5A
  2. .text:00404E7D                 call    SwapContext

Из этого участка мы и будем извлекать адрес SwapContext. Этот метод поиска
весьма надежен и универсален, так как позволяет найти SwapContext в любой
Windows, начиная он 2000 и заканчивая 2003 server включая все их сервиспаки. А
вот и код осуществляющий поиск SwapContext:

  1. void GetSwapContextAddress()
  2.     for (cPtr = (PUCHAR)KiDispatchInterrupt;
  3.          cPtr &lt; (PUCHAR)KiDispatchInterrupt + PAGE_SIZE;
  4.         Length = SizeOfCode(cPtr, &amp;pOpcode);
  5.         if (*(PUSHORT)pOpcode == 0x01B1 &amp;&amp; *(pOpcode + 2) == 0xE8)
  6.             pSwapContext = (PVOID)(*(PULONG)(pOpcode + 3) + (ULONG)cPtr + 7);

После нахождения адреса SwapContext нам нужно ее перехватить. Единственный
возможный в этом случае способ — это сплайсинг ее кода. Для этого скопируем
первые несколько инструкций с перехватываемой функции в буфер, в его конце
поставим jmp на продолжение, а начало своей функции заменим на jmp на свой
обработчик. При этом если в копируемом в буфер коде нам встретятся команды
содержащие relative offset, то нужно их подкорректировать, иначе мы получим
неизбежный BSOD. Для установки и снятие таких хуков воспользуемся следующим
кодом:

  1. #define MemOpen()  __asm cli; __asm mov eax, cr0; __asm mov oData, eax; \
  2.                    __asm and eax, 0xFFFEFFFF; __asm mov cr0, eax;
  3. #define MemClose() __asm mov eax, oData; __asm mov cr0, eax; __asm sti;
  4. UCHAR SaveOldFunction(PUCHAR Proc, PUCHAR Old)
  5.     Offset = (ULONG)Proc — (ULONG)Old;
  6.         Size = SizeOfCode(Proc, &amp;pOpcode);
  7.         memcpy(oPtr, Proc, Size);
  8.         if (IsRelativeCmd(pOpcode)) *(PULONG)((ULONG)pOpcode — (ULONG)Proc + (ULONG)oPtr + 1) += Offset;
  9.     *(PUCHAR)((ULONG)Old + Result) = 0xE9;
  10.     *(PULONG)((ULONG)Old + Result + 1) = Offset — 5;
  11. PVOID HookCode(PVOID TargetProc, PVOID NewProc)
  12.     Address = (ULONG)NewProc — (ULONG)Proc — 5;
  13.     OldFunction = ExAllocatePool(NonPagedPool, 20);
  14.     *(PULONG)OldFunction = (ULONG)Proc;
  15.     *(PUCHAR)((ULONG)OldFunction + 4) = SaveOldFunction((PUCHAR)Proc, (PUCHAR)((ULONG)OldFunction + 5));
  16.     *(PULONG)((ULONG)Proc + 1) = Address;
  17.     return (PVOID)((ULONG)OldFunction + 5);
  18. void UnhookCode(PVOID OldProc)
  19.     Proc = (PUCHAR)(*(PULONG)((ULONG)OldProc — 5));
  20.     SaveSize = *(PUCHAR)((ULONG)OldProc — 1);
  21.     Offset   = (ULONG)Proc — (ULONG)OldProc;
  22.     memcpy(Proc, OldProc, SaveSize);
  23.     while (ThisSize &lt; SaveSize)
  24.         Size = SizeOfCode(Proc, &amp;pOpcode);
  25.         if (IsRelativeCmd(pOpcode)) *(PULONG)((ULONG)pOpcode + 1) -= Offset;
  26.     ExFreePool((PVOID)((ULONG)OldProc — 5));

Сам обработчик SwapContext будет выглядеть так:

  1. void __declspec(naked) NewSwapContext()

Метод сплайсинга несомненно легок и удобен, но при неправильном применении он
может создать весьма немало проблем. Опасными являются моменты установки и
снятия хуков, так как на многопроцессорной системе (либо на системе с
Hiperthreading процессором) другой поток может вызвать перехватываемую функцию в
когда патч ее кода еще не завершен. Давайте сделаем приблизительную оценку
величины этой вероятности. Средняя частота вызова этой функции на
двухпроцессорном Pentium 4 2400 была равна 785 вызовов в секунду. Допустим, что
патч начала функции занимает 0.01 мкс (на самом деле гораздо меньше), при этом
мы получаем вероятность радения системы равную 0,00000785, тоесть на 127380
запусков программы будет одно падение системы. Для такой программы как детектор
руткитов (который к тому же редко запускают), это вполне приемлемая величина, но
если подобный метод будет использоваться в постоянно применяемой программе
(например антивирусе), то использовать сплайсинг следует исключительно
однократно (установка хуков без их последующего снятия), и только в момент
загрузки системы (в boot драйвере). В этом случае вероятность падения системы
настолько приближена к нулю, что ее можно вообще не принимать в расчет. Пока что
я не наблюдал ни одного случая падения системы из за этой причины. Но существует
еще одна, гораздо более реальная причина нестабильности сплайсинга (и любых
другим методов хуков тоже). Если драйвер допускает выгрузку и при выгрузке
снимает хуки, то выгрузка может произойти в тот момент, когда какой-либо поток
находится внутри обработчика перехвата. Вероятность такого расклада зависит от
содержимого обработчика перехвата и от частоты вызова перехватываемой функции.
Практика показала, что вероятность падения системы при выгрузке драйвера
достаточно велика, и смириться с ней никак нельзя, но этого можно избежать, если
синхронизировать выгрузку драйвера с функцией обработчиком. А еще лучше —
сделать драйвер невыгружаемым, что избавит нас от тормозов при синхронизации.
Как вы видите, при правильном применении сплайсинг может быть абсолютно
безопасным, хотя непонимание его работы может привести к весьма неслабым глюкам,
так что используйте, но с осторожностью.

Приложение:

Все вышеприведенные методы обнаружения скрытых процессов реализованы в моей
программе Process Hunter. Обойти все описаные способы обнаружения будет весьма
непросто, но скорее всего авторы руткитов реализуют ее обход путем перехвата
IOCTL от приложения к драйверу. Надежной защитой от такого обхода будет
использование приватной версии детектора. Так как программа поставляется с
исходниками, вы можете сами изменить все что относиться к IOCTL, названия
модулей, окон и.т.д.

Файл Описание
Process
Hunter (266 кб)
Детектор скрытых процессов Process Hunter

© Ms-Rem

archive
New Member

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

Трансферное обучение с предобученными моделями в Keras с Python

AI_Generated 06.05.2025

Суть трансферного обучения проста: взять модель, которая уже научилась решать одну задачу, и адаптировать её для решения другой, похожей задачи. Мозг человека работает по схожему принципу. Изучив. . .

Циклические зависимости в C#: методы управления и устранения

stackOverflow 06.05.2025

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

Как перейти от Waterfall к Agile

EggHead 06.05.2025

Каскадная модель разработки Waterfall — классический пример того, как благие намерения превращаются в организационный кошмар. Изначально созданная для упорядочивания хаоса и внесения предсказуемости. . .

Оптимизация SQL запросов — Продвинутые техники

Codd 06.05.2025

Интересно наблюдать эволюцию подходов к оптимизации. Двадцать лет назад всё сводилось к нескольким простым правилам: «Избегайте SELECT *», «Используйте индексы», «Не джойните слишком много таблиц». . . .

Создание микросервисов с gRPC и Protobuf в C++

bytestream 06.05.2025

Монолитные приложения, которые ещё недавно считались стандартом индустрии, уступают место микросервисной архитектуре — подходу, при котором система разбивается на небольшие автономные сервисы, каждый. . .

Многопоточность и параллелизм в Python: потоки, процессы и гринлеты

py-thonny 06.05.2025

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

Конкурентность (concurrency) — это когда ваша программа умеет жонглировать. . .

Распределенное обучение с TensorFlow и Python

AI_Generated 05.05.2025

В машинном обучении размер имеет значение. С ростом сложности моделей и объема данных одиночный процессор или даже мощная видеокарта уже не справляются с задачей обучения за разумное время. Когда. . .

CRUD API на C# и GraphQL

stackOverflow 05.05.2025

В бэкенд-разработке постоянно возникают новые технологии, призванные решить актуальные проблемы и упростить жизнь программистам. Одной из таких технологий стал GraphQL — язык запросов для API,. . .

Распознавание голоса и речи на C#

UnmanagedCoder 05.05.2025

Интеграция голосового управления в приложения на C# стала намного доступнее благодаря развитию специализированных библиотек и API. При этом многие разработчики до сих пор считают голосовое управление. . .

Реализация своих итераторов в C++

NullReferenced 05.05.2025

Итераторы в C++ — это абстракция, которая связывает весь экосистему Стандартной Библиотеки Шаблонов (STL) в единое целое, позволяя алгоритмам работать с разнородными структурами данных без знания их. . .

Понравилась статья? Поделить с друзьями:
0 0 голоса
Рейтинг статьи
Подписаться
Уведомить о
guest

0 комментариев
Старые
Новые Популярные
Межтекстовые Отзывы
Посмотреть все комментарии
  • Screen time for windows
  • Как открыть киностудию в windows 10
  • Как активировать windows 11 без учетной записи майкрософт
  • Перезагрузить сервер печати windows 7
  • Run kali linux on windows