B
Bdpk
Статья для участия в Конкурсе программистов.
Доброго времени суток, мы начинаем!
0. Предисловие
Всегда наступает такой момент, когда пользователь забывает пароль от архива, в котором спрятал свои секреты. Для самых забывчивых мы сегодня сделаем некоторую программу, функционал которой -- подбор пароля для архивов с расширением zip/rar.
Писать будем на python3(дальше просто python), хотя заранее знаем, что писать локальную брутилку на этом языке не рационально. Однако иногда он используется как черновик, вот и почеркаем немного, чтобы проверить, можно ли такое реализовать. (да, можно

1. Подготовка
Нам понадобится:
- текстовый редактор или IDE;
- интерпретатор python;
- минимальное знание python; ( В противном переходим к чтению серии статей от @al04e )
- желательно, наличие unix;
Мы пишем на python, поэтому не будем засорять основное окружение ненужными пакетами и воспользуемся инструментом
Ссылка скрыта от гостей
:virtualenv Codeby
Сгенерировалось окружение, активируем его!
source bin/activate
Отлично, теперь настало время поговорить о функционале, так сказать выделить главную мысль. Идея очень проста – попытаться открыть архив с первым паролем, потом открыть архив со вторым паролем и так до конца словаря. Так выглядит банальный перебор ключей, этим и займемся.
Если приходилось писать на этом языке, то наверняка знакома следующая
Ссылка скрыта от гостей
, если нет, рекомендую ознакомиться:
Python:
if __name__ == "__main__":
pass
Начало положено! Программа работает с двумя сущностями: архив и словарь, поэтому напишем пару строк, чтобы обработать входящие данные:
Python:
import argparse
parser = argparse.ArgumentParser( '--file ' + '--dict ')
parser.add_argument('-f', '--file', dest='archive', required=True, type=str, help='Archive file')
parser.add_argument('-d', '--dict', dest='dictionary', required=True, type=str, help="Dictionary file")
args = parser.parse_args()
Импортировали модуль для разбора входящих данных.
Создали объект парсер и указали какие аргументы надо парсить, обращаю внимание, что в поле аргумента dest хранится имя нашей сущности, напоследок мы запарсили, все что пришло к нам. Аргумент required=True означает, что программа не будет работать без указания этого параметра, т.е это обязательный флаг для программы.
Программа приобрела интерфейс для пользователя. Теперь необходимо наполнить программу функционалом. Прежде всего, напомню, мы пишем для zip/rar, и они используют свои алгоритмы компрессии.
Сэкономлю ваше время и расскажу, что к чему тут, у нас есть:
Поэтому нам следует изначально определить
Ссылка скрыта от гостей
входного файла.Нам стало известно:
- RAR = RAR!
- ZIP = PK
Python:
def cutMagicNumbers(archive):
with open(archive, 'rb') as file:
currentType = file.read(2).decode()
return launcher(currentType)
cutMagicNumbers(args.archive)(args.archive, args.dictionary)
Заодно вернем полученную сигнатуру другой функции, которая определит расширение архива.
Python:
def launcher(extension):
return {'PK': prepareBruteZip,
'Ra': prepareBruteRar,
}.get(extension, 'Not Found')
Теперь по порядку. В момент вызова функции cutMagicNumbers(args.archive) мы передали архив, открыли его и прочитали первые два бита. Затем вернули результат функции launcher(extension), которая знает как сопоставляется сигнатура и архив. В теле функции launcher определен словарь со значениями, сигнатурой - ключом и функцией.
Используя метод get, мы возвращаем ссылку на функцию(prepareBruteZip или prepareBruteRar) в функцию cutMagicNumbers, которая в свою очередь возвращает в cutMagicNumbers(args.archive)(args.archive, args.dictionary) и полученное значение – функция с двумя аргументами (args.archive, args.dictionary).
Стек вызовов выглядит так:
Осталось определить функции, которые будут отвечать за подготовку и перебор.
Начнем с Zip. Импортируем модуль zipfile, он включен в стандартную библиотеку python.
Python:
def prepareBruteZip(archive, dictionary):
'''
file == 2.0
type(pwd) == byte
'''
zArchive = zipfile.ZipFile(archive)
with open(dictionary, 'r') as wordlist:
for word in wordlist.readlines():
password = word.strip('\n').encode('ascii') # unicode → byte
brute(zArchive, password)
Прокомментирую некоторые моменты. Мне не удалось подобрать пароль к архиву ниже 2.0, поэтому первое условие – версия архива >= 2.0. Чувствуется, что модуль писался под python2, поэтому предварительно приходится преобразовывать к таблице ascii.
Python:
def brute(archive, password):
try:
archive.extractall(pwd=password)
print('[+] Password is {}'.format(password))
except:
pass
Следом определим функцию подбора brute(). Она до жути простая. Замалчивает об ошибках возникающих при переборе, и если удалось открыть, печатает результат.
Перейдем к RAR. Импортируем модуль rarfile, его уже придется скачать.
pip3 install rarfile
Python:
def prepareBruteRar(archive, dictionary):
'''
type(pwd) == str
requirements installed unrar
'''
rArchive = rarfile.RarFile(archive)
with open(dictionary, 'r') as wordlist:
for word in wordlist.readlines():
password = word.strip('\n') # str
brute(rArchive, password)
С архивом rar все туманнее и запутаннее. При подборе архив ожидает, что вы передадите пароль в виде строки, а не байтов. Но интересен тот факт, что модуль rarfile требует установленной программы unrar. “что? ”- подумаете вы. Я так же подумал и пошел в сорцы, и лицезрел следующий текст.
# Basic logic:
# - Parse archive structure with Python.
# - Extract non-compressed files with Python
# - Extract compressed files with unrar.
# - Optionally write compressed data to temp file to speed up unrar,
# otherwise it needs to scan whole archive on each execution.
# - Parse archive structure with Python.
# - Extract non-compressed files with Python
# - Extract compressed files with unrar.
# - Optionally write compressed data to temp file to speed up unrar,
# otherwise it needs to scan whole archive on each execution.
Разработчик ставил перед собой цель научить python запускать unrar, иначе говоря сделать обертку. Комичная ситуация, но на Windows unrar я так и не нашел.
Функция brute() для rar идентична, поэтому мы используем подготовительные функции.
3. Послесловие
Мы написали простой код, который подбирает пароль для архива. Настало время бенчмарков!
Что и ожидалось от rar архива, ведь все время программа проводит в ожидании и просто вызывает unrar
На этом все! Не забывайте свои пароли!
Спасибо за внимание!
Python:
import zipfile
import rarfile
import argparse
def cutMagicNumbers(archive):
with open(archive, 'rb') as file:
currentType = file.read(2).decode()
return launcher(currentType)
def launcher(extension):
return {'Ra': prepareBruteRar,
'PK': prepareBruteZip,
}.get(extension, 'Not Found')
def prepareBruteZip(archive, dictionary):
'''
Ограничения file <ZipName> == 2.0
type(pwd) == byte
'''
zArchive = zipfile.ZipFile(archive)
with open(dictionary, 'r') as wordlist:
for word in wordlist.readlines():
password = word.strip('\n').encode('ascii')
brute(zArchive, password)
def brute(archive, password):
try:
archive.extractall(pwd=password)
print('[+] Password is {}'.format(password))
except:
pass
def prepareBruteRar(archive, dictionary):
'''
type(pwd) == str
requirements installed unrar
'''
rArchive = rarfile.RarFile(archive)
with open(dictionary, 'r') as wordlist:
for word in wordlist.readlines():
password = word.strip('\n')
brute(rArchive, password)
if __name__ == "__main__":
parser = argparse.ArgumentParser(
'--file <archive>' + '--dict <dictionary>')
parser.add_argument('-f', '--file', dest='archive', required=True,
type=str, help='Archive file')
parser.add_argument('-d', '--dict', dest='dictionary', required=True,
type=str, help="Dictionary file")
args = parser.parse_args()
cutMagicNumbers(args.archive)(args.archive, args.dictionary)