LNK2019未解析的外部符号NtOpenFile

Hok*_*ama 6 c++ winapi linker-errors visual-c++

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

dust2.obj

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

dust2.obj:错误LNK2019: 函数_main中引用的未解析的外部符号_RtlAnsiStringToUnicodeStr ing @ 12

dust2.obj:错误LNK2019:未解析的外部符号_RtlInitAnsiString @ 8在函数_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;

}
Run Code Online (Sandbox Code Playgroud)

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

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

#pragma comment(lib, "ntdll")
Run Code Online (Sandbox Code Playgroud)

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

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

0xC*_*22L 25

不能让这个问题像这样没有答案.因为虽然Mladen的评论在很大程度上适用于这个特定的原生API,但整个主题值得深入讨论.

初步警示

在前面我应该注意到,在很多情况下,在Windows上使用本机API函数既不可取也不必要.但是,在某些情况下,Win32 API不提供查询信息甚至操作数据等的方法.其中一个案例是几个可用于NtQueryInformationFile/ZwQueryInformationFile的信息类.

一个很好的例子是文件和目录上的备用数据流的枚举,可以使用Win32 API完成,特别是使用备份API,但在这种情况下需要特殊权限.如果您使用本机API则不是这样.在引入CreateHardLinkWin32 API的Windows 2000之前,硬链接也是如此.虽然在这种特殊情况下,如果你知道你的方式在你身边也可以使用MoveFileEx带有MOVEFILE_CREATE_HARDLINK自从它得到介绍(尽管微软仍然在编写这本书将其标记为保留供未来使用 ... MEH).

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

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

  • Windows Internals,Mark Russinovich等.人.

查看此网站,了解大量原生API函数"已记录":

所以请记住:使用这些功能时的固有风险是它们在未来的Windows版本或其语义更改中消失,恕不另行通知.所以,要小心,当你使用它们,如果你使用它们.

荣耀......

如何调用本机API函数

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

NtOpenFile文档提供了完整的API覆盖的缘故.NtOpenFile等同ZwOpenFile于DDK中记录的功能.有关ZwOpenFile相关功能的更多信息,请访问http://msdn.microsoft.com/library.在左侧窗格中,单击"Windows开发",然后单击"驱动程序开发工具包".

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

你有几个选择:

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

方法1: ntdll.lib

如果你有DDK/WDK - 分别用于驱动程序开发工具包和Windows驱动程序工具包 - 你ntdll.lib已经获得了一整套文件.在我的系统上(Windows 7 WDK 7600.16385.1):

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
Run Code Online (Sandbox Code Playgroud)

创建自己的临时搭建 ntdll.lib

否则,您必须ntdll.lib从输出dumpbin(或通过其他允许解析DLL导出的方法)生成自己,然后您可以将其输出到模块定义文件中,您可以从中创建导出.lib.听起来很复杂?不是那么多,让我们看看;)

使用Ero Carrera的pefilePython模块,我们可以这样做:

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]))
Run Code Online (Sandbox Code Playgroud)

运行此脚本(命名时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
Run Code Online (Sandbox Code Playgroud)

然后我们运行最后一行给出的命令./machine:当然,给出参数更好.这留给读者作为"运动"(*咳嗽**咳嗽*).VS 2012的输出将是:

> 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
Run Code Online (Sandbox Code Playgroud)

恭喜.您现在可以使用ntdll.libMicrosoft自己创建的lib.exe静态导入ntdll.dll,即使.lib您的系统上没有"真实"(原始).

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

使用MinGW时

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

问题

当针对x64(64位)时,上述方法可以正常工作,但对于x86(32位),我有时会遇到链接器错误.

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

微软的这篇知识库文章概述了一种解决问题的方法.

方法2:动态导入,使用 GetProcAddress

MSDN状态(for NtOpenFile)中的文档:

请注意,DDK头文件Ntdef.h对于许多常量定义以及InitializeObjectAttributes宏都是必需的.相关的导入库Ntdll.lib也可以在DDK中使用.您还可以使用LoadLibraryGetProcAddress函数动态链接到Ntdll.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
);
Run Code Online (Sandbox Code Playgroud)

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

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

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

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");
Run Code Online (Sandbox Code Playgroud)

这可以通过使用预处理器字符串化运算符(#)进一步压缩.这是你的选择.

  • 稍微补充一下这个优秀的答案:如果你是如此不幸使用MinGW/MSYS(既没有导入库,也没有官方工具),你可以使用`gendef`来生成`.df`文件`dlltool`将读取(使用`-d`指定def文件,并使用`-l`指示它创建导入lib). (3认同)
  • 这应该标记为这个问题的答案.工作就像一个魅力. (2认同)

Mla*_*vić 5

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

欲了解更多信息,请查看此处