прямое объявление функции ntdll - PullRequest
0 голосов
/ 24 июня 2018

При попытке реализовать какой-то пример кода, который я нашел, я наткнулся на ошибку, которую не совсем понимаю, причину этого.Вот и все.

В шапке я видел, что это объявляет что-то вроде этого.Однако, когда я пытался скомпилировать его, 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 голосов
/ 24 июня 2018

Вы должны понимать, как работают компилятор и компоновщик.Компилятор создает Общий формат файла объекта (COFF) файлов.здесь существует Таблица символов COFF для каждого символа, объявленного в исходном файле.в двоичном виде он реализован как IMAGE_SYMBOL (глядя в winnt.h или ntimage.h).здесь наиболее интересным для нас является Значения номера раздела (SHORT SectionNumber;).в общих словах - символ может быть Определен - Символы, которые были созданы и которым назначены адреса хранения и пространство внутри файла.(индекс на основе единицы в таблицу разделов) или Undefined - символы, на которые есть ссылки в файле, но которым не присвоен адрес хранения.

IMAGE_SYM_UNDEFINED - символьной записи еще не присвоен раздел.Нулевое значение указывает, что ссылка на внешний символ определена в другом месте.Значение, отличное от нуля, является общим символом, размер которого определяется значением.

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

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

compiler create __imp_NtOpenSection (x64, arm, arm64) или __imp__NtOpenSection@12 (x86) символ со значением IMAGE_SYM_UNDEFINED - на самом деле вы здесь объявляете функцию, но вы не реализуете ее.эта функция (NtOpenSection) должна быть определена (реализована) в другом месте.когда ссылка на линкер - это поиск его реализации (__imp_NtOpenSection символ) во всех файлах obj и lib, который передается ему в качестве входных данных.если он не может найти его реализацию - IMAGE_SYMBOL запись с однозначным индексом в таблицу разделов, - говорит он, - неразрешенный внешний символ.так что вы должны либо реализовать функцию (символ) самостоятельно, либо передать компоновщику lib или файл obj, где эта функция реализована.в случае пользовательского режима он реализован в ntdll.lib или ntdllp.lib.поэтому вам и нужно передать один из этих файлов lib на вход компоновщика - это ошибка разрешения.

во втором случае

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

, который вы объявляете, и внедряете переменную.он уже был создан и ему присвоены адреса хранения и пространство внутри файла.в результате здесь нет никаких неразрешенных внешних факторов.

также прочитайте Обработка символов для лучшего понимания этого процесса.

также я предлагаю - скомпилировать ваш файл c / c ++ без опции /GL ( Только опция /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, и вы можете просто использовать его - вызвать NtOpenSection из кода c / c ++ или call [__imp_NtOpenSection] из asm.

во втором случае (?NtOpenSection@@... или [_]NtOpenSection, если вы объявите его с помощью extern "C" или из кода c, _ только для x86) - это будет просто переменная.и когда вы начинаете кодировать execute, здесь будет 0 (если вы объявите его как глобальный / статический) или неопределенное значение (локальная переменная в стеке).перед вызовом NtOpenSection через такую ​​переменную вам нужно сначала ее инициализировать - назначить реальный адрес NtOpenSection.скажем как *(void**)&NtOpenSection = GetProcAddress(GetModuleHandle(L"ntdll"), "NtOpenSection");.после этого вы можете использовать его.и как я говорю - не будет никаких различий в вызове NtOpenSection через объявление # 1 или # 2 - код будет абсолютно одинаковым

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...