2012-01-11 09:15:49 UTC
Что такое Hash Query? Если коротко, то это интерпретатор (или компилятор, если угодно) декларативного языка запросов на вычисление криптографических хэшей для строк и файлов. Hash Query умеет:
- вычислять хэш строки
- вычислять хэш файла, в том числе и его часть (задаваемой смещением и длиной)
- восстанавливать строку по хэшу методом грубой силы (перебор по конечному словарю)
- вычислять хэш для файлов в заданном каталоге, с поддержкой фильтрации файлов по размеру, имени, пути
- проверять файл с использованием известного хэша (валидация файла)
- искать файлы с использованием известного хэша всего файла или его части
Кроме того есть:
- Оценка времени восстановления строки с использованием грубой силы
- Вывод хэша в разном регистре
- Поддержка запросов из файлов и из командной строки
- Поддержка комментариев в файлах с запросами
- Поддержка переменных
Поддерживается следующие типы хэшей:
- MD5
- MD4
- SHA1
- SHA256
- SHA384
- SHA512
- Whirlpool
- CRC32
Использование
hq [OPTION] ...
Доступные опции:
-f [ --file ] <путь>
— Полный путь к файлу c запросами на языке Hash Query
-q [ --query ] <текст>
— Текст запроса на языке Hash Query
-v [ --validate ]
— Проверить синтаксис запроса. Не запускать никаких действий
-t [ --time ]
— Показывать время вычисления хэша (по умолчанию отключено)
-l [ --lower ]
— Выводить хэш в нижнем регистре
-? [ --help ]
— Показать помощь
Язык Hash Query
Этот язык представляет собой простой декларативный LINQ (Language Integrated Queries) или SQL подобный язык. Целью создания языка было желание предоставить единый, унифицированный интерфейс для работы с криптографическими хэшами строк и файлов с некоторыми дополнительными плюшками вроде восстановления строк по хэшам, поиск файлов по хэшам и пр.
Основы синтаксиса
Все задания на работу с хэшами в языке представляют собой предложения разделенные точкой с запятой «;». Они могут передаваться компилятору или через командную строку (опция -q
) или быть записанными в простом текстовом файле, который потом скармливается компилятору (опция -f
). В файле может быть произвольное количество запросов, но не более 10 тыс. (по соображениям производительности).
Пробельные символы в запросах (переводы строк, табуляторы и пр.) не учитываются, за исключением тех, что находятся собственно в данных (строках в кавычках или апострофах).
Общий вид запросов
Все запросы имеют вид:
for … do …;
или
let var = 'value';
После for
и do
идут инструкции специфичные для каждой из категорий запроса (см. далее).
Точка с запятой в конце запроса обязательна, даже в случае единичного запроса.
В инструкциях могут встречаться строки определяемые пользователем (пути к файлам, каталогам, значения хэшей, собственно строки для вычисления по ним хэша). Строка должна быть как в апострофах (одиночных кавычках), так и в обычных кавычках (двойных кавычках). В запросах передаваемых через командную строку должны использоваться апострофы (понятно почему), в запросах из файлов можно использовать любые кавычки.
В инструкциях после for
, но до do
могут встречаться подвыражения let
и where
не являющиеся обязательными.
Инструкции, начинающиеся с let
, определяют переменные, которые могут быть использованы в запросах.
Подвыражение let
Подвыражение let
не является обязательным и может быть опущено. Также, могут быть опущены отдельные части выражения.
Подвыражение let
содержит список инструкций с разделителем запятой идущих после ключевого слова let
, порядок инструкций не имеет значения. Инструкция имеет следующий вид:
<переменная>.<имя атрибута> = <значение атрибута>
Переменная — это любая последовательность латинских букв и цифр, но начинаться она должна обязательно с буквы. Переменная должна быть задано до let
.
Имя атрибута является одним из зарезервированных имен.
Значением атрибута является либо строка (в апострофах или кавычках), либо целое число.
Пример:
let s.md5 = '202CB962AC59075B964B07152D234B70', s.min = 3
Подвыражение where
Подвыражение where используется для задания условий, например для фильтрации файлов по некоторым признакам.
where
cостоит из выражений вида:
<переменная>.<имя атрибута> <условный оператор> <значение>
идущих после ключевого слова where
и соединенных операторами конъюнкции (логическое И) — and
и дизъюнкции (логическое ИЛИ) — or
, или другими словами после ключевого слова where
должно быть булево выражение.
Переменная — это любая последовательность латинских букв и цифр, но начинаться она должна обязательно с буквы. Переменная должна быть задано до where
.
Условный оператор — это один из следующий операторов:
==
(равно)!=
(не равно)>
(больше)<
(меньше)>=
(больше или равно)<=
(меньше или равно)~
(соответствует регулярному выражению)!~
(не соответствует регулярному выражению)
Для группировки частей выражения (изменения порядка выполнения условных операторов) могут быть использованы круглые скобки.
Пример:
where (f.md5 == '202CB962AC59075B964B07152D234B70' and f.limit == 100) or (f.offset == 10 and f.md4 == 'C58CDA49F00748A3BC0FCFA511D516CB')
Категории запросов
Все запросы можно разделить на 5 категорий:
- Определение переменных
- Запросы на вычисление хэша строки
- Запросы на восстановление строки по её хэшу
- Запросы на вычисление хэша отдельных файлов
- Запросы на вычисление хэша файлов в заданном каталоге
Определение переменных
Данные запросы нужны для определения переменных, ссылаться на которые можно в других запросах. Это нужно, например, для устранения дублирования одних и тех же данных, в случае если их по смыслу необходимо использовать в разных местах.
Запросы имеют следующий вид:
let <переменная> = '<строка>';
На определенную в этом запросе переменную, точнее данные, определяемые этой переменной, можно ссылаться в других запросах.
Запросы на вычисление хэша строки
Запросы имеют следующий вид:
for string '<строка>' do <хэш>;
Хэш может принимать одно из следующих значений (регистр имеет значение):
- md5
- md4
- sha1
- sha256
- sha384
- sha512
- whirlpool
- crc32
Запросы на восстановление строки по её хэшу
Общий вид запроса такой:
for string <переменная> from hash '<значение хэша>' [let …] do crack <хэш>;
Здесь хэш является одним из поддерживаемых типов хэшей (см. предыдущий раздел), значение хэша.
Подвыражение let
используется для управления параметрами восстановления строки, а именно:
- словарь (атрибут
dict
) - минимальная длина (атрибут
min
) - максимальная длина (атрибут
max
)
Запросы на вычисление хэша отдельных файлов
Общий вид запроса:
for file f from '<путь к файлу>' [let …] do <хэш|validate>;
Такие запросы предназначены либо для вычисления хэша отдельных файлов, либо для валидации файлов по хэшу, что определяется видом инструкции do
.
Подвыражение let
используется для определения той части файла, по которой производится валидация или для которой вычисляется хэш. Если опущено, то используется весь файл целиком. Атрибуты могут быть следующие:
- Хэш (атрибут
md5
|md4
|sha1
|sha256
|sha384
|sha512
|whirlpool
|crc32
) — имеет смысл только при валидации файла. - Смещение относительно начала файла в байтах (атрибут
offset
) - Размер данных для которых вычисляется хэш в байтах (атрибут
limit
)
Хэш может принимать одно из следующих значений (регистр имеет значение):
- md5
- md4
- sha1
- sha256
- sha384
- sha512
- whirlpool
- crc32
Запросы на вычисление хэша файлов в заданном каталоге
Общий вид запроса:
for file f from dir '<каталог>' [where ...] do <хэш|find> [withsubs];
Такие запросы предназначены либо для вычисления хэша файлов в каталоге(ах) — после do
идет хэш, либо для поиска файлов (по хэшам) — после do
идет ключевое слово find
.
Подвыражение where
используется для фильтрации файлов. Допустимые атрибуты:
- Имя файла (атрибут
name
) - Полный путь к файлу включая имя (атрибут
path
) - Размер файла в байтах (атрибут
size
) - Хэш (атрибут
md5
|md4
|sha1
|sha256
|sha384
|sha512
|whirlpool
|crc32
) — имеет смысл только при поиске файла. - Смещение относительно начала файла в байтах (атрибут
offset
) - Размер данных для которых вычисляется хэш в байтах (атрибут
limit
)
Опция withsubs
задействует вложенные каталоги (по умолчанию выключена).
Для фильтрации файлов по имени и/или пути могут быть использованы Perl совместимые регулярные выражения (PCRE). Для фильтрации файлов по регулярным выражениям используются условные операторы ~
и !~
.
Хэш может принимать одно из следующих значений (регистр имеет значение):
- md5
- md4
- sha1
- sha256
- sha384
- sha512
- whirlpool
- crc32
Формальная грамматика языка
// Определения парсера prog : statement+ | EOF ; statement : expr NEWLINE | NEWLINE ; expr: FOR (expr_string | expr_hash | expr_dir | expr_file) | expr_vardef ; expr_vardef: LET ID ASSIGN_OP STRING ; expr_string: STR source DO hash_clause ; expr_hash: STR id FROM HASH source let_clause? DO brute_force_clause ; expr_dir : FILE id FROM DIR source let_clause? where_clause? DO ( hash_clause WITHSUBS? | FIND WITHSUBS? ) ; expr_file : FILE id FROM source (let_clause)? DO ( hash_clause | VALIDATE ) ; source : STRING; id : ID; attr_clause : ID DOT attr ; attr : str_attr | int_attr ; hash_clause : MD5 | MD4 | SHA1 | SHA256 | SHA384 | SHA512 | CRC32 | WHIRLPOOL ; brute_force_clause : CRACK hash_clause ; let_clause : LET assign (COMMA assign)* ; where_clause : WHERE! boolean_expression ; boolean_expression : conditional_or_expression ; conditional_or_expression : conditional_and_expression (OR conditional_and_expression)* ; conditional_and_expression : not_expression (AND not_expression)* ; not_expression : exclusive_or_expression | NOT_OP exclusive_or_expression ; exclusive_or_expression : relational_expr | OPEN_BRACE boolean_expression CLOSE_BRACE ; relational_expr : ID DOT ( relational_expr_str | relational_expr_int ) ; relational_expr_str : str_attr (EQUAL | NOTEQUAL | MATCH | NOTMATCH) (STRING | ID) ; relational_expr_int : int_attr (EQUAL | NOTEQUAL | GE | LE | LEASSIGN | GEASSIGN) INT ; assign : ID DOT ( str_attr ASSIGN_OP STRING | str_attr ASSIGN_OP ID | int_attr ASSIGN_OP INT ) ; str_attr : NAME_ATTR | PATH_ATTR | DICT_ATTR | MD5 | MD4 | SHA1 | SHA256 | SHA384 | SHA512 | CRC32 | WHIRLPOOL ; int_attr : SIZE_ATTR | LIMIT_ATTR | OFFSET_ATTR | MIN_ATTR | MAX_ATTR ; // Определения лексера NAME_ATTR : 'name'; PATH_ATTR : 'path' ; DICT_ATTR : 'dict' ; SIZE_ATTR : 'size' ; LIMIT_ATTR : 'limit' ; OFFSET_ATTR : 'offset' ; MIN_ATTR : 'min' ; MAX_ATTR : 'max' ; CRACK : 'crack' ; WHERE : 'where' ; OR: 'or' ; AND: 'and' ; NOT_OP: 'not' ; FOR: 'for' ; FROM: 'from' ; DO: 'do' ; FIND: 'find' ; WITHSUBS : 'withsubs' ; VALIDATE : 'validate' ; LET : 'let' ; DIR : 'dir' ; FILE : 'file' ; HASH : 'hash' ; STR : 'string' ; MD5: 'md5'; SHA1: 'sha1' ; SHA256: 'sha256' ; SHA384: 'sha384' ; SHA512: 'sha512' ; MD4: 'md4' ; CRC32: 'crc32' ; WHIRLPOOL: 'whirlpool' ; fragment STRING1 : '\'' ( options {greedy=false;} : ~('\u0027' | '\u000A' | '\u000D'))* '\'' ; fragment STRING2 : '"' ( options {greedy=false;} : ~('\u0022' | '\u000A' | '\u000D'))* '"' ; STRING : STRING1 | STRING2 ; ID : ID_START ID_PART* ; fragment ID_START : '_' | 'A'..'Z' | 'a'..'z' ; fragment ID_PART : ID_START | '0'..'9' ; INT : '0'..'9'+ ; ASSIGN_OP : ASSIGN; NEWLINE: ';'; WS : (' '|'\t'| EOL )+ ; DOT : '.' ; COMMA: ',' ; OPEN_BRACE : '('; CLOSE_BRACE : ')'; COMMENT : ('#' | '/' '/') ~(EOL)* CR? (LF | EOF); fragment EOL : LF | CR ; fragment LF : '\n' ; fragment CR : '\r' ; PLUS: '+' ; EQUAL: ASSIGN ASSIGN ; NOTEQUAL: NOT ASSIGN ; fragment ASSIGN: '=' ; fragment NOT: '!' ; GE: '>' ; LE: '<' ; MATCH: '~' ; NOTMATCH : NOT MATCH ; LEASSIGN : LE ASSIGN; GEASSIGN : GE ASSIGN;
Примеры
Вычисление SHA1 хэша для строки 123
for string '123' do sha1;
Вычисление MD5 хэша для файла
for file f from 'file.txt' do md5;
Вычисление SHA384 хэша для части файла (первый килобайт)
for file f from 'file.txt' let f.limit = 1024 do sha384;
Вычисление SHA256 хэша части файла (один килобайт с пропуском первых 512 байт)
for file f from 'file.txt' let f.limit = 1024, f.offset = 512 do sha256;
Валидация файла по его MD4 хэшу
for file f from 'file.txt' let f.md4 = 'C58CDA49F00748A3BC0FCFA511D516CB' do validate;
Вычисление SHA512 хэша всех файлов каталога c:\dir
for file f from dir 'c:\dir' do sha512;
Вычисление Whirlpool хэша всех файлов каталога c:\dir
а также всех его подкаталогов
for file f from dir 'c:\dir' do whirlpool withsubs;
Вычисление CRC32 суммы всех exe файлов каталога c:\dir
for file f from dir 'c:\dir' where f.name ~ '.*exe$' do crc32;
Вычисление MD5 хэша всех файлов каталога c:\dir кроме файлов с расширением tmp
for file f from dir 'c:\dir' where f.name !~ '.*tmp$' do md5;
Вычисление MD5 хэша всех exe и dll файлов каталога c:\dir
for file f from dir 'c:\dir' where f.name ~ '.*exe$' or f.name ~ '.*dll$' do md5;
Вычисление MD5 хэша всех exe файлов каталога c:\dir
исключая те из них, которые начинаются с bad
for file f from dir 'c:\dir' where f.name !~ '^bad.*' do md5;
Поиск файла на диске C:\
по известному MD4 хэшу
for file f from dir 'c:\' where f.md4 == 'C58CDA49F00748A3BC0FCFA511D516CB' do find withsubs;
Восстановление строки по её MD4 хэшу используя словарь по умолчанию
for string s from hash '3689CA24BF71B39B6612549D87DCEA68' do crack md4;
Восстановление строки по её MD4 хэшу используя свой словарь
for string s from hash '3689CA24BF71B39B6612549D87DCEA68' let s.dict = '0123456789' do crack md4;
Восстановление строки по её MD4 хэшу используя свой словарь сокращенная форма (одни цифры)
for string s from hash '3689CA24BF71B39B6612549D87DCEA68' let s.dict = '0-9' do crack md4;
Восстановление строки по её MD4 хэшу используя свой словарь сокращенная форма (одни буквы в нижнем регистре)
for string s from hash '3689CA24BF71B39B6612549D87DCEA68' let s.dict = 'a-z' do crack md4;
Восстановление строки по её MD4 хэшу используя свой словарь сокращенная форма (цифры, буквы в нижнем и верхнем регистрах)
for string s from hash '3689CA24BF71B39B6612549D87DCEA68' let s.dict = '0-9a-zA-Z' do crack md4;
Восстановление строки по её MD4 хэшу используя свой словарь и определенные длины строки
for string s from hash '3689CA24BF71B39B6612549D87DCEA68' let s.dict = '0123456789', s.min = 2, s.max = 6 do crack md4;
Использование файлов с запросами
Создайте текстовый файл queries.hq
, например со следующим содержимым:
# string query - this is comment for string '123' do sha1; # file query - this is comment for file f from dir 'c:\dir' where f.name !~ '^bad.*' do md5;
Далее запустите компилятор указав ему на этот файл:
hq.exe -f queries.hq
Будет выполнено 2 запроса из файла
Использование переменных
Переменные удобны чтобы не дублировать одни и те же данные много раз при использовании их в разных запросах. Например:
# query that calculates md5 and sha1 for some files in a directory let path = 'c:\dir'; let mask = '^bad.*'; for file f from dir path where f.name !~ mask do md5; for file f from dir path where f.name !~ mask do sha1;