Вопрос Помогите разобраться с хешем

Начинающий
Статус
Оффлайн
Регистрация
11 Дек 2023
Сообщения
11
Реакции[?]
0
Поинты[?]
0
У меня задача разобрать дизассемблерный код и написать генерацию пароля по имени.
С этим я справился, хоть и не без проблем, но меня ставит в тупик другой момент.. Функция генерации хеша из логина и пароля работают отдельно друг, от друга, но при этом результат выполнения обоих функций должен совпадать. Помимо прочего, пароль должен состоять из "a-z, A-Z, 0-9" символов. Каким образом это возможно?

Я не шарю за алгоритмы, а в курсе нечего не было о этом, сам код проверок из проги я вытащил, но что дальше делать не знаю.
Подскажите пожалуйста.


Реверс:
DWORD32 genNameHash(byte *szName)
{
    DWORD32 crc = -1;
    for (DWORD idx = 0; idx < strlen((char *)szName); idx++)
    {
        crc ^= szName[idx];
        for (int j = 0; j < 8; j++)
        {
            char converChar = (crc & 1);
            crc = -converChar & 0xEDB88320 ^ (crc >> 1);

            /*
                .text:00438076 mov     eax, [ebp+crc]
                .text:00438079 and     eax, 1
                .text:0043807C neg     eax                      // CHAR!
                .text:0043807E mov     [ebp+numBuff], eax
                .text:00438081 mov     eax, [ebp+crc]
                .text:00438084 shr     eax, 1
                .text:00438086 mov     ecx, [ebp+numBuff]
                .text:00438089 and     ecx, 0EDB88320h
                .text:0043808F xor     eax, ecx
                .text:00438091 mov     [ebp+crc], eax
            */
        }
    }

    printf("name hash: 0x%X(0x%X)\n", ~crc, ~crc & 0xFF);
    return ~crc;
}

DWORD32 genPassHash(byte* pass)
{
    size_t idx = 0; DWORD32 hash = 0;
    while (pass[idx] != NULL)
    {
        hash += pass[idx++] ^ 0x99;

        /*
            mov     eax, [ebp+pass]
            add     eax, [ebp+i]
            movsx   ecx, byte ptr [eax]
            xor     ecx, 99h
            add     ecx, [ebp+hash]
            mov     [ebp+hash], ecx
        */
    }

    printf("pass hash: 0x%X(0x%X)\n", hash, hash & 0xFF);
    return hash;
}

bool isValid(byte *str)
{
    size_t idx = 0;
    while (str[idx] != NULL)
    {
        if (str[idx] >= 'a' && str[idx] <= 'z' ||
            str[idx] >= 'A' && str[idx] <= 'Z' ||
            str[idx] >= '0' && str[idx] <= '9')
        {
            idx++;
            continue;
        }

        return false;
    }

    return true;
}

bool isAccess(byte* name, byte* pass)
{
    if (!isValid(name)) {
        return MessageBox(NULL, "Name a-z, A-Z, 0-9", "Falsch Daten", 0);
    }

    if (!isValid(pass)) {
        return MessageBox(NULL, "Pass a-z, A-Z, 0-9", "Falsch Daten", 0);
    }

    return (genNameHash(name) & 0xFF) == (genPassHash(pass) & 0xFF);

    /*
        mov     [ebp+nameHash], 0
        mov     [ebp+passHash], 0
        mov     eax, [ebp+name]
        push    eax
        call    j_nameHash
        add     esp, 4
        and     eax, 0FFh
        mov     [ebp+nameHash], eax
        mov     eax, [ebp+pass]
        push    eax             ; Str
        call    j_genPassHash
        add     esp, 4
        and     eax, 0FFh
        mov     [ebp+passHash], eax
        mov     eax, [ebp+nameHash]
        cmp     eax, [ebp+passHash]
        jnz     short loc_437ED8
    */
}

int main()
{
    byte name[] = "hallo", pass[] = "1234"; // false
    printf("Access: %s\n", (isAccess(name, pass)) ? "true" : "false");
    getchar();

    return 1;
}

 
Последнее редактирование:
Начинающий
Статус
Оффлайн
Регистрация
11 Дек 2023
Сообщения
11
Реакции[?]
0
Поинты[?]
0
Скрытое содержимое
Эту функцию нельзя изменять, она только для ознакомления с алгоритмом. Патчить прогу тоже нельзя, нужно именно генератор написать (кейген).
 
Начинающий
Статус
Оффлайн
Регистрация
11 Дек 2023
Сообщения
11
Реакции[?]
0
Поинты[?]
0
Как я понимаю, нужно подбирать число
Скрытое содержимое
Нет.
  • Нужно ввести произвольное имя
  • Алгоритм считает его хеш в функции "genNameHash"

- Мой генератор генерирует из этого хеша ключ-строку

Я ввожу сгенерированный пароль/ключ в программу
- Функция genPassHash считает хеш введенного пароля

- Функция IsAccessсравнивает оба хеша, перед этим сделав логическое & 0xFF с обоими хешами, что оставит только последние 2 байта от хеша
зображення_2023-12-12_130220408.png

Я высчитал что паролем для имени "hallo" будет 1 символ "H", но другим именам нужно подбирать более длинные и заранее спланированные комбинации, так что бы хватало остатка от вычитания для следующего символа. Но возможно нужно использовать какую-то математическую функцию, а не генератор. По сути у меня вообще нет идей.
 
Последнее редактирование:
Начинающий
Статус
Оффлайн
Регистрация
11 Дек 2023
Сообщения
11
Реакции[?]
0
Поинты[?]
0
Скрытое содержимое
Вот что у меня получилось:

C++:
byte* genPass(byte nameHash)
{
    static byte key[37] = { NULL };
    memset(key, 0, sizeof(key));

    // 0xD1 (209)
    /*
        209 ^ 153(0x99) = 48 ("H")
        209 - 209 = 0
        
        name "hallo"  (0xD1)    = pass "H"
        name "hallo1" (0xC4)    = error
        name "hallo2" (0x7E)    = error
        name "hallo3" (0xE8)    = pass "q"
    */

    for (size_t idx = 0; idx < 37; idx++)
    {
        for (byte c = 'z'; c > '0'; c--)
        {
            byte b = c ^ 0x99;
            if (b > nameHash) {
                continue;
            }

            // Проверка символа, подходит ли он и подойдет ли следующий, если вычесть текущий
            if (checkByte(c) && nameHash - b >= '0' || checkByte(c) && nameHash - b == NULL)
            {
                nameHash -= b;
                key[idx] = c;
            }

            // Выход из цикла, если весь хеш разложен на строку
            if (nameHash == 0) {
                return key;
            }
        }
    }
    
    return nullptr;
}
Это срабатывает, но только в половине случаев:

Код:
Name: 'hallo0'  (0x52)  pass: (null)
Name: 'hallo1'  (0xC4)  pass: (null)
Name: 'hallo2'  (0x7E)  pass: (null)
Name: 'hallo3'  (0xE8)  pass: q
Name: 'hallo4'  (0x4B)  pass: (null)
Name: 'hallo5'  (0xDD)  pass: D
Name: 'hallo6'  (0x67)  pass: (null)
Name: 'hallo7'  (0xF1)  pass: h
Name: 'hallo8'  (0x60)  pass: (null)
Name: 'hallo9'  (0xF6)  pass: o
Name: 'hallo10' (0xF7)  pass: n
Name: 'hallo11' (0x61)  pass: (null)
Name: 'hallo12' (0xDB)  pass: B
Name: 'hallo13' (0x4D)  pass: (null)
Name: 'hallo14' (0xEE)  pass: w
Name: 'hallo15' (0x78)  pass: (null)
Name: 'hallo16' (0xC2)  pass: (null)
Name: 'hallo17' (0x54)  pass: (null)
Name: 'hallo18' (0xC5)  pass: (null)
Name: 'hallo19' (0x53)  pass: (null)
Скрытое содержимое
Есть прога в которую вводишь имя и пароль, для каждого имени подходят только определенные пароли. То как эта прога их сверяет я уже разобрал в IDA и переписал с ассемблера на С++. Этот код в шапке.

Теперь мне нужно как-то сгенерировать такой пароль для имени, что бы он принимался прогой. Суть в том, что хеш имени и пароля проходят по разным алгоритмам, но в итоге они должны быть одинаковые.
 
Последнее редактирование:
Начинающий
Статус
Оффлайн
Регистрация
31 Май 2023
Сообщения
54
Реакции[?]
16
Поинты[?]
15K
Вот что у меня получилось:

C++:
byte* genPass(byte nameHash)
{
    static byte key[37] = { NULL };
    memset(key, 0, sizeof(key));

    // 0xD1 (209)
    /*
        209 ^ 153(0x99) = 48 ("H")
        209 - 209 = 0
       
        name "hallo"  (0xD1)    = pass "H"
        name "hallo1" (0xC4)    = error
        name "hallo2" (0x7E)    = error
        name "hallo3" (0xE8)    = pass "q"
    */

    for (size_t idx = 0; idx < 37; idx++)
    {
        for (byte c = 'z'; c > '0'; c--)
        {
            byte b = c ^ 0x99;
            if (b > nameHash) {
                continue;
            }

            // Проверка символа, подходит ли он и подойдет ли следующий, если вычесть текущий
            if (checkByte(c) && nameHash - b >= '0' || checkByte(c) && nameHash - b == NULL)
            {
                nameHash -= b;
                key[idx] = c;
            }

            // Выход из цикла, если весь хеш разложен на строку
            if (nameHash == 0) {
                return key;
            }
        }
    }
   
    return nullptr;
}
Это срабатывает, но только в половине случаев:

Код:
Name: 'hallo0'  (0x52)  pass: (null)
Name: 'hallo1'  (0xC4)  pass: (null)
Name: 'hallo2'  (0x7E)  pass: (null)
Name: 'hallo3'  (0xE8)  pass: q
Name: 'hallo4'  (0x4B)  pass: (null)
Name: 'hallo5'  (0xDD)  pass: D
Name: 'hallo6'  (0x67)  pass: (null)
Name: 'hallo7'  (0xF1)  pass: h
Name: 'hallo8'  (0x60)  pass: (null)
Name: 'hallo9'  (0xF6)  pass: o
Name: 'hallo10' (0xF7)  pass: n
Name: 'hallo11' (0x61)  pass: (null)
Name: 'hallo12' (0xDB)  pass: B
Name: 'hallo13' (0x4D)  pass: (null)
Name: 'hallo14' (0xEE)  pass: w
Name: 'hallo15' (0x78)  pass: (null)
Name: 'hallo16' (0xC2)  pass: (null)
Name: 'hallo17' (0x54)  pass: (null)
Name: 'hallo18' (0xC5)  pass: (null)
Name: 'hallo19' (0x53)  pass: (null)
Есть прога в которую вводишь имя и пароль, для каждого имени подходят только определенные пароли. То как эта прога их сверяет я уже разобрал в IDA и переписал с ассемблера на С++. Этот код в шапке.

Теперь мне нужно как-то сгенерировать такой пароль для имени, что бы он принимался прогой. Суть в том, что хеш имени и пароля проходят по разным алгоритмам, но в итоге они должны быть одинаковые.
чувак мне кажется ты совершил ошибку, я полагаю A-Z a-z значит что пароль должен состоять из символов от a до z тоесть по алфавиту, а ты проверяешь только символы a z 0 и 9
 
Начинающий
Статус
Оффлайн
Регистрация
11 Дек 2023
Сообщения
11
Реакции[?]
0
Поинты[?]
0
чувак мне кажется ты совершил ошибку, я полагаю A-Z a-z значит что пароль должен состоять из символов от a до z тоесть по алфавиту, а ты проверяешь только символы a z 0 и 9
зображення_2023-12-12_175215270.png
А точно, я забыл скинуть функцию checkByte.
Цикл перебирает все символы от 0 до z, между ними находятся 1-9, потом идут спецсимволы, потом A-Z и в конце таблицы в маленьком регистре a-z.
Вот checkByte:


C++:
bool checkByte(byte b) {
    return  (
                (b >= '0' && b <= '9') ||
                (b >= 'a' && b <= 'z') ||
                (b >= 'A' && b <= 'Z')
            );
}

Дизассемблированный код проги:
DWORD32 genNameHash(byte *szName)
{
    DWORD32 crc = -1;
    for (DWORD idx = 0; idx < strlen((char *)szName); idx++)
    {
        crc ^= szName[idx];
        for (int j = 0; j < 8; j++)
        {
            char converChar = (crc & 1);
            crc = -converChar & 0xEDB88320 ^ (crc >> 1);

            /*
                .text:00438076 mov     eax, [ebp+crc]
                .text:00438079 and     eax, 1
                .text:0043807C neg     eax                      // CHAR!
                .text:0043807E mov     [ebp+numBuff], eax
                .text:00438081 mov     eax, [ebp+crc]
                .text:00438084 shr     eax, 1
                .text:00438086 mov     ecx, [ebp+numBuff]
                .text:00438089 and     ecx, 0EDB88320h
                .text:0043808F xor     eax, ecx
                .text:00438091 mov     [ebp+crc], eax
            */
        }
    }

    //printf("name hash: 0x%X(0x%X)\n", ~crc, ~crc & 0xFF);
    return ~crc;
}

DWORD32 genPassHash(byte* pass)
{
    size_t idx = 0; DWORD32 hash = 0;
    while (pass[idx] != NULL)
    {
        hash += pass[idx++] ^ 0x99;

        /*
            mov     eax, [ebp+pass]
            add     eax, [ebp+i]
            movsx   ecx, byte ptr [eax]
            xor     ecx, 99h
            add     ecx, [ebp+hash]
            mov     [ebp+hash], ecx
        */
    }

    //printf("pass hash: 0x%X(0x%X)\n", hash, hash & 0xFF);
    return hash;
}

bool isValid(byte *str)
{
    size_t idx = 0;
    while (str[idx] != NULL)
    {
        if (str[idx] >= 'a' && str[idx] <= 'z' ||
            str[idx] >= 'A' && str[idx] <= 'Z' ||
            str[idx] >= '0' && str[idx] <= '9')
        {
            idx++;
            continue;
        }

        return false;
    }

    return true;
}

bool isAccess(byte* name, byte* pass)
{
    if (!isValid(name)) {
        return MessageBox(NULL, "Name a-z, A-Z, 0-9", "Falsch Daten", 0);
    }

    if (!isValid(pass)) {
        return MessageBox(NULL, "Pass a-z, A-Z, 0-9", "Falsch Daten", 0);
    }

    return (genNameHash(name) & 0xFF) == (genPassHash(pass) & 0xFF);

    /*
        mov     [ebp+nameHash], 0
        mov     [ebp+passHash], 0
        mov     eax, [ebp+name]
        push    eax
        call    j_nameHash
        add     esp, 4
        and     eax, 0FFh
        mov     [ebp+nameHash], eax
        mov     eax, [ebp+pass]
        push    eax             ; Str
        call    j_genPassHash
        add     esp, 4
        and     eax, 0FFh
        mov     [ebp+passHash], eax
        mov     eax, [ebp+nameHash]
        cmp     eax, [ebp+passHash]
        jnz     short loc_437ED8
    */
}
Мой генератор паролей к этой проге (рабочий в 50% случаев):
bool checkByte(byte b) {
    return  (
                (b >= '0' && b <= '9') ||
                (b >= 'a' && b <= 'z') ||
                (b >= 'A' && b <= 'Z')
            );
}

byte* genPass(byte nameHash)
{
    static byte key[37] = { NULL };
    memset(key, 0, sizeof(key));

    // 0xD1 (209)
    /*
        209 ^ 153(0x99) = 48 ("H")
        209 - 209 = 0
      
        name "hallo"  (0xD1)    = pass "H"
        name "hallo1" (0xC4)    = error
        name "hallo2" (0x7E)    = error
        name "hallo3" (0xE8)    = pass "q"
    */

    for (size_t idx = 0; idx < 37; idx++)
    {
        for (byte c = 'z'; c > '0'; c--)
        {
            byte b = c ^ 0x99;
            if (b > nameHash) {
                continue;
            }

            // Проверка символа, подходит ли он и подойдет ли следующий, если вычесть текущий
            if (checkByte(c) && nameHash - b >= '0' || checkByte(c) && nameHash - b == NULL)
            {
                nameHash -= b;
                key[idx] = c;
            }

            // Выход из цикла, если весь хеш разложен на строку
            if (nameHash == 0) {
                return key;
            }
        }
    }
  
    return nullptr;
}

int main() {
    // Crachme Lektion 3
    //return printf("Name: hallo\nKey: %u", genKey("hallo")), true;


    // Crackme Lektion 8
    byte name[] = "hallo", pass[] = "h";
    printf("Access: %s\n", (isAccess(name, pass)) ? "true" : "false");
    for (size_t i = 0; i < 20; i++)
    {
      
        char buff[100];
        sprintf(buff, "%s%d", name, i);
        byte hash = genNameHash((byte *)buff);
        byte* pass = genPass(hash);

        printf("Name: '%s'\t(0x%X)\tpass: %s\n", buff, hash, pass);
    }
  
    getchar();

    return true;
}
 
Последнее редактирование:
Начинающий
Статус
Оффлайн
Регистрация
11 Дек 2023
Сообщения
11
Реакции[?]
0
Поинты[?]
0
Не получилось ничего умного придумать, сделал обычный брут 3х значных паролей с хешированием для +100к ловкости. Я думаю это читерство и задача не считается выполненной, так что было бы узнать еще чьи то идеи))


Мой вариант с брутом:
byte* brutePass(byte nameHash)
{
    static byte key[10] = { NULL }, tab['9' - '0' + 'z' - 'a' + 'Z' - 'A' + 3] = { NULL };
    memset(key, NULL, sizeof(key)); // because static var

    auto gni = [](byte b) -> byte // get next index
    {
        for (byte idx = 0; idx < sizeof(tab); idx++)
        {
            if (tab[idx] == b) {
                return (idx + 1 < sizeof(tab)) ? idx + 1 : 255;
            }
        }

        return 255; // trigger for [idx + 1] = '+'
    };

    if (tab[0] == NULL) // Table loading...
    {
        byte tidx = 0;
        for (byte b = '0'; b <= 'z'; b++)
        {
            if (checkByte(b)) { // 0-9, a-z, A-Z
                tab[tidx++] = b;
            }
        }
    }

    DWORD32 hashes[256] = { 0 };
    static byte hash_cache[256][4] = { NULL };
    if (hash_cache[nameHash][0] != NULL) {
        //return printf("Password findet in table: %s\n", hash_cache[nameHash]), hash_cache[nameHash];
    }
    strcpy((char*)key, "aaa");

    while (true)
    {
        byte res = NULL;
        for (size_t idx = 0; idx < sizeof(tab); idx++)
        {
            key[0] = tab[idx]; // +XX
            byte hash = genPassHash(key);
            strcpy((char*)hash_cache[hash], (char *)key);

            hashes[hash]++;

            if (hash == nameHash) {
                //return key;
            }

            //printf("KEY: %s\t\tHash: 0x%X\t\tZiel: 0x%X\n", key, hash, nameHash);
        }

        for (byte idx = 0; idx < 3; idx++)
        {
            byte nidx = gni(key[idx]);
            if (nidx == 255)
            {
                key[idx] = tab[0];
                if (idx == 2) {
                    //return MessageBox(NULL, "Overflow", "Something going wrong...", NULL), key;

                    DWORD32 c = 0;
                    for (byte ix = 0; ix < 256; ix++)
                    {
                        printf("[%d] Hash 0x%X \t\t %u times...\n", ix, ix, hashes[ix]);
                        c += hashes[ix];
                    }

                    printf("count: %u times...\n", c);
                    return key;
                }

                nidx = gni(key[idx + 1]);
                key[idx + 1] = (nidx == 255) ? '+' : tab[nidx];
            }
        }
    }

    return key;
}
Результат 100%

Результат:
Name: hallo0                Password: 70b
Hash: 0x52:0x52             Access: true


Password findet in table:     uya
Name: hallo1                Password: uya
Hash: 0xC4:0xC4             Access: true


Name: hallo2                Password: C0b
Hash: 0x7E:0x7E             Access: true


Password findet in table:     coa
Name: hallo3                Password: coa
Hash: 0xE8:0xE8             Access: true


Name: hallo4                Password: 11b
Hash: 0x4B:0x4B             Access: true


Password findet in table:     nwa
Name: hallo5                Password: nwa
Hash: 0xDD:0xDD             Access: true


Password findet in table:     Z0b
Name: hallo6                Password: Z0b
Hash: 0x67:0x67             Access: true


Password findet in table:     bga
Name: hallo7                Password: bga
Hash: 0xF1:0xF1             Access: true


Name: hallo8                Password: Z9d
Hash: 0x60:0x60             Access: true


Password findet in table:     ggc
Name: hallo9                Password: ggc
Hash: 0xF6:0xF6             Access: true


Password findet in table:     fgc
Name: hallo10               Password: fgc
Hash: 0xF7:0xF7             Access: true


Password findet in table:     Z8d
Name: hallo11               Password: Z8d
Hash: 0x61:0x61             Access: true


Password findet in table:     jwc
Name: hallo12               Password: jwc
Hash: 0xDB:0xDB             Access: true


Password findet in table:     68d
Name: hallo13               Password: 68d
Hash: 0x4D:0x4D             Access: true


Password findet in table:     goc
Name: hallo14               Password: goc
Hash: 0xEE:0xEE             Access: true


Password findet in table:     B9d
Name: hallo15               Password: B9d
Hash: 0x78:0x78             Access: true


Password findet in table:     qyc
Name: hallo16               Password: qyc
Hash: 0xC2:0xC2             Access: true


Password findet in table:     07d
Name: hallo17               Password: 07d
Hash: 0x54:0x54             Access: true


Password findet in table:     ryc
Name: hallo18               Password: ryc
Hash: 0xC5:0xC5             Access: true


Password findet in table:     17d
Name: hallo19               Password: 17d
Hash: 0x53:0x53             Access: true
 
Не люблю ЧСВ
Забаненный
Статус
Оффлайн
Регистрация
11 Июл 2022
Сообщения
335
Реакции[?]
21
Поинты[?]
20K
Обратите внимание, пользователь заблокирован на форуме. Не рекомендуется проводить сделки.
Не получилось ничего умного придумать, сделал обычный брут 3х значных паролей с хешированием для +100к ловкости. Я думаю это читерство и задача не считается выполненной, так что было бы узнать еще чьи то идеи))


Мой вариант с брутом:
byte* brutePass(byte nameHash)
{
    static byte key[10] = { NULL }, tab['9' - '0' + 'z' - 'a' + 'Z' - 'A' + 3] = { NULL };
    memset(key, NULL, sizeof(key)); // because static var

    auto gni = [](byte b) -> byte // get next index
    {
        for (byte idx = 0; idx < sizeof(tab); idx++)
        {
            if (tab[idx] == b) {
                return (idx + 1 < sizeof(tab)) ? idx + 1 : 255;
            }
        }

        return 255; // trigger for [idx + 1] = '+'
    };

    if (tab[0] == NULL) // Table loading...
    {
        byte tidx = 0;
        for (byte b = '0'; b <= 'z'; b++)
        {
            if (checkByte(b)) { // 0-9, a-z, A-Z
                tab[tidx++] = b;
            }
        }
    }

    DWORD32 hashes[256] = { 0 };
    static byte hash_cache[256][4] = { NULL };
    if (hash_cache[nameHash][0] != NULL) {
        //return printf("Password findet in table: %s\n", hash_cache[nameHash]), hash_cache[nameHash];
    }
    strcpy((char*)key, "aaa");

    while (true)
    {
        byte res = NULL;
        for (size_t idx = 0; idx < sizeof(tab); idx++)
        {
            key[0] = tab[idx]; // +XX
            byte hash = genPassHash(key);
            strcpy((char*)hash_cache[hash], (char *)key);

            hashes[hash]++;

            if (hash == nameHash) {
                //return key;
            }

            //printf("KEY: %s\t\tHash: 0x%X\t\tZiel: 0x%X\n", key, hash, nameHash);
        }

        for (byte idx = 0; idx < 3; idx++)
        {
            byte nidx = gni(key[idx]);
            if (nidx == 255)
            {
                key[idx] = tab[0];
                if (idx == 2) {
                    //return MessageBox(NULL, "Overflow", "Something going wrong...", NULL), key;

                    DWORD32 c = 0;
                    for (byte ix = 0; ix < 256; ix++)
                    {
                        printf("[%d] Hash 0x%X \t\t %u times...\n", ix, ix, hashes[ix]);
                        c += hashes[ix];
                    }

                    printf("count: %u times...\n", c);
                    return key;
                }

                nidx = gni(key[idx + 1]);
                key[idx + 1] = (nidx == 255) ? '+' : tab[nidx];
            }
        }
    }

    return key;
}
Результат 100%

Результат:
Name: hallo0                Password: 70b
Hash: 0x52:0x52             Access: true


Password findet in table:     uya
Name: hallo1                Password: uya
Hash: 0xC4:0xC4             Access: true


Name: hallo2                Password: C0b
Hash: 0x7E:0x7E             Access: true


Password findet in table:     coa
Name: hallo3                Password: coa
Hash: 0xE8:0xE8             Access: true


Name: hallo4                Password: 11b
Hash: 0x4B:0x4B             Access: true


Password findet in table:     nwa
Name: hallo5                Password: nwa
Hash: 0xDD:0xDD             Access: true


Password findet in table:     Z0b
Name: hallo6                Password: Z0b
Hash: 0x67:0x67             Access: true


Password findet in table:     bga
Name: hallo7                Password: bga
Hash: 0xF1:0xF1             Access: true


Name: hallo8                Password: Z9d
Hash: 0x60:0x60             Access: true


Password findet in table:     ggc
Name: hallo9                Password: ggc
Hash: 0xF6:0xF6             Access: true


Password findet in table:     fgc
Name: hallo10               Password: fgc
Hash: 0xF7:0xF7             Access: true


Password findet in table:     Z8d
Name: hallo11               Password: Z8d
Hash: 0x61:0x61             Access: true


Password findet in table:     jwc
Name: hallo12               Password: jwc
Hash: 0xDB:0xDB             Access: true


Password findet in table:     68d
Name: hallo13               Password: 68d
Hash: 0x4D:0x4D             Access: true


Password findet in table:     goc
Name: hallo14               Password: goc
Hash: 0xEE:0xEE             Access: true


Password findet in table:     B9d
Name: hallo15               Password: B9d
Hash: 0x78:0x78             Access: true


Password findet in table:     qyc
Name: hallo16               Password: qyc
Hash: 0xC2:0xC2             Access: true


Password findet in table:     07d
Name: hallo17               Password: 07d
Hash: 0x54:0x54             Access: true


Password findet in table:     ryc
Name: hallo18               Password: ryc
Hash: 0xC5:0xC5             Access: true


Password findet in table:     17d
Name: hallo19               Password: 17d
Hash: 0x53:0x53             Access: true
посему у тебя нейм всегда хало?)
 
Начинающий
Статус
Оффлайн
Регистрация
11 Дек 2023
Сообщения
11
Реакции[?]
0
Поинты[?]
0
посему у тебя нейм всегда хало?)
Это не важно, если хоть 1 символ имени меняется, то получается абсолютно другой хеш, а что бы проверить в цикле генерацию паролей для нескольких имен я добавил 1 цифру к имени.
Че за нанотехнологии :pogchamp:
Сам в шоке, это только 8-я лекция... На 3-й там было изи вообще, имя переводится в хеш и потом этот хеш ксорится, результат это ключ в шеснацетиричном виде переведенный в строку. А тут я чуть голову не сломал...
 
Пользователь
Статус
Оффлайн
Регистрация
14 Апр 2020
Сообщения
162
Реакции[?]
45
Поинты[?]
4K
Это не важно, если хоть 1 символ имени меняется, то получается абсолютно другой хеш, а что бы проверить в цикле генерацию паролей для нескольких имен я добавил 1 цифру к имени.

Сам в шоке, это только 8-я лекция... На 3-й там было изи вообще, имя переводится в хеш и потом этот хеш ксорится, результат это ключ в шеснацетиричном виде переведенный в строку. А тут я чуть голову не сломал...
а что за курс?
 
Начинающий
Статус
Оффлайн
Регистрация
11 Дек 2023
Сообщения
11
Реакции[?]
0
Поинты[?]
0
Внес не большие правки, дело в том, что я по не внимательности сделал перебор от aaa до zzz, а выгоднее сделать от 0 до zzz. Поэтому в функцию gni получающую следующий символ в таблице я добавил условие:
C++:
if (b == NULL) {
    return NULL;
}
В начале функции весь массив заполняется нулями, а при добавлении нового символа конец строки (0й символ) будет заменен на "0", т.е. 1-й символ из таблицы.
Второе изменение заключатся в убирании strcpy((char*)key, "aaa"); так как это задавало конфигурацию для перебора, с изменённой gni это больше не имеет смысла.
Анализ программного обеспечения и обратная разработка
 
Keine panik!
Эксперт
Статус
Оффлайн
Регистрация
29 Апр 2020
Сообщения
812
Реакции[?]
417
Поинты[?]
49K
Подскажите пожалуйста.
Сначала стоит рассмотреть саму функцию проверки.
Помимо очевидных проверок валидности логина и пароля мы видим главную часть.
return (genNameHash(name) & 0xFF) == (genPassHash(pass) & 0xFF);
Т.е. из логина выводится некий продукт и сравнивается с другим продуктом от пароля и они должны совпадать.
Причем из-за 0xFF мы можем понять что таких вариаций может быть всего лишь 256, т.к. это один байт.
Дальше глядим на функу genNameHash(), в ней мелькает константа от какой-то вариации crc32, вероятно у нее нормальное распределение, и заходить через нее не так просто.
Смотрим на genPassHash(), это уже интереснее, из нее можно понять, что весь ее продукт это просто сумма байт, каждый из которых символ ^ 0x99.
То есть, можно зайти с того, что есть произвольный логин, и к нему нужно получить какой-то пароль, который пройдет проверку.
Продукт от логина может 0-255, значит вся задача сводится к тому, чтобы научиться набирать нужную сумму из символов пароля.
Посмотрим какое у нас распределение:
0=0xA9 1=0xA8 2=0xAB 3=0xAA 4=0xAD 5=0xAC 6=0xAF 7=0xAE 8=0xA1 9=0xA0 A=0xD8 B=0xDB C=0xDA D=0xDD E=0xDC F=0xDF G=0xDE H=0xD1 I=0xD0 J=0xD3 K=0xD2 L=0xD5 M=0xD4 N=0xD7 O=0xD6 P=0xC9 Q=0xC8 R=0xCB S=0xCA T=0xCD U=0xCC V=0xCF W=0xCE X=0xC1 Y=0xC0 Z=0xC3 a=0xF8 b=0xFB c=0xFA d=0xFD e=0xFC f=0xFF g=0xFE h=0xF1 i=0xF0 j=0xF3 k=0xF2 l=0xF5 m=0xF4 n=0xF7 o=0xF6 p=0xE9 q=0xE8 r=0xEB s=0xEA t=0xED u=0xEC v=0xEF w=0xEE x=0xE1 y=0xE0 z=0xE3 =0x99
Ксор просто перемешивает символы определенным способом, при этом никакое из значений не теряется, однако, наш входной алфавит позволяет только a-zA-Z0-9, что уже не дает нам найти решение в один символ для любого crc.
И даже в 2 символа, для большинства значений, т.к. весь наш алфавит находится в одной половине ascii таблицы, и двух слагаемых будет недостаточно.
Чтобы быстро найти все возможные решения, можно просто перебрать 3 символа для каждой возможной суммы crc.
0x00="007" 0x01="006" 0x02="027" 0x03="026" 0x04="047" 0x05="046" 0x06="067" 0x07="066" 0x08="19Y" 0x09="09Y" 0x0A="08Y" 0x0B="08X" 0x0C="09Z" 0x0D="08Z" 0x0E="29Z" 0x0F="28Z" 0x10="11Y" 0x11="01Y" 0x12="00Y" 0x13="00X" 0x14="01Z" 0x15="00Z" 0x16="03Z" 0x17="02Z" 0x18="05Z" 0x19="01Q" 0x1A="00Q" 0x1B="00P" 0x1C="00S" 0x1D="00R" 0x1E="00U" 0x1F="00T" 0x20="00W" 0x21="00V" 0x22="00I" 0x23="00H" 0x24="00K" 0x25="00J" 0x26="00M" 0x27="00L" 0x28="00O" 0x29="00N" 0x2A="00A" 0x2B="01C" 0x2C="00C" 0x2D="00B" 0x2E="00E" 0x2F="00D" 0x30="00G" 0x31="00F" 0x32="00y" 0x33="00x" 0x34="01z" 0x35="00z" 0x36="03z" 0x37="02z" 0x38="05z" 0x39="01q" 0x3A="00q" 0x3B="00p" 0x3C="00s" 0x3D="00r" 0x3E="00u" 0x3F="00t" 0x40="00w" 0x41="00v" 0x42="00i" 0x43="00h" 0x44="00k" 0x45="00j" 0x46="00m" 0x47="00l" 0x48="00o" 0x49="00n" 0x4A="00a" 0x4B="01c" 0x4C="00c" 0x4D="00b" 0x4E="00e" 0x4F="00d" 0x50="00g" 0x51="00f" 0x52="00" 0x53="02f" 0x54="02" 0x55="04f" 0x56="04" 0x57="06f" 0x58="06" 0x59="0AA" 0x5A="0BO" 0x5B="0AC" 0x5C="0AB" 0x5D="0AE" 0x5E="0AD" 0x5F="0AG" 0x60="0AF" 0x61="0Ay" 0x62="0Ax" 0x63="0BF" 0x64="0Az" 0x65="0Bx" 0x66="0Cz" 0x67="0Bz" 0x68="0Ez" 0x69="0Aq" 0x6A="0Ap" 0x6B="0As" 0x6C="0Ar" 0x6D="0Au" 0x6E="0At" 0x6F="0Aw" 0x70="0Av" 0x71="0Ai" 0x72="0Ah" 0x73="0Ak" 0x74="0Aj" 0x75="0Am" 0x76="0Al" 0x77="0Ao" 0x78="0An" 0x79="0Aa" 0x7A="0Bo" 0x7B="0Ac" 0x7C="0Ab" 0x7D="0Ae" 0x7E="0Ad" 0x7F="0Ag" 0x80="0Af" 0x81="0A" 0x82="0Bg" 0x83="0Bf" 0x84="0B" 0x85="0Df" 0x86="0D" 0x87="0Ff" 0x88="0F" 0x89="0aq" 0x8A="0ap" 0x8B="0as" 0x8C="0ar" 0x8D="0au" 0x8E="0at" 0x8F="0aw" 0x90="0av" 0x91="0ai" 0x92="0ah" 0x93="0ak" 0x94="0aj" 0x95="0am" 0x96="0al" 0x97="0ao" 0x98="0an" 0x99="0aa" 0x9A="0bo" 0x9B="0ac" 0x9C="0ab" 0x9D="0ae" 0x9E="0ad" 0x9F="0ag" 0xA0="0af" 0xA1="0a" 0xA2="0bg" 0xA3="0bf" 0xA4="0b" 0xA5="0df" 0xA6="0d" 0xA7="0ff" 0xA8="0f" 0xA9="0" 0xAA="2f" 0xAB="2" 0xAC="4f" 0xAD="4" 0xAE="6f" 0xAF="6" 0xB0="AA" 0xB1="ABg" 0xB2="ABf" 0xB3="AB" 0xB4="ADf" 0xB5="AD" 0xB6="AFf" 0xB7="AF" 0xB8="Aaq" 0xB9="Aap" 0xBA="Aas" 0xBB="Aar" 0xBC="Aau" 0xBD="Aat" 0xBE="Aaw" 0xBF="Aav" 0xC0="Aai" 0xC1="Aah" 0xC2="Aak" 0xC3="Aaj" 0xC4="Aam" 0xC5="Aal" 0xC6="Aao" 0xC7="Aan" 0xC8="Aaa" 0xC9="Abo" 0xCA="Aac" 0xCB="Aab" 0xCC="Aae" 0xCD="Aad" 0xCE="Aag" 0xCF="Aaf" 0xD0="Aa" 0xD1="Abg" 0xD2="Abf" 0xD3="Ab" 0xD4="Adf" 0xD5="Ad" 0xD6="Aff" 0xD7="Af" 0xD8="A" 0xD9="Bff" 0xDA="Bf" 0xDB="B" 0xDC="Df" 0xDD="D" 0xDE="Ff" 0xDF="F" 0xE0="999" 0xE1="899" 0xE2="889" 0xE3="888" 0xE4="aam" 0xE5="aal" 0xE6="aao" 0xE7="aan" 0xE8="199" 0xE9="099" 0xEA="089" 0xEB="088" 0xEC="289" 0xED="288" 0xEE="489" 0xEF="488" 0xF0="119" 0xF1="019" 0xF2="009" 0xF3="008" 0xF4="029" 0xF5="028" 0xF6="049" 0xF7="048" 0xF8="069" 0xF9="011" 0xFA="001" 0xFB="000" 0xFC="003" 0xFD="002" 0xFE="005" 0xFF="004"
Причем чем больше символов доступно, тем больше решений на каждый отдельный црц можно получить.
Таким образом у нас есть таблица на каждый возможный црц, достаточно лишь получить црц от имени и найти его в этой таблице, что и будет паролем.
Можно так же манипулировать и каким-то непустым начальным паролем, дополняя его на нужную сумму.
Если хочешь решение именно без перебора, то самое первое забавное что пришло в голову:
Все символы пароля после ксора дают отрицательно число (т.к. >= 0x80), это значит что каждое из них вычитает из текущей crc сколько-то.
Так мы можем вычислить сколько нам надо вычесть: sub = 0 - crc;
Из этого следует что самый интересный символ это вообще f, т.к. он вычитает 1, то из одних только f можно получить любую црц (правда пароль при этом может быть в худшем случае 255 длиной).
Поэтому можно немного оптимизировать задачу сделав шаг не по 1, а скажем по 1,2,4,8,16,32,64,128, так что в худшем случае у нас будет пароль состоять из 8 символов.
Код:
uint8_t sub = 0 - crc;
std::string pass = "";
if (sub & 0x80) pass += 'Y', pass += 'Y'; // >= 128, т.к. у нас нет символа который вычитает 0x80, то вычитаем дважды по 0x40
if (sub & 0x40) pass += 'Y'; // >= 64
if (sub & 0x20) pass += 'y'; // >= 32
if (sub & 0x10) pass += 'i'; // >= 16
if (sub & 0x08) pass += 'a'; // >= 8
if (sub & 0x04) pass += 'e'; // >= 4
if (sub & 0x02) pass += 'g'; // >= 2
if (sub & 0x01) pass += 'f'; // >= 1
Еще интересно, что т.к. продукт это сумма, то буквы в пароле можно переставлять и при этом црц не изменится.
 
Последнее редактирование:
Начинающий
Статус
Оффлайн
Регистрация
11 Дек 2023
Сообщения
11
Реакции[?]
0
Поинты[?]
0
Сначала стоит рассмотреть саму функцию проверки.
Помимо очевидных проверок валидности логина и пароля мы видим главную часть.
return (genNameHash(name) & 0xFF) == (genPassHash(pass) & 0xFF);
Т.е. из логина выводится некий продукт и сравнивается с другим продуктом от пароля и они должны совпадать.
Причем из-за 0xFF мы можем понять что таких вариаций может быть всего лишь 256, т.к. это один байт.
Дальше глядим на функу genNameHash(), в ней мелькает константа от какой-то вариации crc32, вероятно у нее нормальное распределение, и заходить через нее не так просто.
Смотрим на genPassHash(), это уже интереснее, из нее можно понять, что весь ее продукт это просто сумма байт, каждый из которых символ ^ 0x99.
То есть, можно зайти с того, что есть произвольный логин, и к нему нужно получить какой-то пароль, который пройдет проверку.
Продукт от логина может 0-255, значит вся задача сводится к тому, чтобы научиться набирать нужную сумму из символов пароля.
Посмотрим какое у нас распределение:
0=0xA9 1=0xA8 2=0xAB 3=0xAA 4=0xAD 5=0xAC 6=0xAF 7=0xAE 8=0xA1 9=0xA0 A=0xD8 B=0xDB C=0xDA D=0xDD E=0xDC F=0xDF G=0xDE H=0xD1 I=0xD0 J=0xD3 K=0xD2 L=0xD5 M=0xD4 N=0xD7 O=0xD6 P=0xC9 Q=0xC8 R=0xCB S=0xCA T=0xCD U=0xCC V=0xCF W=0xCE X=0xC1 Y=0xC0 Z=0xC3 a=0xF8 b=0xFB c=0xFA d=0xFD e=0xFC f=0xFF g=0xFE h=0xF1 i=0xF0 j=0xF3 k=0xF2 l=0xF5 m=0xF4 n=0xF7 o=0xF6 p=0xE9 q=0xE8 r=0xEB s=0xEA t=0xED u=0xEC v=0xEF w=0xEE x=0xE1 y=0xE0 z=0xE3 =0x99
Ксор просто перемешивает символы определенным способом, при этом никакое из значений не теряется, однако, наш входной алфавит позволяет только a-zA-Z0-9, что уже не дает нам найти решение в один символ для любого crc.
И даже в 2 символа, для большинства значений, т.к. весь наш алфавит находится в одной половине ascii таблицы, и двух слагаемых будет недостаточно.
Чтобы быстро найти все возможные решения, можно просто перебрать 3 символа для каждой возможной суммы crc.
0x00="007" 0x01="006" 0x02="027" 0x03="026" 0x04="047" 0x05="046" 0x06="067" 0x07="066" 0x08="19Y" 0x09="09Y" 0x0A="08Y" 0x0B="08X" 0x0C="09Z" 0x0D="08Z" 0x0E="29Z" 0x0F="28Z" 0x10="11Y" 0x11="01Y" 0x12="00Y" 0x13="00X" 0x14="01Z" 0x15="00Z" 0x16="03Z" 0x17="02Z" 0x18="05Z" 0x19="01Q" 0x1A="00Q" 0x1B="00P" 0x1C="00S" 0x1D="00R" 0x1E="00U" 0x1F="00T" 0x20="00W" 0x21="00V" 0x22="00I" 0x23="00H" 0x24="00K" 0x25="00J" 0x26="00M" 0x27="00L" 0x28="00O" 0x29="00N" 0x2A="00A" 0x2B="01C" 0x2C="00C" 0x2D="00B" 0x2E="00E" 0x2F="00D" 0x30="00G" 0x31="00F" 0x32="00y" 0x33="00x" 0x34="01z" 0x35="00z" 0x36="03z" 0x37="02z" 0x38="05z" 0x39="01q" 0x3A="00q" 0x3B="00p" 0x3C="00s" 0x3D="00r" 0x3E="00u" 0x3F="00t" 0x40="00w" 0x41="00v" 0x42="00i" 0x43="00h" 0x44="00k" 0x45="00j" 0x46="00m" 0x47="00l" 0x48="00o" 0x49="00n" 0x4A="00a" 0x4B="01c" 0x4C="00c" 0x4D="00b" 0x4E="00e" 0x4F="00d" 0x50="00g" 0x51="00f" 0x52="00" 0x53="02f" 0x54="02" 0x55="04f" 0x56="04" 0x57="06f" 0x58="06" 0x59="0AA" 0x5A="0BO" 0x5B="0AC" 0x5C="0AB" 0x5D="0AE" 0x5E="0AD" 0x5F="0AG" 0x60="0AF" 0x61="0Ay" 0x62="0Ax" 0x63="0BF" 0x64="0Az" 0x65="0Bx" 0x66="0Cz" 0x67="0Bz" 0x68="0Ez" 0x69="0Aq" 0x6A="0Ap" 0x6B="0As" 0x6C="0Ar" 0x6D="0Au" 0x6E="0At" 0x6F="0Aw" 0x70="0Av" 0x71="0Ai" 0x72="0Ah" 0x73="0Ak" 0x74="0Aj" 0x75="0Am" 0x76="0Al" 0x77="0Ao" 0x78="0An" 0x79="0Aa" 0x7A="0Bo" 0x7B="0Ac" 0x7C="0Ab" 0x7D="0Ae" 0x7E="0Ad" 0x7F="0Ag" 0x80="0Af" 0x81="0A" 0x82="0Bg" 0x83="0Bf" 0x84="0B" 0x85="0Df" 0x86="0D" 0x87="0Ff" 0x88="0F" 0x89="0aq" 0x8A="0ap" 0x8B="0as" 0x8C="0ar" 0x8D="0au" 0x8E="0at" 0x8F="0aw" 0x90="0av" 0x91="0ai" 0x92="0ah" 0x93="0ak" 0x94="0aj" 0x95="0am" 0x96="0al" 0x97="0ao" 0x98="0an" 0x99="0aa" 0x9A="0bo" 0x9B="0ac" 0x9C="0ab" 0x9D="0ae" 0x9E="0ad" 0x9F="0ag" 0xA0="0af" 0xA1="0a" 0xA2="0bg" 0xA3="0bf" 0xA4="0b" 0xA5="0df" 0xA6="0d" 0xA7="0ff" 0xA8="0f" 0xA9="0" 0xAA="2f" 0xAB="2" 0xAC="4f" 0xAD="4" 0xAE="6f" 0xAF="6" 0xB0="AA" 0xB1="ABg" 0xB2="ABf" 0xB3="AB" 0xB4="ADf" 0xB5="AD" 0xB6="AFf" 0xB7="AF" 0xB8="Aaq" 0xB9="Aap" 0xBA="Aas" 0xBB="Aar" 0xBC="Aau" 0xBD="Aat" 0xBE="Aaw" 0xBF="Aav" 0xC0="Aai" 0xC1="Aah" 0xC2="Aak" 0xC3="Aaj" 0xC4="Aam" 0xC5="Aal" 0xC6="Aao" 0xC7="Aan" 0xC8="Aaa" 0xC9="Abo" 0xCA="Aac" 0xCB="Aab" 0xCC="Aae" 0xCD="Aad" 0xCE="Aag" 0xCF="Aaf" 0xD0="Aa" 0xD1="Abg" 0xD2="Abf" 0xD3="Ab" 0xD4="Adf" 0xD5="Ad" 0xD6="Aff" 0xD7="Af" 0xD8="A" 0xD9="Bff" 0xDA="Bf" 0xDB="B" 0xDC="Df" 0xDD="D" 0xDE="Ff" 0xDF="F" 0xE0="999" 0xE1="899" 0xE2="889" 0xE3="888" 0xE4="aam" 0xE5="aal" 0xE6="aao" 0xE7="aan" 0xE8="199" 0xE9="099" 0xEA="089" 0xEB="088" 0xEC="289" 0xED="288" 0xEE="489" 0xEF="488" 0xF0="119" 0xF1="019" 0xF2="009" 0xF3="008" 0xF4="029" 0xF5="028" 0xF6="049" 0xF7="048" 0xF8="069" 0xF9="011" 0xFA="001" 0xFB="000" 0xFC="003" 0xFD="002" 0xFE="005" 0xFF="004"
Причем чем больше символов доступно, тем больше решений на каждый отдельный црц можно получить.
Таким образом у нас есть таблица на каждый возможный црц, достаточно лишь получить црц от имени и найти его в этой таблице, что и будет паролем.
Можно так же манипулировать и каким-то непустым начальным паролем, дополняя его на нужную сумму.
Если хочешь решение именно без перебора, то самое первое забавное что пришло в голову:
Все символы пароля после ксора дают отрицательно число (т.к. >= 0x80), это значит что каждое из них вычитает из текущей crc сколько-то.
Так мы можем вычислить сколько нам надо вычесть: sub = 0 - crc;
Из этого следует что самый интересный символ это вообще f, т.к. он вычитает 1, то из одних только f можно получить любую црц (правда пароль при этом может быть в худшем случае 255 длиной).
Поэтому можно немного оптимизировать задачу сделав шаг не по 1, а скажем по 1,2,4,8,16,32,64,128, так что в худшем случае у нас будет пароль состоять из 8 символов.
Код:
uint8_t sub = 0 - crc;
std::string pass = "";
if (sub & 0x80) pass += 'Y', pass += 'Y'; // >= 128, т.к. у нас нет символа который вычитает 0x80, то вычитаем дважды по 0x40
if (sub & 0x40) pass += 'Y'; // >= 64
if (sub & 0x20) pass += 'y'; // >= 32
if (sub & 0x10) pass += 'i'; // >= 16
if (sub & 0x08) pass += 'a'; // >= 8
if (sub & 0x04) pass += 'e'; // >= 4
if (sub & 0x02) pass += 'g'; // >= 2
if (sub & 0x01) pass += 'f'; // >= 1
Еще интересно, что т.к. продукт это сумма, то буквы в пароле можно переставлять и при этом црц не изменится.
Я весь этот путь прошел, но за 2 дня размылений. А вот с реализацией вычитания классно придумал, думаю это то что нужно. Спасибо!))
 
Сверху Снизу