Получить определенное пространство памяти процесса - PullRequest
1 голос
/ 25 апреля 2010

У меня есть указатель (void *) на функцию, и я хочу знать, какому процессу принадлежит эта функция. Я понятия не имею, каким путем это сделать, но я думаю, что это возможно при использовании какой-то формы VirtualQuery обмана. Любая помощь будет оценена.

Заранее спасибо,

Уточнение: Под «принадлежать процессу» я имею в виду процесс, в котором находится функция. Например: Допустим, в память загружен исполняемый файл (test.exe). Этот исполняемый файл содержит функцию с именем SayHello, которая расположена в 0xDEADBEEF в памяти. В совершенно ином процессе, откуда мне знать, что 0xDEADBEEF находится в области памяти test.exe.

Надежда, которая проясняет ситуацию.

РАЗЪЯСНЕНИЕ 2: Я уверен, что вы знакомы с "перехватом VTable", когда внешний модуль изменяет указатель VTable в отдельном процессе, чтобы указывать на другую функцию. Таким образом, каждый раз, когда вызывается подключенный элемент, он передается внешнему модулю.

Чтобы предотвратить это (античит), я хочу иметь возможность проверить, все ли методы VTable указывают на модуль, в котором они находятся.

КОД РЕШЕНИЯ:

template<class T>
inline void **GetVTableArray(T *pClass, int *pSize)
{
    void **ppVTable = *(void ***)pClass;

    if(pSize)
    {
        *pSize = 0;

        while(!IsBadReadPtr(ppVTable[*pSize], sizeof(UINT_PTR)))
            (*pSize)++;
    }

    return ppVTable;
}

bool AllVTableMembersPointToCurrentModule(void *pClass)
{
    DWORD dwOldProtect;
    HANDLE hModuleSnap = INVALID_HANDLE_VALUE;
    MODULEENTRY32 moduleEntry;

    // Take a snapshot of all modules in the specified process
    hModuleSnap = CreateToolhelp32Snapshot(TH32CS_SNAPMODULE, GetCurrentProcessId());
    if(hModuleSnap == INVALID_HANDLE_VALUE)
        return false;

    // Set the size of the structure before using it
    moduleEntry.dwSize = sizeof(MODULEENTRY32);

    // Retrieve information about the first module (current process)
    if(!Module32First(hModuleSnap, &moduleEntry))
    {
        CloseHandle(hModuleSnap);
        return false;
    }

    // Grab the base address and size of our module (the address range where
    // the VTable can validly point to)
    UINT_PTR ulBaseAddress = reinterpret_cast<UINT_PTR>(moduleEntry.modBaseAddr);
    UINT_PTR ulBaseSize = moduleEntry.modBaseSize;

    // Get the VTable array and VTable member count
    int nMethods;
    void **ppVTable = GetVTableArray(pClass, &nMethods);

#ifdef VTABLE_FAKING
    // Allow patching
    VirtualProtect(ppVTable, nMethods * sizeof(UINT_PTR), PAGE_EXECUTE_READWRITE, &dwOldProtect);

    // Now take the next module and set the first VTable pointer to point to an
    // invalid address, outside of the current module's address range
    Module32Next(hModuleSnap, &moduleEntry);
    ppVTable[0] = moduleEntry.modBaseAddr;
#endif

    // Don't allow people to overwrite VTables (can easily be bypassed, so make
    // sure you check the VirtualProtect status of the VTable regularly with
    // VirtualQuery)
    VirtualProtect(ppVTable, nMethods * sizeof(UINT_PTR), PAGE_EXECUTE, &dwOldProtect);

    // Clean up the snapshot object
    CloseHandle(hModuleSnap);

    // Ensure all VTable pointers are in our current module's address range
    for(int i = 0; i < nMethods; ++i)
    {
        // Get address of the method this VTable pointer points to
        UINT_PTR ulFuncAddress = reinterpret_cast<UINT_PTR>(ppVTable[i]);

        // Check the address is within our current module range
        if(ulFuncAddress < ulBaseAddress || ulFuncAddress > ulBaseAddress + ulBaseSize)
            return false;
    }

    return true;
}

Ответы [ 5 ]

13 голосов
/ 25 апреля 2010

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

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

Для дальнейшего уточнения: указатель сам по себе не имеет смысла, если вы уже не знаете, какому процессу он принадлежит. Процесс # 1001 может иметь функцию sayHello по адресу 0x12345678, в то время как процесс # 1002 имеет функцию sayGoodbye по адресу 0x12345678, а процесс # 1003 содержит некоторые данные по тому же адресу. Нет способа узнать, из какого процесса пришел указатель.

1 голос
/ 25 апреля 2010

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

1 голос
/ 25 апреля 2010

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

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

Во-первых, чтобы предотвратить взлом VTable? Я не знаю, как это сделать, кроме попытки библиотеки Microsoft Detours , которая теоретически может быть использована для обхода любого перехвата API-вызовов внутри вашего процесса.

1 голос
/ 25 апреля 2010

В любой из операционных систем Windows, которые произошли от Windows NT (то есть для любых целей и задач, включая и после XP, и до этого NT 4 и NT 3.51) каждый процесс имеет свое собственное адресное пространство. В пределах разумного, любой адрес указателя может отличаться в каждом процессе в системе, поскольку все они имеют адрес 0xDEADBEEF, и он может содержать или не содержать то же самое, что и другие процессы. Это было не то же самое с Windows 3.0, 3.1, 95, 98 и ME (у них было одно адресное пространство, которое разделяли все процессы), где ваш вопрос МОЖЕТ иметь больше смысла.

Таким образом, без дескриптора процесса, который идет с вашим адресом указателя, адрес для вас практически бесполезен. Имея дескриптор процесса, вы можете (возможно) решить, что вы хотите, пройдя таблицы импорта для импортируемых вами DLL-библиотек ... Если функция не является импортированной функцией, маловероятно, что вы сможете получить то, что хотите знать.

Обратите внимание, что если адрес относится к функции из «стандартной» системной DLL, тогда вы МОЖЕТЕ выяснить, где она находится, выяснив, какую функцию она представляет в адресном пространстве вашего процесса, поскольку существует велика вероятность того, что DLL будет сопоставлена ​​с тем же базовым адресом в вашем процессе, как и в любом другом процессе.

Почему бы не рассказать нам немного больше о том, что именно вы на самом деле пытаетесь сделать?

Edit:

Ну, как я уже говорил выше, то, что вы предлагаете, невозможно, за исключением очень старых версий Windows. Что возможно, так это то, что вы можете внедрить код в процесс, чтобы заменить тот код, который должен быть выполнен. Адрес этого внедренного кода действителен в адресном пространстве целевого процесса и содержит код, который вы (процесс взлома) создали. Вы делаете это с помощью комбинации выделения памяти в удаленном процессе с помощью VirtualAllocEx() (1) и последующей записи в нее кода с помощью WriteProcessMemory() (2) . Теперь у вас есть код, который вы написали в целевом процессе. Затем вы можете исправить это так, чтобы он вызывался вместо кода, который должен вызываться.

Обычный способ сделать это - Перехват IAT (Перехват импорта таблицы адресов), что позволяет заменять импортированные функции из DLL. Чтобы обнаружить это, вам нужно отсканировать таблицу адресов импорта DLL из образа DLL на диске, выяснить, где находятся функции в памяти, а затем просканировать IAT в памяти, чтобы убедиться, что функции находятся там, где они должны быть; если нет, то их, вероятно, залатали.

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

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

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

0 голосов
/ 25 апреля 2010

Я не совсем понимаю ваш вопрос, поэтому я собираюсь сделать удар в темноте и ответить на то, что я думаю, что вы спрашиваете.

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

Поскольку ваш вопрос не сформулирован правильно, вы не получите правильного ответа.

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