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/


Интересные статьи

Интересные статьи

В этой статье речь пойдёт о некотором моём предложении для сообщества. Это вдвойне сомнительное предложение из-за того, что мой личный трип на битриксе уже закончился. Две недели безвылазно на учёбе п...
Всем привет. Если вы когда-либо работали с универсальными списками в Битрикс24, то, наверное, в курсе, что страница детального просмотра элемента полностью идентична странице редак...
В 1С-Битрикс: Управление сайтом (как и в Битрикс24) десятки, если не сотни настраиваемых типов данных (или сущностей): инфоблоки, пользователи, заказы, склады, форумы, блоги и т.д. Стр...
История сегодня пойдёт про автосервис в Москве и его продвижении в течении 8 месяцев. Первое знакомство было ещё пару лет назад при странных обстоятельствах. Пришёл автосервис за заявками,...
Как быстро определить, что на отдельно взятый сайт забили, и им никто не занимается? Если в подвале главной страницы в копирайте стоит не текущий год, а старый, то именно в этом году опека над са...