Многопоточность Swift. Запуск функции с параметрами
Имеется функция для подбора пароля, состоящего из 5 букв английского алфавита. На вход подается хэш (по нему будет идти проверка), индекс первой буквы для старта перебора, а также интервал перебора.
func bruteforce (hash: String, startIndex: Int, intervalLength: Int) {
var index = startIndex // Индекс буквы алфавита
var resultArray: [Character] = ["0", "0", "0", "0", "0"] // Массив из 5 символов
var resultString = "" // Строка для перебора
var finalResult = "" // Результат
var groundLetter = alphabet[index]
while (index < startIndex + Int(intervalLength)) {
groundLetter = alphabet[index]
resultArray[0] = groundLetter
for firstLetter in alphabet {
resultArray[1] = firstLetter
for secondLetter in alphabet {
resultArray[2] = secondLetter
for thirdLetter in alphabet {
resultArray[3] = thirdLetter
for fourthLetter in alphabet {
resultArray[4] = fourthLetter
resultString = String(resultArray)
if (resultString.sha256() == hash) {
finalResult = resultString
print(finalResult)
} else {
print(resultString, " - failed")
}
index += 1
}
}
}
}
}
passwords.append(finalResult)
}
Так, например bruteforse (hash: "hash", startIndex: 0, interval: 3) переберет все комбинации от aaaaa до czzzz. Метод, конечно, топорный, но работает.
Передо мной стоит задача реализовать многопоточность для данного перебора. То есть пользователь вводит число потоков (от 1 до 26) и вышеописанная функция запускается внутри каждого из потоков с нужными параметрами. Вот так я это реализовал:
func multiThread (threads: Int, hash: String) {
let intervalLength = Int(ceil(26/Double(threads)))
var startIndexes = [Int]() // Стартовые индексы обхода
var index = 0
while (index < 25) {
startIndexes.append(index)
index += Int(intervalLength)
}
for i in startIndexes {
DispatchQueue.global().async {
bruteforce(hash: hash, startIndex: i, intervalLength: intervalLength)
}
}
}
Но ничего не работает - на выход программа ничего не выдает. Помогите, пожалуйста, разобраться, в чем проблема.
UPD. Оставшийся код:
import CommonCrypto
extension Data{
public func sha256() -> String{
return hexStringFromData(input: digest(input: self as NSData))
}
private func digest(input : NSData) -> NSData {
let digestLength = Int(CC_SHA256_DIGEST_LENGTH)
var hash = [UInt8](repeating: 0, count: digestLength)
CC_SHA256(input.bytes, UInt32(input.length), &hash)
return NSData(bytes: hash, length: digestLength)
}
private func hexStringFromData(input: NSData) -> String {
var bytes = [UInt8](repeating: 0, count: input.length)
input.getBytes(&bytes, length: input.length)
var hexString = ""
for byte in bytes {
hexString += String(format:"%02x", UInt8(byte))
}
return hexString
}
}
public extension String {
func sha256() -> String{
if let stringData = self.data(using: String.Encoding.utf8) {
return stringData.sha256()
}
return ""
}
}
var alphabet: [Character] = ["a", "b", "c", "d", "e", "f", "g", "h", "i", "j", "k", "l", "m", "n", "o", "p", "q", "r", "s", "t", "u", "v", "w", "x", "y", "z"]
Пример вызова функции:
multiThread(threads: 3, hash: "3a7bd3e2360a3d29eea436fcfb7e44c735d117c42d1c1835420b6b9942dd4f1b")
Ответы (1 шт):
Наверно вам нужно уточнить, что такое выход :) в фразе "на выход ничего не выдает". У меня, если убрать вывод всех неверных комбинаций (наверно это ускоряет перебор), то в итоге слово apple нормально подбирается (в пределах минуты). Только у вас, если подбор успешен, то метод в этом потоке закрывается, а другие два потока продолжают молотить дальше до упора, получается впустую. Наверно это не нужно и надо как-то прервать их выполнение. Если не трогать сами потоки (хотя можно наверно как-то и их прервать) можно, например, ввести булевый флаг "удачи", который, при успехе в одном из потоков, выставляется в ТРУ, и методы, работающие в других потоках, проверяя его, могут прервать свое выполнение. Еще не понимаю, зачем для результата нужен массив, а не просто переменная, но это уже ваше дело :) Я тут немного добавил в код. Подписал всё, что добавил каментами (ваши удалил, чтобы не запутаться). Только один момент, ваш код вставлен в проект для айфона, мне просто так проще :))) Поэтому там этот класс ViewController и вызов вашего метода из viewDidLoad. Надеюсь вас это не смутит :))) Всё-равно все результаты выводятся в консоль.
import UIKit
import CommonCrypto
class ViewController: UIViewController {
var alphabet: [Character] = ["a", "b", "c", "d", "e", "f", "g", "h", "i", "j", "k", "l", "m", "n", "o", "p", "q", "r", "s", "t", "u", "v", "w", "x", "y", "z"]
// Переменная для прерывания неуспешных методов в других потоках
var bingo = false
// Наблюдатель за изменением массива.
var passwords: [String] = [] {
// Когда значение массива изменяется выполняется код внутри наблюдателя
didSet {
if passwords.count != 0 {
print("Pass:", passwords)
}
}
}
override func viewDidLoad() {
super.viewDidLoad()
// Ваш вызов
multiThread(threads: 3, hash: "3a7bd3e2360a3d29eea436fcfb7e44c735d117c42d1c1835420b6b9942dd4f1b")
}
func bruteforce (hash: String, startIndex: Int, intervalLength: Int) {
let now = Date() // Переменная для замера времени подбора
var index = startIndex
var resultArray: [Character] = ["0", "0", "0", "0", "0"]
var resultString = ""
var finalResult = ""
var groundLetter = alphabet[index]
while (index < startIndex + Int(intervalLength)) {
groundLetter = alphabet[index]
resultArray[0] = groundLetter
for firstLetter in alphabet {
print(String(resultArray)) // Более щадящий вывод в консоль для слежения за происходящим
if bingo { return } // Проверка и выход из метода, если подбор в другом потоке уже получился, то есть если bingo == true
resultArray[1] = firstLetter
for secondLetter in alphabet {
resultArray[2] = secondLetter
for thirdLetter in alphabet {
resultArray[3] = thirdLetter
for fourthLetter in alphabet {
resultArray[4] = fourthLetter
resultString = String(resultArray)
if (resultString.sha256() == hash) {
finalResult = resultString
print("finalResult:", finalResult, ", in: ", -Int(now.timeIntervalSinceNow), " seconds") // Вывод результата и продолжительности времени подбора
bingo = true // Установка флага успешного подбора
passwords.append(finalResult)
return // Выход из метода
} else {
//print(resultString, " - failed")
}
index += 1
}
}
}
}
}
}
func multiThread (threads: Int, hash: String) {
let intervalLength = Int(ceil(26/Double(threads)))
var startIndexes = [Int]()
var index = 0
while (index < 25) {
startIndexes.append(index)
index += Int(intervalLength)
}
for i in startIndexes {
DispatchQueue.global().async {
self.bruteforce(hash: hash, startIndex: i, intervalLength: intervalLength)
}
}
}
}
extension Data{
public func sha256() -> String{
return hexStringFromData(input: digest(input: self as NSData))
}
private func digest(input : NSData) -> NSData {
let digestLength = Int(CC_SHA256_DIGEST_LENGTH)
var hash = [UInt8](repeating: 0, count: digestLength)
CC_SHA256(input.bytes, UInt32(input.length), &hash)
return NSData(bytes: hash, length: digestLength)
}
private func hexStringFromData(input: NSData) -> String {
var bytes = [UInt8](repeating: 0, count: input.length)
input.getBytes(&bytes, length: input.length)
var hexString = ""
for byte in bytes {
hexString += String(format:"%02x", UInt8(byte))
}
return hexString
}
}
public extension String {
func sha256() -> String{
if let stringData = self.data(using: .utf8) {
return stringData.sha256()
}
return ""
}
}
Вывод консоли (для трех потоков):
a0000
j0000
s0000
sazzz
jazzz
aazzz
sbzzz
jbzzz
abzzz
jczzz
sczzz
aczzz
jdzzz
sdzzz
adzzz
jezzz
sezzz
aezzz
sfzzz
jfzzz
afzzz
sgzzz
jgzzz
agzzz
shzzz
jhzzz
ahzzz
sizzz
jizzz
aizzz
sjzzz
jjzzz
ajzzz
skzzz
jkzzz
akzzz
slzzz
jlzzz
alzzz
smzzz
jmzzz
amzzz
snzzz
jnzzz
anzzz
sozzz
jozzz
aozzz
finalResult: apple , in: 43 seconds
Pass: ["apple"]
spzzz
jpzzz