понедельник, 12 апреля 2010 г.

Использование ReadProcessMemory не является предпочтительным способом межпрограммного взаимодействия

Это перевод ReadProcessMemory is not a preferred IPC mechanism. Автор: Реймонд Чен.

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

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

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

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

Что? Предоставление доступа на чтение может привести к повреждению вашего стека?

Если стек потока вырастает до сторожевой страницы (guard page), то фильтр необработанных исключений ловит исключение доступа к сторожевой странице и увеличивает стек. Но когда это происходит в обработчике, который ловит все исключения подряд (например, такой, что использует функция IsBadReadPtr), то исключение обрабатывается на месте и не доходит до фильтра необработанных исключений. В результате, стек не увеличивается, новая сторожевая страница не создаётся. То, что раньше было исключением сторожевой страницы, теперь будет Access Violation, когда код потока начнёт использовать стек, что, скорее всего, приведёт к закрытию потока, а вместе с ним - и всего процесса.

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

Даже если вы каким-то образом запустите обработчик исключения на каком-то "спасательном стеке", то вы всё равно не сможете решить эту проблему. В момент исключения, поток мог делать что угодно. Может быть, он был внутри менеджера кучи, с открытой блокировкой кучи и внутренними данными менеджера кучи, находящимися в переходном/несогласованном состоянии. Для того, чтобы процесс остался в живых, структуры данных кучи должны быть согласованными, а блокировка кучи - освобождена. Но вы не знаете, как это сделать (и что это вообще нужно делать!).

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

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

2 комментария:

  1. Жесть нереальная. :) Особенно по ссылке: "The way you check for bad pointers on Win32 is by calling the IsBadReadPtr and IsBadWritePtr API. Michael Howard calls these APIs “CrashMyApplication” and “CorruptMemoryAndCrashMySystem” respectively."

    ОтветитьУдалить

Можно использовать некоторые HTML-теги, например:

<b>Жирный</b>
<i>Курсив</i>
<a href="http://www.example.com/">Ссылка</a>

Вам необязательно регистрироваться для комментирования - для этого просто выберите из списка "Анонимный" (для анонимного комментария) или "Имя/URL" (для указания вашего имени и ссылки на сайт). Все прочие варианты потребуют от вас входа в вашу учётку (поддерживается OpenID).

Пожалуйста, по возможности используйте "Имя/URL" вместо "Анонимный". URL можно просто не указывать.

Ваше сообщение может быть помечено как спам спам-фильтром - не волнуйтесь, оно появится после проверки администратором.