1337ReverseEngineer’s VMAdventures 1 crackme

от автора

Продолжаем решать головоломки: сегодня это 1337ReverseEngineer’s VMAdventures 1 https://crackmes.one/crackme/63bd7f5733c5d43ab4ecf3ad

Задача: узнать верный пароль, на который программа выдаст «Correct key!».

Проверка пароля

С помощью дизассемблера находим строку «Correct key!» и код, что на нее ссылается. Над ним — цикл проверки пароля: eax пробегает по символам, а в edi — длина пароля.

Строка byte_4032E0 содержит непечатные символы: это не сам пароль, а хеш.

При помощи отладчика выясняется, что в esi хранится указатель на строку пароля, затем вызов loc_401170 «портит» символы.

Выполнение байт-кода

Работа функции управляется байт-кодом: цикл перебирает байты строки .rdata:004032D0 и выполняет команды. Функция предусматривает 5 случаев для следующих значений байтов: 0x0, 0x1, 0x2, 0x3, 0x4.  За исключением команды 0, которая останавливает выполнение байт-кода, команды 1-4 изменяют строку пароля. Таким образом программа шифрует пароль: чтобы расшифровать, выполним обратные действия в обратном порядке.

Расшифровка пароля

Изучим команды. 
0x1 Если внимательно изучать код, увидим, что на блоки размером 64 байта операцией XOR накладывается 16-байтная константа, а для остальных байтов выполняет XOR 54h. Заметим, что длина верного пароля — 32 и XOR по 64-блокам не будет выполняться. Повторное применение XOR,восстанавливает исходное значение: A XOR B XOR B = A.
0x2 Тот же алгоритм, что и у 0x1, но отличается константа: выполняет XOR 24h.
0x3 Выполняет циклический сдвиг влево на 2 бита для каждого символа пароля. Обратная операция — сдвиг вправо.
0x4 Выполняет сложение каждого байта пароля с 0xEB. Обратная операция — вычитание.

Теперь программа сама выдаст секретный пароль:

  • вводим пароль из 32-х символов

  • подадим на вход функции loc_401170 шифр пароля из .rdata:004032E0

  • изменим код loc_401170, чтобы выполнить обратные операции:

    • заменим в case 3 операцию сдвига ROL на ROR

    • заменим в case 4 сложение на вычитание

    • запишем команды байт-кода в обратном порядке

Несложно написать и код дешифратора.

#include <bit> #include <vector> #include <iostream> #include <string>  using namespace std; using BytesVector = vector<uint8_t>;  BytesVector code{0x01, 0x03, 0x04, 0x02, 0x01, 0x03, 0x02, 0x01, 0x02, 0x01, 0x03, 0x02, 0x03, 0x04, 0x02, 0x03};  void xor1(BytesVector& key) {     for (auto &c: key) c ^= 0x54; }  void xor2(BytesVector& key) {     for (auto &c: key) c ^= 0x24; }  void rol2(BytesVector& key) {     for (auto &c: key) c = rotl(c, 2); }  void add(BytesVector& key) {     for (auto &c: key) c += 0xEB; }  void ror2(BytesVector& key) {     for (auto &c: key) c = rotr(c, 2); }  void sub(BytesVector& key) {     for (auto &c: key) c -= 0xEB; }   enum Opcode {     XOR1 = 0x1,     XOR2,     ROL,     ADD, ROR, SUB };  void exec(const BytesVector& code, BytesVector& key) {     for (auto i: code) {         switch(i) {             case XOR1:                 xor1(key);                 break;             case XOR2:                 xor2(key);                 break;             case ROL:                 rol2(key);                 break;             case ADD:                 add(key);                 break; case ROR: ror2(key); break; case SUB: sub(key); break;         }     } }  BytesVector explode(const string& s) {     BytesVector result;     for (char c: s) {         result.push_back(static_cast<uint8_t>(c));     }      return result; }  void encrypt(BytesVector& text) {     exec(code, text); }  void reverseBytecode(BytesVector& code) {         for (uint8_t& op: code) {         switch(op) {             case ROL:                 op = ROR;                 break;             case ADD:                 op = SUB;                 break;             case ROR:                 op = ROL;                 break;             case SUB:                 op = ADD;                 break;         }     } }  void decrypt(BytesVector& text) {     BytesVector rev{code.rbegin(), code.rend()};     reverseBytecode(rev);     exec(rev, text); }  int main() {     BytesVector magic{0xBD, 0x35, 0xA9, 0xA1, 0xD1, 0xE1, 0xD9, 0x35,                        0x31, 0x01, 0x39, 0xD9, 0xAA, 0x95, 0x01, 0xAA,                        0xFD, 0xB9, 0x28, 0xD5, 0x7C, 0xD9, 0x1D, 0x95,                        0x99, 0xCD, 0xD9, 0xF1, 0xAA, 0xD2, 0xEE, 0xF9};      string password;     cout << "Enter password: "s;     cin >> password;     cout << endl;          BytesVector key = explode(password);     encrypt(key); cout << "Encrypted: "s;     for (auto k: key) {         printf("%X ", k);     }          decrypt(key);     cout << "\nDecrypted: "s; for (auto k: key) {         printf("%c", k);     }          decrypt(magic);     cout << "\nTOP SECRET: "s;      for(auto b: magic) {          printf("%c", b);     }      return 0; }


ссылка на оригинал статьи https://habr.com/ru/post/712290/


Комментарии

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

Ваш адрес email не будет опубликован. Обязательные поля помечены *