LNK2019未解析的外部符号NtOpenFile

时间:2011-07-21 11:00:12

标签: c++ winapi visual-c++ linker-errors

我的代码遇到链接器错误。我试图用Win-7 X64位m / c中的Visual Studio命令Prompt(2010)进行编译。 我看到的错误如下。

  

dust2.obj

     

dust2.obj:错误LNK2019:未解析的外部符号_NtOpenFile @ 24引用   在函数_main

中      

dust2.obj:错误LNK2019:未解析的外部符号_RtlAnsiStringToUnicodeStr   在函数_main

中引用了@ 12      

dust2.obj:错误LNK2019:未解析的外部符号_RtlInitAnsiString @ 8参考   enced in function _main

     

dust2.exe:致命错误LNK1120:3个未解析的外部

我的代码的简化版本是这样的:

   #include <windows.h>
   #include <iostream>
   #include <Winternl.h>

   using namespace std;

   int main()
   {
        NTSTATUS Status;
        OBJECT_ATTRIBUTES Obja;
        HANDLE SourceFile;
        PUNICODE_STRING PathName=0;
        PANSI_STRING p_path=0;
        const char* ccp_path = "D:\\txt.txt";
        RtlInitAnsiString( p_path,ccp_path );
        RtlAnsiStringToUnicodeString( PathName, p_path, true );
        IO_STATUS_BLOCK IoStatusBlock;
        wprintf(L"%s", PathName->Buffer);
        InitializeObjectAttributes(
            &Obja,
            PathName,
            OBJ_CASE_INSENSITIVE,
            NULL,
            NULL
        );
        Status = NtOpenFile(
                     &SourceFile,
                     FILE_LIST_DIRECTORY | FILE_READ_EA | FILE_READ_ATTRIBUTES,
                     &Obja,
                     &IoStatusBlock,
                     FILE_SHARE_READ | FILE_SHARE_WRITE,
                     FILE_DIRECTORY_FILE | FILE_OPEN_FOR_BACKUP_INTENT | FILE_OPEN_REPARSE_POINT
        );  
        if(SourceFile == INVALID_HANDLE_VALUE){
            printf("\nError: Could not open file\n");
            return 0;
        }
        cout<<endl<<endl;
        system("pause");
        return 0;

}

在本论坛的另一篇文章中,提到这类问题的解决方案包括#pragma

我通过像这样添加#pragma来尝试这个解决方案

#pragma comment(lib, "ntdll")

但是在编译时我看到另一个错误,上面写着“LINK:致命错误LNK1104:无法打开文件'ntdll.lib'”。

我将非常感谢您帮助解决此问题。感谢..

2 个答案:

答案 0 :(得分:23)

不能让这个问题像这样未答复。因为尽管Mladen的评论在很大程度上适用于这个特定的原生API,但整个主题值得进行深入讨论。

初步警示

在前面我应该注意到在很多情况下它是neither desirable nor necessary to use one of the native API functions on Windows。但是,在某些情况下,Win32 API无法提供查询信息甚至操作数据等的方法。其中一个案例是NtQueryInformationFile/ZwQueryInformationFile可用的几个信息类。

一个很好的例子是文件和目录上的备用数据流的枚举,可以使用Win32 API完成,特别是使用备份API,但在这种情况下需要特殊权限。如果您使用本机API则不是这样。在Windows 2000引入CreateHardLink到Win32 API之前,硬链接也是如此。虽然在这种特殊情况下,如果您知道自己的方式,自从它被引入以来就可以使用MoveFileExMOVEFILE_CREATE_HARDLINK(尽管Microsoft 仍然在撰写本文时将其标记为< em>保留供将来使用 ... meh)。

关于本机API的规范书是这两个:

......还有更多,包括讨论NT 4并且在Nebbett的书之前。但Nebbett的书过去常常围绕原生API开始大肆宣传,就像Hoglund的书开始围绕Windows rootkit的炒作一样。不是关于Native API主题的参考,但仍然很好:

  • Windows Internals,Mark Russinovich等。人

查看此网站,了解大量原生API函数&#34;记录&#34;:

请记住:使用这些功能时的固有风险是,它们会在未来的Windows版本中消失,或者在不事先通知的情况下更改其语义。所以当你使用它们时要小心如果你使用它们。

荣耀......

如何调用本机API函数

实际上有两种方法可以调用这些函数。几年前,微软被迫在一项反托拉斯法诉讼中披露了一些原生API函数。这些被推入SDK的winternl.h。微软表达了这一点:

  

提供NtOpenFile文档是为了完整的API   覆盖。 NtOpenFile等同于ZwOpenFile函数   记录在DDK中。有关ZwOpenFilentdll.lib的更多信息   相关功能,请转到http://msdn.microsoft.com/library。在里面   单击左侧窗格,单击Windows开发,然后单击驱动程序   开发套件。

但是,SDK中没有附带的ntdll.lib文件。 Microsoft建议您动态链接这些功能(下面的第二个选项)。

您有几种选择:

  1. 最常见的是像你一样做。但是GetProcAddress导入库只是WDK的一部分,而不是DDK。
  2. 使用GetModuleHandle查找函数指针并调用它。 ntdll.dll足以满足Win32子系统,因为每个Win32程序都保证加载ntdll.lib
  3. 方法1:ntdll.lib

    如果你有DDK / WDK - 分别用于驱动程序开发工具包和Windows Driver Kit - 你已经获得了一整套C:\WINDDK\7600.16385.1\lib\win7\amd64\ntdll.lib C:\WINDDK\7600.16385.1\lib\win7\i386\ntdll.lib C:\WINDDK\7600.16385.1\lib\win7\ia64\ntdll.lib C:\WINDDK\7600.16385.1\lib\wlh\amd64\ntdll.lib C:\WINDDK\7600.16385.1\lib\wlh\i386\ntdll.lib C:\WINDDK\7600.16385.1\lib\wlh\ia64\ntdll.lib C:\WINDDK\7600.16385.1\lib\wnet\amd64\ntdll.lib C:\WINDDK\7600.16385.1\lib\wnet\i386\ntdll.lib C:\WINDDK\7600.16385.1\lib\wnet\ia64\ntdll.lib C:\WINDDK\7600.16385.1\lib\wxp\i386\ntdll.lib 文件。在我的系统上(Windows 7 WDK 7600.16385.1):

    ntdll.lib

    创建自己的临时ntdll.lib

    否则,您必须自己从dumpbin的输出(或通过其他允许解析DLL导出的方法)生成.lib,然后您可以将其输出到模块定义文件中。您可以构建导出import os, re, sys from os.path import basename, dirname, join, realpath try: import pefile except ImportError: try: sys.path.append(join(realpath(dirname(__file__)), "pefile")) import pefile except: raise def main(pename): from pefile import PE print "Parsing %s" % pename pe = PE(pename) if not getattr(pe, "DIRECTORY_ENTRY_EXPORT", None): return "ERROR: given file has no exports." modname = basename(pename) libname = re.sub(r"(?i)^.*?([^\\/]+)\.(?:dll|exe|sys|ocx)$", r"\1.lib", modname) defname = libname.replace(".lib", ".def") print "Writing module definition file %s for %s" % (defname, modname) with open(defname, "w") as f: # want it to throw, no sophisticated error handling here print >>f, "LIBRARY %s\n" % (modname) print >>f, "EXPORTS" numexp = 0 for exp in [x for x in pe.DIRECTORY_ENTRY_EXPORT.symbols if x.name]: numexp += 1 print >>f, "\t%s" % (exp.name) print "Wrote %s with %d exports" % (defname, numexp) print "\n\nUse this to create the export lib:\n\tlib /def:%s /out:%s" % (defname, libname) if __name__ == '__main__': if len(sys.argv) != 2: sys.exit("ERROR:\n\tSyntax: fakelib <dllfile>\n") sys.exit(main(sys.argv[1])) 。听起来很复杂?不是那么多,让我们看看;)

    使用Ero Carrera的pefile Python module,我们可以这样做:

    fakelib.py

    运行此脚本的示例输出(名称为> fakelib.py ntdll.dll Parsing ntdll.dll Writing module definition file ntdll.def for ntdll.dll Wrote ntdll.def with 1984 exports Use this to create the export lib: lib /def:ntdll.def /out:ntdll.lib 时)为:

    /machine:

    然后我们运行最后一行给出的命令。当然,提供> lib /def:ntdll.def /out:ntdll.lib Microsoft (R) Library Manager Version 11.00.51106.1 Copyright (C) Microsoft Corporation. All rights reserved. LINK : warning LNK4068: /MACHINE not specified; defaulting to X86 Creating library ntdll.lib and object ntdll.exp 参数会更好。这是一个&#34;练习&#34; (*咳嗽* *咳嗽*)给读者。 VS 2012的输出将是:

    ntdll.lib

    祝贺。您现在可以使用Microsoft自己lib.exe创建的ntdll.dll.lib静态导入,即使没有&#34;真实&#34; (原始)gendef在您的系统上。

    根据您的需要和品味调整路径和文件名。

    使用MinGW时

    Damon在评论中指出MinGW附带的工具链包含一个工具dlltool,它可以完成上述Python脚本的工作,输出可以输入__stdcall

    问题

    上述方法在定位x64(64位)时效果很好,但对于x86(32位),我偶尔会遇到链接器错误。

    问题是__stdcall的名称修饰在x64和x86之间有所不同。前者并没有真正使用与x86相同的sizeof(void*),因此只是假装下划线。但是,后者还附加了多个参数int __stdcall foo(int);(即4)。因此,对于一个参数,函数_foo@4的修饰函数名称变为GetProcAddress

    This KB article from Microsoft概述了解决问题的方法。

    方法2:使用NtOpenFile

    动态导入

    MSDN状态(Ntdef.h)的文档:

      

    请注意,许多常量都需要DDK头文件InitializeObjectAttributes   定义以及Ntdll.lib宏。该   DDK中也提供了相关的导入库LoadLibrary。您   也可以使用GetProcAddressNtdll.dll函数   动态链接到TFNNtOpenFile

    声明一个函数类型,例如这里我们声明适合您的类型typedef NTSTATUS (NTAPI *TFNNtOpenFile)( OUT PHANDLE FileHandle, IN ACCESS_MASK DesiredAccess, IN POBJECT_ATTRIBUTES ObjectAttributes, OUT PIO_STATUS_BLOCK IoStatusBlock, IN ULONG ShareAccess, IN ULONG OpenOptions );

    TFNNtOpenFile pfnNtOpenFile = (TFNNtOpenFile)GetProcAddress(GetModuleHandle("ntdll.dll"), "NtOpenFile");
    status = pfnNtOpenFile(...); // can't be bothered to type out all parameters ;)
    

    ...然后检索函数指针并调用它:

    static NTSTATUS (NTAPI  *NtOpenFile)(
      OUT PHANDLE,
      IN ACCESS_MASK,
      IN POBJECT_ATTRIBUTES,
      OUT PIO_STATUS_BLOCK,
      IN ULONG,
      IN ULONG
    );
    (FARPROC)&NtOpenFile = GetProcAddress(GetModuleHandle("ntdll.dll"), "NtOpenFile");
    

    检索函数指针的另一种方法可能是:

    #

    可以通过使用预处理程序字符串化运算符({{1}})进一步压缩。选择是你的。

答案 1 :(得分:3)

这些函数无法直接调用,因为它们属于内部API,不会通过任何库公开。您需要使用GetProcAddress获取这些函数的地址。

有关详细信息,请查看此处:http://msdn.microsoft.com/en-us/library/bb432200.aspx