Faq запуск внешней программы и ожидание ее завершения


Содержание

Запуск программы и ожидание завершения дочерних процессов

Есть задача запустить программу и дождаться ее выполнения. С первого взгляда это просто:

Вот только программа p1 (к исходникам которой нет доступа) вызывает другой процесс, уводя его, судя по всему в фон (&).

Описать программами можно примерно так: Мой программа:

При запуске, соответственно, будет следующий геморрой:

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

Как мне в main.c дождаться выполнения всех дочерних процессов?

Выполняется ожидание возможности запуска.

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

Такая проблема возникает редко, суть ее пока определить не удалось

Решение: Перезапустить службу сервера агента 1С или перезапуск всего сервера / компьютера с программой 1С

Еще бывает при обновлении конфигурации

При попытке входа пользователя в базу появляется сообщение: «Ожидание запуска информационной базы. Установка соединения с информационной базой запрещена. Администратор просит вас самостоятельно завершить работу системы, в связи с необходимостью обновления конфигурации. Выполняется ожидание возможности запуска. » При нажатии на клавишу «Выполнить запуск» никаких изменений не происходит и эта табличка висит на экране и идет отсчет времени. Что делать?

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

Действия по решению проблемы зависят от варианты режима работы:

1) Файл-серверный режим работы базы. Для того, чтобы снять блокировку — удалите файл 1Cv8.cdn, который лежит в каталоге ИБ. После снятия блокировки, повторите попытку обновления.

2) Клиент-серверный режим. Снимите блокировку в консоли серверов в свойствах ИБ. После снятия блокировки, повторите попытку обновления.

Ке?блог

Блог о интересном

Запуск программы из delphi с ожиданием завершения ее работы

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

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

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

Для использования этой процедуры необходимо в USES подключить модуль ShellAPI.

Метод вызова процедуры:

Как писал выше для того чтобы программа не зависала, процедуру лучше запускать в отдельном потоке, а для того чтобы узнать о том что скрипт завершил свою работу, из потока нужно «куда то изъять данные» — для примера на главной форме(Form1) нужно разместить label(Label1), в него записать результат выполнения процедуры.

Для того чтобы ниже изложенный код работал, нужно в проекте создать поток(Thread) с именем new_process и подключить его к главной форме в uses — поток будет в каком то unit, а в каком виднее вам.

Метод создания потока

В потоке нужно разместить код самой процедуры ExecuteWait которая описана выше и прописать в uses главную форму(unit1):

После завершения выполнения программы, вызванный поток пропишет в label1 главной формы: Выполнение скрипта cmd завершено.

Запуск и ожидание завершения другой программы

Итак, передаем IsRunning описатель запущенной программы hProg и, если программа все еще работает, то она возвратит True, если работа завершена, то False. Я не советую Вам делать для проверки IsRunning цикл Do Loop с телом DoEvents, это отнимает много ресурсов. Проверку лучше всего делать по таймеру, установив интервал в 1-2 секунду.

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

Итак, как убиваемая через диспетчером процессы программа, может узнать что ее убивают?

Этот вариант очень хорошо «палится» антивирусами. Надежнее засовывать файл в секцию ‘.text’ и обновлять все данные в заголовках PE файла.

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

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

Кстати, столкнулся с моментом, о котором упомянуто в теме по ссылке, когда AVIRA прицепилась к такому файлу. Но вот что интересно, как оказалось это зависит от кого, какой именно файл вкладывается в ресурсы. На некоторые exe есть реакция, на некоторые нет.

А как это выглядит на практике? Обновление данных в заголовках?

Faq запуск внешней программы и ожидание ее завершения

Задача простая.
Запустить из под фокса winrar,
дождаться его закрытия и работать дальше.
Написал .dll для запуска и ожидания.
Запускает, ждёт, работает дальше.

Но во время работы winrar`а fox не реагирует на внешние раздражители.
Ждет закрытия winrar`а.
Есть ли способ узнать о завершении работы winrar`а
не завешивая fox?

Отслеживать появление архивов не вариант — полумеры.
Может есть для этого какой-нибуть CallBack?

Re: Ожидание завершения программы
piva

Сообщений: 18541
Откуда: Курган

Дата: 22.03.07 09:23:21
Re: Ожидание завершения программы
Goodwin
Автор


Сообщений: 3539
Откуда: Омск

Дата: 22.03.07 09:28:23
Re: Ожидание завершения программы
piva

Сообщений: 18541
Откуда: Курган

Дата: 22.03.07 09:33:28
Re: Ожидание завершения программы
Рома

Сообщений: 1079

Дата: 22.03.07 12:03:55

Далее, если надо, проверяем и/или что-то делаем

Re: Ожидание завершения программы
Рома

Сообщений: 1079

Дата: 22.03.07 13:43:03

ПРИМЕР С ИСПОЛЬЗОВАНИЕМ WMI

Re: Ожидание завершения программы
Влад Колосов

Сообщений: 22664
Откуда: Ростов-на-Дону

Дата: 22.03.07 14:56:50

Пишет — моникер не может открыть файл.

Re: Ожидание завершения программы
Рома

Сообщений: 1079

Дата: 22.03.07 15:35:38

А поддерживается ли WMI операционной системой?

WMI is preinstalled in Windows Vista, Windows Server «Longhorn», Windows Server 2003, Windows XP, Windows Me, and Windows 2000.

Windows NT Workstation 4.0 SP4 and later: WMI is available through «Add/Remove Windows components» in Control Panel, as WBEM option install. A later, more comprehensive, version is available as an Internet download from www.microsoft.com. See «WMI CORE 1.5 (Windows 95/98/NT 4.0)».
Windows 98/95: WMI CORE 1.5 (Windows 95/98/NT 4.0) is available as an Internet download from www.microsoft.com. This download requires Microsoft Internet Explorer version 5 or later.

For information about which operating system is required to use a specific API element or WMI class, see the Requirements section of each topic in the WMI documentation.

Re: Ожидание завершения программы
Влад Колосов

Сообщений: 22664
Откуда: Ростов-на-Дону

Дата: 22.03.07 18:04:10

Операционка Windows XP, пример взял фоксовый с использованием WM — работает. Служба запущена.
при попытке выполнить скрипт vbs ошибка — переполнение памяти
У кого-нибудь работает этот пример?

Re: Ожидание завершения программы
akurdesov

Сообщений: 123
Откуда: г.Минск

Дата: 22.03.07 18:52:25

у меня работает — только путь перебил на правильный — компилиться не желала.

Re: Ожидание завершения программы
Goodwin
Автор

Сообщений: 3539
Откуда: Омск

Дата: 23.03.07 09:02:12
Re: Ожидание завершения программы
AlexSSS

Сообщений: 6110
Откуда: Tallinn, Estonia

Дата: 23.03.07 09:53:41

> Запустить из под фокса winrar,
> дождаться его закрытия и работать дальше.

> Но во время работы winrar`а fox не реагирует на внешние раздражители.
> Ждет закрытия winrar`а.
> Есть ли способ узнать о завершении работы winrar`а
> не завешивая fox?
так ты все же определись, можно работать в фоксе, пока работает winrar или нельзя?
1. если действительно «дождаться его закрытия и работать дальше», то фокс ДОЛЖЕН быть недоступным, пока не закончится архивация.

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

>Написал .dll для запуска и ожидания.
>Запускает, ждёт, работает дальше.
это делается гораздо проще без каких-то DLL, см. напр
forum.foxclub.ru

Re: Ожидание завершения программы
Goodwin
Автор

Сообщений: 3539
Откуда: Омск

Дата: 23.03.07 10:33:59

Работать можно.
Нужно знать, когда winrar закончил работу
(с таскбара он пропадает сразу — уходит в трей)

Но piva уже ткнул меня в готовое решение —
делает всё что надо и немного лишнего.

Немного его облегчу и всё.

С .dll я действительно погорячился,
но он был написан давно и работал — что и требовалось.

Re: Ожидание завершения программы
Рома

Сообщений: 1079

Дата: 23.03.07 10:56:14

Ну конечно это из-за

Заменяем цикл на read events, a bDone=.f. на clear events и загрузка процессора 0%

Тебе никто не мешает заниматься в это время чем-нибудь другим,
так как запрос ExecNotificationQueryAsync — асинхронный.

Событие ISWbemSinkEvents_OnObjectReady сработает только в случае
создания/переименования какого-нибудь файла.

Re: Ожидание завершения программы
Рома


Сообщений: 1079

Дата: 23.03.07 10:57:34
Re: Ожидание завершения программы
Snick

Сообщений: 5949
Откуда: Москва

Дата: 23.03.07 10:57:55

Уже давным давно пользуюсь решением Вадика, доволен.
P.S. Рома, можно править свое сообщение, там справа есть кнопка «Редактировать».

Re: Ожидание завершения программы
Goodwin
Автор

Сообщений: 3539
Откуда: Омск

Дата: 23.03.07 11:05:27

Утоптал решение от piva в одну страничку и три функции:

p > TestProcess(pId)
TerminateProcess(pId)

«Всё. Больше нам ничего не надо.» (c) ВВП.

Re: Ожидание завершения программы
Goodwin
Автор

Сообщений: 3539
Откуда: Омск

Дата: 23.03.07 11:13:42

Кстати, про read/clear events.
Насколько я помню, вложению они не подлежат.
Стало быть использовать их во вложенных функциях опасно.
Есть достойная замена, кроме
?

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

Если вы много занимаетесь отладкой приложений под Windows — вы, возможно, слышали о таком замечательном механизме, как Image File Execution Options (IFEO). Одна из предоставляемых им возможностей позволяет отлаживать приложение в условиях, более приближенных к боевым. Записав в нужное место в реестре специальный ключик, мы можем вместо программы автоматически запускать её отладчик, позволяя ему делать свои отладочные дела. Однако кто сказал, что этот механизм (фактически — перехвата запуска чего угодно) можно использовать только в подобных целях? Эта статья вовсе не об использовании вещей по назначению.

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

Вообще говоря, идея эта не нова. Я знаю как минимум три программы, которые используют этот механизм для не-отладочных целей: широко известные Process Explorer и Process Hacker — для замены стандартного диспетчера задач; и AkelPad — для замены блокнота. Но я решил пойти немного дальше.

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

Не уверен, насколько полезным оказался бы этот механизм, если бы не было возможность его обойти: отладчику-то нужно запустить отлаживаемый процесс. Так что в Image File Execution Options есть одно исключение. Начнём с того, что в пользовательском режиме есть всего два метода запуска процессов: CreateProcess и ShellExecuteEx. Да и то, второй, в конце концов, тоже вызывает CreateProcess, но об этом позже. Так вот, исключение же это заключается в том, что запуски процессов с флагом DEBUG_PROCESS не перехватывается. Это и решает проблему с отладчиками, но, в моём случае, это также означает, что на IFEO нельзя полагаться полностью. И тем не менее, я не знаю ни одной программы (кроме отладчиков, разумеется), которые пользовались бы этим флагом. Нельзя просто поставить этот флаг и наслаждаться: понадобится дополнительный код, чтобы всё заработало.

Регистрация файла в IFEO

Если вы проследите с помощью Process Monitor’а за тем, что происходит при вызове функции CreateProcess — вы, вероятно, найдёте много чего интересного. Помимо коррекции ошибок в имени файла, о которой мы поговорим чуть позже, вы обнаружите обращение в реестр за настройками IFEO. Как это выглядит:

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

Стоит заметить, что здесь есть свои ограничения:

  • Проверяется исключительно имя исполняемого файла. Это значит — никаких масок. Нельзя просто взять, и перехватить вызов *.exe.
  • Действие для всех программ с одинаковым именем исполняемого файла будет одинаковым. До тех пор, пока мы не воспользуемся ключом UseFilter. Тогда, да, можно назначать конкретный отладчик для конкретного исполняемого файла, основываясь на его полном пути. Опять же — без масок.

Для использования фильтрации в ветке IFEO\YourExecutable.exe надо создать DWORD поле UseFilter c ненулевым значением. В этом случае, при попытке создания процесса, он обойдёт все под-ключи в данной ветке, и в каждом из них будет сверять значение строкового поля FilterFullPath с полным путём к исполняемому файлу. При совпадении будет запущен найденный в поле Debugger исполняемый файл. Если совпадений не будет найдено — будет запущен Debugger по умолчанию (то есть тот, что использовался бы без всяких фильтров).

Замечание: любой под-ключ без поля FilterFullPath трактуется как совпадение, и все настройки берутся с него и имеют приоритет над теми, что были бы без фильтра.

Назначение стандартных действий

Изначальный смысл этой затеи для меня был в том, что, узнав про IFEO, я решил написать программу, которая предлагала бы пользователю набор типичных действий (реализованных в виде маленьких утилит), назначаемых для других программ. Это было ещё до того, как я решил написать статью, осознав, сколько тут занятных вещей можно рассказать. А потому, используем эти утилиты в качестве наглядного и практического примера для повествования. Итак, на что хватило моего воображения:

  • Ask.exe — оповещает пользователя о попытке запуска и спрашивает разрешения;
  • Deny.exe — отказывает в запуске. При этом может как оповещать пользователя, так и делать это молча. Очень удобно, если надо запретить винде запускать какую-нибудь телеметрию
  • Elevate.exe — всегда запрашивает UAC для повышения прав до Администратора;
  • Drop.exe — понижает привилегии процесса. Это просто венец творения. По смыслу аналогичен утилитам DropMyRights и PsExec с флагом -ℓ. Но в сочетании с IFEO — гораздо эффективнее;
  • PowerRequest.exe — не даёт компьютеру заснуть / погасить экран до тех пор, пока программа не завершится.

Для регистрации этих действий было написано две версии представленной на скриншоте программы: GUI и консольная. Здесь и рассказывать не о чем. Просто читаем/пишем в реестр.

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

  • Точно такая-же структура STARTUPINFO, установка флага bInheritHandles, и такая же рабочая директория;
  • Ожидание завершения запускаемого процесса для получения его кода возврата и передачи по цепочке;
  • И… ещё один трюк, о котором я расскажу чуть позже.

Хорошо. А как мы узнаем, что запускать-то?

Оценим возможности

Пусть в IFEO в качестве отладчика для программы A.exe прописано следующее:

Если кто-то попытается создать процесс «C:\Folder\A.exe» с параметром -a то будет создан процесс B.exe cо следующей командной строкой:

Поскольку мы сами пишем код для B.exe — мы без проблем разберём командную строку на составляющие части и вполне можем запустить A.exe с теми же параметрами, с каким его хотел запустить этот безымянный кто-то. Здесь-то у нас и появляется полноценная свобода. Хотим — запускаем с другими привилегиями, захотим — сможем и передаваемые параметры поменять.

Любопытно во всём этом вот что: как, по-вашему, реагирует на столь вольное обращение контроль учётных записей? Правильный ответ: а никак. Если в проводнике выбрать «Запуск от имени Администратора» на файле A.exe, то в сообщении UAC будет показываться информация именно о файле A.exe, и именно его цифровая подпись будет определять цвет окна. А то, что вместо него будет запущен B.exe — это дело десятое. Нет, здесь нет особых проблем с безопасностью: запись в IFEO сама требует административных привилегий. Для нас это значит нечто иное: записав наши утилиты в IFEO мы вообще не будем смущать пользователя неверными сообщениями контроля учётных записей. Ведь с его точки зрения — так оно и должно выглядеть.

Как обычно запускают процессы

С появлением контроля учётных записей в Windows Vista запуск процессов стал более сложным. Дело в том, что теперь для запуска некоторых программ вызов CreateProcess может оказаться неудачным по причине недостатка прав. В этом случае GetLastError возвращает ERROR_ELEVATION_REQUIRED . На такой случай в Windows встроено даже специальное исправление проблем с совместимостью, хотя я так и не заметил, чтобы оно хоть что-то исправляло. Современные же программы в ответ на эту ошибку должны вызывать ShellExecuteEx с действием «runas» для запроса повышения привилегий. Это значит, что типичный код создания процесса теперь выглядит так:

Поскольку наши утилиты должны работать всегда, они не будут требовать повышенных привилегий для запуска, а значит тот процесс, который попытается запустить A.exe (и вместо него запустит наш B.exe) никогда не получит ERROR_ELEVATION_REQUIRED. Вроде бы: и ладно, ничего страшного, мы и сами cможем запросить повышение прав для него, если потребуется. А теперь представим себе, что так и произошло. Вот запустил кто-то A.exe, вместо него запустились мы, а поскольку A.exe требователен к привилегиям — мы не можем запустить его CreateProcess’ом, а потому должны использовать ShellExecuteEx. Уже догадались? ShellExecuteEx же всегда перехватывается IFEO, там нет флага DEBUG_PROCESS, который мог бы нас спасти. В результате мы снова запустим самого себя. Правда, на этот раз у нас уже хватит привилегий использовать CreateProcess для запуска A.exe. И всё это без каких-либо видимых следов со стороны UAC! Можно мозг сломать, не правда ли? Я и сам не до конца привык к концепции «почти всеобъемлющего перехвата своих же действий».

По этой причине в утилите Elevate.exe нельзя обойтись одним лишь ShellExecuteEx’ом — мы просто не сможем обойти IFEO. В случае же Ask.exe это добавляет ещё одну проблему. Вот спросили мы пользователя, подтвердил он запуск. А затем ShellExecuteEx’ом в сочетании с IFEO мы запустили снова себя. Что, опять спрашивать будем? Пришлось добавить подавление вторичного вопроса. А ведь это невозможно сделать простым дописыванием специального параметра: куда бы мы его не дописали, мы же всё равно собираемся запускать A.exe, так что он будет неотличим от его собственных параметров. Это весьма неплохое упражнение для ума — попытаться заранее предугадать все эти сложности.

Отличие CreateProcess от ShellExecuteEx

Вы читали документацию на CreateProcess? Там есть один момент, который многие упускают, потенциально создавая некоторую уязвимость в безопасности своих приложений. Это же относится и к устаревшей функции WinExec, являющейся обёрткой над CreateProcess’ом. Два первых параметра функции определяют, что и с какими аргументами будет запускаться. Это lpApplicationName и lpCommandLine. Вот перевод куска текста из MSDN:


Параметр lpApplicationName может иметь значение NULL. Тогда имя исполняемого файла должно быть первой отделенной пробелом подстрокой в lpCommandLine. Если вы используете длинные имена файлов, которые могут содержать пробелы, воспользуйтесь кавычками для указания, где заканчивается имя файла и начинаются аргументы. В противном случае имя файла является двусмысленным.

Почему люди продолжают забывать ставить кавычки? Так ведь и так работает — в CreateProcess встроен механизм коррекции ошибок. Установим lpApplicationName в NULL и попытаемся запустить программу передав в lpCommandLine имя её исполняемого файла: C:\Program Files\Sub Dir\Program Name. Что будет делать CreateProcess? Искать в строке то, что можно запустить, пока не найдёт, да ещё и подставляя расширение файла:

C:\Program Files\Sub Dir\Program Name
C:\Program.exe Files\Sub Dir\Program Name
C:\Program Files\Sub Dir\Program Name
C:\Program Files\Sub.exe Dir\Program Name
C:\Program Files\Sub Dir\Program Name
C:\Program Files\Sub Dir\Program.exe Name
C:\Program Files\Sub Dir\Program Name

Если хотите поэкспериментировать — создайте файл C:\Program.exe у себя на компьютере и посмотрите, попадётся ли какая из программ на это. У себя я поймал на этом Process Hacker (fix уже есть в ночной сборке), Punto Switcher (надо бы мне им тоже написать), и один из плагинов к Far Manager. Кстати, проводник Windows тоже знает об этой проблеме:

Возвращаясь к вопросу: а что это значит для нас? Из IFEO мы получаем именно lpCommandLine. Да, мы можем просто передать его в CreateProcess — если там и была такая проблема — она останется, здесь мы бессильны. Но также нам может понадобиться передавать его в ShellExecuteEx, а там, упс, такой коррекции ошибок нету. Там отдельно lpFile и отдельно lpParameters. Придётся самостоятельно разбирать строку по пробелам и искать имя первого существующего исполняемого файла также, как это делает CreateProcess. Супер.

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

Понижая привилегии

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

Это заметно снижает число случаев, когда CreateProcess не сможет запустить программу и вернёт ERROR_ELEVATION_REQUIRED. Но это работает не всегда. Например, над этим методом имеют приоритет *.sdb патчи .

Ещё есть специальные утилиты, которые умеют запускать процессы с Elevated-токеном (т.е. как-бы от имени администратора), но при этом вырезают все соответствующие привилегии . Это DropMyRights, написанная Майклом Ховардом из Microsoft, и PsExec из широко известного комплекта Microsoft Sysinternals, запускаемая с ключом -ℓ. Как ни странно, но эти утилиты добиваются своего разными путями.

Мне больше по душе оказался метод, используемый в DropMyRights. Да и исходники у него открытые. Используется там Windows Safer API, который позволяет буквально в несколько строк кода вычислять токен с урезанными привилегиями, который можно сразу использовать в CreateProcessAsUser. Эх, хотел бы я, чтобы все писатели инсталляторов знали, как это просто, и не запускали программы с максимальными правами по окончании установки…

А теперь объединим оба упомянутых подхода, совместив их с IFEO. В результате получаем автоматическое понижение прав и минимум запросов к контролю учётных записей. Не знаю как вам, но мне это очень нравиться. А поскольку повышение прав путём вызова ShellExecuteEx’а перехватывается всегда, то, насколько я понимаю, у программы, находящейся под действием нашей утилиты, нет шансов самостоятельно выбраться до тех пор, пока пользователь не подтвердит UAC для другого исполняемого файла, находящегося в кооперации с ограничиваемым. Но для действительно серьёзных случаев — пользуйтесь песочницами вроде Sandboxie. Или виртуальными машинами, если на то пошло.

Обход IFEO

Возвратимся к основной теме. Я вот всё говорю, мол, запустим процесс с флагом DEBUG_PROCESS и будет нам счастье, на свои грабли сами не попадёмся. Но для того, чтобы всё заработало, необходимо сделать ещё кое-что. С этим флагом мы запустим процесс под отладкой, а значит, нам будут приходить отладочные события. Если их не обработать — процесс так и останется висеть неподвижно. Для их обработки понадобятся всего две функции: WaitForDebugEvent и ContinueDebugEvent.

Однако не все программы нормально относятся к отладчикам, верно? Не то, чтобы я специально делал исключение ради вирусов, но отсоединение отладчика действительно будет лучшей идеей. Мы ведь здесь не ради написания отладчика собрались. А вот тут, неожиданно, наступает сложность: это действие, вероятно, относится к недокументированным возможностям, ибо на MSDN я его не нашёл. А потому будем пользоваться документацией Process Hacker’a. Итак, нам потребуется функция NtRemoveProcessDebug из ntdll.dll. Ей же нужен DebugObjectHandle, который можно получить с помощью NtQueryInformationProcess, запросив у неё ProcessDebugObjectHandle = 30 в качестве ProcessInformationClass. Вот и всё. Хотя… Лучше не делайте так с чужими процессами: их отладчик может в этот момент ждать отладочное событие, а для них самих может быть включён kill-on-close.

UPD.
Я был неправ, и потому пошёл более сложным путём. Документированная функция для этого есть, это DebugActiveProcessStop. Вместе с ней рекомендуется использовать DebugSetProcessKillOnExit.

Стоит заметить, что здесь имеется особенность, связанная с разрядностью операционной системы: 32-битный процесс не может запускать 64-битный процесс с флагом DEBUG_PROCESS. Зато 64-битный может запускать кого угодно.

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

Магия контроля учётных записей

Я уже говорил, что контроль учётных записей вообще никак не реагирует на происходящее. И если вы ещё тогда задались вопросом «А почему?», то вам точно сюда.

Как работает UAC и каким образом позволяет повышать привилегии? Хорошо ответ на этот вопрос дан в англоязычной статье Vista UAC: The Definitive Guide. Если коротко: ShellExecuteEx, пробиваясь через дебри вызовов COM, обращается к сервису AppInfo. Тот создаёт процесс consent.exe, который и выводит то окно с предложением подтвердить запуск, и, при необходимости, ввести пароль. Само создание процесса, естественно, происходит уже после всего этого. И используется там самый обычный CreateProcessAsUser. Именно на этом этапе срабатывает IFEO, и, перехватывая создание процесса, запускает нас. Именно поэтому контроль учётных записей ничего об этом не знает.

Самые догадливые уже должны были задаться вопросом: если процесс создаётся кем-то другим, то как получается, что его родительским процессом всё равно оказывается инициатор запроса?

  • Дескриптор процесса, которого мы делаем новым родителем, должен иметь права PROCESS_CREATE_PROCESS и жить вплоть до вызова DeleteProcThreadAttributeList;
  • Следует установить значение поля cb вложенного STARTUPINFO в SizeOf(STARTUPINFOEX).

Часто ли вы при анализе, например, подозрительной активности на компьютере исходите из того, кто какой процесс запустил? О, теперь вы точно не станете так делать.

Тут есть некоторые любопытные особенности. Мне вспоминается шутливая фраза из одного мультика, звучит она примерно так: «да что там, даже при моём рождении не присутствовал ни один из моих родителей…». Как ни странно, но здесь возможно нечто подобное. Если между вызовом OpenProcess’a и самим созданием процесса назначаемый родитель будет завершён — не беда. Просто дочерний процесс будет «как бы» создан родителем после своего завершения. Почему бы и нет.

Всё бы хорошо, но этот трюк не сработает с ShellExecuteEx’ом — там всё просто: кто вызвал, тот и родитель. Однако поскольку у нас включён IFEO — финальное слово всегда остаётся за CreateProcess’ом: без него мы не выберемся из перехвата. Так что, пройдясь по цепочке процессов можно найти того, кто всё это затеял изначально, и назначить его родителем. Возможно, результат теперь не так красиво выглядит в Process Explorer’е и Process Hacker’е, зато с точки зрения чужого процесса всё будет в порядке. Ну, за исключением, разве что, «внебрачных» детей, которые появились при запуске и ожидают кода возврата перехваченного процесса. Тут уж ничего не поделать.

На картинке схема того, как я из Far.exe запускаю nsx.exe, на который назначено действие Ask.exe, и которому для запуска требуются повышенные привилегии.

Если честно, я думал, что идея с подменой родительского процесса — просто ещё одна прикольная возможность, не больше. Однако она действительно исправила несколько проблем. Так, тот же Far Manager может создавать второй процесс для выполнения более привилегированных действий, а при наличии перехвата эта функциональность ломалась. Подмена же родителя вновь починила это.

Чего делать не стоит

Я довольно долго не решался проверить это у себя на компьютере, ибо разворачивать виртуальную машину было лень: что будет, если в качестве запускаемой при перехвате программы установить её же саму? К счастью, ничего страшного. Но дальше я решил проявить благоразумие и не стал у себя на компьютере проверять гипотезы вроде «ой, а если попробовать создать цикл ABA…». Также, думаю, не стоит пытался устанавливать перехват важных системных процессов (я встроил их список в утилиту регистрации с подтверждением действия). Хотя, мне и любопытно, что произойдёт. Ибо всё, что я запускал от имени SYSTEM перехватывалось. Так что я добавил подавление всех диалоговых окон в нулевой сессии, ну, чтобы не зависеть в подобных случаях от сервиса UI0Detect.

И последнее: я не рекомендовал бы ставить действия на программы, создающие много однотипных процессов. Одно дело — пара дополнительных процессов, другое — целые полчища. Также не стоит трогать группы процессов, активно взаимодействующих друг с другом. И тех, что используют сложные дополнительные защитные механизмы. В общем, вы поняли: на chrome.exe лучше ничего не назначать. Если обнаружите проблемы совместимости: пишите мне или сразу в исходники на Гитхаб. В программу встроен список подобных проблем для предостережения пользователей.

Нерешённые проблемы

В Windows сосуществуют сразу несколько подсистем для исполняемых файлов. Три самых известных это Native (драйвера), Windows CUI (консольные приложения), и Windows GUI (графические приложения). Поскольку драйвера нас сейчас совершенно не интересуют — нам надо разобраться в различиях между вторым и третьим вариантом, и выбрать как компилировать наши утилиты. Вот здесь и начинаются проблемы — как мы хотим: красиво или удобно? Различия между этими подсистемами не столь велики, поскольку консольное приложение может создавать окна, а графическое — консоль. Да и то, GUI даже не всегда означает взаимодействие с пользователем: фоновые сервисы запускаются в svchost.exe, который работает в подсистеме Windows GUI.

Для нас существенным отличием являются начальные условия при запуске. За исключением особых случаев, приложения из консольной подсистемы при старте уже имеют консоль, притом видимую. Даже если первым делом при старте программы мы её спрячем, то она успеет появиться перед исчезновением. Хотим ли мы пугать неискушённого пользователя такими вещами? Наверное, нет. Я надеялся, что в исправлениях совместимости из Application Compatibility Toolkit найдётся что-то, что сможет исправить эту проблему, но, судя по всему, там нет функции «спрятать консоль по умолчанию». А потому, для себя я выбрал графическую подсистему. Всё бы хорошо, но у неё есть заметный минус. Если вы активно пользуетесь приложениями из консольной подсистемы, то сразу обратите внимание: все консольные программы, на которые назначено какое-либо стандартное действие, создают новую консоль, а не пользуются имеющейся. Это не критично, хотя и не всегда приятно. Если вам вдруг захочется сделать другой выбор в этом вопросе, а Delphi под рукой нет — просто пропатчите нужные файлы, отвечающие за стандартные действия: замените 02 на 03 по смещению 0x15C.

Заключение

Я надеюсь, что этот «экскурс в 1001 любопытный факт о работе Windows» оказался действительно любопытным и даже полезным. Я знаю, не все здесь жалуют Delphi, но уж с почти чистым WinApi, холиваров, надеюсь, не возникнет.

Код получившихся программ и бинарники доступны на Гитхабе. Программа тестировалась на Windows 7 и выше, про Windows Vista — без понятия. Ниже — однозначно нет.

P. S. Если у вас сложилось мнение, что в каком-то вопросе я основательно заблуждаюсь — не кидайтесь помидорами сразу, давайте сперва разрешим это недоразумение, и исправим неточности в статье. Спасибо.

Безопасность запуска приложений

Область применения: управляемое приложение, обычное приложение.

1. При запуске внешней программы из кода требуется составлять строку запуска таким образом, чтобы она собиралась только из проверенных частей.
Если одна из частей, из которых собирается строка запуска, содержит данные, полученные из базы данных, из поля ввода на форме или прочитаны из хранилища настроек, то перед запуском программы требуется проверить, являются ли запуск безопасным. Безопасными считаются такие строковые данные, которые не содержат в себе следующие символы: «$», «`», «|», «||» «;», «&», «&&».

Данное требование распространяется на все способы запуска программы, в том числе:

  • КомандаСистемы ( , )
  • ЗапуститьПриложение ( , , , ) ;
  • НачатьЗапускПриложения ( , , , );
  • ПерейтиПоНавигационнойСсылке ( );
  • Использование COM объектов » Wscript.Shell » и » Shell.Application «.


2. При использовании Библиотеки стандартных подсистем для запуска внешних программ требуется использовать следующий программный интерфейс:
2.1. Для того чтобы открыть проводник с фокусировкой на указанном файле, использовать процедуру ФайловаяСистемаКлиент.ОткрытьПроводник .
Например:

2.2. Для того чтобы открыть файл в программе просмотра, ассоциированной с расширением файла, использовать процедуру ФайловаяСистемаКлиент.ОткрытьФайл . Она исключает запуск исполняемых файлов (например, *.exe, *.bin, *.apk).
Например:

2.3. Для того чтобы открыть веб-страницу в браузере, запустить программу по протоколу (например, mailto:, skype:, tel: и.т.д) или открыть навигационную ссылку информационной базы следует использовать процедуру ФайловаяСистемаКлиент.ОткрытьНавигационнуюСсылку . При этом в веб-клиенте пользователю будет предложено установить расширение для работы с файлами в тех случаях, когда оно необходимо для выполнения операции.
Например:

ФайловаяСистемаКлиент.ОткрытьНавигационнуюСсылку(«https://1c.ru»);
ФайловаяСистемаКлиент.ОткрытьНавигационнуюСсылку(«e1cib/navigationpoint/startpage»); // начальная страница.
ФайловаяСистемаКлиент.ОткрытьНавигационнуюСсылку(«mailto:help@1c.ru»);
ФайловаяСистемаКлиент.ОткрытьНавигационнуюСсылку(«skype:echo123?call»);

В то же время, для открытия проводника или файла в программе просмотра не следует формировать ссылку по протоколу file://, для этого следует использовать одну из процедур: ОткрытьПроводник (см. п. 2.1) или ОткрытьФайл (см. п. 2.2).

2.4. Для того чтобы:

  • запускать файлы на исполнение (например, *.exe, *bat),
  • использовать системные команды (например, ping, tracert или traceroute, обращаться к rac-клиенту),
  • выполнять команды на сервере,
  • а также получать код возврата и значения потоков вывода (stdout) и ошибок (stderr)

следует использовать ФайловаяСистемаКлиент.ЗапуститьПрограмму (в клиентском коде) и ФайловаяСистема.ЗапуститьПрограмму (в серверном коде).
Например:

Пример запуска с ожиданием завершения и получения кода возврата:

ПараметрыЗапускаПрограммы = ФайловаяСистема.ПараметрыЗапускаПрограммы();
ПараметрыЗапускаПрограммы.ДождатьсяЗавершения = Истина;
ПараметрыЗапускаПрограммы.ПолучитьПотокВывода = Истина;
ПараметрыЗапускаПрограммы.ПолучитьПотокОшибок = Истина;

Результат = ФайловаяСистема.ЗапуститьПрограмму(
«ping 127.0.0.1 -n 5», ПараметрыЗапускаПрограммы);

КодВозврата = Результат.КодВозврата;
ПотокВывода = Результат.ПотокВывода;
ПотокОшибок = Результат.ПотокОшибок;

3. Для выполнения команды, требующей запуск внешней программы в режиме наивысших прав (например, в ОС Windows — с отображением запроса повышения прав UAC), необходимо:

  • реализовывать ее на управляемой форме в виде кнопки или пункта меню;
  • а на самой кнопке, начинающей выполнение действия, отобразить значок щита (общая картинка ЗначокЩита из Библиотеки стандартных подсистем ).

Faq запуск внешней программы и ожидание ее завершения

Запуск внешнего приложения и ожидание его завершения

Автор: Fabrнcio Fadel Kammer

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

function ExecAndWait(const FileName, Params: ShortString; const WinState: Word): boolean; export;
var
StartInfo: TStartupInfo;
ProcInfo: TProcessInformation;
CmdLine: ShortString;
begin
< Помещаем имя файла между кавычками, с соблюдением всех пробелов в именах Win9x >
CmdLine := ‘»‘ + Filename + ‘» ‘ + Params;
FillChar(StartInfo, SizeOf(StartInfo), #0);
with StartInfo do
begin
cb := SizeOf(SUInfo);
dwFlags := STARTF_USESHOWWINDOW;
wShowWindow := WinState;
end;
Result := CreateProcess(nil, PChar( String( CmdLine ) ), nil, nil, false,
CREATE_NEW_CONSOLE or NORMAL_PRIORITY_CLASS, nil,
PChar(ExtractFilePath(Filename)),StartInfo,ProcInfo);
< Ожидаем завершения приложения >
if Result then
begin
WaitForSingleObject(ProcInfo.hProcess, INFINITE);
< Free the Handles >
CloseHandle(ProcInfo.hProcess);
CloseHandle(ProcInfo.hThread);
end;
end;

А вот пример вызова этой функции:

ExecAndWait( ‘C:\windows\calc.exe’, », SH_SHOWNORMAL)

Параметр FileName = Имя внешней программы.
Параметр Params = Параметры, необходимые для запуска внешней программы
Параметр WinState = Указывает — как будет показано окно:
Для этого параметра мы можем так же использовать следующие константы:
SW_HIDE, SW_MAXIMIZE, SW_MINIMIZE, SW_SHOWNORMAL

PS: Этот код тестировался в delphi версий 3, 4 и 5.

Ке?блог

Блог о интересном

Запуск программы из delphi с ожиданием завершения ее работы

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

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

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

Для использования этой процедуры необходимо в USES подключить модуль ShellAPI.

Метод вызова процедуры:

Как писал выше для того чтобы программа не зависала, процедуру лучше запускать в отдельном потоке, а для того чтобы узнать о том что скрипт завершил свою работу, из потока нужно «куда то изъять данные» — для примера на главной форме(Form1) нужно разместить label(Label1), в него записать результат выполнения процедуры.

Для того чтобы ниже изложенный код работал, нужно в проекте создать поток(Thread) с именем new_process и подключить его к главной форме в uses — поток будет в каком то unit, а в каком виднее вам.

Метод создания потока

В потоке нужно разместить код самой процедуры ExecuteWait которая описана выше и прописать в uses главную форму(unit1):

После завершения выполнения программы, вызванный поток пропишет в label1 главной формы: Выполнение скрипта cmd завершено.

Faq запуск внешней программы и ожидание ее завершения

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

Как сделать так чтобы скрипт ожидал окончание работы приложения и только после этого «шёл» дальше?

Или подскажите как «замкнуть» цикл?
т.е. как сделать так чтоб скрипт раз в минуту проверял через Get-Process наличее процеса? как проверить знаю, а вот как раз в минуту, нет.

Зарание спасибо эээ.

Ответы

Если PowerShell считает команду не консольной утилитой, то он просто вызывает её и продолжает дальнейшую работу. А если перенаправить вывод (пусть несуществующий) утилиты куданибудь (хоть в Out-Null, в никуда) с помощью конвейера, то PowerShell придётся подождать завершения работы программы.

Еще можно использовать командлет Start-Process с ключем -Wait, но я не уверен что он есть в v1.0:

Илон Маск рекомендует:  Написание хорошего кода
Понравилась статья? Поделиться с друзьями:
Кодинг, CSS и SQL