В этой части:
- Процессы и их потоки
- Адресное пространство процесса
- Вторжение в чужое пространство памяти
- Перехват API-функций
- Скрытие процессов
Ссылка скрыта от гостей
Лично у меня версия 6, интерфейс которой отличается от 10-ой.4.0. Процессы и их потоки
4.0.0. Структуры KPROCESS и KTHREAD
Запущенная на исполнение программа представляет из-себя самостоятельный процесс. У любого процесса есть как минимум один исполнительный поток Thread, который назвали ‘основным потоком’. Этот основной поток при помощи функции CreateThread() может порождать ещё-и-ещё дополнительных потоков так, что процесс становится многопоточным. У каждого потока своё адресное пространство, свой стек и свои регистры. Определение ‘свои регистры’ подразумевает их сохранение в сегменте состоянии задачи
TSS
Task-State-Segment, при переключении потоков планировщиком Sheduler. Таким образом, базовая единица, которая выполняется в системе - это поток, принадлежащий родительскому процессу.Поскольку всем заправляет ядро оси Ntoskrnl.exe, процессы и потоки рождаются в нём. На программном уровне, каждый процесс представлен структурой ядра KPROCESS и её расширением исполнительной системы EPROCESS. Соответственно каждый поток - структурами (K/E)THREAD. Эти структуры сильно варьируется от версии к версии ядра, поэтому нет смысла приводить тут полное их описание. В узких кругах программные потоки называют ‘тредами’ от слова Thread.
Что-же представляют из себя системные структуры? Разобраться в этом поможет WinDbg. Для отображения структур в нём имеется команда
dt
, что означает Display-Type. Эта команда может искать структуры по-маске в определённом модуле DLL. В качестве модуля мы используем Ntoskrnl.exe под кличкой nt!, в качестве маски – ‘thread’. Запускаем WinDbg в локальном режиме ядра [Ctrl+K --> Local] и ищем структуры:..и точно есть такие, причём в модуле Ntoskrnl.exe, подобраться к которому из юзера будет проблематично - нужен драйвер. Нашей программе доступны только системные библиотеки Ntdll.dll, Kernel32.dll, и прочии, которые система отображает в пространство каждого процесса (отображаемые в память файлы). Ну и ладно.. Всё-равно проникновение в ядро пока не входит в наши планы, мы просто проведём туда экскурсию, а гидом будет WinDbg.
Так-как нужно содержимое структур конкретно нашего процесса, нужно повесить исследуемый процесс в память, чтобы WinDbg вытягивал информацию именно с него. Для этих целей как-нельзя лучше подходит обычный ‘HelloWorld’ с кнопкой ОК, которую на всё время экспериментов нажимать не будем.
C-подобный:
include 'win32ax.inc'
;-------
.data
text db 'Hello World!',0
;-------
.code
start: invoke MessageBox,0,text,0,0
invoke ExitProcess,0
.end start
0х88f72020
. Ядерная она потому, что имеет адрес выше 0х80000000
, а юзерская область находится ниже, ..до адреса 0x7fffffff
(младшая половина). Судя по адресам можно сделать вывод, что в моей юзерской памяти находится только структура РЕВ и DirBase:Теперь у меня есть личный адрес, который я могу подставлять во-все команды и отладчик будет возвращать мне информацию только о данном процессе. Значит для просмотра структуры KTHREAD мне нужно ввести:
dt _kthread 88f72020
, на что получаю такой ответ:
Код:
lkd> dt _kthread 88f72020
nt!_KTHREAD
+0x000 Header : _DISPATCHER_HEADER
+0x010 MutantListHead : _LIST_ENTRY [ 0x88f72030 - 0x88f72030 ]
+0x018 InitialStack : 0x66968000 Void
+0x01c StackLimit : 0x0af69000 Void
+0x020 Teb : (null)
+0x024 TlsArray : (null)
+0x028 KernelStack : (null)
+0x02c DebugActive : 0 ''
+0x02d State : 0 ''
+0x02e Alerted : [2] ""
+0x030 Iopl : 0xac ''
+0x031 NpxState : 0x20 ' '
+0x032 Saturation : 0 ''
+0x033 Priority : 0 ''
+0x034 ApcState : _KAPC_STATE
+0x04c ContextSwitches : 0
+0x050 IdleSwapBlock : 0xa0 ''
+0x051 Spare0 : [3] "v!??????"
+0x054 WaitStatus : 0n-1994295648
+0x058 WaitIrql : 0 ''
+0x059 WaitMode : 0 ''
+0x05a WaitNext : 0 ''
+0x05b WaitReason : 0 ''
+0x05c WaitBlockList : 0x00000001 _KWAIT_BLOCK
+0x060 WaitListEntry : _LIST_ENTRY [ 0x6080000 - 0x100 ]
+0x060 SwapListEntry : _SINGLE_LIST_ENTRY
+0x068 WaitTime : 0x3a000000
+0x06c BasePriority : 0 ''
+0x06d DecrementCount : 0 ''
+0x06e PriorityDecrement : 0 ''
+0x06f Quantum : 0 ''
+0x070 WaitBlock : [4] _KWAIT_BLOCK
+0x0d0 LegoData : 0xa6a077e8 Void
+0x0d4 KernelApcDisable : 0
+0x0d8 UserAffinity : 0x40001
+0x0dc SystemAffinityActive : 0 ''
+0x0dd PowerState : 0 ''
+0x0de NpxIrql : 0 ''
+0x0df InitialNode : 0 ''
+0x0e0 ServiceTable : 0x88f72100 Void
+0x0e4 Queue : 0x88f72100 _KQUEUE
+0x0e8 ApcQueueLock : 0
+0x0f0 Timer : _KTIMER
+0x118 QueueListEntry : _LIST_ENTRY [ 0x0 - 0x893de290 ]
+0x120 SoftAffinity : 0x88f74bd0
+0x124 Affinity : 0
+0x128 Preempted : 0x56 'V'
+0x129 ProcessReadyQueue : 0 ''
+0x12a KernelStackResident : 0 ''
+0x12b NextProcessor : 0 ''
+0x12c CallbackStack : (null)
+0x130 Win32Thread : 0xe368e438 Void
+0x134 TrapFrame : (null)
+0x138 ApcStatePointer : [2] 0xe3c97f58 _KAPC_STATE
+0x140 PreviousMode : 24 ''
+0x141 EnableStackSwap : 0x58 'X'
+0x142 LargeStack : 0x4e 'N'
+0x143 ResourceIndex : 0x89 ''
+0x144 KernelTime : 0
+0x148 UserTime : 0x30
+0x14c SavedApcState : _KAPC_STATE
+0x164 Alertable : 0x80 ''
+0x165 ApcStateIndex : 0x21 '!'
+0x166 ApcQueueable : 0xf7 ''
+0x167 AutoAlignment : 0x88 ''
+0x168 StackBase : (null)
+0x16c SuspendApc : _KAPC
+0x19c SuspendSemaphore : _KSEMAPHORE
+0x1b0 ThreadListEntry : _LIST_ENTRY [ 0x7ffd7000 - 0x0 ]
+0x1b8 FreezeCount : 1 ''
+0x1b9 SuspendCount : 0 ''
+0x1ba IdealProcessor : 0 ''
+0x1bb DisableBoost : 0 ''
Судя по листингу - довольно информативная база потока, при чём у каждого из потоков она своя. В добавок, внутри данной структуры есть и несколько вложенных структур, которые предваряются нижним подчёркиванием. Для их просмотра нужно добавить к основной команде имя интересующего поля, и поставить в конце точку. Например, для поля по нулевому смещению _DISPATCHER_HEADER это выглядит так:
C-подобный:
lkd> dt _kthread 88f72020 header.
nt!_KTHREAD
+0x000 Header :
+0x000 Type : 0x3 ''
+0x001 Absolute : 0 ''
+0x002 Size : 0x1b ''
+0x003 Inserted : 0 ''
+0x004 SignalState : 0n0
+0x008 WaitListHead : _LIST_ENTRY [ 0x88f72028 - 0x88f72028 ]
0x00 - заголовок ‘Header’.
С этой структуры начинаются все ожидабельные объекты ядра, т.е. те объекты, которые могут быть переданы функциям ожидания
KeWaitFor***
для поддержки синхронизации работы нескольких потоков, между собой.0x18 – InitialStack, StackLimit и KernelStack;
Это соответственно начальный стек, лимит стека и текущий ядерный стек потока.
Обычно под начальный стек, потокам предоставляются три 4К-байтные страницы памяти, что составляет 12 килобайт. Но такой размер не предел для стека. Четвёртая страница там сторожевая GUARD-PAGE по достижении которой, система выделит ещё одну страницу, и сторожевой станет пятая, и т.д. Армагедоном будет ситуация, когда программа исчерпает весь стек и он достигнет секции-кода (они двигаются навстречу друг-другу). На х32 в этом случае ничего уже не спасёт и процесс тупо исчезнет, т.к. система не обрабатывает исключения подобного рода. На х64 системах стек фактически бесконечен, поэтому такой ситуации не может быть в принципе.
0x2c – DebugActive;
Если поток захватил отладчик, здесь будет 1.
0x2d - State;
Этот байт описывает состояние потока.
Каждый поток может находиться в одном из 7-ми состояний:
инициализирован, готов, на выполнении, приостановлен, завершён, в ожидании, и переходный.
В исходниках
Ссылка скрыта от гостей
можно найти его определение:
C-подобный:
typedef enum _KTHREAD_STATE {
Initialized = 0
Ready = 1
Running = 2
Standby = 3
Terminated = 4
Waiting = 5
Transition = 6
} KTHREAD_STATE, *PKTHREAD_STATE;
Тут все понятно из названия - IRQL, на котором вызывающий поток стал ожидать на объекте.
0x59 - WaitMode;
Это поле определяет режим ожидания. Сюда записывается соответствующий параметр, переданный функции ожидания
KeWaitFor***
. Соответственно это KernelMode или UserMode. Если он равен UserMode, то поток может принимать UserMode APC (вызов асинхронных процедур).0xE0 - ServiceTable;
Определяет сервисную таблицу функций для потока, под названием SSDT.
Обычно там лежит адрес
KeServiceDescriptorTable/TableShadow
.0x140 - PreviousMode;
Содержит режим процессора, в которым поток был до вхождения в режим ядра.
Это поле играет большую роль при вызовах API-функций типа Nt и Zw. Система проверяет, с какого режима вызываются эти функции. Если из ядра, аргументы у функций не проверяются, а если из юзера, то исполняющая система ядра Executive проверяет их кол-во на соответствие.
**********************************
Обычно у процессов несколько потоков, и каждый из них занимается своим делом внутри одного приложения. Некоторые потоки тупо простаивают и ждут какого-нибудь события типа ‘вывод на печать’, некоторые активно ведут предварительные расчёты и т.д. Ознакомится с кол-вом потоков каждого приложения можно в системном ‘Диспетчере задач’, активировав одноимённый столбец в меню вид:
4.0.1. Структура PEB
Как упоминалось выше, структуры THREAD ядро держит при себе, но по отношению к юзеру это не справедливо – юзер тоже человек и ему тоже хочется.. Поэтому на определённом этапе создания контекста процесса, система создаёт в пространстве пользователя структуры с окружением данного процесса
PEB
(Process-Environment-Block), и для каждого из его потоков - структуры TEB
(Thread-Environment-Block). На текущий TEB всегда указывает сегментный регистр процессора FS
на х32 системах, и GS
на х64.Поскольку эти структуры находятся уже в адресном пространстве пользователя (а не ядра), нужно перезапустить WinDbg и подключиться к процессу нашего ‘Hello World!’ через клавишу [F6] – Attach to Process. Теперь нужно узнать базу исследуемого процесса в памяти, для чего вводим команду
lmD
– Load Module Dynamic. Как и в предыдущем примере, эту базу будем подставлять для просмотра структур PEB и TEB конкретного процесса:База
0х00400000
является базой по-умолчанию для всех РЕ-файлов, если не указать компилятору другое значение директивой at. РЕВ заполняется загрузчиком Ntdll.dll на этапе создания процесса. Эта структура содержит информацию об окружении, загруженных модулях (LDR_DATA), базовой информации по текущему модулю и другие критичные данные необходимые для функционирования процесса. Получающие информацию о процессах функции API, вызывают ReadProcessMemory() для считывания информации из PEB нужного процесса. Посмотрим на содержимое этой структуры командой [COLOR=rgb(247, 218, 100)]dt _peb 400000[/COLOR]
:
Код:
0:001> dt _peb 400000
ntdll!_PEB
+0x000 InheritedAddressSpace : 0x4d 'M'
+0x001 ReadImageFileExecOptions : 0x5a 'Z'
+0x002 BeingDebugged : 0x80 ''
+0x003 SpareBool : 0 ''
+0x004 Mutant : 0x00000001 Void
+0x008 ImageBaseAddress : 0x00100004 Void
+0x00c Ldr : 0x0000ffff _PEB_LDR_DATA
+0x010 ProcessParameters : 0x00000140 _RTL_USER_PROCESS_PARAMETERS
+0x014 SubSystemData : (null)
+0x018 ProcessHeap : 0x00000040 Void
+0x01c FastPebLock : (null)
+0x020 FastPebLockRoutine : (null)
+0x024 FastPebUnlockRoutine : (null)
+0x028 EnvironmentUpdateCount : 0
+0x02c KernelCallbackTable : (null)
+0x030 SystemReserved : [1] 0
+0x034 AtlThunkSListPtr32 : 0
+0x038 FreeList : (null)
+0x03c TlsExpansionCounter : 0x80
+0x040 TlsBitmap : 0x0eba1f0e Void
+0x044 TlsBitmapBits : [2] 0xcd09b400
+0x04c ReadOnlySharedMemoryBase : 0x685421cd Void
+0x050 ReadOnlySharedMemoryHeap : 0x70207369 Void
+0x054 ReadOnlyStaticServerData : 0x72676f72 -> ????
+0x058 AnsiCodePageData : 0x63206d61 Void
+0x05c OemCodePageData : 0x6f6e6e61 Void
+0x060 UnicodeCaseTableData : 0x65622074 Void
+0x064 NumberOfProcessors : 0x6e757220
+0x068 NtGlobalFlag : 0x206e6920
+0x070 CriticalSectionTimeout : _LARGE_INTEGER 0x240a0d2e`65646f6d
+0x078 HeapSegmentReserve : 0
+0x07c HeapSegmentCommit : 0
+0x080 HeapDeCommitTotalFreeThreshold : 0x4550
+0x084 HeapDeCommitFreeBlockThreshold : 0x3014c
+0x088 NumberOfHeaps : 0x5d11146f
+0x08c MaximumNumberOfHeaps : 0
+0x090 ProcessHeaps : (null)
+0x094 GdiSharedHandleTable : 0x010f00e0 Void
+0x098 ProcessStarterHelper : 0x4901010b Void
+0x09c GdiDCAttributeList : 0x200
+0x0a0 LoaderLock : 0x00000400 Void
+0x0a4 OSMajorVersion : 0
+0x0a8 OSMinorVersion : 0x2000
+0x0ac OSBuildNumber : 0x2000
+0x0ae OSCSDVersion : 0
+0x0b0 OSPlatformId : 0x1000
+0x0b4 ImageSubsystem : 0x400000
+0x0b8 ImageSubsystemMajorVersion : 0x1000
+0x0bc ImageSubsystemMinorVersion : 0x200
+0x0c0 ImageProcessAffinityMask : 1
+0x0c4 GdiHandleBuffer : [34] 0
+0x14c PostProcessInitRoutine : (null)
+0x150 TlsExpansionBitmap : (null)
+0x154 TlsExpansionBitmapBits : [32] 0
+0x1d4 SessionId : 0x3000
+0x1d8 AppCompatFlags : _ULARGE_INTEGER 0x600`00000200
+0x1e0 AppCompatFlagsUser : _ULARGE_INTEGER 0x0
+0x1e8 pShimData : (null)
+0x1ec AppCompatInfo : 0xc0000040 Void
+0x1f0 CSDVersion : _UNICODE_STRING ""
+0x1f8 ActivationContextData : (null)
+0x1fc ProcessAssemblyStorageMap : (null)
+0x200 SystemDefaultActivationContextData : (null)
+0x204 SystemAssemblyStorageMap : (null)
+0x208 MinimumStackCommit : 0
Нужно сказать, что некоторые поля этой структуры ещё не инициализированы, т.к. при подключении к процессу WinDbg останавливается далеко не на точке-входа в отлаживаемый процесс. Если поставить бряк на Е-Рoint и запустить отладчик командой Go, то РЕВ инициализируется полностью. Получить указатель на РЕВ своего процесса на программном уровне можно через регистр
FS+30h
.Заслуживающими внимание являются два поля, прочитав которые можно обнаружить Олю и её братию. Первое поле: PEB.02.BeingDebugged при отладки процесса выставляется системой в 1, а второе PEB.68h.NtGlobalFlag - это OR трёх/глобальных флагов системы, которые под отладчиком будут иметь значение
70h
. Системная функция IsDebuggerPresent() проверяет как-раз-таки поле(2) и если дизассемблировать её командой(u) в WinDbg, можно в этом убедиться:
C-подобный:
0:000> u kernel32!IsDebuggerPresent
// kernel32!IsDebuggerPresent:
7c813123 64a118000000 mov eax,dword ptr fs:[00000018h]
7c813129 8b4030 mov eax,dword ptr [eax+30h]
7c81312c 0fb64002 movzx eax,byte ptr [eax+2]
7c813130 c3 ret
7c813131 90 nop
0х80000000
, то функция доступна для модификации и перехвата, иначе от манипуляциями с ней лучше сразу отказаться, т.к. нужно будет пробираться звериными тропами в ядро, или искать уязвимости в драйверах.Небольшой пример, как обнаружить отладчик сподручными средствами без Win-API представлен ниже. Если запустить его в токсичной среде отладки, то код выдаст предупреждение. При обычных обстоятельствах – это просто приветствие:
C-подобный:
include 'win32ax.inc'
.data
text0 db 'Hello World!',0
text1 db 'Внимание! Обнаружен отладчик.',0
;-------
.code
start: mov esi,[fs:30h] ; берём в ESI указатель на РЕВ
add esi,2 ; смещаемся к PEB.02.BeingDebugged
cmp byte[esi],1 ; проверка поля на отладчик
jne @01 ; если не равно
invoke MessageBox,0,text1,0,0 ; иначе: мессага Warning!
jmp @exit ; ..и на выход.
@01: invoke MessageBox,0,text0,0,0 ; если нет отладчика..
@exit: invoke ExitProcess,0 ;
.end start
4.0.2. Структура TEB потока
На 32-битный TEB указывает регистр
FS
, на 64-битный - GS
. Эта структура используется для хранения информации о потоках в текущем процессе, и каждый поток имеет свой собственный TEB. Они создаются системной функцией MmCreateTeb(), а PEB создается функцией MmCreatePeb(). Если интересен процесс их создания и заполнения, можно посмотреть исходники ReactOS, или при помощи WinDbg исследовать алго самостоятельно:
Код:
0:001> dt _teb 400000
ntdll!_TEB
+0x000 NtTib : _NT_TIB
+0x01c EnvironmentPointer : (null)
+0x020 ClientId : _CLIENT_ID
+0x028 ActiveRpcHandle : (null)
+0x02c ThreadLocalStoragePointer : (null)
+0x030 ProcessEnvironmentBlock : (null)
+0x034 LastErrorValue : 0
+0x038 CountOfOwnedCriticalSections : 0
+0x03c CsrClientThread : 0x00000080 Void
+0x040 Win32ThreadInfo : 0x0eba1f0e Void
+0x044 User32Reserved : [26] 0xcd09b400
+0x0ac UserReserved : [5] 0x2000
+0x0c0 WOW32Reserved : 0x00000001 Void
+0x0c4 CurrentLocale : 0
+0x0c8 FpSoftwareStatusRegister : 0xa0003
+0x0cc SystemReserved1 : [54] (null)
+0x1a4 ExceptionCode : 0n116
+0x1a8 ActivationContextStack : _ACTIVATION_CONTEXT_STACK
+0x1bc SpareBytes1 : [24] ""
+0x1d4 GdiTebBatch : _GDI_TEB_BATCH
+0x6b4 RealClientId : _CLIENT_ID
+0x6bc GdiCachedProcessHandle : (null)
+0x6c0 GdiClientPID : 0
+0x6c4 GdiClientTID : 0
+0x6c8 GdiThreadLocalInfo : (null)
+0x6cc Win32ClientInfo : [62] 0
+0x7c4 glDispatchTable : [233] (null)
+0xb68 glReserved1 : [29] 0
+0xbdc glReserved2 : (null)
+0xbe0 glSectionInfo : (null)
+0xbe4 glSection : (null)
+0xbe8 glTable : (null)
+0xbec glCurrentRC : (null)
+0xbf0 glContext : (null)
+0xbf4 LastStatusValue : 0
+0xbf8 StaticUnicodeString : _UNICODE_STRING ""
+0xc00 StaticUnicodeBuffer : [261] 0
+0xe0c DeallocationStack : (null)
+0xe10 TlsSlots : [64] (null)
+0xf10 TlsLinks : _LIST_ENTRY [ 0x0 - 0x0 ]
+0xf18 Vdm : (null)
+0xf1c ReservedForNtRpc : (null)
+0xf20 DbgSsReserved : [2] (null)
+0xf28 HardErrorsAreDisabled : 0
+0xf2c Instrumentation : [16] (null)
+0xf6c WinSockData : (null)
+0xf70 GdiBatchCount : 0
+0xf74 InDbgPrint : 0 ''
+0xf75 FreeStackOnTermination : 0 ''
+0xf76 HasFiberData : 0 ''
+0xf77 IdealProcessor : 0 ''
+0xf78 Spare3 : 0
+0xf7c ReservedForPerf : (null)
+0xf80 ReservedForOle : (null)
+0xf84 WaitingOnLoaderLock : 0
+0xf88 Wx86Thread : _Wx86ThreadState
+0xf94 TlsExpansionSlots : (null)
+0xf98 ImpersonationLocale : 0
+0xf9c IsImpersonating : 0
+0xfa0 NlsCache : (null)
+0xfa4 pShimData : (null)
+0xfa8 HeapVirtualAffinity : 0
+0xfac CurrentTransactionHandle : (null)
+0xfb0 ActiveFrame : (null)
+0xfb4 SafeThunkCall : 0 ''
+0xfb5 BooleanSpare : [3] ""
ТЕВ начинается со-структуры TIB – Thread Information Block, докатившейся до нас ещё с Win98.
C-подобный:
0:001> dt _teb 400000 nttib.
ntdll!_TEB
+0x000 NtTib :
+0x000 ExceptionList : 0x00805a4d _EXCEPTION_REGISTRATION_RECORD
+0x004 StackBase : 0x00000001 Void
+0x008 StackLimit : 0x00100004 Void
+0x00c SubSystemTib : 0x0000ffff Void
+0x010 FiberData : 0x00000140 Void
+0x010 Version : 0x140
+0x014 ArbitraryUserPointer : (null)
+0x018 Self : 0x00000040 _NT_TIB
4.0.3. Локальная память потока TLS
Основной проблемой многопоточных приложений является синхронизация их совместной работы внутри одного процесса. Чем больше у процесса потоков, тем больший получается хаос. Категорически противопоказано использовать в таких приложения глобальные переменные, поскольку за целостность данных в них никто ответственности не несёт. Например, может возникнуть ситуация, когда поток(А) записал своё значение в переменную, а ничего не подозревающий поток(В) тут-же затёр эту переменную под себя.
Логически, правильным решением будет выделение каждому потоку своей памяти, ведь все они исполняются внутри общего пространства родительского процесса. И такая память действительно есть. Это TLS – Thread Local Storage или локальная память потока, которая находится в каждой из структур в поле ТЕВ.0e10.TlsSlots. Внешне она выглядит так:
C-подобный:
//=== TEB (фрагмент структуры) =============================
//==========================================================
02C 00 00 00 00 TlsPointer = 0
030 00 C0 FD 7F PEB = 7FFDC000
E10 TlsSlots
|
+-- 00 00 00 00 00 00 00 00 | 00 00 00 00 30 3C 89 00
00 00 00 00 00 00 00 00 | 00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00 | 00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00 | 00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00 | 00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00 | 00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00 | 00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00 | 00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00 | 00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00 | 00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00 | 00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00 | 00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00 | 00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00 | 00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00 | 00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00 | 00 00 00 00 00 00 00 00 = Всего: 256-байт.
F10 00 00 00 00 TlsLinks.FLink = 0
F14 00 00 00 00 TlsLinks.BLink = 0
F94 00 00 00 00 TlsExpansionSlots = NULL
dword
'ный слот. Вот эти функции:
C-подобный:
TlsAlloc() - возвращает индекс очередного/свободного слота в TEB;
TlsSetValue(index,value) - записывает dword в этот слот;
TlsGetValue(index) - читает в EAX значение из указанного слота;
TlsFree(index) - очищает указанный слот в TEB (и сбрасывает бит в РЕВ).
Последнее редактирование: