Как стать автором
Обновить

1337ReverseEngineer's VMAdventures 1 crackme

Время на прочтение3 мин
Количество просмотров2.5K

Продолжаем решать головоломки: сегодня это 1337ReverseEngineer's VMAdventures 1

Задача: узнать верный пароль, на который программа выдаст "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;
}

Теги:
Хабы:
Всего голосов 13: ↑13 и ↓0+13
Комментарии6

Публикации

Истории

Ближайшие события

7 – 8 ноября
Конференция byteoilgas_conf 2024
МоскваОнлайн
7 – 8 ноября
Конференция «Матемаркетинг»
МоскваОнлайн
15 – 16 ноября
IT-конференция Merge Skolkovo
Москва
22 – 24 ноября
Хакатон «AgroCode Hack Genetics'24»
Онлайн
28 ноября
Конференция «TechRec: ITHR CAMPUS»
МоскваОнлайн
25 – 26 апреля
IT-конференция Merge Tatarstan 2025
Казань