Пользовательское хранилище TIB

После некоторого поиска в Google и нескольких подсказок, данных здесь, мне наконец удалось найти макет сегмента FS (используется Windows для хранения данных TIB). Особый интерес для меня представляет элемент ArbitraryUserPointer, представленный в PSDK:

typedef struct _NT_TIB {
    struct _EXCEPTION_REGISTRATION_RECORD *ExceptionList;
    PVOID StackBase;
    PVOID StackLimit;
    PVOID SubSystemTib;
    union {
        PVOID FiberData;
        DWORD Version;
    };
    PVOID ArbitraryUserPointer;
    struct _NT_TIB *Self;
} NT_TIB;

Насколько безопасно использовать эту переменную (под Vista и выше)? а на х64 еще есть?

Вторичным по отношению к этому является доступ к этой переменной. Я использую MSVC, поэтому у меня есть доступ к встроенным функциям __readfsdword и __readgsqword, однако MSDN по какой-то причине помечает их как привилегированные инструкции:

Эти встроенные функции доступны только в режиме ядра, а подпрограммы доступны только как встроенные функции.

Они, конечно, не только для ядра, но почему они помечены как таковые, просто неправильная документация? (в моих автономных документах VS 2008 этого пункта нет).

Наконец, безопасно ли обращаться к ArbitraryUserPointer напрямую через один __readfsdword(0x14) или предпочтительнее использовать его через линейный адрес TIB? (что по-прежнему потребует чтения из FS).


person Necrolis    schedule 13.02.2012    source источник


Ответы (2)


ArbitraryUserPointer — это внутреннее поле, не предназначенное для общего использования. Операционная система использует его внутри, и если вы перезапишете его, вы испортите материал. Я признаю, что у него очень плохое имя.

person Raymond Chen    schedule 13.02.2012
comment
хм, это разочаровывает, тем более что в старой статье MSVC она помечена как управляемая приложением. Есть ли где-нибудь документация о том, какие элементы управляются приложением, а какие управляются ОС? Я в основном ищу где-нибудь, чтобы скрыть указатель, который не основан на TLS - person Necrolis; 14.02.2012
comment
TLS — это единственное, что я могу придумать в TEB, что приложения могут использовать бесплатно. (Если бы было что-то еще вроде ArbitraryUserPointer, как две библиотеки DLL договорились бы о контроле над ним?) - person Raymond Chen; 14.02.2012
comment
Я вообще не могу взломать токен безопасности SEH/стека? (Я могу гарантировать, что у меня не будет токена SEH/security для того, для чего я хочу его использовать). кроме этого, я думаю, я просто буду придерживаться __declspec(thread) для своих отдельных данных для каждого потока... - person Necrolis; 14.02.2012
comment
TLS — это поддерживаемый способ создания данных для каждого потока. Вы не указали причину, по которой вы не можете его использовать. - person Raymond Chen; 14.02.2012
comment
Это для JIT-волокон, поэтому я не смогу правильно запустить TLS. вот почему мне нужен только один указатель для контекста генерации - person Necrolis; 14.02.2012
comment
Не уверен, что вы имеете в виду, но вы можете использовать FLS для локального хранилища. - person Raymond Chen; 14.02.2012
comment
Рэймонд, одним из возможных вариантов использования ArbitraryUserPointer является способ встраивания пользовательских данных (таких как имя потока) в любой поток таким образом, чтобы их можно было легко получить через окно просмотра отладчика. Можно ли сделать это с помощью TLS и получить доступ к этим данным в окне просмотра? - person Chris Kline; 06.08.2014
comment
Чтобы прояснить мой предыдущий вопрос, проблема с использованием TLS для цели, которую я описал, заключается в том, что у нас нет возможности узнать для данного потока, в каком из TlsSlots / TlsExpansionSlots в структуре TEB могут быть найдены данные, если не считать ручного изучения каждого слот. - person Chris Kline; 06.08.2014
comment
Вы можете поместить номер слота TLS в глобальную переменную с известным именем. Отладчик может прочитать значение этой переменной, чтобы узнать номер слота. - person Raymond Chen; 06.08.2014

Если вы все еще хотите получить ответ, у меня тоже была такая же проблема, и я разместил свой вопрос, похожий на ваш:

Локальное хранилище потока в режиме ядра?

Мне нужен эквивалент TLS в драйвере режима ядра. Точнее, у меня есть глубокое дерево вызовов функций, которое возникает в какой-то момент (например, процедура диспетчеризации драйвера), и мне нужно передать информацию о контексте.

В моем конкретном случае уловка заключается в том, что мне не нужно постоянное хранилище, мне просто нужен заполнитель для конкретного потока для чего-то для одного вызова функции верхнего уровня. Поэтому я решил использовать произвольную запись в массиве TLS для вызова функции, а после этого - восстановить ее исходное значение.

Вы получаете массив TLS следующим образом:

DWORD* get_Tls()
{
    return (DWORD*) (__readfsdword(0x18) + 0xe10);
}

Кстати, я понятия не имею, почему к TIB обычно обращаются, читая содержимое fs:[0x18]. На это просто указывает селектор fs. Но именно так к нему обращается весь код MS, поэтому я решил сделать и это.

Затем вы выбираете произвольный индекс TLS, скажем, 0.

const DWORD g_dwMyTlsIndex = 0;

void MyTopLevelFunc()
{
    // prolog
    DWORD dwOrgVal = get_Tls()[g_dwMyTlsIndex];
    get_Tls()[g_dwMyTlsIndex] = dwMyContextValue;

    DoSomething();

    // epilog
    get_Tls()[g_dwMyTlsIndex] = dwOrgVal;
}

void DoSomething()
{
    DWORD dwMyContext = get_Tls()[g_dwMyTlsIndex];
}
person valdo    schedule 01.04.2012
comment
Я использовал это до того, как задал этот вопрос, моя проблема в том, что мне нужно постоянное хранилище, не полагаясь на TLS, поэтому сейчас я использую фиктивные __declspec(thread) vars - person Necrolis; 01.04.2012