LNK2019 неразрешенный внешний символ NtOpenFile - PullRequest
5 голосов
/ 21 июля 2011

Я столкнулся с ошибкой компоновщика с моим кодом. Я пытаюсь скомпилировать с помощью командной строки Visual Studio (2010) в Win-7 X64 bit m / c. Ошибка, которую я вижу, как показано ниже.

dust2.obj

dust2.obj: ошибка LNK2019: неразрешенный внешний символ _NtOpenFile @ 24 ссылка в функции _main

dust2.obj: ошибка LNK2019: неразрешенный внешний символ _RtlAnsiStringToUnicodeStr ing @ 12 ссылка в функции _main

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;

}

В другом сообщении на этом форуме было упомянуто решение проблемы такого рода, включающее # pragma .

Я попробовал это решение, добавив #pragma следующим образом

#pragma comment(lib, "ntdll")

но при компиляции я вижу другую ошибку, которая говорит: "LINK: фатальная ошибка LNK1104: не удается открыть файл 'ntdll.lib'".

Я буду очень признателен за помощь в решении этой проблемы. Спасибо ..

Ответы [ 2 ]

24 голосов
/ 27 февраля 2013

Не могу оставить этот вопрос без ответа вот так. Поскольку, хотя замечание Младена в значительной степени подходит для этого конкретного нативного API, вся тема заслуживает подробного обсуждения.

Предварительное предупреждение

Вначале я должен отметить, что во многих случаях не желательно и не необходимо использовать одну из встроенных функций API в Windows . Однако есть несколько случаев, когда Win32 API не предоставляет средства для запроса информации или даже манипулирования данными и так далее. Одним из таких случаев будет несколько информационных классов, доступных для NtQueryInformationFile / ZwQueryInformationFile.

Один хороший пример - перечисление альтернативных потоков данных в файлах и каталогах, что можно сделать с помощью Win32 API, в частности, с помощью API резервного копирования, но в этом случае потребуются специальные привилегии. Не так, если вы прибегаете к нативному API. То же самое было справедливо для жестких ссылок до Windows 2000, которая ввела CreateHardLink в Win32 API. Хотя в данном конкретном случае, если бы вы знали свой путь, вы могли бы использовать MoveFileEx с MOVEFILE_CREATE_HARDLINK с момента его появления (хотя Microsoft до сих пор на момент написания этой статьи помечает его как Зарезервировано для использования в будущем ... meh).

Канонические книги о нативном API - это две:

... есть и другие, в том числе те, в которых обсуждается NT 4 и предшествующая книга Неббетта. Но книга Неббетта использовала шумиху вокруг родного API, так же, как книга Хоглунда начала шумиху вокруг руткитов Windows. Не справочник по теме Native API, но все же хорошо:

  • Windows Internals, Марк Руссинович и др. и др.

Посетите этот веб-сайт для огромного количества «задокументированных» функций API:

Итак, помните: при использовании этих функций риск заключается в том, что они исчезнут в будущей версии Windows или их семантика изменится без предварительного уведомления. Так что будьте осторожны , когда вы используете их, , если вы используете их.

На славу ...

Как вызывать собственные функции API

На самом деле есть два способа вызвать эти функции. Несколько лет назад Microsoft была вынуждена раскрыть некоторые функции нативного API в одном из антимонопольных исков. Они были добавлены в winternl.h SDK. Microsoft выражает это так:

Документация NtOpenFile предоставляется для полного API охват. NtOpenFile эквивалентно функции ZwOpenFile задокументировано в ДДК. Для получения дополнительной информации о ZwOpenFile и связанные функции, перейдите к http://msdn.microsoft.com/library. В в левой панели выберите «Разработка Windows», затем нажмите «Драйвер» Комплект разработчика.

Однако в SDK нет сопровождающего файла ntdll.lib. 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

Создайте свой собственный импровизированный ntdll.lib

В противном случае вы должны сгенерировать ntdll.lib самостоятельно из вывода dumpbin (или с помощью других средств, которые позволяют анализировать экспорт DLL), которые затем можно вывести в файл определения модуля, из которого можно построитьэкспорт .lib.Звучит запутанно?Не так уж много, давайте посмотрим;)

Используя модуль Ero Carrera pefile Python , мы можем сделать это:

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

Пример выходных данных запуска этого скрипта(когда названо 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:.Это оставлено читателю как «упражнение» (* кашель * * кашель *).Выход с 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

Поздравляем.Теперь вы можете использовать ntdll.lib, созданный Microsoft lib.exe, для статического импорта из ntdll.dll, даже не имея «реального» (оригинального) .lib в вашей системе.

Настройте путь иимена файлов в соответствии с вашими потребностями и вкусами.

При использовании MinGW

Damon указал в комментарии, что набор инструментов, включенный в MinGW, содержит инструмент gendef, который может выполнятьзадание вышеприведенного скрипта Python и что выходные данные могут быть переданы в dlltool.

Проблемы

Приведенный выше метод отлично работает при нацеливании на x64 (64-битный), но для x86 (32)-bit) Я иногда сталкивался с ошибками компоновщика.

Проблема в том, что оформление имени для __stdcall отличается между x64 и x86.Первый действительно не использует тот же __stdcall, что и x86, и поэтому просто добавляет подчеркивание.Тем не менее, последний также добавляет число аргументов умножить на sizeof(void*) (то есть 4).Поэтому для одного аргумента оформленное имя функции для int __stdcall foo(int); становится _foo@4.

В этой статье базы знаний Майкрософт описан способ обойти эту проблему.

Метод 2: динамически импортируется с использованием GetProcAddress

Документация в состояниях MSDN (для NtOpenFile):

Обратите внимание, что заголовочный файл DDK Ntdef.h необходим для многихопределения констант, а также макрос InitializeObjectAttributes.Связанная библиотека импорта, Ntdll.lib, также доступна в DDK.Вы также можете использовать функции LoadLibrary и GetProcAddress для динамической ссылки на 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
);

... и затем получить указатель функции и вызвать его:

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

, который можно сжать еще больше, используя оператор строкового преобразования препроцессора (#).Выбор за вами.

3 голосов
/ 21 июля 2011

Эти функции нельзя вызывать напрямую, потому что они принадлежат внутреннему API и не доступны ни в одной из библиотек. Вам необходимо получить адреса этих функций, используя GetProcAddress.

Для получения дополнительной информации смотрите здесь: http://msdn.microsoft.com/en-us/library/bb432200.aspx

...