Python. Взлом шифров. Продолжение.

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

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

f_text = open('d:\9\out.txt', 'r')
f_result = open("d:\9\some1.txt", "w")
f_matching = open("d:\9\match1.txt", "r") #файл с сопоставлением словарей

dic={} #словарь

#Прочитаем файл сопоставлений и создадим словарь
for line in f_matching:
    ls=line.split("^")
    if len(ls)>2:
        c=ls[0]
        dic=ls[2]

#прочитаем все строки зашифрованного файла и применим к ним предполгаемый ключ
for line in f_text:
    i=0
    str_len=len(line)
    new_str=""
    while i<str_len:
        c=line[i]
        j=0
        if c!="\n":
            c_dist=dic
            new_str=new_str+c_dist
        i=i+1        
    f_result.write(new_str)

f_result.close()

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

Пример, изображенный на данном скриншоте, получен путем расшифровки шрифта с использованием модифицированного словаря. Как видим, текст стал еще более осмысленный, чем на прошлом уроке. При желании мы можем сделать еще ряд предположений, например, набор букв «Тсследование» означает «Исследование» и поменять в словаре местами буквы «Т» и «И»:

Очевидно, что в тексте «?аучноАисследователыский» зашифрованы слова «Научно-исследовательский», таким образом, мы меняем местами «?» и «Н», а также «А» и «-«, ну и «ы» с «ь».

Смотрим, что получилось:

Как видим, с каждой заменой текст становиться все ближе и ближе к оригиналу. Но это долгий и кропотливый процесс. А тут нам еще и повезло — мы сразу же нашли пробе. А могли бы и не найти, тогда бы мы не смогли видеть в прдобработанном тексте слова, а видели бы какую-то белиберду. Возникает вопрос: «Нельзя ли его как-то сей процесс автоматизировать?». По идее, это уже задачи из области искусственного интеллекта, но мы, все же, попытаемся ее решить.

Итак, как искать слова, похожие на заданные слова? В данном случае рулит мера близости. Мы сравниваем кусок текста с определённым словом и считаем, сколько букв различаться. Самый простой способ. Но вопрос: а какие слова искать? На этот вопрос даст ответ частотный анализ текста, но теперь уже не символьный, а словесный.  В первую очередь ищем в нашем зашифрованном тексте слова, которые часто встречаются. Для выполнения этой операции пишем вот такой несложный код на Python:

f_templ = open('d:\9\шаблон.txt', 'r')
f_result = open("d:\9\words.txt", "w")


dic={} #словарь

#Прочитаем файл шаблона и создадим частотный словарь
for line in f_templ:
    ls=line.split(" ") #получили список слов
    for word in ls:
        if len(word)<2:
            break        
        c=word[len(word)-1]
        if c=="." or c=="," or c=="!" or c=="?" or c==":" or c=="-":
            print(word)
            word=word[0:len(word)-1]
            print(word)
        if dic.get(word)==None:
            dic[word]=1
        else:
            dic[word]=dic[word]+1       

#превратим словарь шаблона в список и отсортируем его
#тип полученного списка - список кортежей (тьюплов)
list_templ=list(dic.items())
list_templ.sort(key=lambda item: item[1], reverse=True)

#выгрузим словарь в файл
len_dic=len(list_templ)
i=0
while i<len_dic:
    f_result.write(str(list_templ[i][0])+";"+str(list_templ[i][1])+"\n")
    i=i+1

f_result.close()

Что в итоге получаем? Вот такой вот выходной файл:

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

if len(word)<2:

ставим:

if len(word)<3:

И получаем на выходе:

Это уже лучше. Теперь можно попробовать осуществить поиск по словам. Как это сделать? Для начала нам надо написать функцию, которая бы сопоставляла два слова одинаковой длины и выводила метрику их различия. Но что взять в качестве метрики? Можно количество букв, на которое различаются слова.  Но еще лучше учесть, насколько сильно отличаются сами буквы. Для этого мы будем рассчитывать, насколько далеко они находятся друг от друга в словаре. Для удобства словарь лучше всего конвертировать в список и осуществлять потом поиск буквы в этом списке, находя его позицию:

f_matching = open("d:\9\match1.txt", "r") #файл с сопоставлением словарей

dic={} #словарь

#Прочитаем файл сопоставлений и создадим словарь
for line in f_matching:
    ls=line.split("^")
    if len(ls)>2:
        c=ls[0]
        dic=ls[2]


ls=list(dic.items())

Для осуществления поиска напишем вот такую функцию:

def search_simbol(s):    
    i=0
    while i<len(ls):
        if ls[i][0]==s:
            return i
        i=i+1
    return -1

Ну, и, собственно, сравнение слов:

def matching_word(w1,w2):
    i=0
    res=0
    while i<len(w1):
        dif=0
        if w1[i]!=w2[i]:
            ind1=search_simbol(w1[i])
            ind2=search_simbol(w2[i])
            if ind1==-1 or ind2==-1:
                dif=len(ls)+1
            else:
                dif=abs(ind1-ind2)
        res=res+dif
        i=i+1
    return res

Ну, и, собственно, веся программа с тестовым примером:

f_matching = open("d:\9\match1.txt", "r") #файл с сопоставлением словарей

dic={} #словарь

#Прочитаем файл сопоставлений и создадим словарь
for line in f_matching:
    ls=line.split("^")
    if len(ls)>2:
        c=ls[0]
        dic=ls[2]


ls=list(dic.items())


def search_simbol(s):    
    i=0
    while i<len(ls):
        if ls[i][0]==s:
            return i
        i=i+1
    return -1

def matching_word(w1,w2):
    i=0
    res=0
    while i<len(w1):
        dif=0
        if w1[i]!=w2[i]:
            ind1=search_simbol(w1[i])
            ind2=search_simbol(w2[i])
            if ind1==-1 or ind2==-1:
                dif=len(ls)+1
            else:
                dif=abs(ind1-ind2)
        res=res+dif
        i=i+1
    return res

print(matching_word("мроба","проба"))
print(matching_word("дроба","проба"))
print(matching_word("кроба","проба"))
print(matching_word("цукеп","проба"))

Который выдал следующее:

56
73
51
242

На этом пока все, но продолжение следует.

Продолжение.

Comments

So empty here ... leave a comment!

Добавить комментарий

Sidebar