В общем есть кое-какие результаты, если кому интересно.
Для изучения собрал в VS 2010 бинарник с TLS.
Сборка стандартная DEBUG, только базовый адрес установил с 0х00400000.
Код программы следующий:
Код:
#include <stdio.h>
#include <conio.h>
#include <Windows.h>
__declspec(thread) int tls_i = 0; // to make TLS section
typedef VOID (NTAPI *PIMAGE_TLS_CALLBACK)(PVOID DllHandle, DWORD Reason, PVOID Reserved);
void NTAPI tls_callback(PVOID DllHandle, DWORD Reason, PVOID Reserved) {
_asm {int 3}
tls_i = 1;
_asm {int 3}
}
#pragma data_seg(".CRT$XLB")
PIMAGE_TLS_CALLBACK p_thread_callback = tls_callback;
#pragma data_seg()
int main() {
printf("test TLS : %d\n", tls_i);
_getch();
return 0;
}
В процессе изучения, выяснилось, что в секции .tls по смещению 0х104 находится значение для инициализации переменной tls_i. Начало данных TLS таблицы равно виртуальному адресу секции .tls, а конец таблицы равен началу+0х208, т.е. 2 блока по 0х104 байта.
Может кто знает, почему именно по 0х104 байта?
Идём дальше. Адрес TLS индекса находится в .data и содержит нули.
Адрес массива колбэков находится в .rdata и содержит адрес, указывающий на первый колбэк, который находится в секции кода (.text). Но это не функция колбэка, а хрен знает, что. Там прыжки какие-то, не углублялся.
Экспериментальным путём выяснилось, что
настоящая функция колбэка находится в другом месте, Оля останавливается на _asm {int 3} , давая возможность изучить код.
Таким образом получается, что адрес, указанный в массиве колбэков указывает не на саму функцию колбэка, а на какие-то прыжки, следовательно, он не нужен, можно сразу передать управление на нужный участок кода.
Что сделал:
1) Перенёс TLS таблицу из .rdata в .tls.
2) Изменил адрес конца таблицы с 0x208 на 0х18 (размер структуры), значение по смещению в .tls+0x104 обнулил.
Вот фрагмент кода функции колбэка, пришлось его не много изменить.
Код:
004190BB 90 NOP
004190A0 CC INT3
004190A1 A1 3C714100 MOV EAX,DWORD PTR DS:[_tls_index]
004190A6 64:8B0D 2C000000 MOV ECX,DWORD PTR FS:[2C]
004190AD 8B1481 MOV EDX,DWORD PTR DS:[ECX+EAX*4]
004190B0 C782 04010000 01000000 MOV DWORD PTR DS:[EDX+tls_i],1
004190BA C3 RETN
004190BB 90 NOP
3) Записал байт-код в секцию .tls, сделав её исполняемой:
Код:
90 CC A1 3C - 71 41 00 64 - 8B 0D 2C 00 - 00 00 8B 14
81 C7 82 04 - 01 00 00 01 - 00 00 00 C3 - 90 00 00 00
4) В массиве колбэков изменил адрес на собственный код в .tls (из пункта 3).
В результате, в секции .tls располагается TLS таблица, чуть дальше находится код колбэка, на который будет передано управление.
Запускаем, и, что видим? А видим, что код колбэка, НАКОНЕЦ-ТО ВЫПОЛНИЛСЯ!
Но не всё так гладко.
Теперь полученные знания применяем на другом примере, бинарник собран без TLS. Секция .tls создана вручную. Выполнил все те же действия, проверяю и... не сработало.
Код колбэка просто не выполняется, даже Оля не останавливается на брейке.
У кого-нибудь есть соображения по этому поводу?
Пока что есть только одна зацепка: при сборке бинарника, как дебаг, колбэк вызывается. А если собрать, как релиз - нет. Но это справедливо для файла, который собирается с TLS.
Если TLS прикрутить вручную после сборки, то хоть дебаг, хоть резил, всё равно ничего не работает.