我发这个是为了说两件事:
1)这里给出的大多数答案都非常糟糕,很容易崩溃。如果您在debug
可执行文件的构建中使用 C 函数指针(使用函数名),并且可能在其他情况下,它可能指向一个没有函数体本身的JMP
shim 。这是一个例子。如果我对下面定义的函数执行以下操作:
FARPROC pfn = (FARPROC)some_function_with_possibility_to_get_its_size_at_runtime;
我得到的pfn
(例如:)0x7FF724241893
将指向这个,这只是一个JMP
指令:
![在此处输入图像描述](https://i.stack.imgur.com/B7tcg.png)
此外,编译器可以嵌套几个垫片,或分支您的函数代码,以便它具有多个Epilogs或ret
指令。哎呀,它甚至可能不使用ret
指令。然后,无法保证函数本身会按照您在源代码中定义的顺序进行编译和链接。
你可以用汇编语言做所有这些事情,但不能用 C 或 C++。
2)所以上面是坏消息。好消息是原始问题的答案是,是的,有一种方法(或hack方法)可以获得确切的函数大小,但它具有以下限制:
这个概念很简单——利用在 x64 Windows 二进制文件中实现 SEH的方式。编译器将每个函数的详细信息添加到 PE32+ 标头(到IMAGE_DIRECTORY_ENTRY_EXCEPTION
可选标头的目录)中,您可以使用它来获取确切的函数大小。(如果您想知道,此信息用于捕获、处理和展开块中的异常__try/__except/__finally
。)
这是一个简单的例子:
//You will have to call this when your app initializes and then
//cache the size somewhere in the global variable because it will not
//change after the executable image is built.
size_t fn_size; //Will receive function size in bytes, or 0 if error
some_function_with_possibility_to_get_its_size_at_runtime(&fn_size);
进而:
#include <Windows.h>
//The function itself has to be defined for two types of a call:
// 1) when you call it just to get its size, and
// 2) for its normal operation
bool some_function_with_possibility_to_get_its_size_at_runtime(size_t* p_getSizeOnly = NULL)
{
//This input parameter will define what we want to do:
if(!p_getSizeOnly)
{
//Do this function's normal work
//...
return true;
}
else
{
//Get this function size
//INFO: Works only in 64-bit builds on Windows!
size_t nFnSz = 0;
//One of the reasons why we have to do this at run-time is
//so that we can get the address of a byte inside
//the function body... we'll get it as this thread context:
CONTEXT context = {0};
RtlCaptureContext(&context);
DWORD64 ImgBase = 0;
RUNTIME_FUNCTION* pRTFn = RtlLookupFunctionEntry(context.Rip, &ImgBase, NULL);
if(pRTFn)
{
nFnSz = pRTFn->EndAddress - pRTFn->BeginAddress;
}
*p_getSizeOnly = nFnSz;
return false;
}
}