Форум программистов, компьютерный форум, киберфорум
C# для начинающих
Войти
Регистрация
Восстановить пароль
Блоги Сообщество Поиск  
 
 
Рейтинг 4.52/25: Рейтинг темы: голосов - 25, средняя оценка - 4.52
2 / 2 / 0
Регистрация: 28.05.2020
Сообщений: 58

Перебор вариантов

29.05.2020, 11:26. Показов 6044. Ответов 72

Студворк — интернет-сервис помощи студентам
Здравствуйте, нужна помощь новичку (мне).
Есть набор k букв, каждой букве может соответствовать несколько значений, например:
А: 1,2,7
Б: 3,6
В: 4
Г: 5
Все значения разные.
Вопрос: как организовать цикл, чтобы на каждой итерации выдавался набор значений по одному от каждой буквы? То есть в этом примере по 4 значения. Чтобы в итоге перебрались все варианты. Следует сказать, что букв может быть до 33, количество значений не ограничено.
0
Лучшие ответы (1)
IT_Exp
Эксперт
34794 / 4073 / 2104
Регистрация: 17.06.2006
Сообщений: 32,602
Блог
29.05.2020, 11:26
Ответы с готовыми решениями:

перебор вариантов...
имеется строка, содержащая слово. (например слово "лампа") Как можно перебрать всевозможные сочитания букв этого слова? разбил слово на...

Перебор всех вариантов
Всем привет! Это моя первая проблема, с которой хотелось бы поделиться. Проблема сложная, но хотелось бы верить, что получится найти ее...

Перебор вариантов из строки
Здравствуйте! Задачка такая. Есть строка с текстом "Ананас; Абрикос; Банан". Требуется, что бы программа выбирала эти элементы...

72
 Аватар для samana
2639 / 1567 / 853
Регистрация: 23.02.2019
Сообщений: 3,876
30.05.2020, 21:48
Студворк — интернет-сервис помощи студентам
shabserg, Я уже столько раз перечитываю это всё, не до конца осознаю задание, но мне кажется что на самом деле всё может быть гораздо проще.
Позвольте я переспрошу у вас задание в виде шагов на простом примере, чтобы убедится в том, что я правильно понимаю о чём вообще речь?

Например пользователь придумал пароль слово - кот.

Затем пользователь придумывает и записывает некое правило для шифровки своего пароля, где для некоторых (или всех) букв из своего пароля - предлагает замену этих букв на некие другие буквы, например:
к к к т т
й ц в п и


и это означает, что буква к может стать одной из й ц в
а буква т может стать одной из п и

предположим, что программа выбрала конкретные замены из предложенных вариантов и
к заменило на ц
т заменило на и

в итоге оригинальный пароль кот превратился в цои.

Затем программа выводит хеш слова цои и передаёт его пользователю, а оригинальный пароль слово кот, добавляет в обычный массив слов где-то у себя в базе.

Пользователь без труда запоминает длиннющий хеш зашифрованного пароля и так же запоминает шифр для букв, то-есть й ц в п и

проходит несколько лет...

Появляется пользователь и хочет проверить - а какой пароль у него тогда был? Сам пароль забыл, но вот неких хеш и шифр й ц в п и был записан на бумажке, поэтому он вводит обе эти строки в нужные поля.

А программа внутри себя переводит хеш обратно в слово цои и с помощью шифра й ц в п и начинает перебирать все сохранённые у себя слова-пароли, пытаясь найти то самое начальное слово кот?
1
2 / 2 / 0
Регистрация: 28.05.2020
Сообщений: 58
30.05.2020, 22:20  [ТС]
samana, ну почти так . Дело в том, что все окно формы состоит из двух частей. В первой части пользователь вводит свой пароль, то есть "цои", нажимает кнопку "вычислить", и ему выдаётся хэш этого пароля, который нужно будет скопировать в часть 2. 1-я часть сделана и работает, нужно решить часть 2.
Во второй части программы я (в качестве хакера) должен отгадать, какое слово из словаря он взял за основу пароля. Мне известен хэш пароля, правило замены (две строки что на что может меняться), а также есть словарь для перебора слов, загаданное слово точно в нем содержится.
В итоге программа должна выдать в 2 текстбокса исходное слово (кот) и пароль (цои). Так что можно сказать, вы поняли все очень точно и объяснили лучше меня
Позвольте я переспрошу у вас задание в виде шагов на простом примере, чтобы убедится в том, что я правильно понимаю о чём вообще речь?
Но хочу добавить, что в заменах могут попасться буквы, которых в исходном слове нет
к к к т т
й ц в п и
То есть могло быть
к к к т т ш
й ц в п и з.

а оригинальный пароль слово кот, добавляет в обычный массив слов где-то у себя в базе.
- оригинальный пароль нам по идее неизвестен (и программе), он должен быть найден подбором по словарю и выведен. На фото окно 2-й части программы, чтобы видно было, какими данными обладает программа на входе.
Миниатюры
Перебор вариантов  
0
2 / 2 / 0
Регистрация: 28.05.2020
Сообщений: 58
30.05.2020, 22:36  [ТС]
Ещё может быть замена
а а а б р
у и б р у
То есть в итоге исходная буква а будет заменена на б, затем б на р, а р на у. То есть замены должны происходить последовательно, а не все сразу
0
 Аватар для samana
2639 / 1567 / 853
Регистрация: 23.02.2019
Сообщений: 3,876
30.05.2020, 23:02
Цитата Сообщение от shabserg Посмотреть сообщение
В первой части пользователь вводит свой пароль, то есть "цои", нажимает кнопку "вычислить"
А разве на первом этапе пользователь не задаёт предполагаемые замены?
к к к т т
й ц в п и
после которых и должно появится "цои" с хешем?
--> Если да вводит на первом этапе, то получается на втором этапе нужно снова ввести эти
к к к т т
й ц в п и
чтобы по ним уже пытаться расшифровать слово и найти подходящее "кот"?
1
2 / 2 / 0
Регистрация: 28.05.2020
Сообщений: 58
31.05.2020, 08:30  [ТС]
samana, я тоже так думал, когда делал эту прогу в первый раз. Оказалось, что нужно не так.
На первом этапе всего одно поле для ввода - поле для "цои" и одно поле для выдачи хэша. То есть этот пароль выдаётся преподавателем, который его как-то сам зашифровал.
В итоге:
1-я часть:
Ввести "цои"
Вывести хэш этого пароля
2-я часть:
Ввести хэш, вычисленный выше (просто мышкой скопировать в это поле)
Ввести правило замен (две строки)
Вывести "цои"
Вывести исходное слово "кот"

Преподавателем сообщается "цои",
к к к т т
й ц в п и
И возможно исходное слово "кот" для проверки результата на правильность, но оно в проге нигде не вводится
0
 Аватар для samana
2639 / 1567 / 853
Регистрация: 23.02.2019
Сообщений: 3,876
31.05.2020, 09:12
shabserg, да, теперь вроде всё задание стало понятно для меня, спасибо.
Смущает лишь тот момент, когда вы говорили, что пользователь не обязан ввести для каждой буквы замену, то-есть может некоторые пропустить, и что самое поразительное - может добавить буквы замены которых и нет в слове.. И что же тогда делать, если загадано слово "кот", а для замен предоставили вообще левые данные, в которых только лишние буквы, например
аалл
диеу
И как по этим данным добраться до "кот"? Получается что нужно каждое слово из словаря проверять на равенство их хеша и хеша полученного на первом этапе?
1
2 / 2 / 0
Регистрация: 28.05.2020
Сообщений: 58
31.05.2020, 10:10  [ТС]
samana, ну это я вам привел теоретический момент, просто чтобы не вылетало никаких ошибок, если в списке замен появятся буквы, которых нет в слове.
Конечно, могут заменяться не все буквы.
И что же тогда делать, если загадано слово "кот", а для замен предоставили вообще левые данные, в которых только лишние буквы, например
аалл
диеу
- в этом случае возможен только один вариант: в качестве пароля преподаватель дает само слово из словаря (кот), т.е. замен никаких в нем не проводилось. В этой ситуации слово и пароль совпадают. Но такого при проверке программы, конечно, не будет. Нам выдадут пароль, который появился действительно после замен, и возможные замены нам сообщат.
Если же, например, дают совершенно левые замены, по которым нельзя прийти из любого слова словаря к данному паролю, то прога перебирает все варианты и в итоге говорит, что не нашла пароль.

Добавлено через 28 минут
Конечно, могут заменяться не все буквы.
(не все буквы в слове) кот -> цой
0
 Аватар для samana
2639 / 1567 / 853
Регистрация: 23.02.2019
Сообщений: 3,876
31.05.2020, 18:21
shabserg, Что-то у меня не получилось сделать по всем вашим правилам. А именно - позволить пропускать буквы замен или добавлять лишние.. Даже не могу придумать как обрабатывать такие данные.

Но если для каждой буквы в пароле предоставить замены, то вроде всё находит правильно.
C#
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
static void Main()
{   
    // это внутренний словарь всех слов
    string[] allWords = { "кот", "кто", "парк", "диван" };
 
    // преподаватель загадал это слово и
    string original = "кот";
 
    // придумал к нему правила замен
    string ruleUp =     "кккооттттт";
    string ruleDown =   "дА4ьыхЦсыФ";
 
    // после замены, допустим что слово "кот" превратилось в
    string outPass = "4ыы";
 
    // потом получили хеш зашифрованного слова
    int outPassHash = outPass.GetHashCode();
 
 
    // следующий метод симулирует вторую часть вашего проекта
    // и возвращает массив слов из словаря, которые теоретически подходят под зашифрованый хеш
 
    // метод принимает данные, указанные в вашей второй части задания:
    // то-есть есть хеш зашифрованного пароля, наборы правил замены.
    // а так же передаём словарь, чтобы потом с ними сверятся.
 
    // последний параметр опционален, чисто для тестов, он вкл/выкл вывод в консоль этапы работы метода.
    List<string> results = PasswordFinder.getOriginalPass(outPassHash, ruleUp, ruleDown, allWords, true);
}
в консоли будет такая информация (если нужно её можно отключить)
Кликните здесь для просмотра всего текста
Code
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
Правило замены символов:
к: д, А, 4
о: ь, ы
т: х, Ц, с, ы, Ф
 
Все комбинации перебора символов:
дьх
дьЦ
дьс
дьы
дьФ
дых
дыЦ
дыс
дыы
дыФ
Аьх
АьЦ
Аьс
Аьы
АьФ
Аых
АыЦ
Аыс
Аыы
АыФ
4ьх
4ьЦ
4ьс
4ьы
4ьФ
4ых
4ыЦ
4ыс
4ыы - этот хеш совпадает с зашифрованным паролем!
4ыФ
 
Подходящие слова из словаря:
кот
кто


Я всё оформил в отдельный статический класс PasswordFinder, у которого только один предоставленный публичный метод getOriginalPass, остальные методы этого класса нужны для внутренней его работы.

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

Весь код консольной программы ниже:
Кликните здесь для просмотра всего текста
C#
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
using System;
using System.Collections.Generic;
 
namespace CyberForum
{
    class Program
    {
        static void Main()
        {
            // это внутренний словарь всех слов
            string[] allWords = { "кот", "кто", "парк", "диван" };
 
            // преподаватель загадал это слово и
            string original = "кот";
 
            // придумал к нему правила замен
            string ruleUp = "кккооттттт";
            string ruleDown = "дА4ьыхЦсыФ";
 
            // после замены, допустим что слово "кот" превратилось в
            string outPass = "4ыы";
 
            // потом получили хеш зашифрованного слова
            int outPassHash = outPass.GetHashCode();
 
 
            // следующий метод симулирует вторую часть вашего проекта
            // и возвращает массив слов из словаря, которые теоретически подходят под зашифрованый хеш
 
            // метод принимает данные, указанные в вашей второй части задания:
            // то-есть есть хеш зашифрованного пароля, наборы правил замены.
            // а так же передаём словарь, чтобы потом с ними сверятся.
 
            // последний параметр опционален, чисто для тестов, он вкл/выкл вывод в консоль этапы работы метода.
            List<string> results = PasswordFinder.getOriginalPass(outPassHash, ruleUp, ruleDown, allWords, true);
        }
 
    }
 
    public static class PasswordFinder
    {
        public static List<string> getOriginalPass(int outPassHash, string ruleUp, string ruleDown, string[] allWords, bool writeProcessInfo = false)
        {
 
            var charsMap = getRaplacementMap(ruleUp, ruleDown);
 
            // просто вывод в консоль
            if (writeProcessInfo)
            {
                Console.WriteLine("Правило замены символов:");
                foreach (var item in charsMap)
                {
                    Console.WriteLine(item.Key + ": " + string.Join(", ", item.Value));
                }
                Console.WriteLine();
            }
 
            //------------------------------------------------
            //------------------------------------------------
 
            var allCominations = getAllValueCombinations(charsMap);
            string findedOutPass = "";
 
            foreach (string s in allCominations)
            {
                if (s.GetHashCode().Equals(outPassHash))
                {
                    findedOutPass = s;
                }
            }
 
            // просто вывод в консоль
            if (writeProcessInfo)
            {
                Console.WriteLine("Все комбинации перебора символов:");
                foreach (string s in allCominations)
                {
                    Console.Write(s);
                    if (s.GetHashCode().Equals(outPassHash))
                    {
                        Console.Write($" - этот хеш совпадает с зашифрованным паролем!");
                    }
                    Console.WriteLine();
                }
                Console.WriteLine();
            }
 
            //------------------------------------------------
            //------------------------------------------------
 
            List<string> suitableWords = new List<string>();
 
            for (int i = 0; i < allWords.Length; i++)
            {
                string currentWord = allWords[i];
                bool wordSuit = true;
 
                for (int j = 0; j < currentWord.Length; j++)
                {
                    char wordChar = currentWord[j];
 
                    if ((charsMap.ContainsKey(wordChar) && charsMap[wordChar].Contains(findedOutPass[j])) == false)
                    {
                        wordSuit = false;
                        break;
                    }
 
                }
 
                if (wordSuit) suitableWords.Add(currentWord);
            }
 
            // просто вывод в консоль
            if (writeProcessInfo)
            {
                Console.WriteLine("Подходящие слова из словаря:");
                if (suitableWords.Count > 0)
                    Console.WriteLine(string.Join(Environment.NewLine, suitableWords));
                else
                    Console.WriteLine("не найдены...");
 
                Console.WriteLine();
            }
 
            return suitableWords;
        }
 
        private static List<string> getAllValueCombinations(Dictionary<char, List<char>> map)
        {
            List<List<char>> rows = new List<List<char>>();
            foreach (var row in map.Values)
            {
                rows.Add(row);
            }
 
            List<string> combinations = new List<string>();
            foo(rows, 0, new char[rows.Count]);
 
            // рекурсивный перебор для нахождения всех вариантов комбинаций
            void foo(List<List<char>> charRows, int row, char[] results)
            {
                for (int i = 0; i < charRows[row].Count; i++)
                {
                    results[row] = charRows[row][i];
                    if (row == charRows.Count - 1)
                    {
                        combinations.Add(string.Join("", results));
                    }
                    else
                    {
                        foo(charRows, row + 1, results);
                    }
                }
            }
 
            return combinations;
        }
 
 
        //source1 = ккоотт
        //source2 = фывапр
 
        //на выходе словарь:
        //key(char)     value(List<char>)
 
        //к             фы
        //о             ва
        //т             пр
        private static Dictionary<char, List<char>> getRaplacementMap(string source1, string source2)
        {
            if (source1.Length != source2.Length)
            {
                throw new Exception("Длины слов не совпадают, или равны нулю.");
            }
 
            Dictionary<char, List<char>> lettersMap = new Dictionary<char, List<char>>();
 
            for (int i = 0; i < source1.Length; i++)
            {
                char c = source1[i];
 
                if (lettersMap.ContainsKey(c) == false)
                {
                    lettersMap.Add(c, new List<char>());
                }
 
                lettersMap[c].Add(source2[i]);
            }
 
            return lettersMap;
        }
    }
}
1
2 / 2 / 0
Регистрация: 28.05.2020
Сообщений: 58
31.05.2020, 20:12  [ТС]
samana, по-моему, здорово получилось, оно работает и это уже многого стоит. Но можете помочь тупому как быть, если у меня это должно быть в Form1 в Windows Forms?) Извините, что разместил эту тему не в том разделе, ведь здесь консольные приложения, а не формс, но на мою тему в том разделе никто не отвечал и я попробовал тут.
В форме две функциональные кнопки: первая выводит хэш пароля в части 1, а вторая отгадывает зашифрованное слово (button 1 и button 2).
У меня функция, отвечающая за действия при нажатии на кнопку private void button2_Click(object sender, EventArgs e) находится в файле form1.cs, а static void Main() в другом (program.cs).
И если я помещаю ваш код, который должен быть в main, в button2_Click (эта кнопка отвечает за поиск слова), то он не видит многие переменные за ее пределами.
Что куда надо поставить правильно? Помогите поправить, пожалуйста
Вот содержимое файла form1.cs сейчас
C#
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
using System.Security.Cryptography;
using System.IO;
using System.Diagnostics;
 
namespace Lab1CSapp
{
    public partial class Form1 : Form
    {
        public Form1()
        {
            InitializeComponent();
        }
        private void button1_Click(object sender, EventArgs e)
        {
            string password = null;
            
            if (password_box.Text.Length != 0)
            { password = password_box.Text; }
 
                using (MD5 md5Hash = MD5.Create())
                {
                    string hash = GetMd5Hash(md5Hash, password);
                    hash_of_password1_box.Text = (hash).ToString();
 
 
                }
                string GetMd5Hash(MD5 md5Hash, string input)
                {
                    // Convert the input string to a byte array and compute the hash.
                    byte[] data = md5Hash.ComputeHash(Encoding.UTF8.GetBytes(input));
                    // Create a new Stringbuilder to collect the bytes
                    // and create a string.
                    StringBuilder sBuilder = new StringBuilder();
                    // Loop through each byte of the hashed data 
                    // and format each one as a hexadecimal string.
                    for (int i = 0; i < data.Length; i++)
                    {
                        sBuilder.Append(data[i].ToString("x2"));
                    }
                    // Return the hexadecimal string.
                    return sBuilder.ToString();
                }
            }
        }
        
        private static Dictionary<char, List<int>> createCharMap(string source)
        {
            Dictionary<char, List<int>> lettersMap = new Dictionary<char, List<int>>();
 
            for (int i = 0; i < source.Length; i++)
            {
                char c = source[i];
 
                if (lettersMap.ContainsKey(c) == false)
                {
                    lettersMap.Add(c, new List<int>());
                }
 
                lettersMap[c].Add(i);
            }
 
            return lettersMap;
        }
 
        private void button2_Click(object sender, EventArgs e)
        {
            string source_letters2 = null;
            string final_letters2 = null;
            string hash_of_password2 = null;
            if (source_letters_box2.Text.Length != 0)
            { source_letters2 = source_letters_box2.Text; }
            if (final_letters_box2.Text.Length != 0)
            { final_letters2 = final_letters_box2.Text; }
            if (hash_of_password2_box.Text.Length != 0)
            { hash_of_password2 = hash_of_password2_box.Text; }
            string password_with_change;
            int found = 0;
            string path = @"dictionary.txt";
            string[] words = File.ReadAllLines(path);
            
           
        }
        static void Main()
        {
            // это внутренний словарь всех слов
            string[] allWords = { "кот", "кто", "парк", "диван" };
 
            // преподаватель загадал это слово и
            string original = "кот";
 
            // придумал к нему правила замен
            string ruleUp = "кккооттттт";
            string ruleDown = "дА4ьыхЦсыФ";
 
            // после замены, допустим что слово "кот" превратилось в
            string outPass = "4ыы";
 
            // потом получили хеш зашифрованного слова
            int outPassHash = outPass.GetHashCode();
 
 
            // следующий метод симулирует вторую часть вашего проекта
            // и возвращает массив слов из словаря, которые теоретически подходят под зашифрованый хеш
 
            // метод принимает данные, указанные в вашей второй части задания:
            // то-есть есть хеш зашифрованного пароля, наборы правил замены.
            // а так же передаём словарь, чтобы потом с ними сверятся.
 
            // последний параметр опционален, чисто для тестов, он вкл/выкл вывод в консоль этапы работы метода.
            List<string> results = PasswordFinder.getOriginalPass(outPassHash, ruleUp, ruleDown, allWords, true);
        }
 
        private void button3_Click(object sender, EventArgs e)
        {
            Process.Start(@"dictionary.txt");
        }
    }
    public static class PasswordFinder
    {
        public static List<string> getOriginalPass(int outPassHash, string ruleUp, string ruleDown, string[] allWords, bool writeProcessInfo = false)
        {
            var charsMap = getRaplacementMap(ruleUp, ruleDown);
 
            // просто вывод в консоль
            //if (writeProcessInfo)
            //{
            //    Console.WriteLine("Правило замены символов:");
            //    foreach (var item in charsMap)
            //    {
            //        Console.WriteLine(item.Key + ": " + string.Join(", ", item.Value));
            //    }
            //    Console.WriteLine();
            //}
 
            //------------------------------------------------
            //------------------------------------------------
 
            var allCominations = getAllValueCombinations(charsMap);
            string findedOutPass = "";
 
            foreach (string s in allCominations)
            {
                if (s.GetHashCode().Equals(outPassHash))
                {
                    findedOutPass = s;
                }
            }
 
            // просто вывод в консоль
            if (writeProcessInfo)
            {
                Console.WriteLine("Все комбинации перебора символов:");
                foreach (string s in allCominations)
                {
                    Console.Write(s);
                    if (s.GetHashCode().Equals(outPassHash))
                    {
                        Console.Write($" - этот хеш совпадает с зашифрованным паролем!");
                    }
                    Console.WriteLine();
                }
                Console.WriteLine();
            }
 
            //------------------------------------------------
            //------------------------------------------------
 
            List<string> suitableWords = new List<string>();
 
            for (int i = 0; i < allWords.Length; i++)
            {
                string currentWord = allWords[i];
                bool wordSuit = true;
 
                for (int j = 0; j < currentWord.Length; j++)
                {
                    char wordChar = currentWord[j];
 
                    if ((charsMap.ContainsKey(wordChar) && charsMap[wordChar].Contains(findedOutPass[j])) == false)
                    {
                        wordSuit = false;
                        break;
                    }
 
                }
 
                if (wordSuit) suitableWords.Add(currentWord);
            }
 
            // просто вывод в консоль
            if (writeProcessInfo)
            {
                Console.WriteLine("Подходящие слова из словаря:");
                if (suitableWords.Count > 0)
                { Console.WriteLine(string.Join(Environment.NewLine, suitableWords)); }//////////////////////////
            else
                Console.WriteLine("не найдены...");
 
                Console.WriteLine();
            }
 
            return suitableWords;
        }
 
        private static List<string> getAllValueCombinations(Dictionary<char, List<char>> map)
        {
            List<List<char>> rows = new List<List<char>>();
            foreach (var row in map.Values)
            {
                rows.Add(row);
            }
 
            List<string> combinations = new List<string>();
            foo(rows, 0, new char[rows.Count]);
 
            // рекурсивный перебор для нахождения всех вариантов комбинаций
            void foo(List<List<char>> charRows, int row, char[] results)
            {
                for (int i = 0; i < charRows[row].Count; i++)
                {
                    results[row] = charRows[row][i];
                    if (row == charRows.Count - 1)
                    {
                        combinations.Add(string.Join("", results));
                    }
                    else
                    {
                        foo(charRows, row + 1, results);
                    }
                }
            }
 
            return combinations;
        }
 
 
        //source1 = ккоотт
        //source2 = фывапр
 
        //на выходе словарь:
        //key(char)     value(List<char>)
 
        //к             фы
        //о             ва
        //т             пр
        private static Dictionary<char, List<char>> getRaplacementMap(string source1, string source2)
        {
            if (source1.Length != source2.Length)
            {
                throw new Exception("Длины слов не совпадают, или равны нулю.");
            }
 
            Dictionary<char, List<char>> lettersMap = new Dictionary<char, List<char>>();
 
            for (int i = 0; i < source1.Length; i++)
            {
                char c = source1[i];
 
                if (lettersMap.ContainsKey(c) == false)
                {
                    lettersMap.Add(c, new List<char>());
                }
 
                lettersMap[c].Add(source2[i]);
            }
 
            return lettersMap;
        }
    }
}
А вот содержимое файла program.cs, где есть настоящая main(). Этот файл создался автоматически.
C#
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using System.Windows.Forms;
 
namespace Lab1CSapp
{
    static class Program
    {
        /// <summary>
        /// Главная точка входа для приложения.
        /// </summary>
        [STAThread]
        static void Main()
        {
            Application.EnableVisualStyles();
            Application.SetCompatibleTextRenderingDefault(false);
            Application.Run(new Form1());
        }
    }
}
И я понял, что в вашем коде подходящих слов может быть несколько. Как сделать, чтобы подходящий пароль и исходные слова выводились в текстбоксы для них?
Вот как я раньше у себя делал, например
C#
1
 password_box.Text = password;
и так же надо отгаданного слова (слов), лучше через запятые
Знаю, что уже надоел вам со своими допросами, спасибо, что помогаете(
0
2 / 2 / 0
Регистрация: 28.05.2020
Сообщений: 58
31.05.2020, 20:16  [ТС]
samana, вот так выглядит все окно формы, вверху файлы проекта
Миниатюры
Перебор вариантов  
0
 Аватар для samana
2639 / 1567 / 853
Регистрация: 23.02.2019
Сообщений: 3,876
31.05.2020, 20:27
shabserg, я так сходу не смогу подсказать, это нужно сесть за компьютер, чтобы ответить наиболее точно. Поэтому скорее всего смогу вернуться к вашему вопросу немного позднее.
1
2 / 2 / 0
Регистрация: 28.05.2020
Сообщений: 58
31.05.2020, 20:55  [ТС]
samana, нет проблем, как вам удобно
0
 Аватар для samana
2639 / 1567 / 853
Регистрация: 23.02.2019
Сообщений: 3,876
01.06.2020, 00:35
shabserg, Так как я точно не знаю все имена ваших элементов на форме, а вашего исходника у меня нет, то пришлось воссоздать форму заново у себя, чтобы потестить. Она почти такая же как на скриншоте, только цвета обычные, но дизайн вы можете уже изменить как угодно.
Внедрил текущий результат всех этих вычислений, но обнаружил ошибку, что если в слове (то, которое в словаре) есть повторяющиеся буквы (например "молоко"), то выдаёт ошибку при вычислениях.
Вообще мне моя текущая реализация кода совсем не нравится, слишком тяжёлая для восприятия как глазами, так и мозгами. Думаю нужно идти каким-то другим вариантом.

Но свой текущий исходник для винформс я вам оставлю, вдруг что-то пригодится из него WindowsFormsApp1.zip
1
2 / 2 / 0
Регистрация: 28.05.2020
Сообщений: 58
01.06.2020, 12:09  [ТС]
samana, да, действительно, сейчас посмотрел, он не может такие слова расшифровывать, пишет выход за пределы массива(
Но преподавателем выдаются именно такие, например "абракадабра". И в слове не обязательно все буквы заменять, я еще раз уточнил.

Мне посоветовали посмотреть аналог itertools.Product из питона в C#. Сказали, что так можно организовать вложенные циклы произвольной глубины (n циклов, так как количество замен изначально неизвестно), а это как раз и нужно.
Это было бы проще, наверное.
В замене
аааааббббррркд
уврнбрапенабнш
сначала перебрать все варианты с "а" на "у", "б" на "р", "р" на "б", "к" на "н", "д" на "ш"
затем в цикле к счетчику +1 и все варианты с "а" на "в", "б" на "р", "р" на "б", "к" на "н", "д" на "ш" и так далее.
то есть каждый цикл отвечает за свою букву для замен (а, б, р, к, д) и на каждом шаге работы хэш полученный сверяется с исходным
0
 Аватар для samana
2639 / 1567 / 853
Регистрация: 23.02.2019
Сообщений: 3,876
01.06.2020, 13:19
Цитата Сообщение от shabserg Посмотреть сообщение
Сказали, что так можно организовать вложенные циклы произвольной глубины (n циклов, так как количество замен изначально неизвестно), а это как раз и нужно.
Это было бы проще, наверное.
В замене
аааааббббррркд
уврнбрапенабнш
сначала перебрать все варианты с "а" на "у", "б" на "р", "р" на "б", "к" на "н", "д" на "ш"
затем в цикле к счетчику +1 и все варианты с "а" на "в", "б" на "р", "р" на "б", "к" на "н", "д" на "ш" и так далее.
Мы практически уже делали это, но сейчас сделал немного иначе (без словаря ключ/значение, а через вложенные массивы).
Например метод, который корректно возвращает все замены, даже если буквы повторяются в слове. На выходе массив строк каждого варианта, из которого можно потом получить хеш.

C#
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
using System;
using System.Collections.Generic;
using System.Linq;
 
namespace CyberForum
{
    class Program
    {
        static void Main()
        {
            string ruleUp = "аааааббббррркд";
            string ruleDown = "уврнбрапенабнш";
            List<string> combinations = getAllCombinations(ruleUp, ruleDown);
 
            Console.WriteLine(string.Join("\n", combinations));
        }
 
 
        private static List<string> getAllCombinations(string ruleUp, string ruleDown)
        {
            List<List<char>> map = new List<List<char>>();
 
            char lastChar = ruleUp[0];
            map.Add(new List<char>() { lastChar, ruleDown[0] });
            for (int i = 1; i < ruleUp.Length; i++)
            {
                char currentChar = ruleUp[i];
                if (currentChar == lastChar)
                {
                    map.Last().Add(ruleDown[i]);
                }
                else
                {
                    map.Add(new List<char>() { currentChar, ruleDown[i] });
                }
                lastChar = currentChar;
            }
 
            List<string> combinations = new List<string>();
            foo(map, 0, new char[map.Count]);
 
            // рекурсивный перебор для нахождения всех вариантов комбинаций
            void foo(List<List<char>> charRows, int row, char[] results)
            {
                for (int i = 1; i < charRows[row].Count; i++)
                {
                    results[row] = charRows[row][i];
                    if (row == charRows.Count - 1)
                    {
                        combinations.Add(string.Join("", results));
                    }
                    else
                    {
                        foo(charRows, row + 1, results);
                    }
                }
            }
 
            return combinations;
        }
    }
}
вывод:
Кликните здесь для просмотра всего текста

урннш
уранш
урбнш
уаннш
уаанш
уабнш
упннш
упанш
упбнш
уеннш
уеанш
уебнш
врннш
вранш
врбнш
ваннш
ваанш
вабнш
впннш
впанш
впбнш
веннш
веанш
вебнш
ррннш
рранш
ррбнш
раннш
раанш
рабнш
рпннш
рпанш
рпбнш
реннш
реанш
ребнш
нрннш
нранш
нрбнш
наннш
наанш
набнш
нпннш
нпанш
нпбнш
неннш
неанш
небнш
брннш
бранш
брбнш
баннш
баанш
бабнш
бпннш
бпанш
бпбнш
беннш
беанш
бебнш


Но это ничего не даст, так как
Цитата Сообщение от shabserg Посмотреть сообщение
И в слове не обязательно все буквы заменять, я еще раз уточнил.
я без понятия, как потом вычислить загаданное слово по этим правилам..
1
2 / 2 / 0
Регистрация: 28.05.2020
Сообщений: 58
01.06.2020, 13:36  [ТС]
А какая разница программе на то, все буквы заменять или нет? Я думал, она должна брать букву из правила замен, проверять, есть ли она в слове (например, по "карте" этого слова, там же видно все содержащиеся в нем символы), если есть, то заменять её на всех позициях.
0
 Аватар для samana
2639 / 1567 / 853
Регистрация: 23.02.2019
Сообщений: 3,876
01.06.2020, 14:55
Цитата Сообщение от shabserg Посмотреть сообщение
есть ли она в слове (например, по "карте" этого слова, там же видно все содержащиеся в нем символы), если есть, то заменять её на всех позициях.
Если в слове есть повторяющиеся буквы, например в слове "дед", а правила замены такие (для простоты преподаватель решил заменить только одну букву в слове):
дд
12

то такая замена обязательно повлияет на все буквы д в слове? То-есть на выходе обязательно будет что-то из одного 1е1, 1е2, 2е1, 2е2?
Или замена может касаться только первой д в слове и тогда на выходе может быть такая ситуация (где первая буква д заменена, а вторая осталась как есть) : 1ед, 2ед ?

Добавлено через 42 минуты
shabserg, Кажется я понял как всё это сделать невзирая на лишние или недостающие буквы замен. И по идее это совсем несложно. Попробую проверить на практике. Если идея работает, обязательно скину вас сюда.
1
2 / 2 / 0
Регистрация: 28.05.2020
Сообщений: 58
01.06.2020, 16:04  [ТС]
Ну тут, вроде, все понятно. Для этого я и пытался создать генератор наборов для замен, где выдавалось не больше одного значения для одной буквы. То есть если
дд
но
то в первом наборе выдалось бы только 0 и мы заменили все д в слове на н, а во втором 1 и мы бы все д заменили на о. Конечно, если заменяется буква "д" в слове, то на всех позициях она заменяется на одно и то же)
0
 Аватар для samana
2639 / 1567 / 853
Регистрация: 23.02.2019
Сообщений: 3,876
01.06.2020, 17:03
Попробуйте протестировать следующий вариант. По-идее всё работает и с лишними буквами и с пропусками.
Здесь интересен только метод Main в котором можно изменять лишь первые четыре переменные (они отмечены цифрами) и смотреть результат в консоли.

C#
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
using System;
using System.Collections.Generic;
using System.Security.Cryptography;
using System.Text;
 
namespace CyberForum
{
    class Program
    {
        static void Main()
        {
            // 1) некий внешний словарь
            string[] dictionary = {  "кто", "дед", "мед", "лёд", "барабашка", "ложка", "кошка" };
 
            // 2) пароль из словаря загаданный преподавателем
            string word = "барабашка";
 
            // 3) придуманые правила замен букв для пароля
            string ruleUp =     "рррааабббккк";
            string ruleDown =   "вльызвалдлли";
 
            // 4) после замен букв, зашифрованый пароль получился допустим таким...
            string pass = "дзлвлвшкв";
 
 
            //-------------------------------------------------------------------------------------------------------
 
            // вычисляем хеш зашифрованного пароля
            string passHash = HashMD5.GetHash(pass);
 
 
            // запускаем поиск слова в словаре, передаём: хеш зашифрованного пароля, правила замены и ссылку на словарь
            List<string> results = PasswordFinder.findWordsInDictionaryByRules(passHash, ruleUp, ruleDown, dictionary);
 
 
            // вывод поиска
            if (results.Count != 0)
            {
                Console.WriteLine("Возможные совпадения:");
                foreach (var item in results)
                {
                    Console.WriteLine(item);
                }
            }
            else
            {
                Console.WriteLine("Совпадения не найдены..");
            }
 
        }
 
    }
 
 
    public static class PasswordFinder
    {
        public static List<string> findWordsInDictionaryByRules(string outPassHash, string ruleUp, string ruleDown, string[] dictionary)
        {
 
            List<string> resuls = new List<string>();
 
            Dictionary<char, List<char>> patternRule = getPatternRule(ruleUp, ruleDown);
 
            List<List<char>> map = new List<List<char>>();
            foreach (var item in patternRule)
            {
                map.Add(item.Value);
            }
 
            //------------------------
 
            for (int i = 0; i < dictionary.Length; i++)
            {
                // word map
                List<List<char>> wordMap = createMapByPatternRule(dictionary[i], patternRule);
 
                //combos
                var combos = getAllCombinations(wordMap);
                foreach (var item in combos)
                {
                    if (HashMD5.GetHash(item).Equals(outPassHash))
                    {
                        if (!resuls.Contains(dictionary[i])) resuls.Add(dictionary[i]);
                    }
                }
            }
 
            return resuls;
        }
 
        /// <summary>
        /// Берём слово из словаря и создаём для него карту вариантов замены каждой буквы по шаблону.
        /// </summary>
        /// <param name="wordFromDict"></param>
        /// <param name="patternRule"></param>
        /// <returns></returns>
        private static List<List<char>> createMapByPatternRule(string wordFromDict, Dictionary<char, List<char>> patternRule)
        {
            List<List<char>> map = new List<List<char>>();
            for (int i = 0; i < wordFromDict.Length; i++)
            {
                char wordChar = wordFromDict[i];
                if (patternRule.ContainsKey(wordChar))
                {
                    map.Add(new List<char>(patternRule[wordChar]));
                }
                else map.Add(new List<char>() { wordChar });
            }
 
            return map;
        }
 
 
 
 
        /// <summary>
        /// Составляет шаблон замен по правилам. Где каждой букве могут соответствовать другие символы
        /// </summary>
        /// <param name="ruleUp"></param>
        /// <param name="ruleDown"></param>
        /// <returns></returns>
        private static Dictionary<char, List<char>> getPatternRule(string ruleUp, string ruleDown)
        {
            Dictionary<char, List<char>> map = new Dictionary<char, List<char>>();
 
            for (int i = 0; i < ruleUp.Length; i++)
            {
                char charKey = ruleUp[i];
                if (!map.ContainsKey(charKey)) map.Add(charKey, new List<char>() { charKey });
 
                map[charKey].Add(ruleDown[i]);
            }
 
            return map;
        }
 
 
 
        /// <summary>
        /// Получает на входе карту символов и возвращает массив всех возможных комбинаций (сверху вниз поочередно)
        /// </summary>
        /// <param name="patternMap"></param>
        /// <returns></returns>
        private static List<string> getAllCombinations(List<List<char>> patternMap)
        {
 
            List<string> combinations = new List<string>();
            foo(patternMap, 0, new char[patternMap.Count]);
 
            // рекурсивный перебор для нахождения всех вариантов комбинаций
            void foo(List<List<char>> charRows, int row, char[] results)
            {
                for (int i = 0; i < charRows[row].Count; i++)
                {
                    results[row] = charRows[row][i];
                    if (row == charRows.Count - 1)
                    {
                        combinations.Add(string.Join("", results));
                    }
                    else
                    {
                        foo(charRows, row + 1, results);
                    }
                }
            }
 
            return combinations;
        }
    }
 
 
    public static class HashMD5
    {
        public static string GetHash(string input)
        {
            using (MD5 md5Hash = MD5.Create())
            {
                byte[] data = md5Hash.ComputeHash(Encoding.UTF8.GetBytes(input));
                StringBuilder sBuilder = new StringBuilder();
 
                for (int i = 0; i < data.Length; i++)
                {
                    sBuilder.Append(data[i].ToString("x2"));
                }
 
                return sBuilder.ToString();
            }
 
        }
    }
}
1
1533 / 540 / 127
Регистрация: 09.01.2018
Сообщений: 1,756
01.06.2020, 20:23
shabserg,
Я подумал и у меня получились вот такие соображения:

Алгоритм шифрования
Слово зашифровывается путем замены символов из одной строки на символы из другой строки. Обе строки имеются. Однако, символы в верхней строке повторяются. Это означает, что у каждого символа из верхней строки возможно несколько вариантов замены.

Для того чтобы зашифровать слово с однозначным результатом шифрования, помимо исходных строк, необходимо знать какая именно из возможных замен использовалась для конкретного символа. Т.е. необходим ключ шифрования.

Например слово КОТ имеет две буквы, совпадающие с набором букв из верхней строки. Пусть это будут буквы К и Т. И например, буква К имеет четыре возможных замены, а буква Т - шесть. Тогда возможный ключ должен выглядеть примерно так: 34
Это означает, что букву К заменить на ее 3-ю замену, букву Т на 4-ю, а букву О оставить как есть и не заменять.

Этот самый ключ преподаватель нам не сообщил и именно его мы должны попытаться угадать.
Ключа мы не знаем, однако теперь у нас есть метод шифрования. В метод передаются следующие параметры:
1. Шифруемое слово
2. Верхняя строка замен
3. Нижняя строка замен
4. Ключ

Имея эти четыре аргумента, можно зашифровать слово из словаря, вычислить его хеш и сравнить с искомым.
Значит задача сводится к тому, чтобы подобрать ключ.

Что мы можем занять о ключе.
Длина ключа равна количеству символов из верхней строки, совпадающих с символами шифруемого слова без повторов.
Максимальное значение для каждой цифры ключа равно количеству возможных замен для символа, информацию о котором предоставляет цифра ключа минус 1.
Минимальное значение цифры ключа - 0

Таким образом, сгруппировав все возможные замены для отдельного слова, мы можем узнать как длину ключа так и максимальные значения для каждой цифры.

Теперь у нас есть и метод брут форса, генерирующий ключи для каждого слова. Нам понадобится перебрать все возможные значения ключа для каждого символа, зная его минимальные и максимальные значения.

Тогда можно описать интерфейс класса, предоставляющего методы для взлома:

Кликните здесь для просмотра всего текста

C#
1
2
3
4
5
6
7
8
9
10
11
    public interface IPasswordHack : ISourceData
    {
        string Encrypt(string src, int[] key);
        IEnumerable<int[]> GetKeys(string wordFromDict);
    }
 
    public interface ISourceData
    {
        string Rule { get; } //верхняя строка правила
        string Alphabet { get; } //нижняя строка
    }
Rule и Alphabet я вынес в отдельный интерфейс, поскольку одни понадобятся и в классе, который будет осуществлять взлом, используя интерфейс IPasswordHack. Это ничего не меняет


Как осуществляется взлом
Имея набор ключей, нужно взять слово из словаря и поочередно зашифровать его каждым ключом, затем вычислить хеш зашифрованного слова и сравнить его с контрольным хешем. В случае совпадения взлом прекращается.

Теперь когда весь алгоритм имеется, можно описать класс, который будет осуществлять взлом.
Ему понадобится словарь и контрольное значение хеша, а также один метод, который будет делать то что описано в предыдущем абзаце.

Интерфейс класса осуществляющего взлом:

Кликните здесь для просмотра всего текста

C#
1
2
3
4
5
6
    public interface IHackTool : ISourceData
    {
        public string[] Dict { get; }
        public string ControlHash { get; }
        bool TryHack(out string result);
    }
Поскольку он будет работать с методами для взлома, ему понадобятся верхняя и нижняя строка. Поэтому он также наследует интерфейс ISourceData


Понадобятся также два вспомогательных метода, для вычисления хеша и его сравнения с контрольным:
Кликните здесь для просмотра всего текста

C#
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
public static class MD5Helpers
    {
        public static string GetMD5Hash(string input)
        {
            var md5 = MD5.Create();
            byte[] data = md5.ComputeHash(Encoding.UTF8.GetBytes(input));
 
            StringBuilder sb = new StringBuilder();
            for (int i = 0; i < data.Length; i++)
            {
                sb.Append(data[i].ToString("x2"));
            }
 
            return sb.ToString();
        }
 
        public static bool CheckMD5Hash(string input, string controlHash)
        {
            var hash = GetMD5Hash(input);
            return hash.Equals(controlHash);
        }
    }


Собственно, все ясно, можно приступать к реализации.

Класс, предоставляющий методы для взлома:
Кликните здесь для просмотра всего текста

C#
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
 public class PasswordHack : IPasswordHack, ISourceData
    {
        private readonly Dictionary<char, List<char>> _map;
        public string Rule { get; }
        public string Alphabet { get; }
 
        public PasswordHack(string rule, string alphabet)
        {
            Rule = rule;
            Alphabet = alphabet;
 
            //заполняем карту
            _map = new Dictionary<char, List<char>>();
            for (int i = 0; i < Rule.Length; i++)
            {
                if (_map.ContainsKey(Rule[i]))
                {
                    _map[Rule[i]].Add(Alphabet[i]);
                }
                else
                {
                    _map.Add(Rule[i], new List<char>() { Alphabet[i] });
                }
            }
        }
 
        public string Encrypt(string src, int[] key)
        {
            var res = src.ToCharArray();
 
            //для сопоставления значений ключа с символами по индексу составим правило
            //правило содержит только те символы, которые содержатся в слове
            //значения ключа находятся в том же порядке
            //по индексу символа из правила мы затем получим необходимое для него значение ключа
            var rule = Rule.ToLower().Intersect(src).ToArray();
 
            for (int i = 0; i < res.Length; i++)
            {
                var currentSymbol = res[i];
                if (_map.ContainsKey(currentSymbol))
                {
                    //если символ содержится в карте, то заменим его 
                    //для этого получим список всех замен
                    var list = _map[currentSymbol];
 
                    //найдем индекс текущего символа в правиле, которое мы только что составили 
                    var index = Array.IndexOf(rule, currentSymbol);
 
                    //и получим значение ключа для этого символа
                    var keyValue = key[index];
 
                    //заменяем
                    res[i] = list[keyValue];
                }
            }
 
            return new string(res);
        }
 
        public IEnumerable<int[]> GetKeys(string wordFromDict)
        {
            //выберем из карты только те символы, которые содержатся в слове, 
            //найдем количество замен для каждого из них и уменьшим их на 1
            //это максимальные значение ключа для каждого символа
            var keyMaxValues = _map
                .Where(kvp => wordFromDict.Contains(kvp.Key))
                .Select(kvp => kvp.Value.Count - 1)
                .ToArray();
 
            //создадим массив, который будем преобразовывать для получения всех комбинаций
            var key = new int[keyMaxValues.Length];
 
            //скопируем в него максимальные значения ключа - это первая комбинация
            Array.Copy(keyMaxValues, key, keyMaxValues.Length);
 
            //вернем первую комбинацию
            yield return key;
 
            //в последней комбинации все разряды ключа равны 0
            //это условие выхода из цикла
            while(!key.All(x => x == 0))
            {
                var n = key.Length;
                
                //Переберем все возможные комбинации
                for (int i = n - 1; i >= 0; i--)
                {
                    if (key[i] > 0)
                    {
                        key[i]--;
                        if (i == n - 1)
                        {
                            yield return key;
                            i++;
                            continue;
                        }
 
                        key[i + 1] = keyMaxValues[i + 1] + 1;
                        i += 2;
                    }
                }
            }
        }


Класс, осуществляющий взлом

Кликните здесь для просмотра всего текста

C#
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
public class HackTool : IHackTool
    {
        readonly IPasswordHack _hack;
        public string[] Dict { get; }
        public string Rule { get; }
        public string Alphabet { get; }
        public string ControlHash { get; }
 
        public HackTool(string[] dict, string rule, string alphabet, string controlHash)
        {
            Dict = dict;
            Rule = rule;
            Alphabet = alphabet;
            ControlHash = controlHash;
            _hack = new PasswordHack(Rule, Alphabet);
        }
 
        public bool TryHack(out string result)
        {
            //Для сокращения времени перебора, сортируем словарь по количеству входящих в него символов из правила
            //Далее будем работать с этим словарем
            var actualDict = Dict.Select(s => s.ToLower())
                .OrderByDescending(s => s.Intersect(Rule.ToLower()).Count());
 
            foreach (var item in actualDict)
            {
                //получаем перечисление ключей
                var keys = _hack.GetKeys(item);
              
                //шифруем и проверяем
                foreach (var key in keys)
                {
                    var encrypted = _hack.Encrypt(item, key);
                    if (MD5Helpers.CheckMD5Hash(encrypted, ControlHash))
                    {
                        result = item;
                        return true;
                    }
                }
            }
 
            result = null;
            return false;
        }
 
    }


Теперь можно потестировать:
Кликните здесь для просмотра всего текста

C#
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
static void Main(string[] args)
        {
            //исходные данные
            string[] dict = { "барк", "ведро", "вишня", "долина", "колос", "кость", "кот", "остров", "порт" };
            var rule =     "аакккппооооррртттттууьььщщ";
            var alphabet = "сдкслакпщеикыдпебинаыпктыс";
 
            //что ищем
            var encrypted = "кщые"; //зашифрованное слово "порт"
            var hash = MD5Helpers.GetMD5Hash(encrypted);
 
            //тест
            IHackTool tool = new HackTool(dict, rule, alphabet, hash);
            if (tool.TryHack(out var result))
            {
                Console.WriteLine("Пароль найден:");
                Console.WriteLine(result);
            }
            else
            {
                Console.WriteLine("Пароль не найден");
            }
 
            Console.WriteLine();
 
            //еще несколько слов для теста
            var checkList = new Dictionary<string, string>();
 
            checkList.Add(MD5Helpers.GetMD5Hash("сщб"), "кот");
            checkList.Add(MD5Helpers.GetMD5Hash("лее"), "кот");
            checkList.Add(MD5Helpers.GetMD5Hash("бдыл"), "барк");
            checkList.Add(MD5Helpers.GetMD5Hash("бсдл"), "барк");
            checkList.Add(MD5Helpers.GetMD5Hash("вишня"), "вишня"); //нет букв для замены
 
            var checkResults = new List<bool>();
 
            foreach (var item in checkList)
            {
                tool = new HackTool(dict, rule, alphabet, item.Key);
                if (tool.TryHack(out var res))
                {
                    Console.WriteLine("Пароль найден:");
                    Console.WriteLine(res);
                    Console.WriteLine("Проверка:");
                    Console.WriteLine(item.Value);
                }
                else
                {
                    Console.WriteLine("Пароль не найден");
                    Console.WriteLine(item.Value);
                }
 
                Console.WriteLine("Equals:");
                Console.WriteLine(item.Value.Equals(res));
                Console.WriteLine();
 
                checkResults.Add(item.Value.Equals(res));
            }
 
            if (checkResults.All(r => r))
            {
                Console.WriteLine("Проверка завершена удачно");
            }
            else
            {
                Console.WriteLine("Проверка завершена неудачно");
            }
 
        }


Результаты:
Кликните здесь для просмотра всего текста

Code
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
Пароль найден:
порт
 
Пароль найден:
кот
Проверка:
кот
Equals:
True
 
Пароль найден:
кот
Проверка:
кот
Equals:
True
 
Пароль найден:
барк
Проверка:
барк
Equals:
True
 
Пароль найден:
барк
Проверка:
барк
Equals:
True
 
Пароль найден:
вишня
Проверка:
вишня
Equals:
True
 
Проверка завершена удачно


Вообще тестировать такие вещи сложно. Поэтому упрошенный вариант прямо в Main.
Все работает.
В комментах постарался подробно описать что и зачем делается в реализации.
1
Надоела реклама? Зарегистрируйтесь и она исчезнет полностью.
BasicMan
Эксперт
29316 / 5623 / 2384
Регистрация: 17.02.2009
Сообщений: 30,364
Блог
01.06.2020, 20:23

Перебор всех возможных вариантов
Доброго всем дня! Есть задача: На вход дается строка из символов '0' '1' и '2' длиной не более 50. Нужно изменить число 0 на 1...

Алгоритм перебора вариантов
Кто нибудь может подсказать эффективный алгоритм без рекурсии желательно: на входе есть массив массива, нужно получить все возможные...

Диаграммы вариантов использования
Мне задали сделать презентацию на тему: &quot;Диаграммы вариантов использования. Наглядное представление ожидаемых действий и поведения системы....

Перечисление возможных вариантов
Приветствую всех. Как можно сделать перечисление всевозможных вариантов? Пример: Ввод чисел с большим количеством знаков после...

Как сделать один из 3 вариантов?
textBox1.Text = new { &quot;А&quot; &quot;У&quot; &quot;Э&quot; }; Добавлено через 2 минуты Чтобы выводилось рандомная буква из этих...


Искать еще темы с ответами

Или воспользуйтесь поиском по форуму:
40
Ответ Создать тему
Новые блоги и статьи
Мастера простых решений
DevAlt 23.08.2026
В сишарп стэках winforms, да и wpf существует сложная система связывания источниках данных и элементов формы(текстовые поля и метки), опирается все это на технологию событий и мета. . .
Цена ошибки
DevAlt 23.08.2026
Человек я беспокойный и потому заинтересовался OCaml, в чате форсили функторы модулей как суперфичу. Пытаясь отдуплить концепт, наткнулся на тутор с простым примером. А главный принцип обучения от. . .
Сегодня суббота, 22.08.2026 at 16:41, и я вновь нахожусь на той стороне, за экраном машины.
zorxor 22.08.2026
Сегодня суббота, 22. 08. 2026 at 16:41, и я вновь нахожусь на той стороне, за экраном машины. Кто Я, откуда Я пришел и куда Я иду? Эти вопросы не оставляют меня ни на секунду. Жизнь на планете Земля. . .
Жизня: рисунок укладки багажа, сделанный клодом
anaschu 21.08.2026
Сделал 15 снимков, он по снимкам сделал схему.
Был там один разговор по поводу свободы в материальном мире.
kumehtar 19.08.2026
Суть: рассматривается живое существо, оказавшееся внутри довольно странной системы (этого мира) и пытающееся обустроить в ней свой кусок пространства. Жизнь действительно предъявляет каждому. . .
Когда логика программы не спасает от человеческих ошибок
Maks 18.08.2026
В последнее время всё чаще и чаще сталкиваюсь с таким явлением, как абсолютная невнимательность (или глупость) пользователей. Проявляется это чаще всего на работе в коллективе. Допустим, человек с. . .
Лето уходит
kumehtar 17.08.2026
Мысли в слух
kumehtar 17.08.2026
Забавно, насколько сейчас стала доступна информация. Например о магии, духовном развитии, медитациях, и других подобных направлениях, ранее зачастую тайных, передаваемых от учителя к ученику. Хотя. . .
КиберФорум - форум программистов, компьютерный форум, программирование
Powered by vBulletin
Copyright ©2000 - 2026, CyberForum.ru