Статьи Исследрвание и лечени вируса Patriot (aka Win32.Friendly)

Discussion in 'Статьи' started by Ra$cal, 8 Mar 2007.

  1. Ra$cal

    Ra$cal Elder - Старейшина

    Joined:
    16 Aug 2006
    Messages:
    670
    Likes Received:
    185
    Reputations:
    78
    Поводом для написания этой статьи стала необходимость лечения вируса Win32.Friendly, принесённого из института, т.к. после его разгула приходилось ручками перед запуском программ просматривать, не заражён ли файлик. Так фиксились почти все программы. К сожалению именно почти. После запуска нефиксенного все труды слетали. Но главный неприятный момент-автор решил себя не утруждать проверкой наличия оверлея, благодаря чему все сетапки, попавшие под это произведение кривых ручек, благополучно портятся без возможности восстановления. Вот поэтому решил писать программку для автоматизации процесса. Делаю это публично, потому как это может кого-нибудь заинтересовать.

    Для понимания всего нижеописанного понадобятся:
    1) Знание ассемблера
    2) Знание C++
    3) Знание API
    4) Знание структуры PE файла (хотя бы общее)
    5) Отладчик (хватит OllyDebug)
    6) MicroSoft Visual Studio 200x

    Содержание :)
    1) Исследование заражённых файлов, работы вируса и Написание программы для лечения заражённых файлов
    2) Выводы, заключения, радость за наш мозг


    Исследование заражённых файлов, работы вируса и Написание программы для лечения заражённых файлов

    Чтобы написать программу, которая будет исправлять заражённые файлы надо для начала (надеюсь вы уже догадались):
    1) Искать эти файлы среди других ( а вы думали ручками будем задавать каждый отдельно? неа, учитесь на моих ошибках ) => надо искать признаки, выдающие заражённые файлы
    2) Проверить, не забирает ли вирус код программы. Если да ( как у нас ), будем искать способы его обнаружения. Также надо понять способ переход управления из вируса в программу.

    Пока в принципе хватит.

    Для начала посмотрим на точку входа программы, заражённой вирусом:

    Code:
    00407796 >/$ RCL ECX,1B
    00407799 |. SUB AL,6D
    0040779B |. OR EDX,ACC599F1
    004077A1 |. ADC EDI,EAX
    004077A3 |. CMP AL,9D
    004077A5 |. JNB SHORT config.004077B6
    004077A7 |. XADD ESI,EAX
    004077AA |. ADC EAX,EBX
    004077AC |. SUB EAX,ECX
    004077AE |. AND EDI,1F
    004077B1 |. INC EBP
    004077B2 |. RCR EDI,11
    004077B5 |. DEC ECX
    004077B6 |> DEC EBX
    004077B7 |. XADD EDI,ECX
    004077BA |. MOV ESI,0CADFA44
    004077BF |. MOV ECX,F3F0B22F
    004077C4 |. MOV EBX,EBP
    004077C6 |. XCHG EAX,ECX
    004077C8 |. XOR EBX,EAX
    004077CA |. SUB EAX,EBX
    004077CC |. ADC ECX,EBP
    004077CE |. PUSH config.00417000
    004077D3 |. CMP ESP,844660E
    004077D9 |. JG SHORT config.004077DF
    004077DB |. SUB EDI,0E
    004077DE |. DEC EAX
    004077DF |> ADD EAX,E60D1631
    004077E4 \. RETN
    Другой пример:

    Code:
    00484001 > XCHG AH,BL
    00484003 MOV EDI,ECX
    00484005 SAR EBX,18
    00484008 SBB EAX,94D657FD
    0048400D JS SHORT fearr.00484014
    0048400F NOP
    00484010 MOVZX EAX,SI
    00484013 NOP
    00484014 ADD EAX,93F74BD8
    00484019 RCL EBP,19
    0048401C SUB EDI,872D8B18
    00484022 AND AH,1C
    00484025 SBB ECX,9
    00484028 MOVZX EDI,DL
    0048402B XCHG AL,DL
    0048402D PUSH fearr.00488000
    00484032 XADD ECX,EDI
    00484035 SHR AH,0C
    00484038 AND EDI,5FCB2508
    0048403E JNS SHORT fearr.00484044
    00484040 XOR EAX,ECX
    00484042 ROL EAX,1
    00484044 OR CL,AL
    00484046 RETN
    Ну и последний:

    Code:
    00AA4330 > ADD EDX,EAX
    00AA4332 MOVZX EBP,CX
    00AA4335 INC ECX
    00AA4336 PUSH FEAR.00D98000
    00AA433B ADC EDX,ESI
    00AA433D OR EDI,ECX
    00AA433F OR EBX,ECX
    00AA4341 AND EDI,EDX
    00AA4343 SUB EDX,DC6F85E2
    00AA4349 ADD EBP,0C
    00AA434C OR EDI,13
    00AA434F ADD EDI,10
    00AA4352 DEC EDI
    00AA4353 JS SHORT FEAR.00AA4360
    00AA4355 AND EDX,ESI
    00AA4357 MOV ECX,4309562F
    00AA435C DEC EBX
    00AA435D ROL ESI,1D
    00AA4360 ROR ESI,0B
    00AA4363 SBB ESI,1B
    00AA4366 DEC ECX
    00AA4367 DEC EBX
    00AA4368 AND EAX,A406A645
    00AA436D ADD EDX,3
    00AA4370 ADD AL,5
    00AA4372 OR ECX,6
    00AA4375 ADC EBP,18
    00AA4378 SBB EAX,63FAA4C9
    00AA437D SBB ESI,ECX
    00AA437F RETN
    Выводы:
    1) Код программы забирается, т.к. не заражённые программы начинаются с тех же адресов, но код там другой
    2) Замещающий код непредсказуем ( пока )
    3) Длина замещающего кода непостоянна

    С первым всё понятно, просто будем искать в работе вируса исправление украденных байтов.
    Второе и третье не предсказуемы. Отсюда получается, что предсказать вроде как не получается. Но пытливые умы прекрасно видят что:
    1) Команд, работающих со стеком 2, всё остальное или с регистром, или прыжки
    2) Переход на код вируса осуществляется конструкцией.

    tratata
    ...
    push xxxxxxxx
    ...
    tratata
    ...
    retn

    где tratata в начале и в конце может и не быть, начинаясь сразу с push xxxxxxxx

    Это нам даёт возможный вариант определения наличия вируса.
    На стоячей программе можем найти лишь ещё один интересный момент - код передаётся на последнюю секцию. Далее программу придётся изучать пошагово...
    В месте, куда ведёт переход, очень много команд, которые нам для разбора принципа работы вируса параллельны. Ищите ПРИМЕРНО такую строчку:
    Code:
    004172E9 XOR DWORD PTR DS:[EBX],7BCDF907
    Примерно потому, как вирус использует метаморфизм, т.е. код разный, а выполняет функцию одинаковую. В данном случае метаморфизм связан с изменяемым регистром ( он может быть и EBX, ECX, EAX, EDX ) и с параметром XOR ( в данном случае число у нас 7BCDF907, но при следующих заражениях оно будет меняться ), также будет меняться адрес этой строки относительно начала этого кода, то-есть в нашем случае 004172E9 - 00417000 = 2E9, но в другом файле будет не так. Но что собственно делает этот код? Он просто расшифровывает код самого вируса, вредоносный код. Вот конец цикла:

    Code:
    0041753A CMP EBX,config.0041902C
    00417540 JNZ config.0041723B
    Делаем точку останова за прыгом, топаем по шагам и приходим сюда:

    Code:
    004176A8 PUSHFD
    004176A9 PUSHFD
    004176AA PUSHAD
    004176AB CALL config.004176B0
    Смотрим теперь сюда:

    Code:
    004176BD CALL config.004177C1
    А там:

    Code:
    004177C8 CMP DWORD PTR DS:[EAX],-1
    004177CB JE SHORT config.004177D1
    004177CD MOV EAX,DWORD PTR DS:[EAX]
    004177CF ^JMP SHORT config.004177C8
    004177D1 MOV EAX,DWORD PTR DS:[EAX+4]
    004177D4 XOR AX,AX
    004177D7 CMP WORD PTR DS:[EAX],5A4D
    004177DC JE SHORT config.004177E5
    004177DE SUB EAX,10000 ; UNICODE "=::=::\"
    004177E3 ^JMP SHORT config.004177D7
    004177E5 MOV ECX,DWORD PTR DS:[EAX+3C]
    Здесь везде уже описанным способом находится адрес библиотеки kernel32.dll через SEH без использования апи. Принцип - полседний SEH указывет на -1 (цепочка кончилась), а обработчик в нём стандартный системный из kernel32.dll. Дальше, т.к. модули грузятся с выравниваниями, вычитая по 10000 от адреса обработчика исключений находится адрес библиотеки (5A4D начало любого приложения или библиотеки). Смотрим далее:

    Code:
    004177E5 MOV ECX,DWORD PTR DS:[EAX+3C]
    004177E8 ADD ECX,EAX
    004177EA MOV ECX,DWORD PTR DS:[ECX+78]
    004177ED ADD ECX,EAX
    004177EF PUSH EDI
    004177F0 PUSH ECX
    004177F1 PUSH EAX
    004177F2 MOV EDX,DWORD PTR DS:[ECX+20]
    004177F5 ADD EDX,EAX
    004177F7 XOR EBX,EBX
    004177F9 XOR EAX,EAX
    004177FB XOR ECX,ECX
    004177FD DEC ECX
    004177FE MOV EDI,ESI
    00417800 REPNE SCAS BYTE PTR ES:[EDI]
    00417802 NOT ECX
    00417804 POP EAX
    00417805 DEC ECX
    00417806 JE SHORT config.0041783B
    00417808 INC ECX
    00417809 PUSH ESI
    0041780A PUSH ECX
    0041780B MOV EDI,DWORD PTR DS:[EDX+EBX*4] Переходит к
    0041780E ADD EDI,EAX                      следующей
    00417810 REPE CMPS BYTE PTR ES:[EDI],BYTE PTR DS:>    Проверяет имя искомой функции и в библиотеке
    00417812 JE SHORT config.00417819
    00417814 INC EBX
    00417815 POP ECX
    00417816 POP ESI
    00417817 ^JMP SHORT config.00417809
    Здесь вирус получает таблицу экспорта kernel32.dll и ищет функции по их имени без использования GetProcAddress. Здесь всё понятно. Нам интересны функции CreateThread и FindFirstFile

    Code:
    004176C8 PUSH EAX
    004176C9 PUSH ESP
    004176CA PUSH 40
    004176CC PUSH 50
    004176CE PUSH DWORD PTR SS:[EBP+1914]
    004176D4 CALL NEAR DWORD PTR SS:[EBP+151C] ; kernel32.VirtualProtect
    004176DA POP EAX
    004176DB MOV EDI,DWORD PTR SS:[EBP+1914]
    004176E1 LEA ESI,DWORD PTR SS:[EBP+1918]
    004176E7 MOV ECX,50
    004176EC REP MOVS BYTE PTR ES:[EDI],BYTE PTR DS:[ESI]

    Здесь вирус разрешает запись в секцию кода (VirtualProtect), т.к. строками ниже он возвращает настоящее начало программы ( 50 байтов ). Запоминаем это место. Пока что информации нам маловато. Ждём любое запоминающееся событие...

    Code:
    004177B0 50 PUSH EAX
    004177B1 54 PUSH ESP
    004177B2 50 PUSH EAX
    004177B3 52 PUSH EDX
    004177B4 53 PUSH EBX
    004177B5 50 PUSH EAX
    004177B6 50 PUSH EAX
    004177B7 FF95 18150000 CALL NEAR DWORD PTR SS:[EBP+1518] ; kernel32.CreateThread
    Этот CreateThread запускает механизм размножения вируса. Давайте немного поблуждаем там...

    Code:
    00417708 PUSHAD
    00417709 MOV EBP,DWORD PTR SS:[ESP+24] ; config.004176B0
    0041770D CALL config.00417D10 //
    Здесь находятся все остальные необходимые API
    Далее ищутся жесткие локальные диски и создаются потоки тем же CreateThread для их инфицирования.
    Эти функции составляют маску (например C:\*.exe) и начинают искать:

    Code:
    00417B6D PUSH EAX
    00417B6E PUSH DWORD PTR SS:[EBP+8]
    00417B71 CALL <config.strcat>
    00417B76 LEA EAX,DWORD PTR DS:[EBX+1794]
    00417B7C PUSH EAX
    00417B7D 08 PUSH DWORD PTR SS:[EBP+8]
    00417B80 CALL <config.strcat>
    00417B85 PUSH DWORD PTR SS:[EBP+C]
    00417B88 08 PUSH DWORD PTR SS:[EBP+8]
    00417B8B CALL NEAR DWORD PTR DS:[EBX+1538] ; kernel32.FindFirstFileA
    Когда файл находится опять составив поный путь к нему происходит следующая интересная проверка:

    Code:
    00417DE7 PUSH EBP
    00417DE8 MOV EBP,ESP
    00417DEA MOV EAX,DWORD PTR SS:[EBP+C]
    00417DED MOV EAX,DWORD PTR DS:[EAX+2C] // Первые 4 буквы имени файла
    00417DF0 AND EAX,DFDFDFDF// типа strupr, все 4 буквы большие
    00417DF5 CMP EAX,45575244 // число представляет из себя буквы DRWE
    00417DFA JE SHORT config.00417E4D
    00417DFC CMP EAX,44495053 //SPID
    00417E01 JE SHORT config.00417E4D
    00417E03 CMP EAX,54534E49 //INST
    00417E08 JE SHORT config.00417E4D
    00417E0A CMP EAX,55544553 //SETU
    00417E0F JE SHORT config.00417E4D
    00417E11 AND EAX,0FFFFFF
    00417E16 CMP EAX,56414B //KAV
    00417E1B JE SHORT config.00417E4D
    00417E1D PUSH DWORD PTR SS:[EBP+8]
    00417E20 CALL config.00417E51
    00417E25 TEST EAX,EAX
    00417E27 JNZ SHORT config.00417E4D
    00417E29 PUSH DWORD PTR SS:[EBP+C]
    00417E2C PUSH DWORD PTR SS:[EBP+8]
    00417E2F CALL config.00417E8B
    00417E34 TEST EAX,EAX
    00417E36 JNZ SHORT config.00417E4D
    00417E38 MOV EDX,DWORD PTR SS:[EBP+8]
    00417E3B CMP BYTE PTR DS:[EDX],41
    00417E3E JE SHORT config.00417E4D
    00417E40 PUSH EAX
    00417E41 PUSH 4E20
    00417E46 CALL NEAR DWORD PTR DS:[EBX+1520]
    00417E4C POP EAX
    00417E4D LEAVE
    00417E4E RETN 8
    Обратите внимание, вирус проверяет, чтобы имя файла не начиналось одним из выше описанных, и имена некоторые весьма интересны:
    SPID(ER) - это модуль DoctorWeb'a
    DRWE(B) - это сам DoctorWeb
    KAV - касперский ав.
    Так вот что интересно - нет упоминаний ни о NOD ни о PANDA, что наталкивает на мысль, что автор из местных земель :)), но это только предположение!!!
    Ладно, теперь пробуем поставить бряку на CreateFile. Проследив немного приходим к такому месту:

    Code:
    00417EE9 MOV DWORD PTR SS:[EBP-C],EAX
    00417EEC CMP WORD PTR DS:[EAX],5A4D
    00417EF1 JNZ config.004180C3
    00417EF7 CMP WORD PTR DS:[EAX+18],40
    00417EFC JNZ config.004180C3
    00417F02 MOV EAX,DWORD PTR DS:[EAX+3C]
    00417F05 MOV DWORD PTR SS:[EBP-1C],EAX
    00417F08 ADD EAX,DWORD PTR SS:[EBP-C]
    00417F0B CMP DWORD PTR DS:[EAX],4550
    00417F11 JNZ config.004180C3
    00417F17 CMP BYTE PTR DS:[EAX-1],2A
    00417F1B JE config.004180BA
    Это типичная проверка PE-файла, но есть здесь одно подозрительное место:
    00417F17 CMP BYTE PTR DS:[EAX-1],2A

    Если истина, то файл закрывается и поиск продолжается => это есть признак заражённости для самого вируса. Его я не использую, итак наличие вируса определяем точно.
    Всё, теперь отладчик можно закрывать.
    Отлично, теперь подведём итог. Что поняли для обнаружения вируса:
    1) В начале программы должны быть 2 команды: push xxxxxxxx и retn, никаких call, pop, push eax и др регистры.
    2) xxxxxxxx должен быть быть в пределах последней секции.

    Что поняли для восстановления настоящего начала программы:
    1) При вызове CreateThread код уже восстановлен.

    Теперь основной вопрос: восстановитель файлов делать динамический (то есть испольнять, но под контролем ) или эмулирующий.


    Динамический восстановитель:
    -при просчёте контроля восстанавливаемая программа выходит в свободное неупрявляемое состояние и вирус делает своё дело.
    +Реализация очень проста и вполне надёжна, если за надёжностью следить!

    Эмулирующий восстановитель:
    -придётся писать эмулятор процессора :)) Пусть далеко не для всех функций, но и того, что надо, хватит надолго.
    +из под контроля вирусу не выйти, т.к. на самом деле не он исполняется, а мы исполняем его

    Не знаю как Вы, а я предпочту динамику. Кто выбрал эмуляцию дальше могут не читать, литературка им понадобится гораздо серьёзнее и углублённее.

    Прежде всего остановимся на определении вируса. Закономерностей математических (байт AA на расстоянии x от BB, а ret на расстоянии Y от начала) нету, поэтому, IMHO самый простой вариант - взять бесплатный дизассемблер (в нашем случае CADT) и на основе него сделать простенький анализатор мнемоник. Запускать программу для этого не понадобится.

    В принципе инфицированные файлы идентифицируются элементарно. Теперь как фиксить. Мы помним, что при вызове CreateThread байты в начале программы уже исправлены. Надо этим воспользоваться, т.е. остановить программу на CreateThread и заменить байты. Как остановить в этот момент, спросите Вы? Тут на помощь нам приходят Debug Api, набор функций для отладки приложений. Они позволяют отслеживать работу программы, менять данные, читать и писать из/в процесс, приостанавливать и т.д.. Таким образом мы напишем простенький отладчик.


    Остальось дело техники: реализовать задуманное. Результат можно взять здесь

    ЗЫ:были проблемы с аспром. ыв принципе нормальные проги фиксит нормально. но сырая лечилка, была написана за день.

    http://ifolder.ru/1312913
    pass: virus
    ВНИМАНИЕ: config.exe пример вируса. не запускайте
     
    #1 Ra$cal, 8 Mar 2007
    Last edited: 8 Mar 2007
    9 people like this.
  2. ShadOS

    ShadOS ы

    Joined:
    11 Feb 2007
    Messages:
    667
    Likes Received:
    351
    Reputations:
    413
    Хотелось бы заметить, что иногда очень удобно для экономии времени и сил перед тем как заняться реверсингом, прогнать заразу через песочницу (sandbox). Примеры: sandboie, CWSandbox, norman.
     
    #2 ShadOS, 9 Mar 2007
    Last edited: 9 Mar 2007
    2 people like this.