Information Security
Reverse engineering
6 May 2015

Trojan-Downloader.Win32.Cabby.cemx — Часть первая — Распаковка

From Sandbox
Привет, Хабр!

Скажу сразу: я не вирусный аналитик и не занимаюсь этой деятельностью профессионально. Работаю сетевым инженером в одной из компаний в группе компаний из трех букв. Так что прошу строго не судить и отнестись с пониманием.

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

Сразу предупрежу, будет много картинок и листингов дизассемблированного кода.

Готов к конструктивной критике и буду рад вашим советам по оптимизации моих мыслей.

В недалеком прошлом в нашу компанию проник нашумевший CTB-Locker, что заинтересовало нашу службу информационной безопасности: почему не сработали антивирусы и фильтрация веб-трафика? Что конкретно делает данный зловред, хорошо описано на securelist. С этим все ясно и не имеет смысла проводить повторный анализ того, что и так неплохо описано.

С целью понимания причин инцидента рассмотрим именно вектор распространения данного зловреда.

Как это выглядит с точки зрения пользователя:
  1. Пришло письмо с вложенным архивом .cab;
  2. Пользователь открывает архив и запускает из него .scr;
  3. Открывается .rtf файл с непонятными и неинтересными данными;
  4. Пользователь спокойно закрывает данный файл и продолжает работу;
  5. Через некоторое время на рабочем столе выскакивает окно CTB-Locker'а с печальной для пользователя информацией.

Декрипт


Итак, достаем из .cab архива злой и кусающийся .scr. Открываем его в Immunity Debugger и, нажав пару раз на F9, попадаем на Entry Point 0x4021C6, не встретив на пути каких-либо приемов антиотладки — уже хорошо.



Полистав немного листинг и окинув взглядом импорт не нашел ничего внятного и «полезного». Ничего связанного с сетевой деятельностью — наводит на мысль, что что-то спрятано глубже. Пакер?

Что нам на это поведает PEiD:



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

Потрейсив немного код, натыкаемся на первую манипуляцию с памятью. Переменные, передаваемые в HeapCreate, не особо хитро завуалированы:



Размер выделяемой памяти 0x6D0. flOptions = 0x40000, что говорит о том, что область памяти будет исполняемой. Ок, наблюдаем за выделенным участком памяти, установив на него Memory BP на запись, и обнаруживаем цикл расшифровки и копирования данных из секции .data в выделенную память (кстати, находится почти сразу за HeapCreate):
00401F4A. 85DB TEST EBX,EBX
00401F4C. 0F84 67010000 JE g_r_pice.004020B9
00401F52. 01C0 ADD EAX,EAX
00401F54. 811D C8404000 >SBB DWORD PTR DS:[4040C8],0CE
00401F5E. 82C4 40 ADD AH,40
00401F61. 83C8 4A OR EAX,4A
00401F64. 31F6 XOR ESI,ESI

00401F66. 0B37 OR ESI,DWORD PTR DS:[EDI] < — Загрузка данных для расшифровки из тела вируса
00401F68. 18E4 SBB AH,AH
00401F6A. 8315 28414000 >ADC DWORD PTR DS:[404128],33
00401F71. 8115 E6404000 >ADC DWORD PTR DS:[4040E6],0A3
00401F7B. 8315 95404000 >ADC DWORD PTR DS:[404095],25
00401F82. F8 CLC
00401F83. 83D7 04 ADC EDI,4
00401F86. 34 F0 XOR AL,0F0
00401F88. 1305 E9404000 ADC EAX,DWORD PTR DS:[4040E9]
00401F8E. 1905 48414000 SBB DWORD PTR DS:[404148],EAX
00401F94. 8335 A2414000 >XOR DWORD PTR DS:[4041A2],2B
00401F9B. 3105 41414000 XOR DWORD PTR DS:[404141],EAX
00401FA1. F8 CLC
00401FA2. 83DE 07 SBB ESI,7
00401FA5. 8325 E0404000 >AND DWORD PTR DS:[4040E0],6A
00401FAC. 82CC E9 OR AH,FFFFFFE9
00401FAF. 2105 68404000 AND DWORD PTR DS:[404068],EAX
00401FB5. 83C8 EE OR EAX,FFFFFFEE
00401FB8. 83F0 FF XOR EAX,FFFFFFFF
00401FBB. F7DE NEG ESI
00401FBD. 810D 07414000 >OR DWORD PTR DS:[404107],0C2
00401FC7. C705 0D404000 >MOV DWORD PTR DS:[40400D],0B8
00401FD1. 3105 C0404000 XOR DWORD PTR DS:[4040C0],EAX
00401FD7. 83C0 05 ADD EAX,5
00401FDA. 29D6 SUB ESI,EDX
00401FDC. 11C0 ADC EAX,EAX
00401FDE. 1305 2D414000 ADC EAX,DWORD PTR DS:[40412D]
00401FE4. 83E8 2B SUB EAX,2B
00401FE7. 1305 29404000 ADC EAX,DWORD PTR DS:[404029]
00401FED. 83D8 3E SBB EAX,3E
00401FF0. 822D BA404000 >SUB BYTE PTR DS:[4040BA],-79
00401FF7. F8 CLC
00401FF8. 83D6 01 ADC ESI,1
00401FFB. 83F0 B7 XOR EAX,FFFFFFB7
00401FFE. 3105 4E414000 XOR DWORD PTR DS:[40414E],EAX
00402004. 810D ED404000 >OR DWORD PTR DS:[4040ED],0A5
0040200E. 31C0 XOR EAX,EAX
00402010. 31C0 XOR EAX,EAX
00402012. 29D2 SUB EDX,EDX
00402014. 29F2 SUB EDX,ESI
00402016. F7DA NEG EDX
00402018. 31C0 XOR EAX,EAX
0040201A. 8335 DE414000 >XOR DWORD PTR DS:[4041DE],64
00402021. 3105 66404000 XOR DWORD PTR DS:[404066],EAX
00402027. 31C0 XOR EAX,EAX
00402029. 83D0 CC ADC EAX,-34
0040202C. 2B05 33414000 SUB EAX,DWORD PTR DS:[404133]
00402032. C1C2 03 ROL EDX,3
00402035. 1305 4D404000 ADC EAX,DWORD PTR DS:[40404D]
0040203B. 11C0 ADC EAX,EAX
0040203D. 83C8 B4 OR EAX,FFFFFFB4
00402040. 19C0 SBB EAX,EAX
00402042. C1C2 05 ROL EDX,5
00402045. 0105 4B404000 ADD DWORD PTR DS:[40404B],EAX
0040204B. 2B05 3C404000 SUB EAX,DWORD PTR DS:[40403C]
00402051. 1305 B0404000 ADC EAX,DWORD PTR DS:[4040B0]
00402057. C705 C4404000 >MOV DWORD PTR DS:[4040C4],0B9
00402061. 56 PUSH ESI

00402062. 8F01 POP DWORD PTR DS:[ECX] < — Сохранение в выделенный участок памяти
00402064. 83E0 0E AND EAX,0E
00402067. 1905 F8414000 SBB DWORD PTR DS:[4041F8],EAX
0040206D. 2905 45404000 SUB DWORD PTR DS:[404045],EAX
00402073. A3 8F404000 MOV DWORD PTR DS:[40408F],EAX
00402078. A3 AF404000 MOV DWORD PTR DS:[4040AF],EAX
0040207D. 83C1 04 ADD ECX,4
00402080. 2905 18414000 SUB DWORD PTR DS:[404118],EAX
00402086. 19C0 SBB EAX,EAX
00402088. 83D0 D9 ADC EAX,-27
0040208B. 0905 3C404000 OR DWORD PTR DS:[40403C],EAX
00402091. 19C0 SBB EAX,EAX
00402093. 8D5B FC LEA EBX,DWORD PTR DS:[EBX-4]
00402096. 83D8 12 SBB EAX,12
00402099. 2105 15414000 AND DWORD PTR DS:[404115],EAX
0040209F. C705 AE414000 >MOV DWORD PTR DS:[4041AE],33
004020A9. C705 18404000 >MOV DWORD PTR DS:[404018],0DA
004020B3. 68 4A1F4000 PUSH g_r_pice.00401F4A
004020B8. C3 RETN



Говорим отладчику дизассемблировать целевую область памяти и получаем ожидаемый результат — вполне себе исполняемый код:



Переход к расшифрованному коду не заставил себя ждать:



В самом начале встречаем интересный метод передачи параметров процедурам. Рассмотрим на примере первой такой функции. Как видно происходит вызов функции LoadLibrary, в которую необходимо передать строковую переменную имени библиотеки для загрузки.
02120000 8B7424 04 MOV ESI,DWORD PTR SS:[ESP+4]
02120004 55 PUSH EBP
02120005 E8 DF010000 CALL 021201E9
0212000A 58 POP EAX
0212000B 50 PUSH EAX

0212000C FFD6 CALL ESI     ; kernel32.LoadLibraryA

Перед указанной функцией происходит вызов процедуры «CALL 021001E9». Вот, что она из себя представляет:
021201E9 58 POP EAX     ; 0210000A
021201EA FFD0 CALL EAX


Что же в этот момент происходит? При вызове «CALL 021001E9» в стек помещается адрес следующей после этого вызова инструкции для возврата из процедуры. После вызова из стека извлекается адрес возврата и помещается в EAX. Далее происходит вызов «CALL EAX», который в свою очередь снова помещает в стек адрес следующей инструкции для возврата. Смотрим в дампе — видим имя библиотеки:



Далее по листингу еще не раз встречается такой метод.

Следующий интересный момент — процедура поиска необходимых функций из подключенной библиотеки. Среди параметров, передаваемых в процедуру — указатель на непонятный набор байтов, полученный вышеописанным методом, далее разберемся зачем и что это. Итак, сама процедура:
0212034B 60 PUSHAD
0212034C EB 07 JMP SHORT 02120355

0212034E AD LODS DWORD PTR DS:[ESI] < — Алгоритм прохода по заголовкам подключенной DLL с целью поиска таблицы экспорта
0212034F ^E2 FD LOOPD SHORT 0212034E
02120351 8D3403 LEA ESI,DWORD PTR DS:[EBX+EAX]
02120354 C3 RETN
02120355 8BE8 MOV EBP,EAX
02120357 8BF3 MOV ESI,EBX
02120359 B9 10000000 MOV ECX,10
0212035E E8 EBFFFFFF CALL 0212034E
02120363 B9 1F000000 MOV ECX,1F
02120368 E8 E1FFFFFF CALL 0212034E
0212036D 56 PUSH ESI
0212036E B9 07000000 MOV ECX,7
02120373 E8 D6FFFFFF CALL 0212034E
02120378 8BD0 MOV EDX,EAX
0212037A 8B3424 MOV ESI,DWORD PTR SS:[ESP]
0212037D B9 09000000 MOV ECX,9
02120382 E8 C7FFFFFF CALL 0212034E
02120387 8BFE MOV EDI,ESI
02120389 8BCA MOV ECX,EDX
0212038B E8 BEFFFFFF CALL 0212034E
02120390 33C0 XOR EAX,EAX
02120392 50 PUSH EAX

02120393 C1C8 07 ROR EAX,7 < — Вычисление хеш-суммы от имени функции из таблицы экспорта DLL
02120396 C10424 0D ROL DWORD PTR SS:[ESP],0D
0212039A 010424 ADD DWORD PTR SS:[ESP],EAX
0212039D AC LODS BYTE PTR DS:[ESI]
0212039E 84C0 TEST AL,AL
021203A0 ^75 F1 JNZ SHORT 02120393
021203A2 58 POP EAX
021203A3 8BF7 MOV ESI,EDI

021203A5 3BC5 CMP EAX,EBP < — Сравнение полученной хеш-суммы со значением в памяти
021203A7 74 03 JE SHORT 021203AC
021203A9 4A DEC EDX
021203AA ^75 DD JNZ SHORT 02120389

021203AC 8B3424 MOV ESI,DWORD PTR SS:[ESP] < — Если хеш совпал, то происходит поиск и возврат адреса искомой функции
021203AF B9 0A000000 MOV ECX,0A
021203B4 E8 95FFFFFF CALL 0212034E
021203B9 0FB70C56 MOVZX ECX,WORD PTR DS:[ESI+EDX*2]
021203BD 5E POP ESI
021203BE 51 PUSH ECX
021203BF B9 08000000 MOV ECX,8
021203C4 E8 85FFFFFF CALL 0212034E
021203C9 59 POP ECX
021203CA E8 7FFFFFFF CALL 0212034E
021203CF 897424 1C MOV DWORD PTR SS:[ESP+1C],ESI
021203D3 61 POPAD
021203D4 C3 RETN


Теперь понятно, данная процедура получает указатель на некую хеш-сумму и адрес DLL, в которой необходимо провести поиск функции. Далее логика по порядку:
  1. По заголовкам PE вычисляется адрес таблицы экспорта целевой DLL;
  2. Берется имя каждой функции из таблицы экспорта и вычисляется некая хеш-сумма;
  3. Проверяется совпадение вычисленной хеш-суммы и полученной в параметре;
  4. Если хеш-суммы совпали вычисляется и возвращается адрес найденной функции.

По мере поиска необходимых функций происходит перезапись исходных хеш-сумм на реальные адреса:



Затем с помощью «GetProcAddress» находятся адреса еще трех функций: HeapAlloc, HeapFree и GetTickCount.
02120039 5F POP EDI
0212003A 83C7 0D ADD EDI,0D
0212003D 57 PUSH EDI
0212003E 53 PUSH EBX

0212003F FF55 08 CALL DWORD PTR SS:[EBP+8]
02120042 8906 MOV DWORD PTR DS:[ESI],EAX
02120044 83C7 0A ADD EDI,0A
02120047 57 PUSH EDI
02120048 53 PUSH EBX

02120049 FF55 08 CALL DWORD PTR SS:[EBP+8]
0212004C 8946 04 MOV DWORD PTR DS:[ESI+4],EAX
0212004F 83C7 09 ADD EDI,9
02120052 57 PUSH EDI
02120053 53 PUSH EBX

02120054 FF55 08 CALL DWORD PTR SS:[EBP+8]     ; kernel32.GetProcAddress
02120057 8946 08 MOV DWORD PTR DS:[ESI+8],EAX

Найденные адреса дописываются в конец уже сформированного массива адресов используемых API:



Массив используемых функций сформирован, после чего сразу начинается его использование с выделения новой области памяти такого же размера, как и в прошлый раз — 0x6D0:



flProtect = 0x40, что снова говорит о возможности исполнения кода в выделяемом участке памяти.
После «VirtualAlloc» сразу встречаем цикл копирования текущего участка памяти в новый, при этом сохранив в стек адрес возврата для «RETN» на смещение 0x81:



«RETN» переносит исполнение кода на новый участок памяти, который выглядит так:
00220081 E8 CB050000 CALL 00220651
00220086 5D POP EBP
00220087 5E POP ESI
00220088 873424 XCHG DWORD PTR SS:[ESP],ESI
0022008B 56 PUSH ESI

0022008C E8 2E050000 CALL 002205BF
00220091 E8 6C050000 CALL 00220602

«CALL 00220651» — уже известная нам процедура, которая хитрым образом возвращает нам адрес массива с адресами восстановленных функций.
«CALL 002205BF» — проверяет валидность загруженного в память процесса по сигнатурам «MZ» и «PE».
002205BF 66:33F6 XOR SI,SI
002205C2 66:BA 4D5A MOV DX,5A4D
002205C6 66:AD LODS WORD PTR DS:[ESI]
002205C8 66:33D0 XOR DX,AX
002205CB 74 08 JE SHORT 002205D5
002205CD 81EE 02100000 SUB ESI,1002
002205D3 ^EB ED JMP SHORT 002205C2
002205D5 8D5E FE LEA EBX,DWORD PTR DS:[ESI-2]
002205D8 84FF TEST BH,BH
002205DA ^75 F1 JNZ SHORT 002205CD
002205DC 8B76 3A MOV ESI,DWORD PTR DS:[ESI+3A]
002205DF 66:BA 5045 MOV DX,4550
002205E3 8D341E LEA ESI,DWORD PTR DS:[ESI+EBX]
002205E6 66:AD LODS WORD PTR DS:[ESI]
002205E8 66:33D0 XOR DX,AX
002205EB ^75 E0 JNZ SHORT 002205CD
002205ED C3 RETN


«CALL 00220602» — выделяет новый участок памяти размером 0x2C00 (теперь только для чтения/записи), находит полное имя файла, запустившего текущий процесс и открывает файл для чтения
00220602 8B7D 70 MOV EDI,DWORD PTR SS:[EBP+70]      ;SS:[002206C4]=00002C00
00220605 6A 04 PUSH 4
00220607 68 00100000 PUSH 1000
0022060C 57 PUSH EDI
0022060D 6A 00 PUSH 0

0022060F FF55 10 CALL DWORD PTR SS:[EBP+10]     ; kernel32.VirtualAlloc
00220612 8BF0 MOV ESI,EAX
00220614 81EC 04010000 SUB ESP,104
0022061A 8BFC MOV EDI,ESP
0022061C 68 04010000 PUSH 104
00220621 57 PUSH EDI
00220622 53 PUSH EBX      ;00400000

00220623 FF55 20 CALL DWORD PTR SS:[EBP+20]     ; kernel32.GetModuleFileNameA
00220626 6A 00 PUSH 0
00220628 68 80000000 PUSH 80
0022062D 6A 03 PUSH 3
0022062F 6A 00 PUSH 0
00220631 6A 01 PUSH 1
00220633 68 00000080 PUSH 80000000
00220638 57 PUSH EDI

00220639 FF55 24 CALL DWORD PTR SS:[EBP+24]     ; kernel32.CreateFileA
0022063C 81C4 04010000 ADD ESP,104
00220642 C3 RETN



После возврата хендла на открытый файл устанавливается указатель со смещением 0x6263:
00220096 8BF8 MOV EDI,EAX
00220098 6A 00 PUSH 0
0022009A 6A 00 PUSH 0

0022009C FF75 6C PUSH DWORD PTR SS:[EBP+6C]      ;SS:[002206C0]=00006263
0022009F 57 PUSH EDI
002200A0 FF55 28 CALL DWORD PTR SS:[EBP+28]     ; kernel32.SetFilePointer

В HEX редакторе можно посмотреть, что по данному смещению действительно хранятся какие-то данные, скорее всего зашифрованные:


Считывание данных из файла размером 0x2C00 по указанному смещению в созданный ранее сегмент памяти:
002200A3 8B4D 70 MOV ECX,DWORD PTR SS:[EBP+70]      ;SS:[002206C4]=00002C00
002200A6 6A 00 PUSH 0
002200A8 54 PUSH ESP
002200A9 58 POP EAX
002200AA FF3424 PUSH DWORD PTR SS:[ESP]      ;Stack SS:[0018FF28]=0000005C
002200AD 50 PUSH EAX
002200AE 51 PUSH ECX

002200AF 56 PUSH ESI      ;00230000
002200B0 57 PUSH EDI
002200B1 FF55 2C CALL DWORD PTR SS:[EBP+2C]     ; kernel32.ReadFile


Далее вызывается процедура расшифровки скопированных данных:



Весь листинг процедуры показывать не буду, вот цикл расшифровки:
00220505 3B75 60 CMP ESI,DWORD PTR SS:[EBP+60]
00220508 75 0D JNZ SHORT 00220517
0022050A 0375 64 ADD ESI,DWORD PTR SS:[EBP+64]
0022050D 037D 64 ADD EDI,DWORD PTR SS:[EBP+64]
00220510 2B4D 64 SUB ECX,DWORD PTR SS:[EBP+64]
00220513 85C9 TEST ECX,ECX
00220515 74 12 JE SHORT 00220529

00220517 AD LODS DWORD PTR DS:[ESI] < — чтение, расшифровка, запись
00220518 50 PUSH EAX
00220519 05 224AFE8D ADD EAX,8DFE4A22
0022051E 0FC8 BSWAP EAX
00220520 33C2 XOR EAX,EDX
00220522 5A POP EDX
00220523 AB STOS DWORD PTR ES:[EDI]
00220524 83E9 03 SUB ECX,3
00220527 ^E2 DC LOOPD SHORT 00220505



После расшифровки в дампе можно наблюдать такую картину:



Сигнатуры «MZ» и «PE» подсказывают, что мы близки к цели.

Формирование новых заголовков и секций текущего процесса.
После расшифровки происходит перезапись 512 байт в ImageBase текущего процесса расшифрованными данными (естественно предварительно поменяв права на целевую область памяти на RW):



Итог — старые заголовки и таблица секций переписана новыми. Как видно из новой таблицы секций — будет две секции ".text" и ".rsrc":



Далее переходим к следующему коду:
00220101 0349 3C ADD ECX,DWORD PTR DS:[ECX+3C]
00220104 8D79 18 LEA EDI,DWORD PTR DS:[ECX+18]
00220107 8B57 20 MOV EDX,DWORD PTR DS:[EDI+20]
0022010A 0FB741 14 MOVZX EAX,WORD PTR DS:[ECX+14]
0022010E 03F8 ADD EDI,EAX
00220110 0FB749 06 MOVZX ECX,WORD PTR DS:[ECX+6]

00220114 60 PUSHAD < — начало цикла формирования новых секций
00220115 8B47 08 MOV EAX,DWORD PTR DS:[EDI+8]
00220118 85C0 TEST EAX,EAX
0022011A 74 42 JE SHORT 0022015E
0022011C E8 CD040000 CALL 002205EE
00220121 8BC8 MOV ECX,EAX
00220123 8B47 24 MOV EAX,DWORD PTR DS:[EDI+24]
00220126 E8 AA020000 CALL 002203D5
0022012B 0377 14 ADD ESI,DWORD PTR DS:[EDI+14]
0022012E FF77 10 PUSH DWORD PTR DS:[EDI+10]
00220131 8B7F 0C MOV EDI,DWORD PTR DS:[EDI+C]
00220134 03FB ADD EDI,EBX
00220136 5B POP EBX
00220137 50 PUSH EAX
00220138 8BD4 MOV EDX,ESP
0022013A 52 PUSH EDX
0022013B 50 PUSH EAX
0022013C 51 PUSH ECX
0022013D 57 PUSH EDI
0022013E 51 PUSH ECX
0022013F 52 PUSH EDX
00220140 6A 04 PUSH 4
00220142 51 PUSH ECX
00220143 57 PUSH EDI
00220144 FF55 0C CALL DWORD PTR SS:[EBP+C]
00220147 59 POP ECX
00220148 33C0 XOR EAX,EAX
0022014A 57 PUSH EDI

0022014B F3:AA REP STOS BYTE PTR ES:[EDI] < — обнуление основной области памяти процесса
0022014D 5F POP EDI
0022014E 85F6 TEST ESI,ESI
00220150 74 08 JE SHORT 0022015A
00220152 85DB TEST EBX,EBX
00220154 74 04 JE SHORT 0022015A
00220156 8BCB MOV ECX,EBX

00220158 F3:A4 REP MOVS BYTE PTR ES:[EDI],BYTE PTR DS:[ESI] < — Запись новой секции
0022015A FF55 0C CALL DWORD PTR SS:[EBP+C]
0022015D 58 POP EAX
0022015E 61 POPAD
0022015F 83C7 28 ADD EDI,28
00220162 ^E2 B0 LOOPD SHORT 00220114


В самом начале производится поиск таблицы секций. Далее по циклу находится RawData и VirtualSize каждой секции, обнуление основных участков памяти и запись новых секции. Тем самым формируется новое «тело» текущего процесса.

Далее встречаем вызов процедуры:
00220169 E8 AE000000 CALL 0022021C

Она выполняет восстановление IAT (Import Address Table) в начале секции ".text". Итог работы процедуры:


Далее освобождается память, в которой ранее хранились данные для формирования нового окружения процесса (0x00230000):
002201BD FF55 14 CALL DWORD PTR SS:[EBP+14]     ; kernel32.VirtualFree


Подошли к эпилогу — извлечение OEP:



И переход к OEP:



Говорим отладчику проанализировать код и получаем красивый и читаемый листинг уже распакованного downloader'а, в котором уже различимы строки, используемые для загрузки основного функционального модуля:



Общая логика декриптора




  1. Выделение области памяти с правами RWE, размером 0x6D0 (1744 байта) с помощью HeapCreate. Расшифровка и копирование данных из секции .data (0x00408E63 — 0x00409532) в выделенную память;
  2. Перенос исполнения кода на нулевое смещение выделенной памяти;
  3. Поиск необходимых API функций и создание массива с их адресами по смещению 0x654 в текущей области памяти;
  4. Создание нового участка памяти с правами RWE, того же размера 0x6D0 (1744 байта) с помощью VirtualAlloc. Копирование содержимого текущего участка памяти в новый;
  5. Перенос исполнения кода на новый участок памяти по смещению 0x81 методом «PUSH — RETN»;
  6. Выделение нового участка памяти размером 0x2C00 (11 264 байта) с правами RW;
  7. Открытие исходного файла для чтения. Копирование данных (с файловым смещением 0x6263 — 0x8E62) из файла в новый участок памяти;
  8. Расшифровка скопированных данных;
  9. Формирование нового окружения процесса;
  10. Переход к OEP (0x004019BA).


Дамп процесса


Остается сделать дамп процесса для получения исполняемого распакованного файла вируса. Чтобы не заниматься ручным фиксом после дампа я воспользовался плагином OllyDumpEx для Immunity Debugger:



Полученный исполняемый файл при запуске имеет следующую структуру:



Заключение


В ходе проделанной работы получили распакованный исполняемый файл Downloader'а. Да, можно было бы сократить описание, пройдясь по ключевым точкам копирования и расшифровки данных и переходу к OEP. В боевых условиях при анализе вредоносного ПО наверняка редко разбирают логику декриптора подробно. Но напомню, цель статьи — погрузиться в реверсинг и, как бонус, понять, как работают протекторы вредоносного ПО.

Цель, которой я так и не смог достичь в данной статье — локализация основных моментов протектора, которые отвечают за «обман» эмулятора антивируса. Данные конструкции наверняка присутствуют в декрипторе. Если кто подскажет, как их идентифицировать — буду признателен.

Во второй части статьи рассмотрим функциональную нагрузку Downloader'а и каким образом запускается сам шифровальщик CTB-Locker. Не обещаю, что она появится на днях, т.к. ограничен во времени, да и не отрицаю своего малого опыта в данной сфере — с некоторыми моментами разбираюсь по ходу написания статей, что тоже отнимает время.

+30
34.7k 166
Comments 17
Top of the day