Троянский котоконь: Как JPEG с логотипом опустошил банк
Ну что, приятели, слышали свежую байку? Прям анекдот, да только чистая правда. Одна... скажем так, очень изобретательная команда хакеров, взяла да и всадила зловред прямиком в корень системы европейского банка. И знаете, куда спрятали? В обычную картинку! Да-да, в тот самый JPEG с логотипом конторы, что годами висел на их сайте. Ну, типа корпоративной культуры там, котик или что-то. (Вот это поворот, а?)

А теперь – стоп.
Потому что ловите мысль: спецы по безопасности, умнейшие ребята, потом месяцами рыли. Логи сетевые – перепахали. Каждый подозрительный бинарь разобрали по винтикам. А эта картинка? Фоном висит себе, никого не колышет. Никому даже в голову не пришло заглянуть внутрь. Проверяли логи? Да. Файлы? Да. А картинку-то с сюрпризом? Нет. Обидно, да? Вот и мне так кажется. И знаете, почему? Потому что мы все, даже лучшие из нас, часто работаем как на автопилоте с файлами. Копируем, перемещаем, права выставляем – рутина. Но когда файл начинает откровенно врать? Когда vacation_photo.jpg на самом деле злой бинарь, а system.conf – упакованный вредонос? Здесь ls -la просто разводит руками. Бессилен. Как и мы, если не копнем глубже.
Хватит пугать народ! Давайте вооружаться. Четыре ваших новых лучших друга.
Серьезно, друзья. Выделите мне десять минут прямо сейчас и я подкину вам в арсенал четыре штуки, которые изменят правила игры. С двумя вы уже знакомы, но ничего, повторение – мать учения. Забудьте про то, что файл сообщает своим расширением. Нам нужно то, что он есть на самом деле. Вот они, ваши детективы в консоли Linux:
- 🔁
file– быстрый взгляд под капот: «Ты кто по жизни, файлик?»; - 🔁
strings– выуживаем текст, даже если он спрятан в бинарной каше. Секреты? Пароли? Намеки? Часто тут; - 🆕
xxdиhexdump– это уже серьезно. Смотрим на сырые байты, шестнадцатерично. Самый глубокий уровень. Видим всё, что скрыто. Магия? Нет, просто чтение того, что есть.
Представьте: сидите вы, пьете кофе утром (или пиво вечером, без осуждения), и коллега тыкает в файлик promo.png. "Чё-то сомнительный". А вы такой – раз! В консоли file promo.png – а он вам: "ELF executable, buddy!". Или strings system_backup.tar.gz – а там строки явно не для бэкапа. Бинго! Не тридцать минут статического анализа, а тридцать секунд в терминале. Вот это навык. Не для галочки, а для реальной безопасности. Этим мы занимались вчера и поняли что это не всегда панацея. Сейчас будем делать упор на более глубокий статический анализ.
Введение
Помните тот разбор полётов с file и strings? Поехали глубже.
Эх, если прошлую статью вы пропустили, там мы разбирали, как не тыкаться в файлы вслепую. Настраивали песочницу, щупали бинари через file, вытягивали ниточки текста strings... Серьёзно, начните чтение с неё. Без базы – как в тёмном подвале с завязанными глазами. Опасно и глупо.
Сегодня же надеваем перчатки и лезем в самые потроха. Встречайте тяжёлую артиллерию: xxd и его вечно спорящего братца hexdump. Проводить анализ без них, как хирургу без скальпеля вырезать аппендицит ложкой. Просто поверьте, если вы в реверсе или просто ковыряете подозрительные файлики, эти две штуки должны быть на автозапуске. Иначе вы просто скользите по поверхности, а там, в глубине...
Чего разберем за сегодня:
fileна стероидах: да, мы уже знаем, что он умеет тыкать в файл и говорить "Эй, это ELF!". Но мало кто копал глубже, а ведь он видит архитектуру, битность, линковку, даже сжатые слои! Как рентген, который показывает не только кости, но и трещинки.strings– не просто «покажи текст»: Unicode? Выдёргиваем. Шумные бинарные хвосты? Отсекаем. Ищем не просто слова, а паттерны: куски кода, подозрительные вызовы, обрывки URL. Как ищейка, которая чует не просто мясо, а конкретную породу.xxdиhexdump: байтовые ниндзя. Вот где начинается магия (или тяжёлая работа, кому как). Смотрим на файл не как на картинку или текст, а как на сырую последовательность байтов. Адреса, смещения, шестнадцатеричные значения, ASCII-представление... Это ваши глаза, видящие ДНК файла. Без этого – слепота.- Охота на спрятанное: файл в файле? Конфиг, набитый мусором и одним зловредным вызовом? Артефакты сокрытия? Учимся видеть аномалии в сырых данных. Как найти иголку в стоге сена, если знать, что иголка магнитная.
- Разборки с маскировкой: тот самый «кот» из первой статьи. Разберём подобный случай по косточкам, прямо в терминале. От
fileдо ручного просмотра hex-дампа в поисках подозрительных сигнатур.
Зачем этот байтовый ад? (Спойлер: без него – никак)

Слушайте, если вы не умеете читать файлы на уровне байтов, вы как детектив, который верит только словам подозреваемого. «Ой, да я просто картинка, .jpg честное слово!» – скажут вам. А потом бац – система падает. Злоумышленники живут за счет обмана расширений и форматов. Без xxd/hexdump вы слепы к их фокусам. Это не просто «полезно» – это фундамент. Бетонный, арматурой прошитый. Без него всё последующее как песочный замок. Хотите по-настоящему понимать, что творится в этом бинарном хаосе? Знакомьтесь с байтами. Лично.
Когда file показывает зубы
Не просто «угадай-ка»: настоящие трюки file
Ох, этот file... Многие думают, он только и умеет бормотать «text» или «ELF». А зря! Это же целая лаборатория в одной строке – может выдать такое, что волосы дыбом встанут. Особенно когда файлы начинают врать.

Опции, от которых мурашки по коже:
-b # Без лишних слов — только суть (имя файла? не, не слышал)
-i # MIME-тип? Кодировка? Дай угадаю – вам это нужно прямо сейчас
-r # Сырой режим: не стесняемся показывать страшные символы
-L # Гоняемся за симлинками как упоротые терьеры
-z # Залезаем внутрь сжатого файла без спросу (это же почти взлом!)
-k # Не останавливаемся на первой находке – копаем до дна
-f # Читаем список файлов как записную книжку шпиона
--exclude-quiet # Игнорируем ошибки тихонько, как настоящие ниндзя
Практика: разбор коллекции «волков в овечьей шкуре»
Вот что вылезло в моей консоли на тестовой папке:
file hidden_files/*
hidden_files/access.log: ELF 64-bit LSB pie executable, x86-64, version 1 (SYSV), dynamically linked, interpreter /lib64/ld-linux-x86-64.so.2, BuildID[sha1]=525a6eeca1f8b4401939a477e102dc61f43f9cf1, for GNU/Linux 3.2.0, with debug_info, not stripped
hidden_files/config_file.txt: ASCII text
hidden_files/document.pdf: PDF document, version 1.4, 1 page(s)
hidden_files/fake_package.deb: gzip compressed data, from Unix, original size modulo 2^32 768000
hidden_files/fake_text.txt: data
hidden_files/fake_tools.rpm: POSIX tar archive (GNU)
hidden_files/hello_encoded.txt: ASCII text
hidden_files/hidden_files.zip: Zip archive data, at least v2.0 to extract, compression method=deflate
hidden_files/hidden_math.tar: POSIX tar archive (GNU)
hidden_files/image_with_hidden_file.png: PNG image data, 1536 x 1024, 8-bit/color RGB, non-interlaced
hidden_files/landscape.png: PNG image data, 1536 x 1024, 8-bit/color RGB, non-interlaced
hidden_files/script_with_binary.sh: Bourne-Again shell script executable (binary data)
hidden_files/secret_data.tar.gz: gzip compressed data, from Unix, original size modulo 2^32 20480
hidden_files/system.conf: ELF 64-bit LSB pie executable, x86-64, version 1 (SYSV), dynamically linked, interpreter /lib64/ld-linux-x86-64.so.2, BuildID[sha1]=11645e11a196b61780bd9f1157e214531c0fff3c, for GNU/Linux 3.2.0, not stripped
Что меня сразу насторожило:
access.log– исполняемый файл?! Да я такие логи только в кошмарах видел!script_with_binary.sh– шелл-скрипт с бинарным текстом внутри? Хм...fake_package.deb– размерчик-то какой упакованный! 768000? Серьёзно?system.conf– опять ELF! Ребят, ну хотя бы старались замаскироваться...
Охота на оборотней: ищем подменыши
Любимый трюк шалунов – переодеть ELF в текстовый костюмчик. Ловите тактику:
find hidden_files/ -name "*.txt" -o -name "*.conf" -o -name "*.log" | xargs file
hidden_files/config_file.txt: ASCII text
hidden_files/fake_text.txt: ELF 64-bit LSB pie executable... <подмена!>
... <остальной вывод> ...
А теперь глубже. Спросим MIME-тип у подозреваемого:
file -i fake_text.txt
fake_text.txt: application/octet-stream; charset=binary # Ага-а! Попался, гаденыш!
Залезем в сжатые файлы без стука — опция -z наш швейцарский нож:
file -z fake_package.deb
fake_package.deb: POSIX tar archive (GNU) (gzip compressed data, from Unix) # Двойное дно!
И ещё один фокус:
file -z hidden_files.zip
hidden_files.zip: ELF 64-bit LSB shared object... (Zip archive data...) # Внутри зомби!
Автоматизируем охоту: скрипты-помощники
Эти приёмы как шпаргалка в рукаве. Меняйте под свои нужды без стеснения.
Когда файлов тьма, ручной file убьёт все нервы. Вот мой любимый автомат:
find hidden_files -type f \( -name "*.txt" -o -name "*.conf" -o -name "*.log" \) -exec file {} \; | grep ELF
Вывод как пощечина:
hidden_files/access.log: ELF 64-bit LSB pie executable...
hidden_files/system.conf: ELF 64-bit LSB pie executable...
find hidden_files
- Рыщем в папке как голодный енот в мусорке. Точка (
.) тоже сойдёт.
-type f
- Только файлы! Папки – мимо.
\( -name ".txt" -o -name ".conf" -o -name "*.log" \)
- Ловим всё с подозрительными хвостиками:
.txt,.conf,.log.
– -o – волшебное «ИЛИ».
– Скобки \( \) – группируем, чтобы шелл не сбился.
-exec file {} \;
- Каждую находку на опознание через
file. {}– подставная кукла для файла.\;– точка в конце команды.
| grep ELF
- Просеиваем вывод через сито – ищем маркер зомби-файлов (ELF).
Ещё один трюк для любителей awk. Ищем несоответствия:
file * | awk -F: ' !~ /text/ && ~ /\.(txt|conf|log)$/ {print " -> " }'
access.log -> ELF 64-bit LSB pie executable... # Что-то не так
fake_text.txt -> data # Подозрительные данные!
system.conf -> ELF 64-bit LSB pie executable... # Снова проклятый ELF
Команда strings: когда бинарник начинает болтать
Слушайте, друзья, этот инструмент настоящий детектор лжи для файлов. Представьте: бинарник молчит как рыба, а strings заставляет его выдать все секреты. ASCII? Да, но это лишь верхушка айсберга.

Копнём глубже: что там под капотом?
strings [опции] файл(ы)
# Основные опции:
-a # Шерстим ВЕСЬ файл, а не кусочки (даже мусорные углы!)
-f # Подписываем каждую улику именем файла – чтобы не запутаться в ночи
-n ЧИСЛО # Фильтр по длине: игнорируем мелочь (типа "АБВГ")
-t ФОРМАТ # Где искать? d (десятичные), o (восьмеричные), x (hex) — как вам удобнее
-o # Старая школа: сразу восьмеричные смещения (для ностальгирующих)
-T ФОРМАТ # Экзотика? Ваш объектный файл – наш пациент
-w # Учитываем ВСЕ пробелы (вдруг там тайные пробельные послания?)
Буквари на разных языках: кодировки
Команда strings позволяет работать с различными кодировками.
# Старый добрый ASCII (работает из коробки):
-e s # Латинница, базовая — но мир шире!
# Unicode-джунгли:
-e S # Тяжеловесы big-endian (16 бит)
-e b # Их младшие братья (16 бит BE)
-e l # Little-endian'чики (16 бит LE)
-e B # Большие боссы (32 бит BE)
-e L # Маленькие гиганты (32 бит LE)
Охота за трофеями: примеры из жизни
Длинные хвосты:
strings -n 20 подозрительный_архив # Ищем солидные "высказывания"
strings -n 2 секретный_файл | grep -E '^[А-Я]{2,4}$' # Ловим команды
Цифровая рыбалка:
# IP-адреса — классика жанра:
strings зловред.exe | grep -E '[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}'
# URL и домены — где прячутся черти:
strings ботнет.log | grep -E 'https?://\S+|\w+\.(com|org|ru)'
# Электронки — кто засветился?:
strings дамп_памяти | grep -E '\b\w+@\w+\.\w{2,5}\b'
# Святая святых: ключи и пароли:
strings конфиг.jar | grep -iE 'pass(word)?|api[ _]?key|secret|token'
# Флаги CTF — где спрятано золото?:
strings квест.bin | grep -E '(FLAG\{|flag\{|SECRET\{)'
# Base64 — шифровки для бедных:
strings документ.pdf | grep -E '^[A-Za-z0-9+/=]{20,}$'
Подводные камни: когда strings врёт

- Мусорный водопад – 90% вывода часто белиберда (как мой старый syslog)
- Оборотни – строки могут быть шифрованными или упакованными в 10 слоёв
- Цунами данных – попробуйте
stringsна 10GB дампе... спираль безумия!
Как не утонуть:
# Фильтруем по смыслу:
strings терабайтный_файл | grep -vE '^\s*$|^[^[:print:]]+$' | \
grep -E '(http|ssh|\.(com|ru)|admin|passw|secret|key)'
# Золотая середина: не слишком длинно, не слишком коротко:
strings файлик | awk 'length>6 && length<50 && /[a-zA-Zа-яА-Я]/'
xxd vs hexdump: режем файлы как шеф-повар
Слушайте, друзья, если strings – это наш детектор лжи, то эти двое – настоящие хирургические скальпели. Да, оба режут в hex, но...
mindmap
root((Эти странные инструменты))
xxd
Приехал с Vim'ом в чемодане
Поддерживает обратное преобразование!
Вывод – чистый, понятный, без заморочек
Для быстрых надрезов в 3 часа ночи
hexdump
Форматирование? Да хоть радугу сделай!
Скрипты глотает как конфеты
Настроек – как звёзд на небе
Ковыряемся с xxd: быстрые надрезы
xxd [ключи] [что_режем [куда_сложить]]
# Мои любимые лезвия:
-l РАЗМЕР # Отрезать ровно столько (не жадничать!)
-s СМЕЩЕНИЕ # Начать с конкретной точки
-r # Алхимия: hex → бинарь (да, это работает!)
-c КОЛОНКИ # Ширина нарезки (16? 32? На твой вкус)
-g ГРУППЫ # Группировка байтов (попробуй 4 для ELF!)
-u # КАПСОМ, чтобы важное бросалось в глаза
Сейчас не будем резать ELF – оставим на завтра. Но глянем на «паспорт» файла:
xxd -l 64 access.log # Первые 64 байта — как отпечатки пальцев (это заголовок)
00000000: 7f45 4c46 0201 0100 0000 0000 0000 0000 .ELF............ <<< Вот же родной!
00000010: 0300 3e00 0100 0000 8010 0000 0000 0000 ..>............. <<< 64-bit, little-endian
00000020: 4000 0000 0000 0000 b83f 0000 0000 0000 @........?......
00000030: 0000 0000 4000 3800 0d00 4000 2500 2400 ....@.8...@.%.$.
А теперь hexdump: тяжёлая артиллерия
Тот же файл, но через универсальный комбайн:
hexdump -C -n 64 access.log # -C — это святое!
# Те же яйца, вид сверху
00000000 7f 45 4c 46 02 01 01 00 00 00 00 00 00 00 00 00 |.ELF............|
00000010 03 00 3e 00 01 00 00 00 80 10 00 00 00 00 00 00 |..>.............|
00000020 40 00 00 00 00 00 00 00 b8 3f 00 00 00 00 00 00 |@........?......|
00000030 00 00 00 00 40 00 38 00 0d 00 40 00 25 00 24 00 |....@.8...@.%.$.|
Хотите экзотики? Пожалуйста:
# Только hex, без соплей (-x):
$ hexdump -x -n 32 bin/hello
0000000 457f 464c 0102 0301 0000 0000 0000 0000 # Big-endian? Серьёзно?!
0000010 0002 003e 0001 0000 1530 0040 0000 0000
# 32-битные куски (-d) — для ценителей:
$ hexdump -d -n 32 bin/hello
0000000 17919 17998 258 771 0 0 0 0
0000010 2 62 1 0 5424 64 0 0 # Десятичные? Почему бы и нет!
Как не сломать глаза, читая эту кашу
00000000 7f 45 4c 46 02 01 01 03 00 00 00 00 00 00 00 00 |.ELF............|
│ │ │ │ │ │ │
│ │ │ │ │ └─ ASCII представление
│ │ │ └─ Вторая группа 8 байт
│ └─ Первая группа 8 байт
└─ Смещение (offset) в hex
Шпаргалка по «волшебным» байтам:
7f 45 4c 46 # ELF (наше всё)
89 50 4e 47 # PNG (котики!)
ff d8 ff # JPEG (фото с корпоратива)
50 4b 03 04 # ZIP (сюрпризы внутри)
1f 8b 08 # GZIP (сплющенный как блин)
42 5a 68 # BZIP2 (редкий зверь)
37 7a bc af # 7zip (тёмная лошадка)
25 50 44 46 # PDF (доки, которые... ну вы поняли)
Кстати, для отработки распознавания формата файла, есть практическое задание, в котором необходимо найти флаг.
Охота за сокровищами: ELF в JPEG. Серьёзно?
hexdump -C котик_с_сюрпризом.jpg | grep -A2 -B2 "7f 45 4c 46" # -A2/B2 — контекст важен!
001763c0 7f 45 4c 46 02 01 01 03 00 00 00 00 00 00 00 00 |.ELF............| <<< Опа! 0x1763c0!
001763d0 02 00 3e 00 01 00 00 00 30 15 40 00 00 00 00 00 |..>.....0.@.....|
А file и xxd скромно молчали:
$ file котик_с_сюрпризом.jpg
котик_с_сюрпризом.jpg: PNG image... # Ну да, ну да...
$ xxd -l 64 котик_с_сюрпризом.jpg
00000000: 8950 4e47 0d0a 1a0a 0000 000d 4948 4452 .PNG........IHDR <<< Чистый как слеза!
Но binwalk не соврёт:
1532864 0x1763C0 ELF, 64-bit LSB executable... # Бинго!
Магия xxd: из дампа в бинарь!
flowchart LR
A[Инструменты] --> B[xxd] --> B1[Обратная сборка]
A --> C[hexdump] --> C1[Только чтение]
Допустим, у нас есть программа hello с выводом «Hello». Найдем где он прячется:
xxd hello | grep 'Hello' # Ага, 0x200!
00002000: 0100 0200 4865 6c6c 6f00 0000 011b 033b ....Hello......;
Сохраняем дамп:
xxd hello > hello.hex
Меняем «Hello» (48 65 6c 6c 6f) на «Bye» (42 79 65) + два нуля для выравнивания:
- 00002000: 0100 0200 4865 6c6c 6f00 0000 011b 033b ....Hello......;
+ 00002000: 0100 0200 4279 6500 0000 0000 011b 033b ....Bye........;
---
config:
flowchart:
securityLevel: 'loose'
useMaxWidth: true
curve: 'basis'
nodeSpacing: 30
rankSpacing: 50
---
flowchart TB
A["<code style = 'white-space: nowrap;'>00002000: 0100 0200 <span style = 'color: red'>4865 6c6c 6f</span>00 0000 011b 033b ....Hello......;</code>"]
-->
B["<code style = 'white-space: nowrap;'>00002000: 0100 0200 <span style = 'color: red'>4279 6500 00</span>00 0000 011b 033b ....Bye........;"]
Собираем обратно:
xxd -r hello.hex > modified_hello # Фокус-покус!
$ ./modified_hello
Bye # Ура!
Далее будем комбинировать инструменты для автоматизации анализа.
Автоматизация: когда руки устали ковырять
Скрипт для экстренного анализа:
#!/bin/bash
# Когда времени в обрез — вот ваш нож для всех файлов
FILE=""
echo "=== ВСКРЫТИЕ $FILE В $(date) ==="
echo -e "\n[1] Базовые показатели:"
file -b "$FILE" # Без воды!
ls -lh "$FILE" # Размер имеет значение
echo -e "\n[2] Заголовок (первые 128 байт):"
xxd -l 128 "$FILE" # Скелет файла
echo -e "\n[3] Текстовые обрывки (первые 20 строк):"
strings "$FILE" | head -20 # Кто внутри?
echo -e "\n[4] Подозрительные артефакты:"
strings "$FILE" | grep -iE "(password|secret|admin|http|ftp|ssh|tcp|\.com|\.org|\.ru|api|key|flag|exec|eval|system)" # Красные флаги!
echo -e "\n[5] Спрятанные файлы? Проверяем:"
hexdump -C "$FILE" | grep -E "(7f 45 4c 46|89 50 4e 47|ff d8 ff|50 4b 03 04)" # ELF/ZIP внутри?
echo "=== АУТОПСИЯ ЗАВЕРШЕНА ==="
Массовый анализ папки (когда всё подозрительное):
#!/bin/bash
# mass_analysis.sh - анализ всех подозрительных файлов в директории
DIR="${1:-.}"
OUTPUT="analysis_report_$(date +%Y%m%d_%H%M%S).txt"
echo "Начинаем массовый анализ файлов в $DIR" > "$OUTPUT"
echo "Время: $(date)" >> "$OUTPUT"
echo "========================================" >> "$OUTPUT"
find "$DIR" -type f | while read -r file; do
echo -e "\n### ФАЙЛ: $file ###" >> "$OUTPUT"
# Определяем тип
echo "ТИП: $(file -b "$file")" >> "$OUTPUT"
# Ищем замаскированные исполняемые файлы
if file "$file" | grep -q "ELF" && [[ "$file" =~ \.(txt|conf|log|tmp)$ ]]; then
echo "⚠️ ВНИМАНИЕ: Замаскированный исполняемый файл!" >> "$OUTPUT"
fi
# Ищем подозрительные строки
SUSPICIOUS=$(strings "$file" 2>/dev/null | grep -i -E "(password|secret|admin|hack|exploit)" | head -3)
if [[ -n "$SUSPICIOUS" ]]; then
echo "🔍 Подозрительные строки:" >> "$OUTPUT"
echo "$SUSPICIOUS" >> "$OUTPUT"
fi
done
echo "Анализ завершён. Результаты в файле: $OUTPUT"
Итоги: что у нас в арсенале
flowchart LR
A[Навыки] --> B["<code>file</code> - видит суть"]
A --> C["<code>strings</code> - вытаскивает текст"]
A --> D["<code>xxd</code>/<code>hexdump</code> - режут на слои"]
A --> E["Скрипты - автоматизируют"]
Что освоили:
- file: определять реальную природу файлов, несмотря на маскировку;
- strings: выуживать текстовые улики даже из бинарной трясины;
- hex-анализ: читать шестнадцатеричные «рентгеновские снимки»;
- Обратная сборка: модифицировать файлы через правку hex-дампа;
- Автоматизация: писать скрипты для массовых проверок.
Что дальше?

В следующей серии будет разбор ELF файлов по косточкам. Будем смотреть:
- как
readelfпоказывает заголовки и секции; - как
objdumpдизассемблирует код; - как
nmработает с символами.
Помните: навыки анализа файлов – это фундамент для всех продвинутых техник реверс-инжиниринга. Чем больше вы практикуетесь с этими базовыми инструментами, тем легче вам будет освоить более сложные техники. Увидимся в следующей статье!