• Курсы Академии Кодебай, стартующие в мае - июне, от команды The Codeby

    1. Цифровая криминалистика и реагирование на инциденты
    2. ОС Linux (DFIR) Старт: 16 мая
    3. Анализ фишинговых атак Старт: 16 мая Устройства для тестирования на проникновение Старт: 16 мая

    Скидки до 10%

    Полный список ближайших курсов ...

Статья ASM для х86. (4.0) Системные механизмы Windows


В этой части:
  1. Процессы и их потоки
  2. Адресное пространство процесса
  3. Вторжение в чужое пространство памяти
  4. Перехват API-функций
  5. Скрытие процессов
Для просмотра структур системы, на данном этапе понадобится ядерный отладчик WinDbg. Он поставляется как в составе пакета WDK, так и отдельно. Версию на любой вкус и цвет можно Лично у меня версия 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] и ищем структуры:


wdbg_0.png


..и точно есть такие, причём в модуле 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
Теперь запускаю этот экзешник и даю WinDbg команду !process 0 0, на что отладчик ответит листингом всех процессов в памяти. Как видно мой – последний, и его структура EPROCESS находится в ядерной памяти по адресу 0х88f72020. Ядерная она потому, что имеет адрес выше 0х80000000, а юзерская область находится ниже, ..до адреса 0x7fffffff (младшая половина). Судя по адресам можно сделать вывод, что в моей юзерской памяти находится только структура РЕВ и DirBase:

wdbg_1.png


Теперь у меня есть личный адрес, который я могу подставлять во-все команды и отладчик будет возвращать мне информацию только о данном процессе. Значит для просмотра структуры 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 ]
Описывать все поля структуры KTHREAD не имеет смысла, т.к. их имена говорят сами-за-себя, тем-более что львиная часть нам вообще не интересна. Остановимся только на некоторых из них (цифра – это смещение от начала структуры).

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;
0x58 - WaitIrql;
Тут все понятно из названия - IRQL, на котором вызывающий поток стал ожидать на объекте.

0x59 - WaitMode;
Это поле определяет режим ожидания. Сюда записывается соответствующий параметр, переданный функции ожидания KeWaitFor***. Соответственно это KernelMode или UserMode. Если он равен UserMode, то поток может принимать UserMode APC (вызов асинхронных процедур).

0xE0 - ServiceTable;
Определяет сервисную таблицу функций для потока, под названием SSDT.
Обычно там лежит адрес KeServiceDescriptorTable/TableShadow.


0x140 - PreviousMode;
Содержит режим процессора, в которым поток был до вхождения в режим ядра.
Это поле играет большую роль при вызовах API-функций типа Nt и Zw. Система проверяет, с какого режима вызываются эти функции. Если из ядра, аргументы у функций не проверяются, а если из юзера, то исполняющая система ядра Executive проверяет их кол-во на соответствие.
**********************************

Обычно у процессов несколько потоков, и каждый из них занимается своим делом внутри одного приложения. Некоторые потоки тупо простаивают и ждут какого-нибудь события типа ‘вывод на печать’, некоторые активно ведут предварительные расчёты и т.д. Ознакомится с кол-вом потоков каждого приложения можно в системном ‘Диспетчере задач’, активировав одноимённый столбец в меню вид:


task_0.png



4.0.1. Структура PEB

Как упоминалось выше, структуры THREAD ядро держит при себе, но по отношению к юзеру это не справедливо – юзер тоже человек и ему тоже хочется.. Поэтому на определённом этапе создания контекста процесса, система создаёт в пространстве пользователя структуры с окружением данного процесса PEB (Process-Environment-Block), и для каждого из его потоков - структуры TEB (Thread-Environment-Block). На текущий TEB всегда указывает сегментный регистр процессора FS на х32 системах, и GS на х64.


k_0.png


Поскольку эти структуры находятся уже в адресном пространстве пользователя (а не ядра), нужно перезапустить WinDbg и подключиться к процессу нашего ‘Hello World!’ через клавишу [F6] – Attach to Process. Теперь нужно узнать базу исследуемого процесса в памяти, для чего вводим команду lmD Load Module Dynamic. Как и в предыдущем примере, эту базу будем подставлять для просмотра структур PEB и TEB конкретного процесса:

wdbg_2.png


База 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
Здесь, поле по смещению TEB.00.ExceptionList указывает на системный SEH-фрейм, который отлавливает аппаратные и программные исключения. SEH переводится как Structured Exception Handle, или структурная обработка исключений. Система не утруждает себя обработкой всех исключений, т.к. физически не может их все предугадать. Поэтому программисту дозволено вставлять в цепочку свои обработчики, помещая указатели на них исключительно в стек. На первый обработчик в цепочке указывает как-раз поле TEB.00.ExceptionList.

seh.png



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
Поскольку у каждого потока своя структура ТЕВ, то и память получается своя. Детальное рассмотрение этой памяти выходит за рамки данного топика, поэтому ограничусь лишь системной поддержкой. Для динамического выделения локальной памяти, система предоставляет тредам всего 4 функции. Так-как эти функции оперируют только индексами, тред не может работать с отдельными байтами TLS - мин.порцией является dword'ный слот. Вот эти функции:
C-подобный:
  TlsAlloc() - возвращает индекс очередного/свободного слота в TEB;
  TlsSetValue(index,value) - записывает dword в этот слот;
  TlsGetValue(index) - читает в EAX значение из указанного слота;
  TlsFree(index) - очищает указанный слот в TEB (и сбрасывает бит в РЕВ).
 
Последнее редактирование:

Marylin

Mod.Assembler
Red Team
05.06.2019
305
1 366
BIT
305
я прописал invoke Sleep,1000 перед Invoke Exitprocess, окно остаётся, сообщение не прописывается (пустое консольное окно)
Как вариант, это из-за дескрипторов ввода/вывода 3 и 7 соответственно.

C-подобный:
invoke   WriteConsoleA,7,capt,[cLen],0,0
invoke   ReadConsoleA,3,buff,512,rSize,0

По идеи их нужно запрашивать у системы функцией GetStdHandle(), а я сразу прописал константами. Если консольное окно в единственном экземпляре, то 3 и 7 может прокатить, а если активных консолей много, то у каждого окна будет (отличающийся от 3 и 7) свой дескриптор.

Ещё может быть из-за того, что созданный поток не успевает отработать, и приложение сразу закрывается. Если сделать паузу перед ExitProcess(), это даст время потоку. Вот изменённый вариант, в котором стрелками показаны дополнения - у меня работает:

C-подобный:
format PE console
include 'win32ax.inc'
.data
fName   db  'backup.txt',0
capt    db   'Type text for save to file',13,10
        db   '--------------------------',13,10,0
cLen    dd   $ - capt
final   db   '--------------------------',13,10
        db   'Save to backup.txt - OK!  ',0
fLen    dd   $ - final
buff    db   512 dup(0)    ; буфер для ввода
rSize   dd   0             ; реальная длина ввода.
tHndl   dd   0             ; хэндлы: - потока  Thread
fHndl   dd   0             ;         - файла   File
eHndl   dd   0             ;         - события Event
iHndl   dd   0             ;//       - stdIn    <------
oHndl   dd   0             ;//       - stdOut   <------
;-------
.code
start:
;// Создаём событие и доп.поток
        invoke   CreateEvent,0,0,0,0
        mov      [eHndl],eax
        invoke   CreateThread,0,0,threadFunc,0,0,0
        mov      [tHndl],eax

;// Получаем дескрипторы консольного ввода/вывода     <-------
        invoke   GetStdHandle,STD_INPUT_HANDLE
        mov      [iHndl],eax
        invoke   GetStdHandle,STD_OUTPUT_HANDLE
        mov      [oHndl],eax

;// Выводим заголовок программы в консоль
;// и принимаем от юзера ввод
        invoke   WriteConsoleA,[oHndl],capt,[cLen],0,0
        invoke   ReadConsoleA,[iHndl],buff,512,rSize,0

;// Включаем объект события для доп.потока
        invoke   SetEvent,[eHndl]

;// Выводим финальную мессагу в основном потоке
        invoke   WriteConsoleA,[oHndl],final,[fLen],0,0
        invoke   ReadConsoleA,[iHndl],buff,512,rSize,0      ;// <------
        invoke   ExitProcess,0

;//vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv
;//--- Функция дополнительного потока ----
proc    threadFunc
        invoke   WaitForSingleObject,[eHndl],-1   ; ждём события..
        invoke  _lcreat,fName,0                   ; создать файл R/W
        invoke  _lwrite,eax,buff,[rSize]          ; запись в него из буфера!
        invoke  _lclose,[fHndl]                   ; закрыть файл
        invoke   ExitThread,0                     ;   ..и выгрузить поток.
        ret
endp
.end start
 

Hardreversengineer

Green Team
20.08.2019
49
28
BIT
14
Как вариант, это из-за дескрипторов ввода/вывода 3 и 7 соответственно.
Ух ты, спасибо ! Всё работает.
Я не совсем понял, что поменялось.Что такое дескриптор ? Я читал определение, но смысла не догнал.
И не поможете с одной штукой - Как можно сделать GET-запрос в ассемблере ? Есть какая то команда ?
Если есть, нужно ли предварительно осуществлять какие то подключения, рукопожатия итд ?)
 
Последнее редактирование:

Marylin

Mod.Assembler
Red Team
05.06.2019
305
1 366
BIT
305
Что такое дескриптор ? Я читал определение, но смысла не догнал.
Дескриптор - это связанное с объектом целое число, которое определяет его порядковый номер в системной таблице-объектов. Дескрипторы присваиваются системой только разделяемым объектам, например: файл, устройство, процесс, поток, событие и т.д. Виндовая консоль - это объект типа "файл" под названием CON (console). В этот файл можно записывать WriteConsole(), и можно его читать ReadConsole().

Если кроме нас кому-то ещё потребуется этот "файл", то система создаст дубликат данного объекта и пропишет его в свою таблицу-объектов. Теперь в таблице уже пара одинаковых объектов, с порядковыми номерами 1 и 2, которые и представляют собой дескриптор, или хэндл объекта. В заголовке объекта на который указывает дескриптор, хранятся привелегии запросившего, квота на объект, возможные операции с ним и прочее. Другими словами дескриптор - это указатель на конкретную запись в системной таблице-объектов.

Как можно сделать GET-запрос в ассемблере ?
Точно так-же, как в примере WHOIS.
Единственное отличие в том, что нужно поменять порт на 80,
и вместо запроса "mail.ru",13,10 нужно вписать такие строки:
C-подобный:
"GET / mail.ru / HTTP/1.0", 13,10,10
Сервер вернёт нам ответ в виде HTML-кода, т.е. вместе с тегами. Без проблем можно будет вывести его на консоль прямо в таком виде, но лучше пропарсить приёмный буфер и убрать в нём все html-теги. Например нашли открывающий тег символ "<" и пропускаем все символы, пока не встретим закрывающий тег ">". Ато читать выхлоп неудобно, хотя можно скинуть буфер в файл, как.. *.html
 

dreamseller

Green Team
02.03.2019
36
7
BIT
0
C-подобный:
invoke  VirtualProtect,[mbAddr],1,PAGE_EXECUTE_READWRITE,oldProt
если не хватает места в шеллкоде, можно адрес не выравнивать и размер взять любой в пределах страницы
Поскольку функция работает только с 4К-байтными страницами
 
  • Нравится
Реакции: rulet1337 и Marylin

SearcherSlava

Red Team
10.06.2017
943
1 260
BIT
146
Здравы будьте, цифровые братья и сестры!

Тыц - ссылка на архив изданий (5.77Gb, 500 pdf file).
Некоторые представляют собой библиографические раритеты (см.вложение pdf).
Отладка, низкоуровневое программирование, ассемблер, компиляторы, реверс-инжиниринг, и т.д.

Отладка – нолей и единичек ладка
Отладка – нолей и единиц наладка
Хоть в жизни не всегда все сладко
И иногда, увы, бывает неполадка
Приходится учиться и многое уметь

Everything that you do and see, one more event in your memory
Every bit takes another bite without control over wrong or right
You must screen every entry made, the consequences must be weighed
The only way to security is every thought in captivity
Computer brains, put garbage in
Computer brains, get garbage out
Computer brains, programming you
Computer brains, what can you do?
Break out
Are you a user or being used, has your memory been abused?
Take random samples from your mind and analyze what you may find
You can clear all your memory and be transformed when you find the key
Think on the things that will bring you peace, confusing data soon will cease
 

Вложения

  • Отладка.pdf
    3,3 МБ · Просмотры: 971
Последнее редактирование:
  • Нравится
Реакции: bin1101d, CKAP и Marylin

Marylin

Mod.Assembler
Red Team
05.06.2019
305
1 366
BIT
305
Ого - вот это библиотека!!! Спасибо большое!!!
 

SearcherSlava

Red Team
10.06.2017
943
1 260
BIT
146
Ого - вот это библиотека!!! Спасибо большое!!!

Здрав будь!

IT-шная мудрость гласит: Кто с ассемблером дружен, тому и реверс инжиниринг нужен.
Дополнил предыдущий список литературы.

Говорят, в очумелых руках
Реверс инжиниринг безопасен
С переходом ноля в единицу
BIOS на изменения согласен
 

Вложения

  • Asm, RE, Pro вирусы, etc...pdf
    2 МБ · Просмотры: 383
  • Нравится
Реакции: bin1101d

Mikl___

Green Team
01.09.2019
28
47
BIT
40
Здравствуйте SearcherSlava
Ссылка нерабочая или у меня что-то не так, или после 24.01.2020 вы ее отключили? Если отключена, можно ли подключить повторно?
 

SearcherSlava

Red Team
10.06.2017
943
1 260
BIT
146
Здравствуйте SearcherSlava
Ссылка нерабочая или у меня что-то не так, или после 24.01.2020 вы ее отключили? Если отключена, можно ли подключить повторно?

Здравствуйте, Mikl___ !

У Вас все так. Ссылки обновлены, для скачивания подготовлены.

Ноль с единицей в основе всего
Больше не знают о них ничего

 
  • Нравится
Реакции: bin1101d

Hardreversengineer

Green Team
20.08.2019
49
28
BIT
14
Здравствуйте, Mikl___ !

У Вас все так. Ссылки обновлены, для скачивания подготовлены.

Ноль с единицей в основе всего
Больше не знают о них ничего

А можно ссылку на ваш сборник стихов ?
 

bin1101d

One Level
09.03.2021
7
4
BIT
0
Здравствуйте, Mikl___ !

У Вас все так. Ссылки обновлены, для скачивания подготовлены.

Ноль с единицей в основе всего
Больше не знают о них ничего


SearcherSlava, пожалуйста продублируйте библиотеку ещё раз, файлы уже удалены, как будет время, заранее признателен.

Marylin спасибо огромное за Ваш труд.​

 
  • Нравится
Реакции: Marylin

SearcherSlava

Red Team
10.06.2017
943
1 260
BIT
146
SearcherSlava, пожалуйста продублируйте библиотеку ещё раз, файлы уже удалены, как будет время, заранее признателен.

Marylin спасибо огромное за Ваш труд.​

Здрав будь!

Библиотека продублирована. В 3-ей ссылке 2-х томник по аппаратным троянам. Ссылки обновлены.

Живя в поиске идей
Создать ценность для людей

Codeby, помилуй
Codeby, прости
Помоги мне Си осилить
На моем пути
Так же Асм в придачу
Реверс, стало быть
Помоги задачу
Трудную решить

К программной закладке
Имей, бро, заплатку
Когда не все гладко
Умей, бро, отладку

Реверс инжинирингу печатных плат
Далеко не каждый профи будет рад
Независимо от умений и наград
Видишь цель - не ставь преград!

Бывают дни, когда опустишь руки
Не абы как, а в поисковую строку
И Гуглу сразу станет не до скуки
И вежливо ответит он «Попьем чайку?»

 

Вложения

  • Отладка.pdf
    3,3 МБ · Просмотры: 244
  • Asm, RE, Pro вирусы, etc...pdf
    2 МБ · Просмотры: 839
Последнее редактирование:

bin1101d

One Level
09.03.2021
7
4
BIT
0
Здрав будь!

Библиотека продублирована. В 3-ей ссылке 2-х томник по аппаратным троянам.

Живя в поиске идей
Создать ценность для людей

Codeby, помилуй
Codeby, прости
Помоги мне Си осилить
На моем пути
Так же Асм в придачу
Реверс, стало быть
Помоги задачу
Трудную решить

К программной закладке
Имей, бро, заплатку
Когда не все гладко
Умей, бро, отладку

Реверс инжинирингу печатных плат
Далеко не каждый профи будет рад
Независимо от умений и наград
Видишь цель - не ставь преград!

Бывают дни, когда опустишь руки
Не абы как, а в поисковую строку
И Гуглу сразу станет не до скуки
И вежливо ответит он «Попьем чайку?»

Каким-то магическим образом, книга А.И. Белоуса именно то, что я искал. Судьба. Низкий поклон.
 

SearcherSlava

Red Team
10.06.2017
943
1 260
BIT
146
Каким-то магическим образом, книга А.И. Белоуса именно то, что я искал. Судьба. Низкий поклон.
Здрав будь!

Магия в силе желания - оказался в нужном месте в нужное время и заполнил свой резервный сектор информационных потребностей. Удачи и творческих успехов!
 

bin1101d

One Level
09.03.2021
7
4
BIT
0
В общую копилку прикреплю одну очень хорошую книгу, для новичков и не только:

Цифровая схемотехника
и архитектура компьютера
второе издание
Дэвид М. Харрис и Сара Л. Харрис

Рассмотренно создание микропроцессора MIPS c RISC архитектурой, всё начинается с самых азов, от физического уровня, до уровня программирования на ассемблере и си. Очень толковая книга, в конце каждой главы есть задания для самопроверки изученного материала, подойдёт для тех, кто хочет реально осознать понятие компьютер.

 
Последнее редактирование:

bin1101d

One Level
09.03.2021
7
4
BIT
0
4.1.4.4. Простое многопоточное приложение

Небольшой пример дву/поточного приложения..
Основной поток подаёт сигнал к действию доп.потоку посредством объекта-синхронизации 'Event' (событие). Значит сначала нужно создать этот объект функцией CreateEvent() с таким прототипом:

C-подобный:
HANDLE CreateEvent (
    lpEventAttributes    // (0) - атрибут безопасности
    bManualReset         // (0) - авто/переключение в предыдущее состояние
    bInitialState        // (0) - начальное состояние объекта (выкл)
    lpName               // (0) - указатель на имя объекта (у нас безымянный).
   );
Эта fn. вернёт дескриптор Handle данного объекта-события.
Включать/выключать событие позволяют функции SetEvent() и ResetEvent() соответственно, но поскольку в аргументах мы задали авто/переключение в предыдущее состояние, нужно будет только включить его по Set. После того-как тред отработает, он сам обратно выключит объект.

Везде, где есть объекты-синхронизации, должна быть функция WaitForSingleObject(), которая будет наблюдать за этим объектом, и в зависимости от его состояния - замораживать или пробуждать доп.поток, для исполнения своего кода. Если нужно наблюдать сразу за несколькими объектами, то вместо Single используем WaitForMultipleObject(). Эти функции удобны тем, что в момент ожидания не занимают процессорное время. Прототип у Single такой:

C-подобный:
DWORD WaitForSingleObject (
    hHandle,              // Handle объекта, за которым нужно наблюдать
    dwMilliseconds        // сколько времени наблюдать. -1 = 0xFFFFFFFF = вечно (INFINITE)
   );
Это - что касается объекта.. Теперь сам поток..
Создаётся он функцией CreateThread() с нижеследующим прототипом (в скобках мои значения):

C-подобный:
HANDLE CreateThread (
    lpThreadAttributes,   // (0) - атрибут безопасности (в дефолте)
    dwStackSize,          // (0) - размер стека для потока (в дефолте)
    lpStartAddress,       // указатель на исполняемую функцию потока
    lpParameter,          // (0) - можно передать потоку 1 аргумент
    dwCreationFlags,      // (0) - после создания, сразу запустить поток на исполнение
    lpThreadId            // (0) - указатель для сохранения Thread-ID (нам TID не нужен)
   );
Функция возвращает Handle созданного потока, через который можно к нему обращаться.
Среди функций работы с потоками выделяются следующие:

  • SuspendThread (handle) - усыпить поток;
  • ResumeThread (handle) - пробудить поток;
  • ExitThread (0) - выгрузить поток из памяти.
Консольная программа ниже демонстрирует, как применять эти функции на практике. Здесь, в основном потоке пользователю предлагается ввести текст, а доп.поток сохранит этот текст в файл. Командой для начала работы потока является SetEvent(). Дескриптором вывода STDOUT (монитор) является константа(7), а дескриптором ввода STDIN (клавиатура) - константа(3). Их будем подставлять в fn. Read/WriteConsole().

Стандартные функции Win32 для работы с файлами типа CreateFile() - слишком громоздки, поэтому задействуем их аналоги из Win2000, которые для обратной совместимости поддерживаются всеми Kernel32/64.dll. Просмотреть список функций-экспорта из системных библиотек можно как-минимум прямо из TotalCommander, нажав в нём комбинацию Ctrl+Q и перейдя на вкладку Импорт/Экспорт. Для их исследования, я скопировал основные файлы системы в отдельную папку, и теперь могу свободно трассировать их в отладчике (иначе система не даст этого сделать):


Посмотреть вложение 31308

Ну и собственно многопоточный код:
C-подобный:
format PE console
include 'win32ax.inc'
.data
fName   db  'backup.txt',0
capt    db   'Type text for save to file',13,10
        db   '--------------------------',13,10,0
cLen    dd   $ - capt
final   db   '--------------------------',13,10
        db   'Save to backup.txt - OK!  ',0
fLen    dd   $ - final
buff    db   512 dup(0)    ; буфер для ввода
rSize   dd   0             ; реальная длина ввода.
tHndl   dd   0             ; хэндлы: - потока  Thread
fHndl   dd   0             ;         - файла   File
eHndl   dd   0             ;         - события Event
;-------
.code
start:
;// Создаём событие и доп.поток
        invoke   CreateEvent,0,0,0,0
        mov      [eHndl],eax
        invoke   CreateThread,0,0,threadFunc,0,0,0
        mov      [tHndl],eax

;// Выводим заголовок программы в консоль
;// и принимаем от юзера ввод
        invoke   WriteConsoleA,7,capt,[cLen],0,0
        invoke   ReadConsoleA,3,buff,512,rSize,0

;// Включаем объект события для доп.потока
        invoke   SetEvent,[eHndl]

;// Выводим финальную мессагу в основном потоке
        invoke   WriteConsoleA,7,final,[fLen],0,0
        invoke   ExitProcess,0

;//vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv
;//--- Функция дополнительного потока ----
proc    threadFunc
        invoke   WaitForSingleObject,[eHndl],-1   ; ждём события..
        invoke  _lcreat,fName,0                   ; создать файл R/W
        invoke  _lwrite,eax,buff,[rSize]          ; запись в него из буфера!
        invoke  _lclose,[fHndl]                   ; закрыть файл
        invoke   ExitThread,0                     ;   ..и выгрузить поток.
        ret
endp
.end start
Понадобились потоки, компилю пример.
При открытии на доли секунды вылетает консоль, отддебажил просмотрел Олей, при дебаге висит консоль, но не выводит инфу и не забирает, пустой висит, в Оле код отрабатывает правильно, ложит в стек параметры, исполняет функции, не выдает ошибок, код пересмотрел раз 10, всё ровно. машина win10 x32
Посоветуете, куда смотреть?

Добавлено: братцы выручайте советом...
 
Последнее редактирование:

bin1101d

One Level
09.03.2021
7
4
BIT
0
Понадобились потоки, компилю пример.
При открытии на доли секунды вылетает консоль, отддебажил просмотрел Олей, при дебаге висит консоль, но не выводит инфу и не забирает, пустой висит, в Оле код отрабатывает правильно, ложит в стек параметры, исполняет функции, не выдает ошибок, код пересмотрел раз 10, всё ровно. машина win10 x32
Посоветуете, куда смотреть?

Добавлено: братцы выручайте советом...
Решил, код ниже:
C-подобный:
format   PE console
include  'win32ax.inc'
;---------
.data
fName    db  'backup.txt',0
capt         db  'Input txt to save in backup.txt:',0
cLen        dd  $ - capt
final         db  'Save to backup.txt - OK!. Press any key...',0
fLen        dd  $ - final
buff         db  512 dup(0)
rSize       dd  0
tHndl      dd  0
fHndl      dd  0
eHndl    dd  0
;---------
.code
start:
         invoke         CreateEvent,0,0,0,0
         mov            [eHndl],eax
         invoke         CreateThread,0,0,threadFunc,0,0,0
         mov            [tHndl],eax

         invoke         WriteConsole,<invoke GetStdHandle,STD_OUTPUT_HANDLE>,capt,[cLen],0,0     ;через GetStdHandle беру хендл на ввод-вывод
         invoke         ReadConsole,<invoke GetStdHandle,STD_INPUT_HANDLE>,buff,512,rSize           ;win10x32 не работают 7,3
         invoke         SetEvent,[eHndl]
         invoke         WaitForSingleObject,[tHndl],-1                           ;ждем пока отработает тред
         invoke         WriteConsole,<invoke GetStdHandle,STD_OUTPUT_HANDLE>,final,[fLen],0,0
         invoke         ReadConsoleInput,<invoke GetStdHandle,STD_INPUT_HANDLE>,buff,1,rSize     ;ждём нажатия любой клавиши
         invoke         ExitProcess,0

proc     threadFunc
         invoke         WaitForSingleObject,[eHndl],-1
         invoke         _lcreat,fName,0
         invoke         _lwrite,eax,buff,[rSize]
         invoke         _lclose,[fHndl]
         invoke         ExitThread,0
         ret
endp
;---------
.end start
 
  • Нравится
Реакции: Pernat1y и Marylin
Мы в соцсетях:

Обучение наступательной кибербезопасности в игровой форме. Начать игру!