10 мая 2012 г.

Немного про антиотладку.

Решил поколупать всякие простенькие приемчики антиотладки, конечно с плагинами которые вешаются на дебаггеры типа OllyDBG и IDA они не шибко помогут, но остановят начинающего крякера, да и самому интересно стало поковыряться.

Итак, первый прием самый банальный - использование стандартной виндовой функции IsDebuggerPresent, она возвращает 1 в случае если программа находиться под отладчиком или 0 если нет, использовать ее можно так:
invoke    IsDebuggerPresent ; вызываем процедурку
    .IF    eax == 0 ; сравниваем если eax вернулся 0
        invoke    isDPnext ; прыгаем на след точку
    .ENDIF

Эта процедурка очень простая и содержит в себе всего 3 команды:
mov     eax, DWORD PTR FS:[18h]
mov     eax, DWORD PTR [eax+30h]
movzx   eax, byte ptr [eax+2]

А значит можно их вызвать и в ручную, например так, не используя прямого вызова функции IsDebuggerPresent:
assume fs:nothing
mov     eax, DWORD PTR FS:[18h]
mov     eax, DWORD PTR [eax+30h]
movzx   eax, byte ptr [eax+2]
.IF    eax == 0
        invoke    chkNTHeapFlag
.ENDIF

Что еще можно сделать, ну поглядеть на флаг NtGlobalFlag, если он не ноль, то скорей всего программа работает в отладчике. Добыть этот флаг можно вот так:
assume fs:nothing
mov     eax, DWORD PTR FS:[18h]
mov     eax, DWORD PTR [eax+30h]
mov    bl, BYTE PTR[eax + 68h]
mov    ntFlag, bl

Все теже строчки, что и в предыдущем примере, только смотрим по смещению 68h, дальше опять сравниваем и выходим если там не 0. Ну и под конец этих вариантов, можно посмотреть как при помощи исключений делается перехват отладчика. Для начала необходимо объявить функцию - SetUnhandledExceptionFilter. Смысл в ней следующий, если этой функции передать некий адрес в программе, то после возникновения исключения, например деление на 0, программа будет продолжать работать начиная с того адреса который получила эта функция. Ну и пример конечно:
invoke SetUnhandledExceptionFilter, offset nfdebug
mov        ebx, 0
div        ebx

В случае возникновения исключения, а тут оно является делением на 0, программа начнет работу с адреса nfdebug.

А теперь большой пример нескольких комбинированных попыток защититься от отладчика:

.386
.model flat, stdcall

option casemap: none

      include \masm32\include\windows.inc
      include \masm32\macros\macros.asm

      uselib kernel32, user32, masm32, comctl32


nfdebug            PROTO ;прототип функции nfdebug
isDPnext        PROTO ;прототип функции isDPnext
crtxcept        PROTO ;прототип функции crtxcept
chkNTHeapFlag    PROTO ; прототип функции chkNTHeapFlag    
decodeString    PROTO: DWORD, :BYTE ; прототип функции decodeString
encodeString    PROTO: DWORD, :BYTE ; прототип функции encodeString

      
.data
    kern32        db    0Eh ; кодируем слово kernel32.dll
                db    17h
                db    1Ch
                db    0Bh
                db    09h
                db    5Fh
                db    01h
                db    1Ch
                db    4Ah
                db    08h
                db    00h
                db    6Ch
                db    00h
    excpName    db    "SetUnhandledExceptionFilter",0 ; название функции SetUnhandledExceptionFilter
    header        db    0Dh ; кодируем тайтл сообщения
                db    00h
                db    6Ch
                db    6Fh
                db    04h
                db    4bh
                db    00h
    
    textMSG        db    21h ; кодируем сообсно сообщение, чтобы было сложнее его поймать
                db    07h
                db    17h
                db    12h
                db    00h
                db    02h
                db    17h
                db    52h
                db    49h
                db    1Ah
                db    1Dh
                db    0Eh
                db    14h
                db    54h
                db    46h
                db    09h
                db    1Ah
                db    1Bh
                db    0Ah
                db    45h
                db    21h
                db    00h

.data?
    kernel32p    dd    ? ; указатель на kernel32
    ntFlag        db    ? ; NtGlobalFlag
    setExcp        dd    ? ; указатель на SetUnhandledExceptionFilter



.code
start:
    
    invoke    decodeString, ADDR kern32, 0Ch ; декодируем строку
    invoke    LoadLibrary, ADDR kern32 ;загружаем kernel32.dll
    invoke    encodeString, ADDR kern32, 0Ch ; обратно закодируем строку
    mov        kernel32p, eax ; сохраним указатель на kernel32.dll
    invoke    GetProcAddress, kernel32p, ADDR excpName ; получаем указатель на функцию SetUnhandledExceptionFilter
    mov        setExcp, eax ; сохраняем указатель
    
    push    offset nfdebug  ; передаем адрес, с которого продолжим выполение в случае исключения
    call    setExcp ; вызываем функцию SetUnhandledExceptionFilter
    invoke    IsDebuggerPresent ; вызываем IsDebuggerPresent 
    .IF    eax == 0 ; проверяем на 0
        invoke    isDPnext ; прыгаем на следующую проверку
    .ENDIF
    

exit:
    invoke    ExitProcess, 0 ; выход из программы

isDPnext proc ; развернутая функция IsDebuggerPresent 
    assume fs:nothing
    mov     eax, DWORD PTR FS:[18h] ; бродим по структуре
    mov     eax, DWORD PTR [eax+30h] ; бродим по структуре
    mov        bl, BYTE PTR[eax + 68h] ; добываем флаг NtGlobalFlag
    mov        ntFlag, bl ; запишем флаг NtGlobalFlag
    movzx   eax, byte ptr [eax+2] 
    .IF    eax == 0 ; проверяем на 0
        invoke    chkNTHeapFlag прыгаем на следующую проверку
    .ENDIF
    
    Ret
isDPnext EndP

crtxcept proc ; функция вызывающая исключение
    mov        ebx, 0 ; поместим в ebx 0
    div        ebx ; поделим на 0 и вызываем исключение
    Ret
crtxcept EndP


chkNTHeapFlag proc  ; процедура проверки флага NtGlobalFlag
    .IF ntFlag == 0
        invoke    crtxcept
    .ENDIF
    
    Ret
chkNTHeapFlag EndP


nfdebug proc ; выводим что все ок, и нет дебагера
    invoke    decodeString, ADDR textMSG, 15h ; декодируем сообщение
    invoke    decodeString, ADDR header, 6 ; декодируем тайтл
    invoke    MessageBox, NULL, ADDR textMSG, ADDR header,MB_OK ; выводим сообщение
    Ret
nfdebug EndP

decodeString proc uses eax ecx ebx address:DWORD, count:BYTE ; процедура которая кодирует строку
    mov        eax, address ; копируем адрес строки
    xor        ecx, ecx ; обнуляем ecx
    mov        cl, count ; в eax загружаем количество строк
    add        eax, ecx ; получаем конец строки
encode:
    mov        bl,    BYTE PTR [eax] ; кидаем в bl текущий символ
    xor        BYTE PTR [eax-1], bl ;xorим ее с предыдущим
    dec        eax ; уменьшаем eax
    loop    encode ; переходим к следующему символу
    Ret
decodeString EndP

encodeString proc uses eax ecx ebx address:DWORD, count:BYTE ; процедура которая декодирует строку
    mov        eax, address ; копируем адрес строки
    xor        ecx, ecx ; обнуляем ecx
    mov        cl, count ; в eax загружаем количество строк
nextcode:
    mov        bl,    BYTE PTR [eax+1] ; кидаем в bl следующий символ
    xor        BYTE PTR [eax], bl  ;xorим ее с текущим
    inc        eax ; увеличиваем eax
    loop    nextcode ; переходим к следующему символу
    Ret
encodeString EndP

end start
Можно закодировать в принципе все строки и вызовы чтобы ничего найти нельзя было в строках, плюс еще использовать непрямое обнаружение kernel32.dll, да еще попробовать поискать дебаггер при помощи FindWindow, Proccess32First и Process32Next но мне уже стало лень этим заниматься. Может быть как нибудь в следующий раз. А на сегодня пока все...

Комментариев нет:

Отправить комментарий