Криптография - статьи

       

Стандартный алгоритм восстановления паролей к PWL-файлам


  • Считываем из исходного PWL-файла по смещению 0x24C - CheckSeed (DWORD).
  • -//- по смещению 0x252 - CryptoSign (16 байт).
  • -//- по смещению 0x262 - CheckSign (16 байт).
  • Формируем массив X (размером 64 байта) следующим образом:
  • 0xFFFFFFFF (DWORD)
  • Логин в верхнем регистре
  • 0x0 (1 байт)
  • CheckSeed (DWORD)
  • 0x80 (1 байт)
  • 0x0 (по адрес 0x37 включительно)
  • По адресу 0x38 записываем (0x48 + (длина логина << 3)) (DWORD)
  • 0x0 (до конца массива)
  • Выполняем MD5 (массив X), получаем массив Cnt (16 байт), т.е. производим свертку логина.


  • Формируем массив Y (размером 64 байта) следующим образом:
  • Логин в верхнем регистре
  • 0x0 (0x11 байт)
  • 0x80 (1 байт)
  • 0x0 (по адрес 0x37 включительно)
  • По адресу 0x38 записываем (0x88 + (длина логина << 3)) (DWORD)
  • 0x0 (до конца массива)
  • Формируем массив Z (размером 64 байта) следующим образом:
  • Пароль
  • 0x0 (1 байт)
  • Cnt (16 байт)
  • 0x80 (1 байт)
  • 0x0 (по адрес 0x37 включительно)
  • По адресу 0x38 записываем (0x88 + (длина пароля << 3)) (DWORD)
  • 0x0 (до конца массива)
  • Выполняем MD5 (массив Z), получаем массив Key (16 байт), т.е. производим свертку пароля.
  • Выполняем RC4, используя в качестве ключа Key.
  • Копируем во временный буфер Temp (32 байта):
  • CryptoSign (16 байт)
  • CheckSign (16 байт)
  • Выполняем процедуру XorT (массив M, массив Temp), получаем модифицированный массив Temp.
  • Копируем первые 16 байт из массива Temp в буфер Y с адреса (длина логина + 1)
  • Выполняем MD5 (массив Y), получаем массив TempKey (16 байт).
  • Сравниваем 16 байт массива TempKey и вторые 16 байт из массива Temp и если они не равны, то инкремент пароля и возврат на пункт 7, иначе - пароль верный!

  • Внимание! А на "десерт" - самое вкусненькое ;) - то, что позволило в свое время программе PWLInside существенно увеличить скорость перебора и по этому параметру здорово "оторваться" от своих конкурентов.

    Оказывается, для быстрой оценки - тот пароль или нет, можно пункты 10...14 не выполнять, а значит и не вызывать второй раз MD5, который (как уже было сказано) выполняется около 300 тактов, что дает прирост скорости еще на 20-25%!

    Дело вот в чем.

    Если внимательно присмотреться к процедуре декодирования ресурсов, то мы увидим, что с помощью массива M после пункта 9 стандартного алгоритма при проходе с правильным паролем мы декодируем:

  • начальный буфер Temp - это не что иное, как 32 байта исходного PWL-файла с адреса 0x252,
  • смещения (адреса) блоков ресурсов с адреса 0x272,
  • и, наконец, сами ресурсы.
  • Поэтому можно не выполнять операции над CryptoSign и CheckSign (для проверки правильности пароля), а просто проверить после XOR'a адрес первого блока ресурсов, который находится по адресу 0x272.

    Если он лежит в диапазоне 0x292...(длина PWL-файла), то есть вероятность, что этот пароль верный! Для окончательной же проверки правильности пароля нужно выполнить пункты 10...14, т.к. только в этом случае (когда совпадут все 16 байт массива Temp), можно быть уверенным, что это именно верный пароль.

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

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

    Поэтому код предварительной оценки пароля в упрощенном виде будет выглядеть так:

    ...

    //Массив M перерабатывается с помощью RC4

    ...

    byte t; //Временная ячейка

    byte A = 0; //Здесь будем получать новый псевдослучайный индекс




    WORD Offset = (WORD *)&lpFile[0x272]; //Зашифров. адрес 1-го блока ресурсов

    WORD Len = XXX; //Длина исходного PWL-файла

    //

    for (int i = 1; i < 35; i++)

    {

    A += M[i]; // Вычисляем новый индекс для обмена байтами

       t = M[i]; 

       M[i] = M[A]; //Меняем местами i-й байт и байт по вычисленному индексу A

       M[A] = t;

       //

       t = M[i] + M[A]; //Вычисляем еще один индекс

       if (i == 33)

          ((byte *)&Offset)[0] ^= M[t]; //Декодируем 1-й байт адреса

       if (i == 34)

           ((byte *)&Offset)[1] ^= M[t]; //Декодируем 2-й байт адреса

    }

    //

    if ((Offset > 0x291) && (Offset < Len))

    {

       //Есть вероятность, что пароль верный,

       //делаем полную перепроверку по пунктам 10...14

    }

    else

    {

       //Однозначно пароль неверный, переходим на новый пароль

    }

    Как видим, переработка массива M все равно остается, но(!) - первые 32 итерации мы НИЧЕГО не XOR'им, а декодируем ТОЛЬКО на 33 и 34 итерациях адрес блока ресурсов.

    Таким образом, зачем нам делать пункты 10...14 и проверять 16 байт, когда можно выполнить "упрощенный" вариант процедуры XorT и проверить всего 2 байта! ;)

    При всем моем уважении к Microsoft, однако, это еще одна их "дыра" в реализации качественных методов хранения паролей пользователей!

    Итак, что мы получили в среднем:


    • ~40 тактов на инициализацию массива M.


    • ~300 тактов на первый проход MD5.


    • ~1000 тактов на проход RC4.


    • ~150 тактов на все остальное (формирование массива Z, инкремент пароля, "упрощенная" функция XorT, проверки и пр.)


    • В итоге мы получаем суммарное время обработки одного пароля около 1500 тактов

      на всех типах процессоров, что приведены в начале статьи, кроме процессора Pentium 4. :(

      Дело в том, что микроархитектура P4 по сравнению с P-II/III была существенно переработана - увеличено время выполнения команды (до 20 стадий), изменились размеры строк кэша данных и кода и еще ряд "усовершенствований", поэтому этот код (в особенности, реализация алгоритма RC4) для P4 не является оптимальным и в следующей версии PWLInside будет модифицирован. При этом на процессорах AMD, даже последних, таких проблем не возникает - на Athlon'e XP 1700+ (1466МГц) с ядром ThoroughBred программа исправно выдает около миллиона паролей в секунду. Вот так AMD делает аналоги процессоров Intel в чем-то лучше, чем сама Intel. :)


      Содержание раздела