Вопрос по memorystream, unmanaged, embedded-resource, executable – Как запустить неуправляемый исполняемый файл из памяти, а не с диска

8

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

Я не могу найти метод для запуска исполняемого файла только из его потока байтов. Windows требует, чтобы это было на диске, или есть способ запустить это из памяти? Если Windows требует, чтобы он был на диске, есть ли в .NET Framework простой способ создать виртуальный диск / файл какого-либо типа и сопоставить файл с потоком памяти исполняемого файла?

Вы можете использовать утилиту, такую как Imdisk Martin

Ваш Ответ

4   ответа
11

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

(Кстати, я не знаю, почему вы считаете, что управление временными файлами является обременительным. BCL делает это за вас:http://msdn.microsoft.com/en-us/library/system.io.path.gettempfilename.aspx )

Allocate enough memory to hold the executable. It can't reside on the managed heap, of course, so like almost everything in this exercise you'll need to PInvoke. (I recommend C++/CLI, actually, so as not to drive yourself too crazy). Pay special attention to the attribute bits you apply to the allocated memory pages: get them wrong and you'll either open a gaping security hole or have your process be shut down by DEP (i.e., you'll crash). See http://msdn.microsoft.com/en-us/library/aa366553(VS.85).aspx

Locate the executable in your assembly's resource library and acquired a pinned handle to it.

Memcpy() the code from the pinned region of the managed heap to the native block.

Free the GCHandle.

Call VirtualProtect to prevent further writes to the executable memory block.

Calculate the address of the executable's Main function within your process' virtual address space, based on the handle you got from VirtualAlloc and the offset within the file as shown by DUMPBIN or similar tools.

Place the desired command line arguments on the stack. (Windows Stdcall convention). Any pointers must point to native or pinned regions, of course.

Jump to the calculated address. Probably easiest to use _call (inline assembly language).

Pray to God that the executable image doesn't have any absolute jumps in it that would've been fixed up by calling LoadLibrary the normal way. (Unless, of course, you feel like re-implementing the brains of LoadLibrary during step #3).

Retrieve the return value from the @eax register.

Call VirtualFree.

Шаги № 5 и № 11 должны быть выполнены в блоке finally и / или использовать шаблон IDisposable.

Другой основной вариант - создать RAM-диск, записать в него исполняемый файл, запустить его и очистить. Это может быть немного безопаснее, поскольку вы не пытаетесь писать самоизменяющийся код (что сложно в любом случае, но особенно в тех случаях, когда код даже не ваш). Но я уверен, что для этого потребуется еще больше вызовов API платформы, чем для динамического внедрения кода - все они, естественно, требуют C ++ или PInvoke.

Оглядываясь назад на все эти шаги и молитвы, я надеюсь, что становится очевидным, почему код должен быть на диске. Правило так обременительно. Поскольку исполняемый код в конечном итоге загружается в память, я должен иметь возможность запускать его непосредственно из памяти или загружать с диска в память, а затем запускать его. Текущий .NET API потребовал бы, чтобы я взял код в памяти, записал его на диск, работал с системой временного управления файлами, только чтобы прочитать его обратно в память, где он уже был на первом месте! Это просто демонстрация того, что происходит, когда вы оборачиваете старую технологию в новую одежду и даете ей новое имя. Triynko
Кроме того, это «низкоуровневый, специфичный для платформы» функция уже находится в управляемой платформе .NET высокого уровня как «Process.Start». Проблема заключается в том, что единственный вид указателя, который он принимает на байты исполняемого кода, - это имя файла, что означает, что диск является единственным местом, откуда должен исходить код. Triynko
Я просто не думаю, что разрешения на доступ к файлам вообще когда-либо должны входить в микс, и, к сожалению, с временными файлами, которые они делают. См. Комментарий к MSDN для Path.GetTempFilename: в средах с низким уровнем прав, например в некоторых конфигурациях ASP.NET эта функция завершится с ошибкой безопасности ... потому что ей нужно найти временный каталог, а для чтения "Environment" ... ей требуется EnvironmentPermission для неограниченного доступа к переменным среды. Связанное перечисление: PermissionState.Unrestricted Triynko
Нет. Вызов Process.Start очень сильно отличается от простого задания CS: IP случайного смещения в области исполняемой памяти и перехода туда. ОСОБЕННО из управляемого кода. Даже в родных приложениях Win32 загрузка произвольных модулей .exe / .dll занимает гораздо больше, чем вы думаете. То, что вы видите в моем ответе выше, не должно быть обременительным; на самом деле это чрезмерное упрощение. Вот статья, которая [начинает] царапать поверхность всего, что обрабатывает LoadLibrary для вас:msdn.microsoft.com/en-us/magazine/cc301727.aspx
Когда я сказал, что эта функция уже есть в .NET Framework, я просто имел в виду Process.Start. Я признал, что это зависит от нативных методов (со сложной функциональностью), когда я сказал, что «оборачиваю старую технологию в новую одежду», суть в том, что новая технология ограничена старой технологией. В этом случае все, что я хочу сделать, это пропустить часть, в которую он загружает данные с диска. Я просто говорю, что это смешно, что существующие собственные API-интерфейсы, от которых зависит Process.Start, настолько укоренились в требовании, чтобы в качестве отправной точки использовался диск / имя файла, а не адрес памяти. Triynko
2

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

Инъекция удаленной библиотеки

.NET Framework применяет подразумеваемое правило, согласно которому «исполняемый код должен происходить на диске». предоставляя перегрузки Process.Start, которые в конечном итоге будут принимать только имя файла в качестве указателя на исполняемый код. Он не будет принимать ни управляемый массив байтов, ни указатель на неуправляемые байты, которые содержат исполняемый код. Triynko
Каждая другая основная ОС также основывает свою модель безопасности на учетных записях пользователей. И для этого есть очень веские причины. Недостаточно места в комментариях, чтобы перечислить все причины, по которым вы не правы, поэтому я позволю Рэймонду выступить с речью:blogs.msdn.com/oldnewthing/archive/2006/08/18/705957.aspx
Что вы имеете в виду? Там нет такого правила. Это просто факт из жизни, что вирусные сканеры (которые не написаны MS) становятся подозрительными каждый раз, когда процесс внезапно начинает выполнять самоизменяющийся код. Для меня это звучит как соответствующее поведение, независимо от того, на какой ОС работает сканер. Если вы не хотите полагаться на антивирусные сканеры для такого рода защиты, вот почему в Windows встроен DPE в течение многих лет.
Интересно, но я ищу способ использовать что-то вроде Process.Start, передавая байтовый массив, а не имя файла. Мне не нравится их оправдание для того, чтобы не запускать процессы из памяти, поскольку "антивирусный сканер" не может сканировать программы, которые никогда не существуют на диске "... что за копирование. Если они изначально собирают операционную систему для безопасного запуска программ, то "должен исходить из диска". Правило было бы ненужным. Triynko
Хороший API в первую очередь принимает байты и поддерживает имена файлов как экономию времени, а не наоборот. Код в конечном итоге оказывается в памяти, но существующий API вынуждает его копировать байты кода из памяти, на диск и обратно в память, работая с временным файлом между ними. Это ненужная работа, и инфраструктура делает это только потому, что оборачивает базовый API платформы (LoadLibrary и т. Д.), Который придерживается идеи, что исполняемый код всегда происходит с диска. Код в байтах. Тот факт, что ОС требует наличия файла, на самом деле должен быть абстрагирован от фреймворка. Думаю об этом. Triynko
-1

Это явно запрещено в Vista +. Вы можете использовать недокументированные вызовы Win32 API в XP, чтобы сделать это, но в Vista + это было сломано, потому что это была огромная дыра в безопасности, и единственными, кто использовал ее, были авторы вредоносных программ.

Это имеет значение, и это не смешно. Например; Как правильно и безопасно проверить подпись исполняемого образа, если файл отсутствует на диске? Как будут работать драйверы фильтров теперь, когда вы выполняете из образа памяти? Как будут работать изображения поддержки теперь, когда вы используете поток памяти, а не имя файла? Это всего лишь несколько, но у вас все еще есть миллион угловых случаев, таких как работа с параметрами выполнения файла изображения, несоответствия с отладкой и т. Д. Просто написать файл просто, это имеет дело с внутренностями очень сложного ОПЕРАЦИОННЫЕ СИСТЕМЫ.
Ну, это смешно. Поток байтов - это поток байтов, независимо от того, как вы на него смотрите или откуда он исходит. Принудительное выполнение этих байтов из FILE, а не из MEMORY, бесполезно. Malware? Шутки в сторону? Дайте мне перерыв ... поэтому им просто нужно сначала записать его в файл, чтобы запустить его. Вы говорите, что в API была дыра в безопасности (Ладно, я полагаю), или вы говорите, что есть дыра в безопасности в идее выполнения кода из потока байтов памяти, а не из потока файлов байтов (определенно НЕТ) , Triynko
IFEO - это эксплойт, ожидающий своего появления, и не более чем ленивое удобство для запуска процессов с определенной конфигурацией. Это небрежный дизайн. Я вижу, что в реестре есть такие записи, как & quot; HKLM \ Software \ Microsoft \ Windows NT \ CurrentVersion \ Параметры выполнения файла изображения \ SearchIndexer.exe & quot ;. Так что теперь, если я запускаю файл с именем SearchIndexer.exe, будет ли это скрытое, спровоцированное системой, специальное поведение, которое происходит без ведома пользователя? Это подвиг, ожидающий, IMO. Отладка, опять же, в аналогичной лодке. Triynko
Если вы следуете моей логике, я даже не удивлюсь, что поиск "использует IFEO" включает простой эксплойт, который позволяет человеку получить доступ к командной строке с повышенными правами с экрана входа в систему, активируя липкие клавиши, нажимая клавишу Shift 5 раз (redmondpie.com/…). Все, что мне нужно, - это 10 секунд на любом компьютере друга, чтобы добавить ключ реестра с файлом .reg для этой работы. Жалкий и явно плохой дизайн. Triynko
Проверьте подпись, читая байты из блока памяти вместо блоков диска! Там не должно быть разницы; это плохой дизайн. Драйверы фильтров не имеют значения, поскольку мы говорим о коде, который уже выполняется, запрашивая запуск какого-то другого конкретного кода, и никакие фильтры не должны мешать этому коду. Если вам действительно нужны фильтры, ОС может предоставить низкоуровневые перехватчики, позволяющие фильтрам сканировать память непосредственно перед ее защитой и началом выполнения. Опять же, нет ничего особенного в том, что данные находятся на диске, все это плохая архитектура. Triynko
1

Создание RAM-диска или выгрузка кода в память и последующее его выполнение являются возможными, но чрезвычайно сложными решениями (возможно, в большей степени в управляемом коде).

Это должен быть исполняемый файл? Если вы упакуете его как сборку, вы можете использовать Assembly.Load () из потока памяти - пару тривиальных строк кода.

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

Я с тобой согласен. Я просто думаю, что вам будет трудно найти что-либо столь же простое в реализации и тестировании, как и запись временного файла.
Я согласен, что нет ничего проще, это именно то, чего я пытаюсь избежать, потому что я не хочу записывать на диск по любой причине. Triynko
Даже «потому что вы могли бы сделать это 2 месяца назад и перейти к другой проблеме»? Или "потому что это стандартный подход и будет работать только для любого исполняемого файла (управляемого или неуправляемого и т. Д.), Потому что он специально поддерживается .net"?
Исполняемый файл - это просто набор байтов, и я не вижу причин, по которым ОС должна заботиться о том, происходят ли байты с диска или из памяти, при условии, что оба типа местоположений могут быть заблокированы (и могут). Кажется, ОС ЗАВИСИТ от исполняемого файла, находящегося в файловой системе, почему? Если мы можем использовать файлы карты памяти, почему бы не сделать обратное, зарегистрировав уникальный UNC-путь к виртуальному файлу, поддерживаемому только заблокированной областью памяти? Windows 7 все еще не может даже смонтировать ISO-образ на виртуальный диск без стороннего программного обеспечения, даже если все собственные материалы Microsoft распространяются в виде ISO-образов. Triynko
Я не думаю, что смогу упаковать стороннюю утилиту неуправляемой командной строки, скомпилированную в c ++, как сборку, которую можно использовать с Assembly.Load. Единственное, что не так при записи временных файлов, - это то, что я не считаю это необходимым, и это требует от меня поиска временного каталога и обеспечения доступа для записи, что также не требуется. Есть только все эти ненужные шаги, которые могут вызвать проблемы, которых я пытаюсь избежать, но ОС и .NET Framework затрудняют это. Triynko

Похожие вопросы