Решение mopy - Simple math keygenme ;P

Сложность: 2 - Needs a little brain (or luck)
Платформа: Windows
Язык: Assembler
Дано: Simple math keygenme ;P

I think this little keygenme is to hard for you :P
It uses simple math instructions,but in a hard order :D

-no patching

on success:
-write a keygen + solution

Решение (автор MR.HAANDI)
опубликовано 20.06.2012

Инструменты: OllyDBG (лично я использовал IDA)

1. Анализ

Запустив приложение, замечаем, что исходными данными является только ключ (serial). Его и требуется активировать.

image00.jpg

Открываем «Keygenme.exe» в IDA и находим следующее:



Общий анализ:
а) 0040107A – Читаем ключ из диалога GetDlgItemTextA
б) 00401089 – Проверяем длину ключа. Она должна быть равна 64 символам (64 байта).
в) 004010B0 (CALL 00403F20) – Ключ из строки (64 байта) преобразуем в 32 байта. Например, “2519” => 0x25 0x19
г) 004010BF (CALL 00401125) – Последовательность из 32 байт преобразуем в восемь 32-хбитных чисел
{s1; s2; … ; s8}
Если мы в качестве ключа введем строку «0000000100000002000000030000000400000005000000060000000700000008»
То в результате получим целые числа {s1 = 1; s2 = 2;…; s8 = 8}.
д) 004010C4 (CALL 00401151) – вся «магия» (основной алгоритм проверки ключа) происходит здесь. Функция возвращает 1 в случае успеха и 0 – в случае ошибки. Соответственно, goodboy-сообщение «You did it! Now write a keygen and a tutorial!» и badboy-сообщение «Work harder!». Посмотрим, что внутри функции. Вычисляются некоторые выражения. И что интересно. В коде вообще нет никаких циклов, всего лишь пять условий (CMP, JNZ). И если мы хотим добиться успеха, условия должны выполниться. Поэтому берем ручку и шаг за шагом выписываем вычисляемые подвыражения v__.

v282 = ((s2 XOR s8) AND 0x6487169F) XOR s2
v288 = ((s2 XOR s8) AND 0x6487169F) XOR s8

v355 = ((s3 XOR s5) AND 0x5B7A027F) XOR s5
v353 = ((s3 XOR s5) AND 0x5B7A027F) XOR s3

v474 = ((s4 XOR s7) AND 0x11258023) XOR s4
v477 = ((s4 XOR s7) AND 0x11258023) XOR s7

v166 = ((s1 XOR s6) AND 0x20890481) XOR s6
v161 = ((s1 XOR s6) AND 0x20890481) XOR s1

А теперь пять условий, которые должны выполниться:

  • v474 XOR v288 XOR 0x35059FC5 == 0x48A86FC4
  • v166 XOR v355 XOR 0x54A80618 == 0x489AC581
  • v477 XOR v474 XOR 0x35059FC5 == 0x6582138E
  • NOT (((umulhi(0xF0F0F0F1, v161 XOR 0x698B93C2) » 4) * (v355 XOR 0x54A80618) + (umulhi(0x4EC4EC4F, v161 XOR 0x698B93C2) » 2) * (v474 XOR 0x35059FC5) + (v355 XOR 0x54A80618) * (v474 XOR 0x35059FC5)) == v282
  • NOT ((v161 XOR 0x698B93C2) - (umulhi(0xAAAAAAAB, (v161 XOR 0x698B93C2) * (v474 XOR 0x35059FC5)) » 1) + (v355 XOR 0x54A80618)) == v353

umulhi – таким образом, мы выделили операцию взятия старших 32 бит от 64-хбитного произведения двух 32-хбитных беззнаковых чисел.
» - логический сдвиг вправо

Если рассматривать подвыражения v__ как переменные, то мы можем использовать пять условий для вычисления всех v__ из заранее определенных v161, v474, v355.

  • v288 = v474 XOR 0x35059FC5 XOR 0x48A86FC4
  • v166 = v355 XOR 0x54A80618 XOR 0x489AC581
  • v477 = v474 XOR 0x35059FC5 XOR 0x6582138E
  • v282 = NOT (((umulhi(0xF0F0F0F1, v161 XOR 0x698B93C2) » 4) * (v355 XOR 0x54A80618) + (umulhi(0x4EC4EC4F, v161 XOR 0x698B93C2) » 2) * (v474 XOR 0x35059FC5) + (v355 XOR 0x54A80618) * (v474 XOR 0x35059FC5))
  • v353 = NOT ((v161 XOR 0x698B93C2) - (umulhi(0xAAAAAAAB, (v161 XOR 0x698B93C2) * (v474 XOR 0x35059FC5)) » 1) + (v355 XOR 0x54A80618))

Возникает вопрос, как вычислить сам ключ из известных подвыражений. Давайте взглянем на структуру подвыражения:
((a XOR b) AND c) XOR a

А теперь представим, что a, b, c состоят из одного бита. Тогда:
Если c = 0, то
((a XOR b) AND c) XOR a = ((a XOR b) AND 0) XOR a = 0 XOR a = a
Если c = 1, то
((a XOR b) AND c) XOR a = ((a XOR b) AND 1) XOR a = (a XOR b) XOR a = b

Таким образом, значение маски «с» просто выбирает значение бита либо из первого аргумента «а», либо из второго «b». Обратите внимание, что каждая пара подвыражений (например, v282 и v288) используют одну и ту же маску (например, 0x6487169F). Следовательно, можно полностью восстановить часть ключа s2 и s8 из этих подвыражений.
Итак, получаем части ключа:

s2 = ((v282 XOR v288) AND 0x6487169F) XOR v282
s8 = ((v282 XOR v288) AND 0x6487169F) XOR v288

s1 = ((v161 XOR v166) AND 0x20890481) XOR v161
s6 = ((v161 XOR v166) AND 0x20890481) XOR v166
и т.д.

2. Кейген

Алгоритм прост:
1-й шаг – зададим случайным образом или вручную значения подвыражений (беззнаковые 32-хбитные числа) v161, v474, v355
2-й шаг – вычислим значения оставшихся подвыражений (v288, v166, v477, v282, v353) на основании заданных v161, v474, v355
3-й шаг – обратим подвыражения v__ в соответствующие части ключа {s1 … s8}

# -*- coding: cp1251 -*-
import random
 
def umulhi(a, b):
    return ((a * b) >> 32) & 0xFFFFFFFF
 
v161 = v166 = v353 = v474 = v477 = v282 = v288 = 0
mask28 = 0x6487169F
mask16 = 0x20890481
mask35 = 0x5B7A027F
mask47 = 0x11258023
 
#задаем значения подвыражений (v161, v474, v355)
#случайным образом
v161 = random.randint(0, 0x7FFFFFFF)
v474 = random.randint(0, 0x7FFFFFFF)
v355 = random.randint(0, 0x7FFFFFFF)
#хотя можно установить и руками
#v161 = 1
#v474 = 1
#v355 = 1
 
#1-я проверка v474 XOR v288 XOR 35059FC5 == 48A86FC4
v288 = 0x48A86FC4 ^ 0x35059FC5 ^ v474;
#2-я проверка v166 XOR v355 XOR 54A80618 == 489AC581
v166 = 0x489AC581 ^ 0x54A80618 ^ v355;
#3-я проверка v477 XOR v474 XOR 35059FC5 == 6582138E
v477 = 0x6582138E ^ 0x35059FC5 ^ v474;
#4-я проверка ... == v282
v282  = (((umulhi(0xF0F0F0F1, v161 ^ 0x698B93C2)>>4) * (v355 ^ 0x54A80618)) +
    ((umulhi(0x4EC4EC4F, v161 ^ 0x698B93C2) >> 2) * (v474 ^ 0x35059FC5)) +
    ((v355 ^ 0x54A80618) * (v474 ^ 0x35059FC5)))
#v282 = ~v282
v282 = (v282 & 0xFFFFFFFF) ^ 0xFFFFFFFF
 
#5-я проверка  ... == v353
 
#здесь мы разбиваем все выражение на составляющие, связано это с тем,
#что в Python нет типа - беззнаковое целое число (числа всегда со знаком),
#поэтому нам надо правильно обработать операцию "вычитание" с заемом из
#старшего разряда
x = umulhi(0xAAAAAAAB,  ((v161 ^ 0x698B93C2) * (v474 ^ 0x35059FC5)) & 0xFFFFFFFF) >> 1
y = (v161 ^ 0x698B93C2)
if y < x:
    y |= 0x100000000
v353 = y - x
v353 = y - x + (v355 ^ 0x54A80618)
 
#v353 = ~v353
v353 = (v353 & 0xFFFFFFFF) ^ 0xFFFFFFFF
 
#вычисляем числа, входящие в serial
s = [0,0,0,0,0,0,0,0]
s[0] = ((v161 ^ v166) & mask16) ^ v161
s[5] = ((v161 ^ v166) & mask16) ^ v166
 
s[2] = ((v353 ^ v355) & mask35) ^ v353
s[4] = ((v353 ^ v355) & mask35) ^ v355
 
s[3] = ((v474 ^ v477) & mask47) ^ v474
s[6] = ((v474 ^ v477) & mask47) ^ v477
 
s[1] = ((v282 ^ v288) & mask28) ^ v282
s[7] = ((v282 ^ v288) & mask28) ^ v288
 
#печатаем ключ
serial = ''
for i in range(8):
    serial += format(s[i],"08X")
print ("[+]Serial = "+serial)


Примеры:
[+]Serial = CD7FFBBD983B06E2775E92CEEF9BBFC08E7F497DEBED8857BF1C338B8237DF49
[+]Serial = 3323D71F67B9A92ADBF03D781A3139283A1D8CE947474B614AB6B563539B4BB5
[+]Serial = 4DE7773752A70D615A9494322E2F9C6306C767A2622FA6AA7EA810282782FC68
[+]Serial = 233B4C948C96FD04FE6AF2157936E546234F7F456655BC0D29B1690D50988754
[+]Serial = 25D71C27EB6C19AE29F7EE0A15BCA0AC669B8ED611404912453B2CE77C90D4AD

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