• Курсы Академии Кодебай, стартующие в мае - июне, от команды 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
296
1 312
BIT
123
4.1. Адресное пространство процесса

4.1.0. Виртуальная память

Для каждого процесса система Win32 выделяет отдельную память размером ~2Gb, из имеющихся в наличии 4Gb. Пространство это виртуальное и к физической памяти DDR имеет лишь посредственное отношение, поэтому в ОЗУ одновременно могут находиться сразу несколько 2-гигабайтных процессов. По большому счёту, в Р-моде понятие ‘физическая память’ отсутствует как-таковое - им оперирует только ядро ОСи. На машине может быть одна планка DDR размером 512Мб, но для винды всё-равно будет 4Gb виртуальной. От куда берётся столько памяти? Давайте разбираться..

В основе виртуальной памяти лежит идея, что у каждой программы имеется собственное адресное пространство, которое разбивается страницы. Эти страницы отображаются на физ.память, но для запуска программы одновременное присутствие в памяти всех страниц необязательно – большая часть из них ожидают своей очереди на диске. Когда программа ссылается на часть своего пространства, которого на данный момент нет в физ.памяти, ОС генерит исключение Page-Fault и отсутствующая страница подгружается в память с диска, после чего потерпевшая неудачу команда повторяется. Отработавшие страницы выгружаются обратно на диск, в т.н. файл-подкачки ‘pagefile.sys’.

Все линейки NT работают в 'гибридном режиме', когда на сегментную модель накладывается страничная. Размеры страниц могут быть двух типов – 4Кб (стандартная) или 4Мб, если включён режим РАЕPhysical Address Extension. В большинстве случаях страницы 4К-байтные. Такой размер позволяет менеджеру памяти более гибко управлять общим пространством. Размеры сегментов больше, чем размеры страниц и ОС имеет отдельные аллокаторы памяти для каждого из слоёв: Virtual, Local и Heap Alloc. VirtualAlloc() требует выравнивания выделяемых блоков памяти на 64К-байтную границу, т.е. ориентируется на сегменты. LocalAlloc() выделяет память страницами, а HeapAlloc() уже произвольное кол-во байт.



4.1.1. Аппаратная поддержка виртуальной памяти

Страничная трансляция включается битом(31) управляющего регистра CR0. На вход транслятора страниц приходит линейный адрес, который MMU разбивает на 3 части: старшие 10-бит выделяются под каталог страниц (10-бит = 1024 записи PDE), средние 10-бит под таблицу страниц (одна из 1024 страниц PTE), и младшие 12-бит выбирают смещение в странице физической памяти. Страницу физ.памяти назвали Physical Frame, или страничный кадр в памяти DDR-SDRAM. Размеры страничных кадров всегда равны страницам виртуальной памяти, т.е. 4Кб.

Так адресуется глобальное пространство памяти процесса: 1024*1024*4096=4Gb. У каждого процесса свой каталог-страниц, линейный адрес которого лежит в его регистре CR3 (PDBR) - Page Directory Base Register. Схематически это выглядит так:


paging.png


При такой схеме памяти, все запускаемые процессы получают одинаковые виртуальные адреса, например базу загрузки образа в память 0х00400000. Если-бы это была не виртуальная память а физическая, то процесс(А) мог-бы запросто затереть данные процесса(В), ведь адрес у них совпадает.

Чтобы исключить такую ситуацию, система ведёт список адресов физических DRAM-страниц, выделенных для каждого процесса. Этот список назвали MDL - Memory Descriptor List, или ‘таблица распределения физической памяти’. Важно понять, что виртуальный адрес 0х00400000 процесса(А) отображается на одну физическую страницу DDR, а тот-же адрес процесса(В) отображается уже на другую физическую страницу памяти. Таким образом, процесс(А) никогда не затерёт данные процесса(В), хоть у них и одинаковый виртуальный адрес. Общая схема трансляции адреса от процессора и до физической DRAM, представлена ниже:


segPag.png



4.1.2. Секции программы

Пространство каждого процесса делится на секции, по аналогии с сегментами реального режима. Распространённый их список в формате fasm’a приводится ниже:

C-подобный:
section '.code'    code (атрибуты) - исполняемый код;
section '.data'    data (атрибуты) - инициал.данные (строки и переменные);
section '.bss'     data (атрибуты) - неинициал.данные ('0' или '?');
section '.idata'   data (атрибуты) - импорт функций;
section '.edata'   data (атрибуты) - экспорт функций;
section '.reloc'   data (атрибуты) - релокация адресов при смене ImageBase;
section '.rsrc'    data (атрибуты) - ресурсы (иконки, курсоры и пр.);
section '.tls'     data (атрибуты) - локальная память потока (Thread Local Storage);
section '.rdata'   data (атрибуты) - отладочная информация;
section '.marylin' data (атрибуты) - своя секция под барахло.
Обратите внимание, что исполняемой является только секция-кода, которой нужно выставить флаг 'executable'. Политика безопасности Win рекомендует запрещать в неё запись, чтобы предотвратить модификацию кода, но как-всегда это написано ‘вилами на воде’. Fasm – один из немногих компиляторов, который позволяет задавать атрибуты секциям. Эти атрибуты можно комбинировать нужным образом:
C-подобный:
executable  - исполняемая секция;
readable    - секция доступна на чтение;
writable    - секция доступна на запись;
discardable - при ограниченной физ.памяти, это первый кандидат на выгрузку в своп;
notpageable - из физ.памяти секцию сбрасывать нельзя! (невыгружаемая);
fixups      - авто/коррекция адресов и ошибок;
shareable   - расшаренная секция (вреда от которой больше, чем пользы).
На форумах можно встретить советы, мол нужно закрывать доступ на запись в секцию-кода, и если авер обнаружит обратное, мы у него точно окажемся под колпаком. Он посчитает, что код нашей программы занимается само/модификацией, а это присуще вирусам. Попробуем опровергнуть этот миф, и напишем пример такой программы. Это просто смена байта с нуль на 90h, после чего байт превратится в инструкцию NOP (опкод 0x90).
C-подобный:
include 'win32ax.inc'
.data
text    db  'Самомодификация!',0
;// Открываем секции-кода доступ на запись ---------------------
section '.code' code executable writable     ;<-- волшебный атрибут
start:  nop
        mov     byte[@01],90h                ; запись в секцию-кода!
@01:    db      0                            ; модифицируемый байт
        invoke  MessageBox,0,text,0,0
        invoke  ExitProcess,0
.end start
Теперь идём на и проверяем этот код на малвару.
Как видим, из 15-ти антивирусов никто-ничего не заподозрил и это доказывает,
что само/модифицируемый код и под виндой имеет место быть, а не только под досом:


jotti.png
 

CKAP

Green Team
10.06.2019
68
39
BIT
0
> Запускаем WinDbg в локальном режиме ядра [Ctrl+K --> Local] и ищем структуры:
31188


Я наверное сильно погорячился, что поставил Хрюшку.. ну хорошо не далеко ушли, пойду её сносить.. а то чет проблемы на проблеме.
 

Marylin

Mod.Assembler
Red Team
05.06.2019
296
1 312
BIT
123
пойду её сносить
Системы х64 запускают 32-битные процессы через подсистему WOW64 (Windows-on-Windows). Соответственно х32 выше XP запускают 16-битные программки через WOW32. В вашей ошибке говорится, что:
Система не поддерживает локальную отладку ядра.
Отладка ядра WinXP должна иметь Административные привилегии, и не поддерживается в WOW64. Локальный сеанс отладки отключён по умолчанию в Vista, и нужно запустить "bcdedit -debug on" и перезагрузиться, чтобы включить её.
Так-что смена системы с хрюши на другую в вашем случае думаю не даст результата. Видимо отладку ядра нужно проводить только с реальной машины, а не ВМ и с административными правами.

А в обычном режиме с ВМ у вас запускается WinDbg? чз Ctrl+E или F6..
Может это только в ядро не пускает, а обычная отладка поддерживается.
 

CKAP

Green Team
10.06.2019
68
39
BIT
0
Так-что смена системы с хрюши на другую в вашем случае думаю не даст результата. Видимо отладку ядра нужно проводить только с реальной машины, а не ВМ и с административными правами.

Оу, ну я не зря спрашивал ранее про ВМ и Физическую машину.. Я дето уже слышал, что у ВМ есть проблемы... точно не вспомню какие, но точно помню что советовали юзать физическую машину.

По остальному.. ХРюшу уже снес, на ней и инстайлеры устаревшие. Вобчем гемора хвает по мимо. А семку есче не поставил. Возможно вечером.
 

Marylin

Mod.Assembler
Red Team
05.06.2019
296
1 312
BIT
123
4.1.3. Карта памяти процесса

Запущенный на исполнение файл представляет из-себя процесс, который может иметь до 2000 подчинённых потоков ‘Thread’. Треды одного процесса могут свободно общаться между собой, сигнализируя о том или ином событии посредством системных объектов Event, Semaphore, Mutex. Однако один процесс не может вмешаться в работу другого, что собственно и подразумевает защищённый режим. Ясно, что это всё условности и можно без проблем преодолеть это ограничение - тут главное не спалиться антивирусу.

Если в общих чертах, то когда система создаёт процесс, в первую очередь в пространстве ядра для него создаётся структура EPROCESS, затем список MDL отведённых процессу физ.страниц памяти. С этого момента виртуальное пространство процесса готово, и система копирует в юзерскую область пространства дисковый файл Ntdll.dll, который представляет из-себя загрузчик образа программ. Помимо загрузчика (функции с приставками Ldr), Ntdll.dll выдаёт на экспорт огромное кол-во Win32-API, общее число которых на XP равно 1.316 штук. Причём сама эта DLL не импортирует ни одной функции с других библиотек, т.е. у неё отсутствует секция-импорта.

hiew_02.png


Теперь загрузчик подгружает с диска в память образ запускаемой программы и просканировав его секцию-импорта, подтягивает в ОЗУ остальные библиотеки типа Kernel32.dll, User32.dll и GDI32.dll. После того-как все библиотеки импорта загрузятся в память, загрузчик передаёт управление на Entry-Point программы и она стартует. Загрузчик Ntdll.dll и три указанные библиотеки х32.dll остаются в памяти процесса на всё время его исполнения и ждут, когда программа вызовет из них API-функции. Мало того, система всегда загружает их по одному-и-тому-же адресу.

Такая картина предначертана судьбою одному процессу. Когда наступит время запустить следующий процесс, система полностью повторит описанный алгоритм, и новый процесс получит свою копию загрузчика Ntdll.dll, и остальных библиотек с функциями Win32-API. То-есть у каждого процесса свой набор ‘отображаемых в память’ системных файлов, что создаёт определённые проблемы при перехвате API. Например, перехват функции из библиотеки Kernel32.dll, которая находится в моём пространстве, никак не повлияет на эту-же функцию, которая находится в пространстве соседнего процесса.

Выше упоминался тип секций с атрибутом ‘Shareable’ – расшаренная. Такой тип имели системные библиотеки оси Win-98. Эта система загружала свои библиотеки в глобальную память и открывала к ним доступ всем-желающим, тем-самым расшаривая их. У программ была только одна общая Kernel32.dll в памяти, и её перехват действовал на глобальном уровне, приводя все процессы в ступор. Начиная с Win2000 Microsoft отказалась от раздачи секций на шару, посчитав это брешью в системе безопасности. Так, каждый процесс получил свою порцию системных файлов.

Загрузим свой ‘HelloWorld’ в отладчик OllyDbg и нажав на пимпу (М) получим карту памяти процесса (на скрине Оля версии 2). Здесь, я выделил синим принадлежащие моему коду адреса, а красным – это регион системных библиотек, которые были подгружены лоадером Ntdll.dll в моё пространство. Видно, что для них выделяется верхний регион памяти. 12К-байтный стек по-умолчанию, занял позиции наоборот в подвале, начиная с адреса 0х0006d000 (три страницы). 1000h байт – это одна 4К-байтная страница виртуальной памяти. Мой процесс HelloWorld называется Example.exe, и каждая его секция – размером в страницу:


mmap.png


Обратите внимание на атрибуты ‘Access’ системных файлов. На запись(W) доступны только их секции данных, в которых нет ничего интересного. Чтобы записать в них исполняемый шелл-код, нужно будет переназначить атрибуты функцией VirtualProtect(). Эта функция позволяет манипулировать атрибутами памяти в своём процессе, а VirtualProtectEx() – способна устанавливать эти атрибуты и в чужих страницах, не принадлежащих нашему процессу.

Позже мы ещё вернёмся к ним, а пока напишем код, который вытащит базы системных библиотек в памяти, что получить к ним доступ на программном уровне. Получив базу, мы можем через GetProcAddress() узнать смещение нужной функции в любой из библиотек, чтобы потом перехватить их. Базу возвращает GetModuleHandle(), которой и воспользуемся:

C-подобный:
include 'win32ax.inc'
.data
capt    db  'mMap v0.1',0
text    db  'База в памяти системных библиотек',13,10
        db  '---------------------------------',13,10
buff    db   256 dup(0)
frmt    db  'GDI32.dll....: %08x',13,10       ;// спецификаторы для wsprintf()
        db  'Kernel32.dll.: %08x',13,10
        db  'Ntdll.dll....: %08x',13,10
        db  'USER32.dll.... %08x',0
;-------
.code
start:
;// Получаем базы модулей,
;// и сохраняем их в стеке в нужном порядке
        invoke  GetModuleHandle,<'user32.dll',0>
        push    eax
        invoke  GetModuleHandle,<'ntdll.dll',0>
        push    eax
        invoke  GetModuleHandle,<'kernel32.dll',0>
        push    eax
        invoke  GetModuleHandle,<'gdi32.dll',0>
        push    eax
;// Преобразуем числа из стека в строку
;// все пуши считаются аргументами wsprintf()
        invoke  wsprintf,buff,frmt

        invoke  MessageBox,0,text,capt,0
        invoke  ExitProcess,0
.end start
Чтобы убедиться в валидности результата этого кода, сравним его со-скрином выше, или запросим у WinDbg командой lmi информацию о модулях Load-Module-Info. Как видим, весь софт возвращает одну-и-ту-же базу, что подтверждает загрузку системных библиотек всегда по фиксированному адресу:

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

CKAP

Green Team
10.06.2019
68
39
BIT
0
А в обычном режиме с ВМ у вас запускается WinDbg? чз Ctrl+E или F6..
Может это только в ядро не пускает, а обычная отладка поддерживается.

Ctr + E
31239

И по F6
31241

С ядром таже хрень, как вы и говорили.. хм.. не критично для учебы?
зы. ОС на Виртуал Бокс от Оракле.
 

SearcherSlava

Red Team
10.06.2017
943
1 259
BIT
113

С ядром таже хрень, как вы и говорили.. хм.. не критично для учебы?
зы. ОС на Виртуал Бокс от Оракле.

Гуглоуточка: project oracle cia pdf
Машина - виртуальная, наши данные - реальные.
 
  • Нравится
Реакции: CKAP

CKAP

Green Team
10.06.2019
68
39
BIT
0
Хоспади, как же все сложно с этими вашими Ядерными делами.
Пересел уже за физ машину.
31245


В консоле от рута bcdedit /debug on + ребут
И на выходе
31246


Балин может я что то не то делаю. Просто с этой прогой не знаком. Но уже хоть консолька появилась.
 
  • Нравится
Реакции: Marylin

Marylin

Mod.Assembler
Red Team
05.06.2019
296
1 312
BIT
123
может я что то не то делаю.
Ну вот.. консолька есть.. теперь нужно настроить в WinDbg приём 'отладочных символов'. Эти символы с расширением *.pdb хранятся на сервере Microsoft и позволяют придавать осмысленные имена функциям, служебным структурам и т.п. Вам нужно создать в родительской папке WinDbg подпапку SYM, если таковой там нет по-умолчанию. Позже все скаченные символы будут сбрасываться в неё. У меня путь такой: C:\Program Files\Debugging Tools for Windows (x86)\sym\

При первом запуске отладчика, в данном хранилище ничего нет - оно будет пополняться динамически с сервера Microsoft (вы должны быть On-line). Для настройки символов, нужно запустить WinDBG в Kernel-режиме [Ctrl+K-->Local] и в окне меню "File-->Symbol_File_Path" прошить путь до локального хранилища, и через разделитель(*) указать дополнительный сервер символов Microsoft. В конечном итоге, адрес должен иметь следующий формат:

SRV*C:\Program Files\Debugging Tools for Windows (x86)\sym*

wdb_74.png


Если на данном этапе вы находитесь в On-line, то после нажатия ОК отладчик начнёт скачивать в своё/локальное хранилище символы основных файлов ядра - это 'ntoskrnl.exe' и 'ntdll.dll'. Для начала этого набора символов будет достаточно, а остальные будут загружаться по мере необходимости, о которой WinDBG будет сам нас оповещать.

Проверить символы можно командой .sympath, и если указанный выше путь прописан, то проверить символы конкретно модуля NT можно командой !lmi nt. В отчёте будет строка Symbol Type: PDB, по которой и можно сделать вывод о наличии отладочных символов. Ну или просто заглянуть в указанную папку \SYM.. там должен быть файл *.pdb.
 

Marylin

Mod.Assembler
Red Team
05.06.2019
296
1 312
BIT
123
что-то ссылка на сервер мелкософта сама поменялась..
нужно задать адрес как на скрине
 
  • Нравится
Реакции: Mikl___ и CKAP

Marylin

Mod.Assembler
Red Team
05.06.2019
296
1 312
BIT
123
у меня этих символов уже набралось 146 папок, для каждого модуля (драйвера) отдельно. Чтобы не потерять их, периодически делаю из этой папки бэкап. Это позволит при переустановки системы не качать их заново, а просто восстановить в папку sym
sym.png
 
  • Нравится
Реакции: Mikl___ и CKAP

Marylin

Mod.Assembler
Red Team
05.06.2019
296
1 312
BIT
123
Как пополнится, можете просмотреть например структуру eprocess командой dt _eprocess
Поскольку отладчик у вас на реальной машине, вам придётся компилировать примеры fasm'a на виртуалке, а тестить их на реальной машине.

Компилировать х32 приложения на системе х64 не получится, т.к. код использует библиотеки х32, а на системе эти библиотеки в формате х64. Как я говорил, функции у них разные, например тот-же MessageBox на х64 требует аргументы в регистрах, а в х32 они передаются через стек.
 
  • Нравится
Реакции: Mikl___ и CKAP

CKAP

Green Team
10.06.2019
68
39
BIT
0
Поскольку отладчик у вас на реальной машине, вам придётся компилировать примеры fasm'a на виртуалке, а тестить их на реальной машине.

Да не.. я полностью перетащил все на физ 32 битную машину. Вы тут рассказываете как руками дотянутся до важных мест. Так что, на хрен ВМ.
 
Последнее редактирование:
  • Нравится
Реакции: Marylin

CKAP

Green Team
10.06.2019
68
39
BIT
0
Я потерялся где то тут

> т.к. при подключении к процессу WinDbg останавливается далеко не на точке-входа в отлаживаемый процесс. Если поставить бряк на Е-Рoint и запустить отладчик командой Go

Не понял куда чё ставить.. хм.
------------
И вот есче..
31253


Чет много всего весит на одних адресах..
прога эта
----------------------
include 'win32ax.inc'
;-------
.data
text db 'Hello World!',0
;-------
.code
start: invoke MessageBox,0,text,0,0
invoke ExitProcess,0
.end start
-----------

Зы. Пробираюсь по вашему посту как по непроходимым джунглям (:)
 
Последнее редактирование:

Marylin

Mod.Assembler
Red Team
05.06.2019
296
1 312
BIT
123
Я потерялся где то тут
Для начала вам нужно сменить дефолтный фейс WinDbg, чтобы он отображал регистры, память, стек и прочее в своём окне. По-молчанию у него этого нет, и некоторые схемы лежат в папке %WinDbg%\themes. Значит сейчас вам нужно проделать следующие шаги:
  1. Закрыть отладчик WinDBG.
  2. Запускаем "regedit" и идём по пути: HKCU\Software\Microsoft\Windbg\..
  3. Удаляем в этой папке весь раздел "Workspaces", co-всей подноготной.
  4. Теперь из дира C:\Program Files\Debugging Tools for Windows\themes\ запускаем файл-реестра "standardvs.reg", подтверждая свои намерения буттоном "ОК".
  5. Запускаем WinDBG и видим уже несколько (выстроенных каскадом) окон. Остаётся из меню "View-->Font" выбрать подходящий шрифт (у меня "Courier New") и подвигав за маркеры окон, настроить их в удобное расположение.
  6. Чтобы запомнить ново/испечённый Workspace нужно сохранить его с расширением *.wew, для чего проследуем в меню "File-->Save_Workspace_to_File" и задав ему имя, сохраним в той-же папке "..\themes". После этого можно загнать его и в локальное хранилище, через "Save_Workspace_As..".
Теперь отладчик будет стартовать в приглядном виде,
преобразившись от полного нуля до старшего майора - у меня получилось так:
WinDbg_face.png
Для просмотра структур PEB/TEB текущего процесса можно воспользоваться командами !peb и !teb соответственно.
 
  • Нравится
Реакции: Mikl___ и CKAP

CKAP

Green Team
10.06.2019
68
39
BIT
0
> Это просто смена байта с нуль на 90h
31261


Очень интересный момент... хотя минут пять тупил в регистры, но там кроме указателя команды не чего не меняется.
 

Marylin

Mod.Assembler
Red Team
05.06.2019
296
1 312
BIT
123
4.1.4. Системные объекты, Handle, PID процессов и связные списки

Рассматривая системные механизмы, нельзя пропустить такую важную их часть как ‘объекты ядра’. Без доли сарказма объекты можно назвать основой-основ многозадачных систем. Ими управляет Object Manager (диспетчер), который создаёт объекты только для разделяемых ресурсов ОСи, и если ресурс используется в рамках одного процесса, назначать его в виде объекта не имеет смысла. Объектный подход позволяет в будущем добавлять ещё системных ресурсов, не переписывая при этом код ядра Ntoskrnl.exe.

4.1.4.0. Объекты ядра

В пространстве ядра имеется каталог объектов, с под/каталогами для каждого типа объектов. На этапе загрузки системы каталог пуст и заполняется он динамически, по мере обращения системы к различным своим ресурсам. В качестве ресурсов могут выступать: процессы, потоки, файлы, устройства, и всё остальное. Марк Руссинович написал утилиту WinObj для изучения объектов ядра, хотя лично я предпочитаю аналогичную программу от хакера Four-F, который внёс огромный вклад в область исследования драйверов - она называется WinObjExplorer. 64-битную её версию можно стянуть с гитхаба, а для х32 я прицепил её в скрепке:

winObj.png


В данной директории каталога объектов, представлены все имеющиеся типы-объектов ‘Type’. Объекты создаются для того, чтобы один раз собрав информацию о нём, запомнить эту инфу в соответствующей структуре данных. Тогда при следующем обращении достаточно будет возвратить только указатель на структуру объекта, без повторного сбора информации. Например, при обнаружении устройства, для его представления создаётся объект устройства Device-Objects. Для управления этим устройством загружается драйвер и создаётся объект драйвера Driver-Object, в котором представлены свойства данного устройства и даны указатели на реализованные в нём функции ввода-вывода. После этого, к драйверу обращаются через его именованный объект, например Device\HardDisk0.

На физ.уровне, объект представляет из-себя просто структуру в памяти, в которой описываются свойства данного объекта. Всякий объект имеет одинаковый для всех заголовок ‘Header’, и привязанное к типу объекта - тело ‘Body’. Заголовками управляет диспетчер-объектов, а телами – исполняющая подсистема ядра Executive (в файле Ntoskrnl.exe). Посмотрим на эти структуры в отладчике WinDbg, для чего достаточно дать команду на отображение структур
dt _object_header:
Код:
lkd> dt _object_header
nt!_OBJECT_HEADER
   +0x000 PointerCount       : Int4B
   +0x004 HandleCount        : Int4B
   +0x004 NextToFree         : Ptr32 Void
   +0x008 Type               : Ptr32 _OBJECT_TYPE
   +0x00c NameInfoOffset     : UChar
   +0x00d HandleInfoOffset   : UChar
   +0x00e QuotaInfoOffset    : UChar
   +0x00f Flags              : UChar
   +0x010 ObjectCreateInfo   : Ptr32 _OBJECT_CREATE_INFORMATION
   +0x010 QuotaBlockCharged  : Ptr32 Void
   +0x014 SecurityDescriptor : Ptr32 Void
   +0x018 Body               : _QUAD

0x00 – число процессов, использующих данный объект;
0x04 – счётчик дескрипторов на объект;
0x08 – информация о типе объекта;
0x0С – указатель на имя каталога, в котором живёт объект (в Unicode, в ядре нет ASCII-строк);
0х0Е – квота на объект (ограничение на использование ресурса процессами);
0x0F – флаги доступа к объекту;
0х14 – что позволено делать с объектом (чтение/запись/удаление);
0х18 – указатель на тело объекта.
Эта информация является общей для всех объектов, а характеристики конкретно взятого объекта, лежат в структуре Object_Type, указатель на которую хранится по смещению(08) в заголовке:
Код:
lkd> dt _object_type
nt!_OBJECT_TYPE
   +0x000 Mutex                    : _ERESOURCE
   +0x038 TypeList                 : _LIST_ENTRY
   +0x040 Name                     : _UNICODE_STRING
   +0x048 DefaultObject            : Ptr32 Void
   +0x04c Index                    : Uint4B
   +0x050 TotalNumberOfObjects     : Uint4B
   +0x054 TotalNumberOfHandles     : Uint4B
   +0x058 HighWaterNumberOfObjects : Uint4B
   +0x05c HighWaterNumberOfHandles : Uint4B
   +0x060 TypeInfo                 : _OBJECT_TYPE_INITIALIZER
   +0x0ac Key                      : Uint4B
   +0x0b0 ObjectLocks              : [4] _ERESOURCE

4.1.4.1 Handle (дескрипторы) процессов

С понятием объект тесно связан ‘дескриптор объекта’ – система возвращает его нам в ответ, например, открытия файла. В момент, когда мы открываем файл, ядро создаёт дубликат своего объекта типа "File", и оформив его в виде дескриптора, вручает нам. Процедура оформления состоит из сброса/установки определённых полей в заголовке-объекта, исходя из наших привилегий в системе. Дескриптор, который нам возвращают функции API, на самом деле никакой не дескриптор, а порядковый номер записи в таблице-дескрипторов объектов ObjectTable:

objTab.png


Следующий код открывает файл и выводит на экран его дескриптор, который возвращает API:

C-подобный:
include 'win32ax.inc'
.data
fName   db  'C:\boot.ini',0           ;// файл для открытия
capt    db  'Handle v0.1',0
text    db  'Дескриптор файла: %08X',0
buff    db  32 dup(0)
;-------
.code
start:  invoke  _lopen,fName,0        ; открыть файл
        or       eax,eax              ; проверка на ошибку
        jns      @ok                  ; если не отрицательно, иначе..
        invoke   MessageBox,0,<'Нет такого файла!'>,0,0
        jmp      @exit
@ok:    push     eax                  ;// запомнить дескриптор файла
       cinvoke   wsprintf,buff,text   ; преобразовать его в символы
        pop      eax                  ;
        invoke  _lclose,eax           ;// закрыть файл по дескриптору

        invoke  MessageBox,0,buff,capt,0
@exit:  invoke  ExitProcess,0
.end start
handle.png


Команда в WinDbg позволит ознакомится с дескрипторами объектов текущего процесса, где 0 7 это флаги фильтрации вывода. То-есть открываем любой экзешник по Ctrl+E и получаем выделенные для него дескрипторы, причём пока отлаживаемая программа не обратится к объекту, информации о нём не будет в этом списке, т.к. объекты создаются динамически:
C-подобный:
0:000> !handle 0 7
Handle 00c
  Type             File
  Attributes       0
  GrantedAccess    0x100020:   Synch,Execute/Traverse
  HandleCount      3
  PointerCount     4
Handle 7ec
  Type             Port
  Attributes       0
  GrantedAccess    0x1f0001:   Delete,ReadControl,WriteDac,WriteOwner,Synch,Connect
  HandleCount      2
  PointerCount     4
  Name             <none>
Handle 7f0
  Type             Directory
  Attributes       0x10
  GrantedAccess    0xf000f:    Delete,ReadControl,WriteDac,WriteOwner,Query,Traverse,Create,CreateSubdir
  HandleCount      28
  PointerCount     33
  Name             \Windows
Handle 7f8
  Type             Directory
  Attributes       0x10
  GrantedAccess    0x3:        Query,Traverse
  HandleCount      29
  PointerCount     68
  Name             \KnownDlls
Handle 7fc
  Type             KeyedEvent
  Attributes       0x10
  GrantedAccess    0xf0003:    Delete,ReadControl,WriteDac,WriteOwner,Wait,Wake
  HandleCount      29
  PointerCount     31
  Name             \KernelObjects\CritSecOutOfMemoryEvent
;-----------------
5 Handles
Type               Count
File               1
Port               1
Directory          2
KeyedEvent         1

4.1.4.2. PID процесса

PID – это идентификатор процесса. Его формирует планировщик ОСи, и он всегда кратен 4. Система записывает PID текущего процесса в его структуру EPROCESS по смещению 0х84 ‘UniqueProcessID’. Все Win32-API работающие с процессами, требуют в качестве одного из аргументов именно PID процесса, к которому обращаются. Поскольку для отладчика WinDbg все процессы это клиенты, порой он обзывает идентификатор Cid’ом, что означает ClientID. Узнать свой PID в отладчике можно так:

pid.png


Функция GetCurrentProcessId() возвращает идентификатор своего процесса, но в большинстве случаях нам нужен будет PID чужого. Узнав PID можно получить дескриптор (handle) любого процесса функцией OpenProcess(), через который можем вытворять с процессом, что угодно. Код ниже выводит свой PID, а чужой попытаемся получить позже, сканированием всех процессов в памяти. Обратите внимание, что PID процесса не статичен и назначается планировщиком от фонаря, при каждом запуске программы:

C-подобный:
include 'win32ax.inc'
.data
capt    db  'PID v0.1',0
text    db  'Идентификатор текущего процесса: %08X',0
buff    db  64 dup(0)
;-------
.code
start:  invoke   GetCurrentProcessId        ;// запросить у системы свой ид.
       cinvoke   wsprintf,buff,text,eax     ; преобразовать его в символы

        invoke  MessageBox,0,buff,capt,0
        invoke  ExitProcess,0
.end start
Больше функций по работе с процессами и их потоками можно

4.1.4.3. Связные списки LIST_ENTRY

Разработчики операционных систем садятся на шпагат при выборе между статическими и динамическими структурами данных. Статические легче прогать, динамические обладают большей гибкостью. Очевидный пример – таблица процессов. В Win98 для каждого процесса просто выделялся фиксированный массив структур. Если таблица-процессов состояла из 64 элементов, то в любой момент могло существовать в памяти не более 64 процесса, т.к. для 65-го просто не было места в таблице. Аналогичные соображения были справедливы и для таблицы открытых файлов. Такой лимит назвали квотой.

Альтернативная стратегия заключается в создании таблицы-процессов в виде связного списка LIST_ENTRY, начиная с единственной мини-таблицы. Когда эта таблица заполняется, в глобальном пуле выделяется память под следующую таблицу, которая связывается с первой таблицей при помощи указателей. Таким образом, таблица процесса не переполнится, пока не закончится вся память у ядра.

Другой вариант – использовании таблицы фиксированного размера, но с выделением (когда эта таблица заполнится) новой таблицы фиксированного размера, в два раза большей. При этом текущие записи копируются из старой таблицы в новую, а память (которая была занята старой таблицей) возвращается в пул. Таким образом, таблица всегда остаётся непрерывной, а не связной. Недостаток этого подхода – требуется менеджер таблиц, + адрес таблицы будет переменным вместо константы.

Современные винды всех мастей используют только динамические структуры в виде связных списков LIST_ENTRY, поэтому в большинстве структурах присутствует указанное поле – например в той-же EPROCESS:


blink.png


Тело любого/связного списка LIST_ENTRY хранит два указателя – Flink и Blink.
Первое поле от слова ‘Forward’ указывает на следующую структуру EPROCESS в цепочке процессов в памяти, а второе поле от ‘Backward’ – это указатель на предыдущую структуру в цепочке. Таким образом, если найти хоть одну/любую структуру EPROCESS в ядерной памяти, можно потянув за LIST_ENTRY вытянуть структуры всех процессов, получив тем-самым полный список запущенных процессов. Рисунок ниже наглядно демонстрирует устройство данного механизма. В последнем разделе этой части мы рассмотрим это дело подробней.

flink_0.png
 

Вложения

  • WinObjEx.zip
    122,6 КБ · Просмотры: 397
Мы в соцсетях:

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