ntdll函数前向声明

时间:2018-06-24 09:31:53

标签: c++ windows winapi

当尝试实现我发现的一些示例代码时,我遇到一个错误,我不十分了解其原因。这样就可以了。

在标题中,我看到它声明了类似这样的内容。但是,当我尝试编译它时,VS使用此“无法解析的外部符号...”给我一个错误。

NTSYSAPI
NTSTATUS
NTAPI
NtOpenSection(
    OUT PHANDLE             SectionHandle,
    IN ACCESS_MASK          DesiredAccess,
    IN POBJECT_ATTRIBUTES   ObjectAttributes
);

但是,当我将其更改为类似内容时,我再也看不到错误了。

NTSTATUS(NTAPI *NtOpenSection)(
   OUT PHANDLE              SectionHandle,
   IN ACCESS_MASK           DesiredAccess,
   IN POBJECT_ATTRIBUTES    ObjectAttributes
); 

我不确定是什么原因造成的。如果有人也发布一些在线参考资料对我来说更好,这将是很棒的。

1 个答案:

答案 0 :(得分:3)

您需要了解编译器和链接器如何工作。编译器创建Common Object File Format (COFF)个文件。源文件中声明的每个符号都存在COFF Symbol Table。以二进制形式实现为IMAGE_SYMBOL(从winnt.hntimage.h看)。对我们来说最有趣的是部分编号值SHORT SectionNumber;)。一般而言-符号可以已定义 –已创建的符号,并为其分配了文件中的存储地址和空间。 (指向节表的基于索引的索引)或未定义 –已在文件中引用但尚未分配存储地址的符号。

  

IMAGE_SYM_UNDEFINED-符号记录尚未分配   部分。零值表示对外部的引用   符号在其他地方定义。非零值是常见符号   的大小由值指定。

使用时

NTSYSAPI
NTSTATUS
NTAPI
NtOpenSection(
    OUT PHANDLE             SectionHandle,
    IN ACCESS_MASK          DesiredAccess,
    IN POBJECT_ATTRIBUTES   ObjectAttributes
);

编译器使用__imp_NtOpenSection部分值创建__imp__NtOpenSection@12(x64,arm,arm64)或IMAGE_SYM_UNDEFINED(x86)符号-实际上,您在这里声明函数,但是您不实施。此功能(NtOpenSection)必须在其他位置定义(实现)。当链接器链接时-它在所有obj和lib文件中搜索实现(__imp_NtOpenSection符号),并将其作为输入传递给他。如果找不到它的实现-他说IMAGE_SYMBOL记录到节表中有一个基于索引的索引-未解析的外部符号。因此,您必须自己或自己实现功能(符号),或者将链接器lib或obj文件交给实现该功能的链接器。在用户模式下,它是在ntdll.libntdllp.lib中实现的。因此,您需要将此lib文件之一传递给链接器输入-此解决错误。

第二种情况

NTSTATUS(NTAPI *NtOpenSection)(
   OUT PHANDLE              SectionHandle,
   IN ACCESS_MASK           DesiredAccess,
   IN POBJECT_ATTRIBUTES    ObjectAttributes
); 

您声明和实施变量。它已经被创建并在文件中分配了存储地址和空间。结果,这里没有任何未解决的外部因素。

也请阅读Symbol Processing,以更好地了解此过程。

我还建议

-编译不带/GL选项的c / c ++文件(只有/HEADERS DUMPBIN选项可用于由/GL编译器选项生成的文件。 )并运行

link.exe /dump /symbols your.obj > some.txt

并在此处查找NtOpenSection

00000000 UNDEF  notype       External     | __imp_NtOpenSection

在第一种情况下

00000000 SECT4  notype       External     | ?NtOpenSection@@3P6AJPEAPEAXKPEAUOBJECT_ATTRIBUTES@@@ZEA (long (__cdecl* NtOpenSection)(void * *,unsigned long,struct OBJECT_ATTRIBUTES *))

第二种情况。

UNDEF SECTx


在两种情况下,都从二进制视图

声明指针大小(4或8字节)变量(__imp_NtOpenSection?NtOpenSection@@...),该变量将用作函数的保存地址。在两种情况下,二进制级别的间接调用都将相同

call [__imp_NtOpenSection]call [?NtOpenSection@@...]。不同-在__imp_中-此变量(__imp_NtOpenSection)的地址将被写入PE结构Import Lookup Table中,并将其放置在Import Address Table中。结果,加载程序(ntdll中的代码)将自动解析地址或NtOpenSection并将此地址存储在__imp_NtOpenSection变量中。或无法加载您的PE(如果地址无法解析)。因此,当您的代码开始执行时-在__imp_NtOpenSection内部已经是NtOpenSection函数的有效地址,您可以使用它-从c / c ++代码调用NtOpenSection或从call [__imp_NtOpenSection]调用asm。

在第二种情况下(如果使用?NtOpenSection@@...[_]NtOpenSection或从c代码中声明,extern "C"_仅适用于x86)-这将是简单的变量。当您开始执行代码时-这将是0(如果您声明为全局/静态)或未定义的值(堆栈中的局部变量)。在通过此类变量调用NtOpenSection之前,您需要首先对其进行初始化-分配NtOpenSection的实际地址。说成*(void**)&NtOpenSection = GetProcAddress(GetModuleHandle(L"ntdll"), "NtOpenSection");。在此之后,您可以使用它。以及我怎么说-通过#1或#2声明在调用NtOpenSection中不会有任何不同-代码是绝对相同的