Blog. Just Blog

Использование SEH для антиотладки

Версия для печати Добавить в Избранное Отправить на E-Mail | Категория: Образ мышления: Assembler | Автор: ManHunter
Использование SEH для антиотладки
Использование SEH для антиотладки

В сегодняшней статье разберем использование SEH в качестве антиотладочного приема. Трюк старый, как бивень мамонта, опытным реверсерам обнаружить и обойти его не составит абсолютно никакого труда, а новичкам может быть интересно. Например, в исполняемых файлах, упакованных PECompact, управление с точки входа на процедуру распаковки передается именно таким способом.

Принцип использования этого трюка в точности повторяет штатное использование SEH для обработки ошибок и исключений. То есть установка SEH на определенный адрес обработчика и искусственное создание условий для его исполнения. Вот упомянутый обработчик из PECompact:

Передача управления в PECompact
Передача управления в PECompact

Устанавливается адрес, на который будет выполнен переход, затем генерируется исключение типа ACCESS VIOLATION при попытке записи в нулевую ячейку памяти.

Немного более продвинутый способ антиотладки с использованием SEH - внедрение в линейный код большого количества команд, которые будут генерировать исключения, и "мусорного" кода, который никогда не будет выполняться. При просмотре дизассемблерного листинга такие участки могут ввести исследователя в заблуждение. Вот простейший пример для демонстрации этой техники:
  1.         ; Добавить наш обработчик в цепочку
  2.         push    seh_antidebug
  3.         push    dword [fs:0]
  4.         mov     [fs:0],esp
  5.  
  6.         ; Первое срабатывание - SINGLE STEP
  7.         pushfd
  8.         ; Установить флаг отладки
  9.         or      dword [esp],0x100
  10.         popfd
  11.         or      eax,eax
  12. skip_data:
  13.         invoke  ExitProcess,0
  14. len_skip = $-skip_data
  15.  
  16.         ; Второе срабатывание - BREAKPOINT
  17.         int3
  18.         invoke  ExitProcess,0
  19.  
  20.         ; Третье срабатывание - ACCESS VIOLATION
  21.         xor     eax,eax
  22.         mov     [eax],ecx
  23.         invoke  ExitProcess,0
  24.  
  25.         ; Четвертое срабатывание - ILLEGAL INSTRUCTION
  26.         ud2
  27.         invoke  ExitProcess,0
  28.  
  29.         ; Убрать наш обработчик
  30.         pop     dword[fs:0]
  31.         add     esp, 4
Основная прелесть обработки исключений в том, что мы можем изменить адрес выполнения следующей команды. Наверняка вы обратили внимание, что сразу после каждого блока кода, который генерирует исключение, находится вызов функции ExitProcess. Мы будем менять адрес исполнения EIP таким образом, чтобы перепрыгнуть эту функцию. Но поскольку мы используем разные способы для генерации исключения, в обработчике надо предусмотреть проверку, какой именно способ был использован.
  1. ;-------------------------------------------------------------------------
  2. ; Обработчик критических ошибок
  3. ;-------------------------------------------------------------------------
  4. proc seh_antidebug pExcept:DWORD, pFrame:DWORD, pContext:DWORD, pDispatch:DWORD
  5.         mov     edi,[pContext]
  6.  
  7.         ; Получить адрес, по которому произошло срабатывание
  8.         mov     eax,[edi+0xB8]
  9.  
  10.         ; Пропустить ud2
  11.         cmp     word[eax],0B0Fh
  12.         jne     @f
  13.         add     dword [edi+0xB8],2
  14. @@:
  15.         ; Пропустить mov [eax],ecx
  16.         cmp     word[eax],0889h
  17.         jne     @f
  18.         add     dword [edi+0xB8],2
  19. @@:
  20.         ; Пропустить int3
  21.         cmp     byte[eax],0CCh
  22.         jne     @f
  23.         inc     dword [edi+0xB8]
  24. @@:
  25.         ; Увеличить regEip на длину вызова ExitProcess
  26.         add     dword [edi+0xB8],len_skip
  27.         xor     eax,eax
  28.         ret
  29. endp
Для всех методов кроме SINGLE STEP придется увеличивать адрес исполнения на длину команды, которая вызвала исключение. После этого адрес исполнения надо увеличить на длину вызова ExitProcess, для удобства я оформил его в качестве константы len_skip. Таким образом, получив управление, обработчик в итоге передаст управление на команду, следующую после вызова ExitProcess.

А вот теперь переходим к самому интересному - наномитам. Эта технология применялась в навесном протекторе Armadillo. Смысл наномитов заключается в том, что протектор анализирует бинарный код и заменяет некоторые условные переходы на вызов точки останова int 3. Все адреса замененных переходов, их условия и адреса передачи управления в случае выполнения условий сохраняются в секции протектора. При получении управления замененной командой срабатывает исключение, протектор проверяет адрес, откуда пришло исключение, по нему получает условие и адреса переходов и обрабатывает это все в своем коде.

Давайте попробуем сделать нечто подобное, но на основе своего кода. Например, реализуем обработку двух типов переходов - условного JE и безусловного JMP. Для этого в коде будет конструкция, включающая в себя команду для генерации исключения, тип перехода, а также адрес, куда нужно передать управление в случае выполнения условия.
  1. NANO_JE  = 1
  2. NANO_JMP = 2
  3. NANO_FIX_LEN = 3
Вот простейшая программа, сперва выводящая окно сообщения с кнопками "Yes", "No" и "Cancel", а потом, в зависимости от выбранной пользователем кнопки, выводящая название этой кнопки. Проверки на основе условных переходов и безусловные переходы по веткам алгоритма реализованы с помощью наномитов и виртуальной машины. Управление виртуальной машине передается через обработчик SEH.
  1.         ; Добавить наш обработчик в цепочку
  2.         push    seh_antidebug
  3.         push    dword [fs:0]
  4.         mov     [fs:0],esp
  5.  
  6.         invoke  MessageBox,HWND_DESKTOP,szMess,szTitle,MB_YESNOCANCEL
  7.  
  8.         cmp     eax,IDYES
  9.         ; JE loc_yes
  10.         db      0x0F,0x0B,NANO_JE
  11.         dw      loc_yes-$+NANO_FIX_LEN
  12.  
  13.         cmp     eax,IDNO
  14.         ; JE loc_no
  15.         db      0x0F,0x0B,NANO_JE
  16.         dw      loc_no-$+NANO_FIX_LEN
  17.  
  18.         invoke  MessageBox,HWND_DESKTOP,szCancel,szTitle,MB_OK
  19.         ; JMP loc_exit
  20.         db      0x0F,0x0B,NANO_JMP
  21.         dw      loc_exit-$+NANO_FIX_LEN
  22.  
  23. loc_no:
  24.         invoke  MessageBox,HWND_DESKTOP,szNo,szTitle,MB_OK
  25.  
  26. loc_exit:
  27.         ; Убрать наш обработчик
  28.         pop     dword[fs:0]
  29.         add     esp, 4
  30.  
  31.         invoke  ExitProcess,0
  32.  
  33. loc_yes:
  34.         invoke  MessageBox,HWND_DESKTOP,szYes,szTitle,MB_OK
  35.         ; JMP loc_exit
  36.         db      0x0F,0x0B,NANO_JMP
  37.         dw      loc_exit-$+NANO_FIX_LEN
Теперь обработчик. Первым делом он проверяет, что исключение было сгенерировано наномитом. Затем следующей проверкой определяется тип перехода - условный или безусловный. Если переход безусловный, то вычисляется адрес перехода и в контексте процесса заменяется адрес исполнения EIP. В случае условного перехода проверяется значение регистра флагов и по результатам проверки адрес исполнения меняется или на следующую команду, или на адрес условного перехода.
  1. ;-----------------------------------------------
  2. ; Обработчик критических ошибок
  3. ;-----------------------------------------------
  4. proc seh_antidebug pExcept:DWORD, pFrame:DWORD, pContext:DWORD, pDispatch:DWORD
  5.         mov     edi,[pContext]
  6.  
  7.         ; Получить адрес, по которому произошло срабатывание
  8.         mov     eax,[edi+0xB8]
  9.  
  10.         ; Это наш наномит?
  11.         cmp     word [eax],0B0Fh
  12.         jne     .loc_ret
  13.  
  14.         ; Обработка команды JE
  15.         cmp     byte [eax+2],NANO_JE
  16.         je      .loc_je
  17.         ; Обработка команды JMP
  18.         cmp     byte [eax+2],NANO_JMP
  19.         je      .loc_jmp
  20.  
  21.         ; Неизвестная команда
  22.         jmp     .loc_ret
  23.  
  24.         ;----------------------------------------
  25.         ; Эмуляция JMP
  26.         ;----------------------------------------
  27. .loc_jmp:
  28.         ; Получить длину перехода
  29.         movsx   eax,word [eax+3]
  30.         ; Увеличить/уменьшить regEip на длину перехода
  31.         add     dword [edi+0xB8],eax
  32.         jmp     .loc_ret
  33.  
  34.         ;----------------------------------------
  35.         ; Эмуляция JE
  36.         ;----------------------------------------
  37. .loc_je:
  38.         ; Проверить ZF (бит 6) в regFlag
  39.         test    dword [edi+0xC0],40h
  40.         jnz     @f
  41.  
  42.         ; Условие для JE не выполнено
  43.         add     dword [edi+0xB8],5
  44.         jmp     .loc_ret
  45. @@:
  46.         ; Получить длину условного перехода
  47.         movsx   eax,word [eax+3]
  48.         ; Увеличить/уменьшить regEip на длину условного перехода
  49.         add     dword [edi+0xB8],eax
  50.  
  51. .loc_ret:
  52.         xor     eax,eax
  53.         ret
  54. endp
Если посмотреть в дизассемблере код программы, то в основной части алгоритма не обнаружится ни одного перехода, более того, большая часть кода превратится в фарш. И это при условии, что тут кода всего с комариный х(..востик). При необходимости в реальном проекте степень виртуализации можно накручивать сколь угодно сложно.

Это лишь краткий пример, подобную обработку можно использовать для динамической модификации кода, расшифровки кода перед выполнением и шифрованием после выполнения, да вообще, при наличии фантазии и нужного радиуса кривизны рук возможности открываются поистине безграничные.

В приложении примеры программ с исходными текстами, которые используют описанные в статье антиотладочные техники.

Примеры программ с исходными текстами (FASM)Примеры программ с исходными текстами (FASM)

SEH.Antidebug.Demo.zip (3,490 bytes)


Поделиться ссылкой ВКонтакте
Просмотров: 1560 | Комментариев: 0

Внимание! Статья опубликована больше года назад, информация могла устареть!

Комментарии

Отзывы посетителей сайта о статье
Комментариeв нет

Добавить комментарий

Заполните форму для добавления комментария
Имя*:
Текст комментария (не более 2000 символов)*:

*Все поля обязательны для заполнения.
Комментарии, содержащие рекламу, ненормативную лексику, оскорбления и т.п., а также флуд и сообщения не по теме, будут удаляться. Нарушителям может быть заблокирован доступ к сайту.
Наверх
Powered by PCL's Speckled Band Engine 0.2 RC3
© ManHunter / PCL, 2008-2024
При использовании материалов ссылка на сайт обязательна
Время генерации: 0.07 сек. / MySQL: 2 (0.0047 сек.) / Память: 4.5 Mb
Наверх