Решение ideku_nih - Code this

Сложность: 3 – Getting harder
Платформа: Windows
Язык: (Visual) Basic
Дано: Code this

This program is set for Cracker Prof.

Your mission :
Get Code and Serial

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

Скажу честно, вся сложность crackme заключается в анализе p-code Visual Basic'а. Сам алгоритм защиты несложный. Автор решения использовал инструменты: RDG Packer Detector (для того, чтобы узнать, на чем написан crackme), VB Decompiler, Exdec (декомпиляторы Visual Basic). Мы же ограничимся VB Decompiler. Приведу цитату с сайта разработчика.

VB Decompiler - это декомпилятор программ (EXE, DLL, OCX), написанных на Visual Basic 5.0 и 6.0 и дизассемблер программ, написанных на любом из языков .NET технологии. Как известно, программы, разработанные на Visual Basic'е, могут быть скомпилированы либо в интерпретируемый p-code, либо в выполняемый native code. Так как p-code представляет собой высокоуровневые команды, то появляется реальная возможность восстановить из этого кода исходный (правда имена переменных и некоторых функций само собой восстановить не удастся). VB Decompiler восстанавливает исходный код из псевдокода максимально близко к оригинальному, поэтому его при некоторых доработках реально довести до компилируемого.


Список команд p-code можно найти здесь


01 -> Анализ


Основная форма

image02.jpg

На что обращаешь внимание - кнопка "ОК" заблокирована, и что удивительно - кнопка "Quit" не работает вообще. Далее, мы должны получить исходный код crackme. Загружаем его в декомпилятор. Основные функции crackme отлично видны в декомпиляторе (их всего четыре).

image00.jpg

Достаточно статического анализа кода функций, чтобы понять логику програмы:

image01.jpg
Функция KeyUp (00402Е44) - при каждом нажатии клавиши в поле CODE срабатывает этот обработчик

Private Sub CODE_KeyUp(KeyCode As Integer, Shift As Integer) '402E44
'Data Table: 402724
loc_402DB8: …
loc_402DCD: FnLenStr (Me.CODE.Text)
loc_402DCE: LitI4 4
loc_402DD3: GtI4
loc_402DD4: …
loc_402DDA: BranchF exit; длина поля Me.CODE.Text должна быть более 4-х символов
loc_402DDD: …
loc_402DF9: ourserialsum= checkcode(Me.CODE.Text)
loc_402DFE: …
loc_402E0E: LitVarI2 is70, 70; число 70
loc_402E13: …
loc_402E14: GtVar var_A0; > 70
loc_402E18: …
loc_402E1B: LitVarI2 is90, 90; число 90
loc_402E20: …
loc_402E21: LtVar var_E0; < 90
loc_402E25: AndVar var_F0; and
loc_402E29: CBoolVarNull
loc_402E2B: BranchF exit; должно выполниться условие CBool((ourserialsum > 70) and (ourserialsum < 90))
loc_402E2E: ..
loc_402E3A: Me.Enabled; включить доступность кнопки "ОК"
loc_402E3F: …

exit:
loc_402E42: …
loc_402E43: …
End Sub

Псевдокод:



Функция checkcode

Public Function checkcode(a = Me.CODE.Text) '402D68
'Data Table: 402724
loc_402CEC: …
loc_402CFE: ForVar i in 1..Len(a) {
Возвращает строку, содержащую указанное число знаков строки. В нашем случае - один символ
loc_402D11: ImpAdCallI2 res = Mid$(a, i, 1)
Возвращает код символа
loc_402D19: ImpAdCallI2 res2 = Asc(res)
loc_402D31: AddVar allres += res2
loc_402D3C: NextStepVar }
loc_402D42: …
loc_402D45: ILdI4 a
loc_402D48: FnLenStr; получить длину строки
loc_402D49: CVarI4
loc_402D4D: DivVar allres = allres / Len(a)
loc_402D51: FStVar
loc_402D55: …
loc_402D58: FnIntVar allres = Int(allres); возвращает только целую часть числа
loc_402D5C: …
loc_402D60: ExitProcCbHresult
End Function

Псевдокод:

Функция по кнопке "ОК" (00402F70)

Private Sub Command1_Click() '402F70
'Data Table: 402724
loc_402E90: FLdRfVar var_90
loc_402E93: …
loc_402EAA: ImpAdCallI2 Left$(Me.CODE.Text, 0x0A) -> ourcode взять первые 10 символов CODE
loc_402EAF: …
loc_402ECD: CVarStr Me.SERIAL.Text -> ourserial
loc_402ED0: …
loc_402ED7: x= cript(ourcode)
loc_402EDC: …
loc_402EDF: EqVarBool сравниваем ourserial и x
loc_402EE1: …
loc_402EEB: BranchF если не равно, то переходим к badboy-сообщению
loc_402EEE: показываем на экран goodboy-сообщение
loc_402F17: End

badboy-сообщение:
loc_402F19: показываем на экран badboy-сообщение
End Sub

Псевдокод:


Функция cript

Public Function cript(a) '403108
'Data Table: 402724
loc_402FBC: …
loc_402FCF: ImpAdCallFPR4 up_a = Ucase(a) строку перевести в верхний регистр
loc_402FD4: …
loc_402FEE: ForVar i in 1..Len(up_a) {
loc_402FF4: …
loc_403001: ImpAdCallI2 ordchr = Mid$(up_a, i, 1)
loc_403009: ImpAdCallI2 res = Asc(ordchr) возвращает код символа
loc_40300E: LitI2_Byte 9
loc_403010: SubI2 res = res - 9
loc_403011: LitI2_Byte 0x58
loc_403013: XorI4 res = res ^ 0x58
loc_403015: …
loc_40301B: AddVar res = res + i
loc_40301F: …
loc_40302C: LitVarI2 2
loc_403031: PwrVar res = res ** 2
loc_403035: …
loc_403039: ILdRf allres
loc_40303C: …
loc_403043: AddVar allres = allres + res
loc_403047: CI4Var
loc_403049: FStR4 allres
loc_40304C: …
loc_403052: NextStepVar }
loc_403058: …
loc_403065: ForVar i in 1..100 {
loc_40306B: … cur_cnt = i
loc_403090: ImpAdCallFPR4 chr = Mid$(Me.CODE.Text, cur_cnt, 1)
loc_403095: …
loc_4030A9: LitVarStr var_AC, vbNullString
loc_4030AE: HardType
loc_4030AF: EqVarBool chr == vbNullString
loc_4030B1: BranchF next_chr
loc_4030B4: Branch end_loop

next_chr:
loc_4030B7: …
loc_4030BA: NextStepVar }

end_loop
loc_4030C0: ILdRf allres
loc_4030C3: CVarI4
loc_4030C7: FLdRfVar cur_cnt
loc_4030CA: LitVarI2 is2, 2
loc_4030CF: DivVar (cur_cnt / 2)
loc_4030D3: FnIntVar Int(cur_cnt / 2)
loc_4030D7: MulVar allres * Int(cur_cnt / 2)
loc_4030DB: LitVarI2 is16, 16
loc_4030E0: MulVar allres * Int(cur_cnt / 2) * 16
loc_4030E4: …
loc_4030F4: ImpAdCallFPR4 result_cript = Hex(allres)
loc_4030F9: …
loc_403100: ExitProcCbHresult
End Function

Псевдокод:


02 -> Кейген - алгоритм



1. Входной параметр - имя (длина более 4-х символов)
2. Вычисляем CODE (имя + дополнение - лично я беру из строки вида "0..9a..z" - чтобы удовлетворить условию функции checkcode)
3. На основании CODE считаем SERIAL (берем первые 10 символов CODE и считаем SERIAL по функции cript)

alpha = '1234567890abcdefghijklmnopqrstuvwxyz'
 
def check_code(s):
    res = 0
    z = 0
    for i in range(0,len(s)):
        z += ord(s[i])
    z = z / len(s)
    if ((z > 70) & (z < 90)):
        res = 1
 
    return res
 
def create_code(s):
    temp = s
    i = 0
    while check_code(temp) == 0:
        temp += alpha[i]
        i += 1
 
    return temp
 
#входной параметр, здесь пишем любое имя
s = 'crackmes.de' 
 
#вычисляем CODE
code = create_code(s)
 
#берем первые 10 символов CODE и переводим строку в верхний регистр
up_a = code[0:10].upper() 
serial = 0
 
#вычисляем SERIAL: 
#ремарка - в Visual Basic счетчик цикла начинается с 1, поэтому
#в коде встречается такой финт - i + 1 вместо i
for i in range(0,len(up_a)):
    serial = serial + (((ord(up_a[i]) - 9) ^ 0x58) + i + 1) ** 2
 
#len(code) + 1 - позиция первого нулевого байта строки CODE
serial *= int((len(code) + 1) / 2) * 16
 
print "[*]Name = "+s
print "[+]CODE = "+code
print "[+]SERIAL = "+hex(serial)[2:].upper()


Примеры:

[*]Name = solutionmes
[+]CODE = solutionmes123456
[+]SERIAL = 2D2400

[*]Name = deurus
[+]CODE = deurus1234
[+]SERIAL = 6441F0

[*]Name = ideku_nih
[+]CODE = ideku_nih1234
[+]SERIAL = 5AC2C0

[*]Name = abcde
[+]CODE = abcde12
[+]SERIAL = 4D8240

image04.jpg

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