Решение thePCdepp - kgm no 1

Сложность: 2 - Needs a little brain (or luck)
Платформа: Windows
Язык: С/С++
Дано: kgm no 1

hokay homiez, so this should be pretty straightforward, no antidebugging, get to the keycheck via the usual GetWindowText reference, and a nice ass keycheck routine written in hand-written assembly for your convenience, so no pain there, but writing a proper keygen might prove a little bit nifty.

valid solution is a keygen that generates valid keys for any given name. it is absolutely not necessary to rip any code. the keys will look kinda whack but that's okay, this aint no beauty contest.

a version of the keygenme with the actual keygen included is included, so yall can see that this kgm aint no non-working bullshit. disassembling the keygen kinda defies the whole point of solving a keygenme, so if you do, you must be kinda dum or somethin.

peace out negus

thePCdepp

Решение (автор promix17)
опубликовано 02.06.2012

Инструменты: OllyDBG

Нас просят ввести имя и соответствующий ему ключ. Имя и ключ могут быть длиной до 64 символов. Проверка происходит по нажатию кнопки «Check».

image00.jpg

ОК. Грузим файл в OllyDBG и находим место проверки ключа.



Как видим алгоритм проверки ключа выполняется в функции sub_401050. Причем, результат работы этой функции должен быть равен 0, если мы хотим решить задачку. Посмотрим, что у нее внутри:

1) Сначала идет проверка введенных символов. Код символов должен быть не более 0x80.



2) Берем указанное имя и формируем мини-хэш в 32 бита. Весь хэш умещается в регистре EAX.



Последняя операция AND регистра eax с маской 0x0F0F0F0F задает итоговый шаблон хэша.
«0х0х0х0х» - где «х» принимает значения от 0 до F.

3) И тут начинается самая интересная часть алгоритма.



Это своего рода виртуальная машина, код которой задается содержимым ключа. Указателем команд является регистр ESI, указывающий на текущий байт ключа. Каждый байт ключа – это некая команда следующего формата:

image02.jpg

xxx – любое значение
yyy – здесь задается счетчик цикла (EDX) или смещение для указателя команд
zz – опкод команды виртуальной машины (выделено 2 бита, значит, всего команд может быть – только 4):
00 (0) – EDX = yyy (установить счетчик цикла)
01 (1) – ROR EAX, 8
10 (2) – DEC AL (AL = Al - 1)
11 (3) – CMP EDX, 0 ? EDX = EDX – 1 : ESI = ESI - yyy (условие: цикл завершен? Если цикл не завершен, то в битах «yyy» находится смещение для указателя команд на начало цикла)
Например, символ «<» = ASCII код 0x3C = 0011 1100 = 001 111 00 = команда 00, а значение 111 = EDX=7

Код, представленный выше (00401094 - 004010EC) – это разбор опкодов и выполнение команд виртуальной машины.
А теперь задумаемся: какую логику должна выполнять виртуальная машина? Вспомним, что произошло: взяли имя, сформировали мини-хэш, хэш находится в регистре EAX. И что дальше? Нам известно, что функция проверки должна вернуть 0, чтобы было всё тип-топ. Следовательно, логика виртуальной машины такова: взять хэш в EAX и обнулить его (EAX = 0), используя символы ключа как команды самой виртуальной машины (помним, команд всего 4).

Наши действия:

image01.jpg

Итак, чтобы сгенерировать ключ, надо:
1. Посчитать хэш имени
2. Для каждого байта хэша создать команды обнуления на «языке» виртуальной машины (по сути, описать цикл – инициализация счетчика цикла, декремент значения байта, проверка окончания цикла).

Алгоритм создания ключа на Python:

# -*- coding: cp1251 -*-
# Операция - циклический сдвиг вправо
def ror(num, count, type):
    cnt = count % type
    a = type - cnt
    numcur = num
    if numcur < 0:
        numcur &= 0x7FFFFFFF
        ror_low = numcur >> cnt
        ror_low = ror_low | (1<<(a-1))
    else:
        ror_low = numcur >> cnt
    numcur = num
    ror_hi = numcur << a
    ror = ror_hi | ror_low
    if type == 8:
        ror = ror & 0x000000FF
    if type == 16:
        ror = ror & 0x0000FFFF
    if type == 32:
        ror = ror & 0xFFFFFFFF
    return ror
 
# ОСНОВНАЯ ЧАСТЬ
# Здесь задаем имя. Для примера, я указал «aaaa» (хэш такого имени 
# имеет максимальное значение 0F0F0F0F)
name = 'aaaa'
 
# Формируем хэш нашего имени
eax = 0
for i in range(len(name)):
    eax = ror(eax, 8, 32)
    ch_1 = ((eax & 0xFF) - ord(name[i])) & 0xFF
    eax = (eax & 0xFFFFFF00) | ch_1
# Накладываем на хэш битовую маску
eax = eax & 0x0F0F0F0F
 
# Создание ключа для нашего имени
# Базовый ASCII код символа я беру 0х40, хотя можно взять и 0х20, и 0х60
key = ''
for i in range(4):
    ch_1 = eax & 0xFF
    while ch_1 > 0:
        # вычисляем значение счетчика цикла
        if ch_1 > 8:
            cnt = 7
        else:
            cnt = ch_1 - 1
        # команда 0 - MOV EDX, значение счетчика цикла
        key += chr(0x40 | (cnt << 2) | 0)
        # команда 2 - DEC AL (начало цикла, кстати, весь цикл у нас состоит из одной команды )
        key += chr(0x40 | 0 | 2)
        # команда 3 - CMP EDX, 0 ? EDX = EDX – 1: ESI = ESI - yyy
        # yyy = (8 >> 2 = 2 – смещение для указателя команд на начало цикла)
        key += chr(0x40 | 8 | 3)
        ch_1 = ch_1 - 8
 
    eax = eax >> 8
    if eax == 0:
        break
    # команда 1 - ROR EAX, 8
    key += chr(0x40 | 0 | 1)
 
print ("[*]Name = "+name)
print ("[+]Key = "+key)

image03.jpg

Примеры:
[*]Name = Promix17
[+]Key = \BKLBKAXBKATBK

[*]Name = crackmes.de
[+]Key = PBKA\BKDBKALBKA\BKPBK

[*]Name = solutionmes
[+]Key = DBKA\BKPBKA\BKLBKAHBK

Пока не указано иное, содержимое этой страницы распространяется по лицензии Creative Commons Attribution-ShareAlike 3.0 License