Многие Win32 API используют указатели на структуры с определенными макетами. Из них большое подмножество следует общему шаблону, где первый DWORD должен быть инициализирован, чтобы иметь размер структуры перед его вызовом. Иногда им требуется передать блок памяти, в который они будут записывать структуру, а блок памяти должен иметь размер, который определяется первым вызовом того же API с указателем NULL и чтением возвращаемого значения для обнаружения правильного размер. Некоторые API выделяют структуру и возвращают указатель на нее, так что указатель должен быть освобожден при втором вызове.
Я не удивлюсь, если набор API, которые могут быть с пользой вызваны за один раз, с отдельными аргументами, конвертируемыми из простого строкового представления, будет совсем небольшим.
Чтобы сделать эту идею общеприменимой, нам нужно пойти на крайний случай:
typedef void DynamicFunction(size_t argumentCount, const wchar_t *arguments[],
size_t maxReturnValueSize, wchar_t *returnValue);
DynamicFunction *GenerateDynamicFunction(const wchar_t *code);
Вы бы передали простой фрагмент кода в GenerateDynamicFunction, и он обернул бы этот код в некоторый стандартный шаблон, а затем вызвал компилятор / компоновщик C, чтобы сделать из него DLL (доступно довольно много свободных опций), содержащий функция. Затем LoadLibrary
этой DLL-библиотеки и GetProcAddress
найдет функцию, а затем вернет ее. Это будет дорого, но вы сделаете это один раз и кэшируете полученный DynamicFunctionPtr для повторного использования. Вы можете сделать это динамически, сохраняя указатели в хеш-таблице, определяемой самими фрагментами кода.
Шаблон может быть:
#include <windows.h>
// and anything else that might be handy
void DynamicFunctionWrapper(size_t argumentCount, const wchar_t *arguments[],
size_t maxReturnValueSize, wchar_t *returnValue)
{
// --- insert code snipped here
}
Таким образом, пример использования этой системы будет:
DynamicFunction *getUserName = GenerateDynamicFunction(
"GetUserNameW(returnValue, (LPDWORD)(&maxReturnValueSize))");
wchar_t userName[100];
getUserName(0, NULL, sizeof(userName) / sizeof(wchar_t), userName);
Вы можете улучшить это, заставив GenerateDynamicFunction
принять количество аргументов, чтобы он мог генерировать проверку в начале оболочки, что передано правильное количество аргументов. И если вы поместите туда хеш-таблицу, чтобы кэшировать функции для каждого обнаруженного кода, вы можете приблизиться к исходному примеру. Функция Call взяла бы фрагмент кода вместо просто имени API, но в противном случае была бы такой же. Он будет искать фрагмент кода в хеш-таблице, а если его нет, он вызовет GenerateDynamicFunction и сохранит результат в хеш-таблице в следующий раз. Затем он будет выполнять вызов функции. Пример использования:
wchar_t userName[100];
Call("GetUserNameW(returnValue, (LPDWORD)(&maxReturnValueSize))",
0, NULL, sizeof(userName) / sizeof(wchar_t), userName);
Конечно, не было бы особого смысла делать что-либо из этого, если бы идея не заключалась в том, чтобы открыть какую-то общую дыру в безопасности. например выставить Call
как веб-сервис. Значения безопасности существуют для вашей первоначальной идеи, но менее очевидны просто потому, что предложенный вами оригинальный подход не будет настолько эффективным. Чем мощнее мы в целом, тем больше проблем с безопасностью.
Обновление на основе комментариев:
.NET Framework имеет функцию p / invoke, которая существует именно для решения вашей проблемы. Поэтому, если вы делаете это как проект для изучения вещей, вы можете посмотреть на p / invoke, чтобы понять, насколько это сложно. Вы могли бы нацелить платформу .NET на свой язык сценариев - вместо интерпретации сценариев в реальном времени или компиляции их в свой собственный байт-код, вы можете скомпилировать их в IL. Или вы можете разместить существующий язык сценариев из множества доступных на .NET.