Windows: 在C++中获取函数地址

7

我正在尝试获取函数地址,以便使用memcpy将其内容复制到缓冲区中。

我遇到的问题是获取函数地址。以下是我的代码:

__declspec(noinline) int gio()
{
    const char yle[] = "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA";
    return 7;
}

int main(int argc, TCHAR* argv[])
{
    int(*funcptr)() = gio;
    unsigned char *p = (unsigned char *)&funcptr;
    size_t ik;

    for (ik = 0; ik < sizeof funcptr; ik++)
    {
        printf("%02x ", p[ik]);
    }
    putchar('\n');

    printf("gio x -> %x\n", gio);
    printf("gio p -> %p\n", gio);
}

我创建了一个小测试程序,尝试使用不同的方式打印函数地址。

我正在使用Visual Studio,并关闭了优化和内联函数扩展(但仍然使用了noinline)。所有的打印语句都输出相同的结果(0x00d213ca 截图),但是当我将光标放在gio函数上时(在VS中),它显示完全不同的地址(0x00d218c0 截图)。

当我右键点击gio函数并选择转到反汇编器时,我跳转到了当我将光标放在它上面时显示的地址(0x00d218c0 截图)。这清楚地显示了这个函数的真实位置。

我有点困惑,在这里似乎我不理解某些东西。为什么打印语句会显示错误的值?获取“真实”函数地址的方法是什么?


2
我认为像这样的问题最近才出现。没有优化,函数通过调度表被调用,跳转到函数体。函数指针存储调度表条目地址,而调试器可能显示函数体地址。 - user7860670
3
@IInspectable 这个地址用于打印函数地址的字节。 "我尝试使用不同的方法打印函数地址" - user7860670
@IInspectable printf("gio p -> %p\n", gio); << 但这里有一个变量吗? - Zhani Baramidze
1
@ZhaniBaramidze:函数指针不需要与数据指针大小相同。printf格式说明符%p仅适用于数据指针。 - Ben Voigt
1
请问您能提供一些关于调度表的阅读资料吗? - Ojs
显示剩余6条评论
1个回答

5
Visual Studio在这里对你进行了“欺骗”。当你打印funcptrgio时,你看到的是实际地址,也就是写入时跳转的地址。
gio() // or funcptr()

启用增量链接后,由于thunking的原因,gio上悬停时Visual Studio显示的值与此值不同,请参见/INCREMENTAL

可能包含跳转thunks以处理将函数重定位到新地址。

要找出代码的地址,您可以禁用增量链接或使用DIA SDK

// initialize DIA SDK, etc.

void (*fp)() = &foo;
printf("foo(): %p\n\n", foo);

HMODULE hModule = GetModuleHandle(NULL);
DWORD rva = (DWORD)((size_t)fp - (size_t)hModule);

IDiaSymbol *pDiaSymbol;
hr = pDiaSession->findSymbolByRVA(rva, SymTagPublicSymbol, &pDiaSymbol);
if(FAILED(hr) || !pDiaSymbol)
    return hr;

ULONGLONG targetVA;
hr = pDiaSymbol->get_targetVirtualAddress(&targetVA);
pDiaSymbol->Release();

if(FAILED(hr))
    return hr;

if(hr == S_OK)
    printf("foo is a thunk, actual address: %p\n\n", (LPVOID)((size_t)hModule + targetVA));

但是可能有一种我不知道的更简单的方法。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接