Статья [Reverse-Engineering] Решаем сложный crackme от P4SH3

  • colby57
  • UNEXPECTED_KERNEL_MODE_TRAP_M (1000007f)
  • 155
  • 2
  • 238
Шалом.
Сегодня на разборе у меня довольно тяжелый крякми для новичков, который я не мог пройти как только вкатился в реверс. На самом деле тут было всё очень просто.
Просто из-за постоянной шифровки импортов тяжело было понять шо именно делал крякми. Перейдем сразу к разбору.

Начнем по порядку.

1. Шифрование импортов + строк
2. Создание подпроцесса, выделение памяти и запись в процесс.
3. Нахождение ключа

1. Шифрование импортов + строк

О том, что шифруются импорты я узнал только когда решил перепройти этот крякми спустя год
Импорты скрыты.png


Со строками было все понятно, просто для декрипта строку кидало в цикл для расшифровки.
Декрипт строки нужного импорта.png


Результат декрипнутой строки вначале передавался в ESP+1C, затем из в ESP+1C в EAX для того шо бы стать параметром для вызова нужного импорта. Но эта заготовка с декрипнутой строкой будет использоваться чуть-чуть позже, потому шо автор крякми после этих инструкции передает в EAX строку 'kernel32' и уже внутри следующей функции использует декрипнутый импорт с ESP+1C
Сейчас объясню суть крипты импортов. Дело в том, что после декрипта нужного импорта крякми вызывает следующую функцию, назовем FindImport. Суть этой функции заключается в том шо бы принимать один параметр и затем перебирать в нужной дллке её экспорты, если он находит нужную функцию, то он возвращает адрес нужного ему экспорта. НО, перед вызовом нужного импорта в программе он постоянно таким же методом вызывает LoadLibraryA(dll_name); и только потом уже сам импорт. Вначале я думал, что вызывается только kernel32.dll, но затем я заметил шо под раздачу попал и shell32.dll, но там уже ничо такого интересного я не находил.

Декрипт строки нужного импорта + LoadLibraryA.png


Вот пример первого импорта для крякми. Как я и писал выше, вначале идет вызов FindImport("LoadLibraryA"); затем после получения адреса он уже вызывает CALL DWORD PTR SS:[EBP-10], в котором сидит адрес который вернул нам FindImport. После нас джампит на 0040385C [MOV EDX, DWORD PTR SS:[EBP+C]], где EBP+C является строкой нужного импорта.

Код:
MOV ECX,EAX ; ECX = "LoadLibraryA"
CALL <kreker_v2.FindImport> ; FindImport("LoadLibraryA")

PUSH DWORD PTR SS:[EBP+8] ; Имя нужной библиотеки для загрузки, для LoadLibraryA это - kernel32
CALL DWORD PTR SS:[EBP-10] ; Вызов LoadLibraryA

После вызова LoadLibraryA начинается своего рода рекурсия, но с подменой других значений.
Вот пример вызова GetCurrentProcessId

Вызов нужного импорта.png


Вначале программа проделывает те же действия, шо и на второй скрине, затем видим, что программа вначале пихает декрипнутый "GetCurrentProcessId", а затем вызывает FindImport.
Код:
MOV EDX,DWORD PTR SS:[EBP+C] ; EDX = "GetCurrentProcessId"
CALL <kreker_v2.FindImport> ; FindImport("GetCurrentProcessId");
MOV DWORD PTR SS:[EBP-18], EAX ; В DWORD PTR SS:[EBP-18] суется адрес GetCurrentProcessId

CALL DWORD PTR SS:[EBP-18] ; Вызов GetCurrentProcessId

Надеюсь, что вы поняли метод крипты и вызова импорта :)
Кстати, не советую эту кашу закидывать в иду

2. Создание подпроцесса, выделение памяти и запись в процесс.
Почти разобрались с импортами, но вот как тогда передаются параметры в функцию? Как мы знаем GetCurrentProcessId не имеет при себе параметров, а шо по поводу других WinAPI-функции? На самом деле тут все просто, перед последующим вызовом FindImport с LoadLibrary и т.д. в качестве параметров в стек суются нужные параметры и название импорта, вот как пример VirtualAllocEx
Вызов VirtualAllocEx.png


Шо бы не было путаницы щас покажу шо происходит с EAX во время вызова, ситуация такая же как и с LoadLibraryA. Вначале в стек суется EAX, в котором хранится строка "kernel32.dll", затем в EAX суется уже DWORD PTR SS:[EBP+27], в котором хранилась строка "VirtualAllocEx". Откуда взялась декрипнутая строка "VirtualAllocEx"? По всей видимости функция с декриптом является встраиваемой функций (__forceinline) и до параметров сверху находилась та функция с декриптом (не в виде колла, в этом и суть __forceinline :) )
Про встраиваемые функции можно почитать здесь: тык

Код:
LEA EAX,DWORD PTR SS:[ESP+1C] ; EAX = "VirtualAllocEx"
PUSH 0
PUSH 340
PUSH 3000
PUSH 40
PUSH 5
PUSH EAX ; push VirtualAllocEx
LEA EAX,DWORD PTR SS:[ESP+27] ; EAX = "kernel32.dll"
PUSH EAX ; push kernel32.dll
CALL kreker_v2.4037C0 ; Вызов функции, в которой декрипнется снова импорт, получит адрес и совершит вызов с нужными параметрами

Теперь с шифрованием импортов точно всё. Осталась легкая часть, зная как работает крипта импортов мы можем спокойно оставить бряки на одном вызове и мониторить какие функции будут вызываться, VirtualAllocEx мы уже пропалили.
И действительно, через вызов нужного импорта постоянно совершался на одном и том же месте, поэтому ничо уже не мешало отследить все вызовы.

Вначале спалился CreateProcessW

Вызов CreateProcessW.png

У него к слову путь к нотпаду тоже был зашифрованным, но метод декрипта мы разбирать не будем, ибо это вообще не имеет никакого смысла.

Затем вызвался снова наш VirtualAllocEx, WriteProcessMemory, SetThreadContext и GetThreadContext, CloseHandle. Потом снова CreateProcessW с параметром 00496480 L"C:\\Windows\\system32\\cmd.exe /c pause"

По итогу можно делать вывод, шо проверка на ключ находится в notepad.exe, которую создал наш крякми, выделив место и записав какой-то код.

3. Нахождение ключа

Подходим к завершающему этапу этой статейки.
Деаттачимся от процесса и аттачимся к нотпаду.
Сразу видим "интересный" вызов к месту, которое было выделено из нашего крякми, переходим к нему.
Выделенное место в нотпаде.png


Нотпад открывает хендл нашего крякми и вызывает ReadProcessMemory для чтения адреса, думаю уже догадались для чего:)
Читаем данные.png


Видим, что дальше идет сравнение CMP DWORD PTR SS:[EBP-5C], 23D4
Но и до этого DWORD PTR SS:[EBP-5C] использовался как lpBuffer в ReadProcessMemory, поэтому наш ключ который мы ввели хранится именно в этой переменной.

Ключ: 23D4 или 9172.

Win!!.png


Вот и подошел к концу разбор этого крякми, сравнение с паролем конечно немного разочаровало, можно было придумать чо-нибудь и покруче чем сравнение переменной и значения правильного ключа. Если кто захочет опробовать крякми - тык


Подписывайтесь на мой бложик: t.me/colby5engineering
Всего доброго! :)
 
Активность
Пока что здесь никого нет
Сверху Снизу