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

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

    Скидки до 10%

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

Статья Скрытый потенциал регистров отладки DR0-DR7

Слово отладка, у большинства из нас ассоциируется с инструментальными средствами реверса типа WinDbg, x64Dbg, OllyDbg и прочие. Но в данной статье хотелось-бы рассмотреть это направление с другой проекции, а именно – использование отладочных возможностей CPU вне отладчика, непосредственно из пользовательского приложения. Исследования в этом ключе раскрывают огромный потенциал и что немаловажно, все действия остаются прозрачными для самих отладчиков, т.к. мы отнимаем у них ресурсы по типу "кто первым встал, того и тапки".
----------------------------------------


Формат регистров DR0-DR7 процессора

Процессоры х86 имеют восемь регистров отладки DR0-DR7, два из которых DR4.5 отправлены в резерв и не используются. В совокупности, отслеживая обращения к памяти они позволяют устанавливать аппаратные точки-останова на её чтение, запись, или исполнение. Более того, если в регистре-управления процессором CR4 сброшен бит[3] (Debug Extensions), то под надзор силовых структур попадают и обращения к портам ввода-вывода I/O. Взяв за основу фишку с отслеживанием физических портов, можно написать к примеру эмулятор какого-нибудь внешнего девайса, типа флопика или LPT/COM устройства.

К сожалению, под адреса Breakpoint'ов (далее HBP, Hardware-Breakpoint), инженеры выделили всего 4-регистра с номерами DR0-DR3. Соответственно мы можем установить столько-же аппаратных бряков за один раз. Если нужно больше.., придётся переназначать их динамически, после того-как отработает первая партия. На рисунке ниже, для лучшего восприятия принадлежность битов к каждой из четырёх точек выделены своим цветом. Наиболее информативным является регистр-конфигурации DR7, в котором программист должен определить характер Breakpoint 'ов – рассмотрим его формат подробней..

• В 2-битном поле LEN мы указываем размер ячейки памяти, при обращении к которой сработает бряк. Если HBP ставится на исполнение инструкции по определённому адресу, это поле игнорируется и обязательно должно быть сброшено в нуль (что равносильно размеру в 1-байт). Во-всех остальных случаях – как говорится "размер имеет значение".

• Следующее 2-битное поле RW определяет условие, на которое должен отреагировать проц исключением #DB (код = 0x80000004). Инженеры представили нам на выбор 4 варианта: исполнение адреса, запись в него, обращение к портам I/O, и чтение адреса. В этой статье я буду ставить бряки только на исполнение инструкций в секции-кода, поэтому старшая половина регистра DR7 с битами Len и RW всегда будет сброшена в нуль (на рисунке они выделены красным). Четыре пары младших бит[7:0] активируют соответствующую точку – для надёжности Intel рекомендует взводить сразу оба бита.

• Флаги 13,9,8 регистра DR7 относятся больше к системе, чем непосредственно к точкам-останова. В пользовательском режиме, системный протекторат позволяет нам оперировать только битом[8] Local-Break, а остальные – принудительно сбрасываются в нуль. При хороших обстоятельствах (в режиме ядра), установленный бит[13] генерил-бы исключение #DB всякий раз, когда процессору попадались инструкции обращения к регистрам DR0-DR7. Но поскольку любой отладчик типа OllyDbg наделён способностью ставить аппаратные бряки, система маскирует бит[13], чтобы не отнимать у отладчиков эту возможность.

DR0-DR7.png


Четыре регистра DR0-DR3 хранят линейные адреса 4-х точек останова, а регистр DR6 является информационным. Как только сработает любая из точек, в DR6 взведётся привязанный к ней битовый флаг. Здесь нужно отметить, что процессор не очищает флаги [3:0] регистра DR6, а только устанавливает их. Поэтому после обработки брэйкпоинта, их очистка для следующего раза полностью лежит на нашей совести. Биты [15,14,13] регистра DR6 уточняют условие срабатывания НВР и в данном случае нам не интересны.


Проблема доступа к регистрам DR0-DR7

Процессор имеет огромное кол-во регистров, большая часть из которых не доступна программисту с прикладного уровня – в их числе и регистры отладки DRx. Когда мы в реальном режиме или-же пишем драйвер под Win, то имеем полную власть над регистрам без каких-либо ограничений. Но возня с драйверами выходит за рамки данной статьи, так-что мы попытаемся утянуть эти регистры в защищённом режиме, с правами обычного юзера.

По сути, система ограничивает наш аппетит на уровне сегментного регистра CS (Code-Segment). Регистры CS/DS/ES и т.п. имеют видимую нам 2-байтную часть "селектор", и скрытую от посторонних глаз 8-байтную часть "дескриптор" – итого 10 байт. Дескриптор это отдельная тема – нам интересен сейчас только 16-битный селектор, в двух младших битах которого имеется поле RPL – Request Privilege Level, или уровень привилегии запроса.

Когда процессору встречается запрещённая для юзера инструкция (типа обращение к регистрам DRx), он в первую очередь проверяет, с какого именно кольца-защиты поступает запрос – нулевого (где тусуются драйвера и ядро системы), или третьего кольца (обитель наших программ). Для этого, процессору достаточно чекнуть поле RPL текущего регистра CS. У всех программ пользовательского уровня RPL=3 (взведены оба бита в селекторе), а у подопечных системы RPL=0. Это наглядно демонстрирует рисунок ниже:

RPL_00.png


Посмотрите на значения сегментных регистров в окне отладчика OllyDbg..
Как видим, селектор CS=1Bh, а это в двоичном 00011.0.11. Значит уровень привилегий =3, дескриптор сегмента находится в глобальной таблице GDT, и имеет в ней индекс [3]. Аналогичная картина и с селекторами данных DS/ES =23h = 00100.0.11, что подразумевает RPL=3, GDT, индекс записи =4.

Таким образом, из пользовательского приложения мы не имеем возможности оперировать регистрами отладки DR0-DR7, поскольку отправляя в свободный полёт, система вручила нашему коду самую низкую привилегию [3], отфутболив его обитать на последнее из колец Сатурна. Без драйвера, напрямую пробуриться из юзера в ядро нереально, а значит нужно искать обходные пути, и как оказалось такие пути есть.

Логику продуманов из Microsoft понять трудно – закрывая на 10-замков одну дверь, они сами-же отворяют на распашку другую. Причём обе лазейки ведут в один коридор, в конце которого виден тусклый свет ядра. В любом случае, именно такие забытые люки добавляют красок в скучную жизнь прикладного программиста. Судя по-всему, заранее раскурив наш план, индусы любезно предоставили нам полный доступ к контексту регистров, ..причем не только к контексту своего приложения, но и абсолютно любого, к которому мы сможем подобраться функцией OpenProcess().


Структура "CONTEXT"

В многозадачной системе, процессор берёт на грудь некоторое кол-во потоков Thread, и гоняет их исполнение по-кругу в виде циферблата часов. Каждому из этих потоков выделяется определённый квант времени (как-правило 31 ms), после чего управление передаётся сл.потоку в очереди. При таких обстоятельствах, чтобы прерванный поток смог продолжить свою работу с того-же места, система предварительно должна где-то сохранить значения всех его регистров – этот процесс известен как "Переключение контекста". В результате, у каждого из работающих в системе потоков оказывается свой собственный набор регистров CPU.

Контекст отражает состояние процессора на момент последнего исполнения потока, и записывается это состояние в одноимённую структуру "CONTEXT". Что примечательно, в этой структуре нашлось место и для регистров отладки DR0-DR7, и это способствует их изменению прямо во-время работы нашего приложения. Вот как отображает эту структуру ядерный отладчик WinDbg:

Cntx.png


В составе Win32-API имеются функции для чтения и модификации этой структуры – Get/SetThreadContext() соответственно. При использовании этих функции мы должны явно указать системе, блок каких именно регистров хотим прочитать/записать. Для этого, в структуре "CONTEXT" имеется самый первый её член "ContextFlags" (на рис.выше он выделен зелёным), который может принимать следующие значения:


C-подобный:
CONTEXT_CONTROL          = 0x10001
CONTEXT_INTEGER          = 0x10002
CONTEXT_SEGMENTS         = 0x10004
CONTEXT_FLOATING_POINT   = 0x10008
CONTEXT_DEBUG_REGISTERS  = 0x10010   ;// <---- наш клиент
CONTEXT_FULL             = 0x1001f

;//----------------------------
BOOL SetThreadContext (   // <---- Обновить контекст регистров!
   HANDLE hThread,        // дескриптор потока (-2 для своего основного потока)
   LPCONTEXT lpContext    // адрес подготовленной структуры "CONTEXT"
);

По-умолчанию, в нёдрах компилятора FASM нет описаний структур для работы с контекстом, поэтому я приведу их здесь, а вам остаётся лишь добавить эти строки в инклуд "fasm\include\equates\kernel32.inc" (можно в самый конец файла).

C-подобный:
;// инклуд для fasm'a,
;// для пользовательской SEH-обработки внутрипоточных исключений
;// ------------------------------------------------------------
EXCEPTION_MAXIMUM_PARAMETERS = 15
SIZE_OF_80387_REGISTERS      = 80
MAXIMUM_SUPPORTED_EXTENSION  = 512

CONTEXT_CONTROL          = 0x10001
CONTEXT_INTEGER          = 0x10002
CONTEXT_SEGMENTS         = 0x10004
CONTEXT_FLOATING_POINT   = 0x10008
CONTEXT_DEBUG_REGISTERS  = 0x10010
CONTEXT_FULL             = 0x1001f

struct FLOATING_SAVE_AREA
  ControlWord          dd  0
  StatusWord           dd  0
  TagWord              dd  0
  ErrorOffset          dd  0
  ErrorSelector        dd  0
  DataOffset           dd  0
  DataSelector         dd  0
  RegisterArea         rb SIZE_OF_80387_REGISTERS
  Cr0NpxState          dd  0
ends

struct CONTEXT
  ContextFlags         dd  0
  iDr0                 dd  0
  iDr1                 dd  0
  iDr2                 dd  0
  iDr3                 dd  0
  iDr6                 dd  0
  iDr7                 dd  0
  FloatSave            FLOATING_SAVE_AREA
  regGs                dd  0
  regFs                dd  0
  regEs                dd  0
  regDs                dd  0
  regEdi               dd  0
  regEsi               dd  0
  regEbx               dd  0
  regEdx               dd  0
  regEcx               dd  0
  regEax               dd  0
  regEbp               dd  0
  regEip               dd  0
  regCs                dd  0
  regFlag              dd  0
  regEsp               dd  0
  regSs                dd  0
  ExtendedRegisters    rb  MAXIMUM_SUPPORTED_EXTENSION
ends

struct EXCEPTION_RECORD
  ExceptionCode        dd  0
  ExceptionFlags       dd  0
  pExceptionRecord     dd  0
  ExceptionAddress     dd  0
  NumberParameters     dd  0
  ExceptionInformation rd  EXCEPTION_MAXIMUM_PARAMETERS
ends

struct EXCEPTION_POINTERS
   pExceptionRecord    dd  0
   pExceptionFrame     dd  0
   pExceptionContext   dd  0
   pParam              dd  0
ends

Ниже представлен фрагмент кода, который позволит установить аппаратный брейк на исполнение функции "Example". Здесь я просто заполняю нужные поля в структуре "CONTEXT", после чего вскармливаю её основному потоку через SetThreadContext():

C-подобный:
.data
ctx     CONTEXT         ;// импорт структуры CONTEXT из инклуда kernel32.inc
;//---------
.code
start:
        mov     ax,0000000000000000b     ;// АХ = биты Len/RW четырёх точек HBP для регистра DR7
        shl     eax,16                   ;// сделать их ст.словом в EAX (сдвинуть на 16 влево)
        mov     ax,1100000011b           ;// в мл.слове EAX – биты включения HBP (здесь только нулевая точка)

        mov     [ctx.iDr7],eax           ;// записать условие в регистр DR7 контекста
        mov     [ctx.iDr0],@Example      ;// DR0 = адрес нулевой точки-останова
        mov     [ctx.ContextFlags],CONTEXT_DEBUG_REGISTERS  ;// флаг = только регистры отладки

        invoke  SetThreadContext,-2,ctx  ;// Обновить контекст регистров нашего/основного потока!!!

В документации мелкомягких можно встретить термин "псевдо/дескриптор". Это константы, которые мы можем использовать при вызовах API, вместо запроса их посредством увесистых функций. Так, если нам нужен дескриптор Handle своего процесса, мы можем использовать его превдо/дескриптор с константой -1. Аналогично, фиктивный дескриптор основного-потока приложения равен -2 или 0хFFFFFFFE (см.пример выше). Не пытайтесь использовать константу -2 для дополнительных потоков – их дескрипторы будут уже другими, и возвращает их функция создания потока CreateThread().


SEH – обработчик исключения #DB

Будем считать, что регистры DRx настроены и мы загрузили их в своё приложение. Теперь нужно запеленговать отладочное исключение #DB (INT-01h), которое сгенерит процессор при выполнении заданного нами в DR7 условия (т.е. нарвётся на брейкпоинт). В системах класса Win, ошибки и исключения попадают в цепкие лапы SEH – Structured Exception Handling – структурный обработчик исключений. Что это такое и с чем его едят обсуждалось в отдельной статье, поэтому повторяться не буду, однако в логику обработки эксепшена #DB окунуться стоит.

Если в двух словах, то при возникновении программных исключений, система передаёт управление по строго определённому адресу и этот адрес хранится в стековом SEH-фрейме. Если мы хотим перехватить исключение, то должны подкорректировать в этом фрейме указатель так, чтобы он указывал на наш заранее подготовленный обработчик.

Фишка в том, что в момент исключения система не просто передаёт управление в SEH-фрейм, но и предоставляет код-ошибки с указателем на контекст-регистров глючного потока (т.е. структуру Context). Более благоприятных обстоятельств для нашего случая и придумать сложно. В своём обработчике, нам остаётся лишь проверить код-ошибки на 0х80000004 (отладочное исключение #DB) пропуская ниже своих радаров все остальные. В общем случае, обработчик может быть оформлен примерно так:


C-подобный:
.code
start:
;// Добавляем свой SEH-фрейм перед системным
        xor     ebx,ebx           ;//
        push    HBPhandler        ;// адрес нашего обработчика
        push    dword[fs:ebx]     ;// адрес следующего в цепочке
        mov     [fs:ebx],esp      ;// зарегистрировать свой SEH-фрейм!

;//////////////////////////////////////////////////////
;// Теперь все ошибки будет отлавливать наш обработчик
;//////////////////////////////////////////////////////

proc    HBPhandler  pRecord,pFrame,pContext,pParam   ;// система передаёт нам 4-аргумента

        mov     eax,[pRecord]      ;// указатель на код-ошибки
        mov     eax,[eax]          ;// возмём его в EAX
        cmp     eax,0x80000004     ;// проверить код на точку-останова #DB
        je      @found             ;// если это наш клиент..
        mov     eax,1              ;// иначе: выходим из своего обработчика,
        ret                        ;//  ..передавая управление системе.

;// Значит сработа какая-то из наших точек HBP
;//--------------
@found:
        mov     esi,[pContext]           ;// ESI = указатель на контекст регистров
        mov     eax,[esi+CONTEXT.iDr6]   ;// EAX = значение регистра-статуса DR6
        bt      eax,0                    ;// проверяем, какая именно точка сработала..
        jc      @00                      ;// если это нулевая..
        bt      eax,1                    ;// или первая
        jc      @01                      ;//                     
;//...
;//==== Здесь обрабатываем точки-останова на своё усмотрение ==========
@00:    nop
        nop
@01:    nop
        nop
;//=====================================================================
        mov     esi,[pContext]               ;// ESI = указатель на контекст
        mov    [esi+CONTEXT.iDr6],0          ;// очистить флаги в DR6 для сл.раза!!!
        mov    [esi+CONTEXT.regEip],@metka   ;// установить EIP на нужный адрес в коде
        xor     eax,eax                      ;// команда "ребут" диспетчеру исключений
        ret                                  ;// продолжить исполнение кода..
endp


Практическая часть

В демонстрационном примере, я зашифрую две процедуры разными ключами и поставлю на начало каждой из них свой аппаратный бряк DR0 и DR1. В результате, как-только процессор дойдёт до первой процедуры, сработает первое исключение и его обработчик расшифрует свою процедуру. При этом вторая будет дожидаться очереди и по-прежнему лежать в памяти в зашифрованном виде. То-есть код программы будет раскрывать свои нёдра не сразу, а по мере продвижения регистра EIP. Это на порядок усложняет взлом.

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

CopyShell.png


Обратите внимание на значение регистров-отладки DR0-DR7 рисунка выше.. На этот момент, я уже сконфигурировал их точками-останова функцией SetThreadContext() и под живым процессором они будут определены правильно. Однако отладчик упорно не хочет воспринимать этот факт как действительность, и показывает в своём окне девственное их состояние. В результате, он сам-же себе стреляет в ногу и уже не сможет раскусить наш план, поскольку мы оба с ним используем одни и те-же программные ресурсы.


C-подобный:
format   pe console
include 'win32ax.inc'
entry    start
;//---------
.data
ctx     CONTEXT       ;//<---- импорт структуры CONTEXT из инклуда
title   db     '*** HBP example ver.0.1 ***',0
ok      db     ' Pass OK!',10,10,0
wrong   db     ' <<-- Wrong pass -->>',0
pass    db     10,' Type pass: ',0
size    dd     32
buff    db     0
;//---------
section '.code' data readable writable   ;// секцию-кода нужно открыть на запись
start:
        call    Encrypt                  ;// Шифрование двух процедур в тестовом варианте
        invoke  SetConsoleTitle,title    ;// Обзовём консоль..

;// Вставляем свой SEH-фрейм в цепочку
        xor     ebx,ebx
        push    HBPhandler
        push    dword[fs:ebx]
        mov     [fs:ebx],esp

;//********************************************************
;// Конфигурируем аппаратные брейкпоинты DR0 и DR1
;//********************************************************
        mov     ax,0000000000000000b     ;// поля Len/RW четырёх HBP для DR7
        shl     eax,16                   ;// отправить их в ст.часть EAX
        mov     ax,1100001111b           ;// включить две точки: 0 и 1
        mov     [ctx.iDr7],eax           ;// записать в контекст DR7
        mov     [ctx.iDr0],@CheckPass    ;// DR0 = адрес нулевой точки
        mov     [ctx.iDr1],@BonusProc    ;// DR1 = адрес первой
        mov     [ctx.ContextFlags],CONTEXT_DEBUG_REGISTERS  ;// перезаписывать только DRx
        invoke  SetThreadContext,-2,ctx  ;// Обновить контекст регистров!!!

;// Запрашиваем пароль у юзвера..
       cinvoke  printf,pass
       cinvoke  gets,buff

;//********************************************************
;// Здесь сработает нулевая точка-останова!!!
;// Процедура считает 16-битную сумму введённого пароля,
;// и проверяет её на валидность.
;// Должна лежать в памяти в зашифрованном виде,
;// т.к. обработчик исключения #DB будет расшифровывать её.
;//********************************************************
        nop
        nop
@CheckPass:
        xor     ebx,ebx
        mov     eax,ebx
        mov     esi,buff
@@:     lodsb
        add     bx,ax         ;//<--- сумма
        or      al,al
        jne     @b
        nop
        cmp     bx,0x03CB   
        je      @f
       cinvoke  printf,wrong
        jmp     @exit
@@:    cinvoke  printf,ok
@CheckPassEnd:
        nop
        nop
;//********************************************************
;// Здесь сработает следующая точка-останова DR1 !!!
;// Так-же должна лежать в зашифрованном виде.
;// Если пароль правильный, то сбоксит в окно имя компа.
;//********************************************************
@BonusProc:
        invoke  GetComputerName,buff,size
        mov     esi,buff
        add     esi,[size]
        mov     word[esi],'  '
        mov     edi,esi
        add     edi,2
        mov     esi,buff
        mov     ebx,34
@@:     mov     ecx,[size]
        add     ecx,2
        rep     movsb
        dec     ebx
        jnz     @b
        invoke  MessageBox,0,buff,<'Hardware-Break',0>,0
@BonusProcEnd:
        nop
        nop

;//==== Конец программы ===================================
@exit: cinvoke  gets,buff     ;// ждём клаву..
       cinvoke  exit,0        ;// на выход!

;/////////////////////////////////////////////////////////////////
;//***************************************************************
;// Секция с пользовательским SEH-обработчиком.
;// Ключи, длина и адрес начала блоков для декрипта,
;// лежат в виде локальных данных.
;// Кроме того, в самом хвосте секции имеется функция шифрования,
;// которую можно использовать в отладочной версии программы.
;//***************************************************************
section '.help' data readable writable

proc    HBPhandler pRecord,pFrame,pContext,pParam
locals
    @HBp0   dd  0x7373,(@CheckPassEnd - @CheckPass)/2, @CheckPass   ;// данные нулевой,
    @HBp1   dd  0x315f,(@BonusProcEnd - @BonusProc)/2, @BonusProc   ;//   ..и первой точки-останова.
endl

        mov     eax,[pRecord]      ;//
        mov     eax,[eax]          ;//
        cmp     eax,0x80000004     ;// проверить код-ошибки на исключение #DB
        je      @found             ;// если наш клиент..
        mov     eax,1              ;// иначе: выходим из своего обработчика,
        ret                        ;//  ..передавая управление диспетчеру-исключений.

@found:
        mov     esi,[pContext]           ;// ESI = указатель на контекст
        mov     eax,[esi+CONTEXT.iDr6]   ;// EAX = значение регистра-статуса DR6
        bt      eax,1                    ;// проверить на точку #1
        jc      @01                      ;// если да..
                                   ;//<------------------------- значит точка #0
        mov     ebx,[@HBp0 + 0]    ;// ключ расшифровки
        mov     ecx,[@HBp0 + 4]    ;// длина блока в словах
        mov     esi,[@HBp0 + 8]    ;// адрес начала
@@:     xor     word[esi],bx       ;//
        add     esi,2              ;//
        loop    @b                 ;// расшифровать весь блок!
        mov     esi,[pContext]           ;//
        mov     eax,@CheckPass           ;//
        mov    [esi+CONTEXT.regEip],eax  ;// выставить EIP на начало расшифрованного блока
        mov     eax,[esi+CONTEXT.iDr7]   ;//
        and     eax, not 0011b           ;// выключить нулевую точку в регистре DR7
        mov    [esi+CONTEXT.iDr7],eax    ;// обновить его
        jmp     @reboot                  ;// уходим на ребут контекста!

;//==== Аналогично оформляем и обработчик первой точки-останова
@01:    mov     ebx,[@HBp1 + 0]    ;//
        mov     ecx,[@HBp1 + 4]    ;//
        mov     esi,[@HBp1 + 8]    ;//
@@:     xor     word[esi],bx       ;//
        add     esi,2              ;//
        loop    @b                 ;//
        mov     esi,[pContext]            ;//
        mov     eax,@BonusProc            ;//
        mov    [esi+CONTEXT.regEip],eax   ;//
        mov     eax,[esi+CONTEXT.iDr7]    ;//
        and     eax, not 1100b            ;//
        mov     [esi+CONTEXT.iDr7],eax    ;//
@reboot:
        and    [esi+CONTEXT.iDr6],not 1111b   ;// очистить флаги в регистре-статуса DR6 !!!
        xor     eax,eax                       ;// команда "Ребут-контекста" системному диспетчеру.
        ret
endp

;//***************************************************************
;// Функция шифрования процедур, для отладочной версии программы.
;// Обязательно поместить в самый конец кода,
;// чтобы она не повлияла на смещение адресов, после её удаления.
;//***************************************************************
Encrypt:
        mov     ecx,(@CheckPassEnd - @CheckPass)/2
        mov     bx,0x7373
        mov     esi,@CheckPass
@@:     xor     word[esi],bx
        add     esi,2
        loop    @b

        mov     ecx,(@BonusProcEnd - @BonusProc)/2
        mov     bx,0x315f
        mov     esi,@BonusProc
@@:     xor     word[esi],bx
        add     esi,2
        loop    @b
        ret
;//**********************************
section '.idata' import data readable
library  msvcrt,'msvcrt.dll',kernel32,'kernel32.dll',user32,'user32.dll'
import   msvcrt, printf,'printf',scanf,'scanf',gets,'gets',exit,'exit'
include 'api\kernel32.inc'
include 'api\user32.inc'

Чтобы не хранить пароли в открытом виде, имеет смысл сравнивать не сам введённый пароль, а его контрольную сумму. Вычислить сумму оригинального пароля можно и в обычном калькуляторе, складывая все ASCII-коды символов вручную. Но для автоматизации этого процесса можно воспользоваться и услугами 16-тиричного редактора "HxD". Вводим текст, и меню "Анализ" выбрав соответствующий пункт получаем сумму требуемой разрядности:

HxD.png


В скрепке я прикрепил готовый к исполнению файл, по типу Crackme. Можно загрузить его в отладчик и проверить свой скилл во-взломе. В нём всё лежит на поверхности и при желании можно подобрать к нему пароль меньше чем за минуту. Это чисто для того, чтобы продемонстрировать недееспособность отладчиков к подобного рода извращениям. Ну и для тренировки мозгов не помешает..


Заключение

Снискавшие себе заслуженную славу аппаратные Breakpoint'ы доминируют перед программными тем, что позволяют более гибко определять условия. Ведь что такое софт-точка? Это просто внедрённый перед какой-либо инструкцией 1-байтный код CCh. Дойдя до этого байта, процессор сгенерит исключение #BP, которое дёрнет INT-3. Достоинства программных точек лишь в том, что в отличии от четырёх/аппаратных, их может быть бесчисленное количество.

Другое дело регистры DR0-DR7.. Как было сказано выше, в регистре-конфигурации DR7 мы можем определять четыре условия их срабатывания – чтение, запись, исполнение памяти, а так-же контроль за обращением к портам ввода-вывода I/O. Пруф выше доказывает, что можно использовать аппаратные точки с любыми правами пользователя, причём работает эта фишка на всех системах, начиная от Win-2000 и вплоть до Win-10. Не сомневаюсь, что забросив эту технику в сундук, мы обязательно найдём ей применение в будущем.
 

Вложения

  • Crackme_DRx.zip
    950 байт · Просмотры: 738
Последнее редактирование:

JoY_MnK

One Level
23.07.2019
8
4
BIT
0
Очень понравилось!
Побольше бы аналогичного плана изложения, академично.
 
  • Нравится
Реакции: rusrst
Мы в соцсетях:

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