LNK2019 unresolved external symbol NtOpenFile
Asked Answered
H

2

8

I am facing linker error with my code. I am trying to compile with Visual Studio command Prompt (2010) in Win-7 X64 bit m/c. The error which i see are as below.

dust2.obj

dust2.obj : error LNK2019: unresolved external symbol _NtOpenFile@24 referenced in function _main

dust2.obj : error LNK2019: unresolved external symbol _RtlAnsiStringToUnicodeStr ing@12 referenced in function _main

dust2.obj : error LNK2019: unresolved external symbol _RtlInitAnsiString@8 refer enced in function _main

dust2.exe : fatal error LNK1120: 3 unresolved externals

The simplified version of my code is like this:

   #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;

}

In another post in this forum the solution of these kind of problem was mention to include a #pragma.

I tried this solution by adding #pragma like this

#pragma comment(lib, "ntdll")

but on compilation i see another error that says "LINK : fatal error LNK1104: cannot open file 'ntdll.lib'".

I will much appreciate your help to resolve this problem. Thanks..

Hodgkins answered 21/7, 2011 at 11:0 Comment(0)
P
6

These functions cannot be called directly because they belong to internal API and are not exposed through any of the libraries. You need to obtain addresses of these function using GetProcAddress.

For more information look here.

Paederast answered 21/7, 2011 at 11:5 Comment(0)
M
30

Can't let this question go unanswered like this. Because although the remark by Mladen is largely right for this particular native API, the whole topic deserves an in-depth discussion.

Preliminary cautionary note

Up front I should note that in many many cases it is neither desirable nor necessary to use one of the native API functions on Windows. However, there are a few cases where the Win32 API doesn't provide the means to query information or even manipulate data and so on. One such case would be several of the information classes available for NtQueryInformationFile/ZwQueryInformationFile.

One good example is the enumeration of alternate data streams on files and directories, which can be done using the Win32 API, in particular by using the backup APIs, but will require special privileges in that case. Not so if you resort to the native API. The same used to be true for hard links until Windows 2000, which introduced CreateHardLink to the Win32 API. Although in this particular case if you knew your way around you could have used MoveFileEx with MOVEFILE_CREATE_HARDLINK ever since it got introduced (although Microsoft still as of this writing marks it as Reserved for future use ... meh).

The canonical books about the native API are these two:

... there are more, including one that discusses NT 4 and preceded Nebbett's book. But Nebbett's book used to start the hype around the native API, much like Hoglund's book started the hype around Windows rootkits. Not a reference regarding the Native API topic but still good:

  • Windows Internals, Mark Russinovich et. al.

Check out this website for a huge number of native API functions "documented":

So remember: the inherent risk when using these functions is that they go away in a future Windows version or their semantics change without prior notice. So be careful when you use them, if you use them.

On to glory ...

How to call native API functions

Actually there are two ways to call these functions. Microsoft was forced to disclose some of the native API functions in one of the antitrust law suits some years ago. These were shoved into winternl.h of the SDK. Microsoft expresses it thus:

The NtOpenFile documentation is provided for the sake of full API coverage. NtOpenFile is equivalent to the ZwOpenFile function documented in the DDK. For more information on the ZwOpenFile and related functions, go to http://msdn.microsoft.com/library. In the left-hand pane, click Windows Development, then click Driver Development Kit.

However, there is no accompanying ntdll.lib file in the SDK. Microsoft suggests you link those functions dynamically (second option below).

You have several options:

  1. The most common is to do as you did. But the ntdll.lib import library is only part of the WDKs, not the DDKs.
  2. Use GetProcAddress to find the function pointer and call it. GetModuleHandle is sufficient for the Win32 subsystem since every Win32 program is guaranteed to have loaded ntdll.dll.

Method 1: ntdll.lib

If you have the DDK/WDK - for Driver Development Kit and Windows Driver Kit respectively - you get a full set of ntdll.lib files already. On my system (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

Create your own makeshift ntdll.lib

Otherwise you have to generate ntdll.lib yourself from the output of dumpbin (or by other means that allow to parse the exports of the DLL) which you can then output into a module definition file from which you can build an export .lib. Sounds convoluted? Not so much, let's see ;)

Using Ero Carrera's pefile Python module, we can do this:

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]))

A sample output of running this script (when named fakelib.py) would be:

> 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

Then we run the command as given on the last line. It is even better to give the /machine: parameter, of course. This is left as an "exercise" (*cough* *cough*) to the reader. The output with VS 2012 will be:

> 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

Congratulations. You can now use the ntdll.lib created by Microsoft's own lib.exe to statically import from ntdll.dll, even without having the "real" (original) .lib on your system.

Adjust the path and file names to your needs and tastes.

When using MinGW

Damon pointed out in a comment that the toolchain included with MinGW contains a tool gendef that can do the job of above Python script and that the output can be fed to dlltool.

Issues

The above method works perfectly when targeting x64 (64-bit), but for x86 (32-bit) I have encountered linker errors at times.

The issue is that the name decoration for __stdcall differs between x64 and x86. The former doesn't really use the same __stdcall as x86 and therefore just prepends an underscore. However, the latter also appends a the number of arguments times sizeof(void*) (i.e. 4). So for one argument the decorated function name for a function int __stdcall foo(int); becomes _foo@4.

This KB article from Microsoft outlines a way to get around the issue.

Method 2: dynamically imported, using GetProcAddress

Documentation in MSDN states (for NtOpenFile):

Note that the DDK header file Ntdef.h is necessary for many constant definitions as well as the InitializeObjectAttributes macro. The associated import library, Ntdll.lib is also available in the DDK. You can also use the LoadLibrary and GetProcAddress functions to dynamically link to Ntdll.dll.

Declare a function type, e.g. here we declare the type TFNNtOpenFile suitable in your case:

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
);

... and then retrieve the function pointer and call it:

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

an alternative way of retrieving the function pointer could be this one:

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");

which could be condensed even further by using the preprocessor stringification operator (#). The choice is yours.

Maynardmayne answered 27/2, 2013 at 16:55 Comment(8)
Slight addition to this excellent answer: If you are so unlucky as to use MinGW/MSYS (which neither has the import lib, nor an official tool), you can use gendef to produce a .def file which dlltool will read (using -d for specifying the def-file, and -l for telling it to create the import lib).Boxwood
This should be marked as the Answer to this question. Worked like a charm.Singlefoot
I'm still getting unresolved external symbol after doing the python run. I ran pefile on \windows\SysWOW64\ntdll.dll because I'm compiling for x32. My previous solution was to add ordinals to the .def file: ZwQueryInformationProcess@20 .@1665 But this ordinal changes When I get unresolved ex. sym. There is .@num after the func, if I add this .@num It links, but using depend walker I see it actually tries to import ZwQueryInformationProcess@20 , and when I run the file I get: The entry point RtlComputeCrc32@12 could not be located in the dynamic link library ntdll.dll. - OK -Soar
It seems like ntdll.lib is included in the Windows 10 SDK these days, at least I find it in %ProgramFiles(x86)%\Windows Kits\10\Lib\10.0.14393.0\um\<Platform>?! Together with #include <winternl.h> it then becomes a no-brainer.Crystallization
@klaustriendl: indeed, it does. Have you been able to verify that it is a full ntdll.lib and not one providing only a subset of the exported functions? Which exact SDK version are you referring to, btw?Maynardmayne
@Maynardmayne I didn't verify whether it's a full ntdll.lib or only a subset, but it seems to offer the kind of publicly documented Nt* API (all the functions the OP mentioned are exported). As hidden in the path I am referring to SDK v10.0.14303.0, I'll see somewhen in the near future whether this is still true for newer SDKs.Crystallization
@klaustriendl ntdll.lib doesn't contain full exports (Ki* and string routines) ref: github.com/dynamorio/dynamorio/issues/894Ahola
@MohdShahril not exactly sure what it is you're trying to say, but either way you should specify which version of ntdll.lib you're referring to, as this matters. The "string routines" you seem to refer to are C runtime functions. They were never supposed to be used in the first place, so removing them seems a consistent change. Or perhaps you mean those from strsafe.lib?Maynardmayne
P
6

These functions cannot be called directly because they belong to internal API and are not exposed through any of the libraries. You need to obtain addresses of these function using GetProcAddress.

For more information look here.

Paederast answered 21/7, 2011 at 11:5 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.