获取特定的进程内存空间

时间:2010-04-24 20:00:36

标签: c++ windows winapi

我有一个函数指针(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 个答案:

答案 0 :(得分:13)

每个进程都有自己的地址空间。这意味着相同的地址将包含不同进程的不同内容,因此无法按照您的要求进行操作。

如果此指针指向当前程序中的某个函数(即当前可以调用的函数),则答案很简单:它属于当前进程。

进一步阐明:指针本身没有意义,除非你已经知道它属于哪个进程。进程#1001可以在地址0x12345678处具有函数sayHello,而进程#1002在地址0x12345678处具有函数sayGoodbye,并且进程#1003包含在相同地址处的一些数据。无法知道指针来自哪个进程。

答案 1 :(得分:1)

在任何来自Windows NT的Windows操作系统中(因此,对于所有意图和目的,包括XP之后,以及之前的NT 4和NT 3.51),每个进程都有自己的地址空间。在合理范围内,任何指针地址在系统中的每个进程中都可以不同,因为它们都具有0xDEADBEEF地址,并且它可能包含或可能不包含与其他进程相同的内容。这与Windows 3.0,3.1,95,98和ME(它们有一个所有进程共享的地址空间)不一样,你的问题可能更有意义。

因此,如果没有处理指针地址的进程,那么地址对你来说几乎没用。通过处理进程的句柄,您可以(可能)通过遍历导入的DLL的导入表来计算出您想要的内容...如果该函数不是导入的函数,那么您不太可能找到您想要的内容知道。

请注意,如果地址是来自“标准”系统DLL的函数,那么您可以通过找出它在进程的地址空间中表示的函数来计算它所在的位置,因为很有可能将DLL映射到进程中的相同基址,就像在其他每个进程中一样。

为什么不告诉我们更多关于你究竟想要做什么的事情呢?

修改

好吧,正如我上面所描述的那样,除了非常旧版本的Windows之外,你所建议的是不可能的。可能的是,您可以将代码注入进程以替换应该执行的代码。此注入代码的地址在目标进程的地址空间中有效,并包含您(黑客进程)创建的代码。您可以通过将远程进程中的内存分配到VirtualAllocEx() (1),然后使用WriteProcessMemory() (2)将代码写入其中来实现此目的。您现在拥有在目标进程中编写的代码。然后,您可以对其进行修补,以便调用它而不是应该调用的代码。

执行此操作的常用方法是IAT hooking(导入地址表挂钩),这使您可以从DLL替换导入的函数。要检测到这一点,您需要从磁盘上的DLL映像扫描DLL的导入地址表,找出函数在内存中的位置,然后扫描内存IAT以检查函数是否应该在哪里;如果他们不是那么他们可能已被打补丁。

您建议有人替换任意C ++ vTable条目。这可以使用相同的技术,但它更难,因为没有方便的地址名称表,您可以使用它来确定修补的位置。无论如何,假设坏人可以找到正确的补丁地址,他可以使用与上面相同的技术在你的过程中创建自己的功能。

由于缺少地址查找的名称,检测vTable问题变得更加复杂,但是如果您正处于被黑客攻击的过程中,您只需编写代码即可在启动时获取相关函数的地址。存储在某处并稍后进行比较。但是,您可能最好在内存中复制整个函数本身并与之进行比较,因为您可能会发现坏人只是寻找一些可识别的函数签名字节并在某个地方修改它们到自己的代码或简单地跳过你的。

祝你好运并抓住自己一本好书,比如杰弗里里希特(Jeffrey Richter)的一本好书,它会比我更好地解释这一点。

答案 2 :(得分:1)

VTable中被劫持的函数指针只能在你的进程中,因为其他人已经回答了。内存地址只对您的进程有意义。如果有人要覆盖你的一个VTable点,那么他们首先必须挂钩你的进程,这意味着在你的进程中运行代码。有很多win API提供挂钩。

请参阅EnumProcessModule以查看流程中的所有模块。请参阅this about modules info,包括模块的基址。然后,您必须检查您的VTable,以确保在模块的地址范围内存在所解决的那些。

首先防止VTable劫持?除了尝试Microsoft's Detours library之外,我不知道如何做到这一点,理论上它可以用来绕过你进程中的任何钩子API调用。

答案 3 :(得分:1)

如果您有模块句柄,则可以检查图像标头以确保vtable指针位于该模块的虚拟地址空间中。

答案 4 :(得分:-1)

我真的不明白你的问题,所以我会在黑暗中刺伤并回答我认为你在问的问题。

您正在询问如何从函数指针找到它所属的模块。 解决方案理论上相当简单,在内存中向后扫描以找到标头,然后享受使用此函数GetModuleFileName。

由于你的问题措辞不当,你的答案不够好。