Skip to content

Commit

Permalink
detecting more spam, updated apk
Browse files Browse the repository at this point in the history
  • Loading branch information
simondankelmann committed Jan 4, 2024
1 parent 5f62726 commit 56c1244
Show file tree
Hide file tree
Showing 17 changed files with 251 additions and 67 deletions.
15 changes: 1 addition & 14 deletions .idea/deploymentTargetDropDown.xml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Binary file modified app/debug/app-debug.apk
Binary file not shown.
Binary file modified app/release/app-release.apk
Binary file not shown.
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import android.bluetooth.le.AdvertisingSetParameters
import android.util.Log
import de.simon.dankelmann.bluetoothlespam.Callbacks.GenericAdvertisingSetCallback
import de.simon.dankelmann.bluetoothlespam.Callbacks.GenericAdvertisingCallback
import de.simon.dankelmann.bluetoothlespam.Constants.Constants
import de.simon.dankelmann.bluetoothlespam.Enums.AdvertiseMode
import de.simon.dankelmann.bluetoothlespam.Enums.AdvertisementSetRange
import de.simon.dankelmann.bluetoothlespam.Enums.AdvertisementSetType
Expand All @@ -18,7 +19,7 @@ import de.simon.dankelmann.bluetoothlespam.Models.AdvertisementSet
import de.simon.dankelmann.bluetoothlespam.Models.ManufacturerSpecificData
import kotlin.random.Random

class ContinuityActionModalAdvertisementSetGenerator: IAdvertisementSetGenerator {
class ContinuityActionModalAdvertisementSetGenerator: IAdvertisementSetGenerator {

private val _logTag = "ContinuityActionModalAdvertisementSetGenerator"

Expand Down Expand Up @@ -83,7 +84,7 @@ class ContinuityActionModalAdvertisementSetGenerator: IAdvertisementSetGenerator
}
}

private val _manufacturerId = 76 // 0x004c == 76 = Apple
private val _manufacturerId = Constants.MANUFACTURER_ID_APPLE
override fun getAdvertisementSets(inputData: Map<String, String>?): List<AdvertisementSet> {
var advertisementSets: MutableList<AdvertisementSet> = mutableListOf()

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import android.bluetooth.le.AdvertisingSetParameters
import android.util.Log
import de.simon.dankelmann.bluetoothlespam.Callbacks.GenericAdvertisingCallback
import de.simon.dankelmann.bluetoothlespam.Callbacks.GenericAdvertisingSetCallback
import de.simon.dankelmann.bluetoothlespam.Constants.Constants
import de.simon.dankelmann.bluetoothlespam.Enums.AdvertiseMode
import de.simon.dankelmann.bluetoothlespam.Enums.AdvertisementSetRange
import de.simon.dankelmann.bluetoothlespam.Enums.AdvertisementSetType
Expand All @@ -25,7 +26,7 @@ class EasySetupBudsAdvertisementSetGenerator:IAdvertisementSetGenerator{
// Logic also from here:
// https://github.com/tutozz/ble-spam-android/blob/main/app/src/main/java/com/tutozz/blespam/EasySetupSpam.java

private val _manufacturerId = 117 // 0x75 == 117 = Samsung
private val _manufacturerId = Constants.MANUFACTURER_ID_SAMSUNG
private val prependedBudsBytes = StringHelpers.decodeHex("42098102141503210109")
private val appendedBudsBytes = StringHelpers.decodeHex("063C948E00000000C700") // +16FF75

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import android.bluetooth.le.AdvertisingSetParameters
import android.util.Log
import de.simon.dankelmann.bluetoothlespam.Callbacks.GenericAdvertisingCallback
import de.simon.dankelmann.bluetoothlespam.Callbacks.GenericAdvertisingSetCallback
import de.simon.dankelmann.bluetoothlespam.Constants.Constants
import de.simon.dankelmann.bluetoothlespam.Enums.AdvertiseMode
import de.simon.dankelmann.bluetoothlespam.Enums.AdvertisementSetRange
import de.simon.dankelmann.bluetoothlespam.Enums.AdvertisementSetType
Expand All @@ -25,7 +26,7 @@ class LovespousePlayAdvertisementSetGenerator:IAdvertisementSetGenerator {

private val _logTag = "LovespousePlayAdvertisementSetGenerator"

private val lovespousePlays = mapOf(
val lovespousePlays = mapOf(
"E49C6C" to "Classic 1",
"E7075E" to "Classic 2",
"E68E4F" to "Classic 3",
Expand Down Expand Up @@ -55,7 +56,7 @@ class LovespousePlayAdvertisementSetGenerator:IAdvertisementSetGenerator {
"ACD0A2" to "Independent 2-9",
)

private val _manufacturerId = 255 // 0xFF == 255 == Typo Products, LLC
private val _manufacturerId = Constants.MANUFACTURER_ID_TYPO_PRODUCTS

override fun getAdvertisementSets(inputData: Map<String, String>?): List<AdvertisementSet> {
var advertisementSets: MutableList<AdvertisementSet> = mutableListOf()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import android.bluetooth.le.AdvertisingSetParameters
import android.util.Log
import de.simon.dankelmann.bluetoothlespam.Callbacks.GenericAdvertisingCallback
import de.simon.dankelmann.bluetoothlespam.Callbacks.GenericAdvertisingSetCallback
import de.simon.dankelmann.bluetoothlespam.Constants.Constants
import de.simon.dankelmann.bluetoothlespam.Enums.AdvertiseMode
import de.simon.dankelmann.bluetoothlespam.Enums.AdvertisementSetRange
import de.simon.dankelmann.bluetoothlespam.Enums.AdvertisementSetType
Expand All @@ -25,13 +26,13 @@ class LovespouseStopAdvertisementSetGenerator:IAdvertisementSetGenerator {

private val _logTag = "LovespouseStopAdvertisementSetGenerator"

private val lovespouseStops = mapOf(
val lovespouseStops = mapOf(
"E5157D" to "Classic Stop",
"D5964C" to "Independent 1 Stop",
"A5113F" to "Independent 2 Stop",
)

private val _manufacturerId = 255 // 0xFF == 255 == Typo Products, LLC
private val _manufacturerId = Constants.MANUFACTURER_ID_TYPO_PRODUCTS

override fun getAdvertisementSets(inputData: Map<String, String>?): List<AdvertisementSet> {
var advertisementSets: MutableList<AdvertisementSet> = mutableListOf()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import android.bluetooth.le.AdvertiseSettings
import android.bluetooth.le.AdvertisingSetParameters
import de.simon.dankelmann.bluetoothlespam.Callbacks.GenericAdvertisingCallback
import de.simon.dankelmann.bluetoothlespam.Callbacks.GenericAdvertisingSetCallback
import de.simon.dankelmann.bluetoothlespam.Constants.Constants
import de.simon.dankelmann.bluetoothlespam.Enums.AdvertiseMode
import de.simon.dankelmann.bluetoothlespam.Enums.AdvertisementSetRange
import de.simon.dankelmann.bluetoothlespam.Enums.AdvertisementSetType
Expand Down Expand Up @@ -36,7 +37,7 @@ class SwiftPairAdvertisementSetGenerator : IAdvertisementSetGenerator {
"Device 10" to "Not used...",
)

private val _manufacturerId = 6 // 0x0006 == 6 = Microsoft
private val _manufacturerId = Constants.MANUFACTURER_ID_MICROSOFT
override fun getAdvertisementSets(inputData: Map<String, String>?): List<AdvertisementSet> {
var advertisementSets:MutableList<AdvertisementSet> = mutableListOf()

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,5 +15,12 @@ class Constants {
const val REQUEST_CODE_MULTIPLE_PERMISSIONS = 2
const val REQUEST_CODE_SINGLE_PERMISSION = 3


// ManufacturerIds
const val MANUFACTURER_ID_APPLE = 76 // 0x004c == 76 = Apple
const val MANUFACTURER_ID_MICROSOFT = 6 // 0x0006 == 6 = Microsoft
const val MANUFACTURER_ID_SAMSUNG = 117 // 0x75 == 117 = Samsung
const val MANUFACTURER_ID_TYPO_PRODUCTS = 255 // 0xFF == 255 == Typo Products, LLC

}
}
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,9 @@ package de.simon.dankelmann.bluetoothlespam.Enums
enum class SpamPackageType {
UNKNOWN,
FAST_PAIRING,
CONTINUITY_DEVICE,
CONTINUITY_NEW_AIRTAG,
CONTINUITY_NEW_DEVICE,
CONTINUITY_NOT_YOUR_DEVICE,
CONTINUITY_ACTION_MODAL,
CONTINUITY_IOS_17_CRASH,
SWIFT_PAIRING,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,14 +2,21 @@ package de.simon.dankelmann.bluetoothlespam.Helpers

import android.bluetooth.le.ScanResult
import android.os.ParcelUuid
import android.util.Log
import de.simon.dankelmann.bluetoothlespam.AdvertisementSetGenerators.LovespousePlayAdvertisementSetGenerator
import de.simon.dankelmann.bluetoothlespam.AdvertisementSetGenerators.LovespouseStopAdvertisementSetGenerator
import de.simon.dankelmann.bluetoothlespam.Constants.Constants
import de.simon.dankelmann.bluetoothlespam.Enums.FlipperDeviceType
import de.simon.dankelmann.bluetoothlespam.Enums.SpamPackageType
import de.simon.dankelmann.bluetoothlespam.Helpers.StringHelpers.Companion.toHexString
import de.simon.dankelmann.bluetoothlespam.Models.BluetoothLeScanResult
import de.simon.dankelmann.bluetoothlespam.Models.FlipperDeviceScanResult
import de.simon.dankelmann.bluetoothlespam.Models.SpamPackageScanResult
import java.util.Locale

class BluetoothLeDeviceClassificationHelper {
companion object {
private const val _logTag = "BluetoothLeDeviceClassificationHelper"
fun isFlipperDevice(bluetoothLeScanResult: BluetoothLeScanResult):Boolean{
return getFlipperDeviceType(bluetoothLeScanResult) != FlipperDeviceType.UNKNOWN
}
Expand All @@ -33,23 +40,99 @@ class BluetoothLeDeviceClassificationHelper {
return FlipperDeviceType.UNKNOWN
}

fun getFlipperDeviceScanResult(scanResult: ScanResult):FlipperDeviceScanResult{
var flipperDeviceScanResult:FlipperDeviceScanResult = BluetoothLeScanResult.parseFromScanResult(scanResult) as FlipperDeviceScanResult

flipperDeviceScanResult.flipperDeviceType = getFlipperDeviceType(flipperDeviceScanResult)
return flipperDeviceScanResult
}

fun isSpamPackage(bluetoothLeScanResult: BluetoothLeScanResult):Boolean{
return getSpamPackageType(bluetoothLeScanResult) != SpamPackageType.UNKNOWN
}

fun getSpamPackageType(bluetoothLeScanResult: BluetoothLeScanResult):SpamPackageType{

// Fast Pairing
if(bluetoothLeScanResult.serviceUuids.contains(ParcelUuid.fromString("0000fe2c-0000-1000-8000-00805f9b34fb"))){
return SpamPackageType.FAST_PAIRING
}

return SpamPackageType.UNKNOWN
var spamPackageType = SpamPackageType.UNKNOWN

//Check if there is manufacturer specific data
if(bluetoothLeScanResult.manufacturerSpecificData.isNotEmpty()){
bluetoothLeScanResult.manufacturerSpecificData.forEach { manufacturerId, manufacturerData ->
val dataString = manufacturerData.toHexString()

// Detect Apple Spam
if(manufacturerId == Constants.MANUFACTURER_ID_APPLE){
// Check Apple Action Modal, Example: 0f05c027c72b7c OR iOs17 Crash, Example: 0f05c00d39f791000010720dab
if(dataString.lowercase(Locale.ROOT).startsWith("0f05c0") || dataString.lowercase(Locale.ROOT).startsWith("0f0540")){
if(dataString.contains("000010")){
spamPackageType = SpamPackageType.CONTINUITY_IOS_17_CRASH
} else {
spamPackageType = SpamPackageType.CONTINUITY_ACTION_MODAL
}
}

// NEW AIRTAG, Example: 07190500305546493200006af02720e126b2854197e6e2f07e75d0
if(dataString.lowercase(Locale.ROOT).startsWith("071905")){
spamPackageType = SpamPackageType.CONTINUITY_NEW_AIRTAG
}

// NEW DEVICE, Example: 0719070f205560144c00009775af597963b2ac9a29cafcc152f567
if(dataString.lowercase(Locale.ROOT).startsWith("071907")){
spamPackageType = SpamPackageType.CONTINUITY_NEW_DEVICE
}

// NOT YOUR DEVICE, Example: 0719010e20550850ca00007f34801a49cb0e3ca35be8ec17222a31
if(dataString.lowercase(Locale.ROOT).startsWith("071901")){
spamPackageType = SpamPackageType.CONTINUITY_NOT_YOUR_DEVICE
}
}

// Detect Swift Pairing Spam
if(manufacturerId == Constants.MANUFACTURER_ID_MICROSOFT){
if(dataString.lowercase(Locale.ROOT).startsWith("030080")){
spamPackageType = SpamPackageType.SWIFT_PAIRING
}
}

// Detect Easy Setup Spam
if(manufacturerId == Constants.MANUFACTURER_ID_SAMSUNG){
// Buds, Example:
if(dataString.lowercase(Locale.ROOT).startsWith("42098102141503210109".lowercase(Locale.ROOT)) && dataString.lowercase(Locale.ROOT).endsWith("063C948E00000000C700".lowercase(Locale.ROOT))){
spamPackageType = SpamPackageType.EASY_SETUP_BUDS
}

// Watch, Example: 010002000101ff0000431a
if(dataString.lowercase(Locale.ROOT).startsWith("010002000101FF000043".lowercase(Locale.ROOT))){
spamPackageType = SpamPackageType.EASY_SETUP_WATCH
}
}

// Detect Lovespouse Spam
if(manufacturerId == Constants.MANUFACTURER_ID_TYPO_PRODUCTS){
Log.d(_logTag, "LS Data: ${dataString}")
val prefix = "FFFF006DB643CE97FE427C".lowercase(Locale.ROOT)
val prefix2 = "6DB643CE97FE427C".lowercase(Locale.ROOT)
val appendix = "03038FAE".lowercase(Locale.ROOT)
if((dataString.lowercase(Locale.ROOT).startsWith(prefix) || dataString.lowercase(Locale.ROOT).startsWith(prefix2))){
val payload = dataString.replace(appendix, "").replace(prefix, "").replace(prefix2, "")

val stopGenerator = LovespouseStopAdvertisementSetGenerator()
stopGenerator.lovespouseStops.forEach{
if(it.key.lowercase() == payload.lowercase()){
spamPackageType = SpamPackageType.LOVESPOUSE_STOP
}
}

val playGenerator = LovespousePlayAdvertisementSetGenerator()
playGenerator.lovespousePlays.forEach{
if(it.key.lowercase() == payload.lowercase()){
spamPackageType = SpamPackageType.LOVESPOUSE_PLAY
}
}
}
}
}
}

return spamPackageType
}

}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,22 +4,40 @@ import android.Manifest
import android.bluetooth.le.ScanResult
import android.os.ParcelUuid
import android.util.Log
import androidx.core.util.forEach
import androidx.core.util.plus
import de.simon.dankelmann.bluetoothlespam.AppContext.AppContext
import de.simon.dankelmann.bluetoothlespam.Helpers.StringHelpers
import de.simon.dankelmann.bluetoothlespam.Helpers.StringHelpers.Companion.toHexString
import de.simon.dankelmann.bluetoothlespam.PermissionCheck.PermissionCheck
import java.sql.Time
import java.time.LocalDate
import java.time.LocalDateTime

open class BluetoothLeScanResult {
private val logTag = "BluetoothLeScanResult"
var deviceName = ""
var address = ""
var scanRecord = byteArrayOf()
var rssi = 0
var firstSeen = LocalDate.now()
var lastSeen = LocalDate.now()
var firstSeen = LocalDateTime.now()
var lastSeen = LocalDateTime.now()
var serviceUuids = mutableListOf<ParcelUuid>()
var manufacturerSpecificData = mutableMapOf<Int, ByteArray>()

fun parseFromBluetoothLeScanResult(bluetoothLeScanResult: BluetoothLeScanResult){
deviceName = bluetoothLeScanResult.deviceName
address = bluetoothLeScanResult.address
scanRecord = bluetoothLeScanResult.scanRecord
rssi = bluetoothLeScanResult.rssi
firstSeen = bluetoothLeScanResult.firstSeen
lastSeen = bluetoothLeScanResult.lastSeen
serviceUuids = bluetoothLeScanResult.serviceUuids
manufacturerSpecificData = bluetoothLeScanResult.manufacturerSpecificData
}

companion object {
private const val _logTag = "BluetoothLeScanResult"
fun parseFromScanResult(scanResult: ScanResult):BluetoothLeScanResult{
var model = BluetoothLeScanResult()

Expand All @@ -33,8 +51,18 @@ open class BluetoothLeScanResult {
model.serviceUuids.add(it)
}
}

// get manufacturer specific data
if(scanResult.scanRecord!!.manufacturerSpecificData != null){
val resultManufacturerSpecificData = scanResult.scanRecord!!.manufacturerSpecificData
resultManufacturerSpecificData.forEach{manufacurerId, data ->
//Log.d(_logTag, "ID: ${manufacurerId} Data: ${data.toHexString()}")
model.manufacturerSpecificData[manufacurerId] = data
}
}
}

// get mac address
model.address = scanResult.device.address

// get device data
Expand All @@ -44,8 +72,9 @@ open class BluetoothLeScanResult {
}
}

// get rssi
model.rssi = scanResult.rssi

return model
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,14 +12,7 @@ class FlipperDeviceScanResult: BluetoothLeScanResult() {
fun parseFromBluetoothLeScanResult(bluetoothLeScanResult: BluetoothLeScanResult):FlipperDeviceScanResult{
val flipperDeviceScanResult = FlipperDeviceScanResult()

flipperDeviceScanResult.deviceName = bluetoothLeScanResult.deviceName
flipperDeviceScanResult.address = bluetoothLeScanResult.address
flipperDeviceScanResult.scanRecord = bluetoothLeScanResult.scanRecord
flipperDeviceScanResult.rssi = bluetoothLeScanResult.rssi
flipperDeviceScanResult.firstSeen = bluetoothLeScanResult.firstSeen
flipperDeviceScanResult.lastSeen = bluetoothLeScanResult.lastSeen
flipperDeviceScanResult.serviceUuids = bluetoothLeScanResult.serviceUuids

flipperDeviceScanResult.parseFromBluetoothLeScanResult(bluetoothLeScanResult)
flipperDeviceScanResult.flipperDeviceType = BluetoothLeDeviceClassificationHelper.getFlipperDeviceType(bluetoothLeScanResult)

return flipperDeviceScanResult
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,14 +13,7 @@ class SpamPackageScanResult: BluetoothLeScanResult() {
fun parseFromBluetoothLeScanResult(bluetoothLeScanResult: BluetoothLeScanResult):SpamPackageScanResult{
val spamPackageScanResult = SpamPackageScanResult()

spamPackageScanResult.deviceName = bluetoothLeScanResult.deviceName
spamPackageScanResult.address = bluetoothLeScanResult.address
spamPackageScanResult.scanRecord = bluetoothLeScanResult.scanRecord
spamPackageScanResult.rssi = bluetoothLeScanResult.rssi
spamPackageScanResult.firstSeen = bluetoothLeScanResult.firstSeen
spamPackageScanResult.lastSeen = bluetoothLeScanResult.lastSeen
spamPackageScanResult.serviceUuids = bluetoothLeScanResult.serviceUuids

spamPackageScanResult.parseFromBluetoothLeScanResult(bluetoothLeScanResult)
spamPackageScanResult.spamPackageType = BluetoothLeDeviceClassificationHelper.getSpamPackageType(bluetoothLeScanResult)

return spamPackageScanResult
Expand Down
Loading

0 comments on commit 56c1244

Please sign in to comment.