Всем привет! Есть у меня crackme где есть вот такой код: Код (Text): 0040B051 56 PUSH ESI 0040B052 51 PUSH ECX 0040B053 50 PUSH EAX 0040B054 8D75 87 LEA ESI,DWORD PTR SS:[EBP-79] 0040B057 B9 B1000000 MOV ECX,0B1 0040B05C 0FB6440E FF MOVZX EAX,BYTE PTR DS:[ESI+ECX-1] 0040B061 03D8 ADD EBX,EAX 0040B063 C1CB 04 ROR EBX,4 0040B066 03D9 ADD EBX,ECX 0040B068 ^E2 F2 LOOPD SHORT fp2.0040B05C 0040B06A 58 POP EAX 0040B06B 59 POP ECX 0040B06C 5E POP ESI 0040B06D C3 RETN 0040B06E 64:67:FF36 0000 PUSH DWORD PTR FS:[0] 0040B074 64:67:8926 0000 MOV DWORD PTR FS:[0],ESP 0040B07A E8 00000000 CALL fp2.0040B07F 0040B07F 5D POP EBP 0040B080 8D7D 3B LEA EDI,DWORD PTR SS:[EBP+3B] 0040B083 8B5D CE MOV EBX,DWORD PTR SS:[EBP-32] 0040B086 CC INT3 ; INT3 anti-debug 0040B087 E8 C5FFFFFF CALL fp2.0040B051 ; being debugged 0040B08C B9 EE010000 MOV ECX,1EE ; not being debugged 0040B091 F7C1 0F000000 TEST ECX,0F 0040B097 75 10 JNZ SHORT fp2.0040B0A9 ; jump if ZF = 0 0040B099 9C PUSHFD 0040B09A 810C24 00010000 OR DWORD PTR SS:[ESP],100 0040B0A1 9D POPFD 0040B0A2 90 NOP 0040B0A3 90 NOP 0040B0A4 E8 A8FFFFFF CALL fp2.0040B051 0040B0A9 D3C3 ROL EBX,CL 0040B0AB 305C0F FF XOR BYTE PTR DS:[EDI+ECX-1],BL ; PTR 40B2A7 - for first iteration 0040B0AF ^E2 E0 LOOPD SHORT fp2.0040B091 ; Loop while equal, after this loop code is decrypted Насколько я понимаю, CALL fp2.0040B051 выполняется только в случае если программу дебажат. Чтобы обойти этот момент я пробовал занопить INT3 и CALL fp2.0040B051, однако если сохраняю эти изменения на диск, то crackme перестает работать. Далее я попробовал установить в настроках Olly "Ignore (pass to program) following exception: INT3 breaks". Правильно ли я понимаю, что благодаря этой опции crackme сам будет обрабатывть CC opcode и не поймет, что его дебажат? В таком случае получается, что после Step Into (F7) в 0040B086 EIP становится 0040B0A3 а вот если F8 то EIP -> 0040B08C Откуда такое разное поведение и как оно работает на самом деле? Спасибо за помощь PS файл crackme приложил PPS Использую OllyDgb v1.10
Я немного продвинулся в понимании этого кода, получается, что в Код (Text): 0040B06E 64:67:FF36 0000 PUSH DWORD PTR FS:[0] 0040B074 64:67:8926 0000 MOV DWORD PTR FS:[0],ESP ; new SEH exception Добавляется новая запись в SEH, в stack это выглядит как Код (Text): 0019FF4C 0019FFCC Pointer to next SEH record 0019FF50 0040B006 SE handler И когда случается INT3 прерывание, то управление должно переходить по адресу 0040B006 (должно ли?) Правда в отладчике этого не происходит, что меня совсем запутало ((
В коде устанавливается SEH обработчик по адресу 0040B006, по этому адресу содержится код который проверяет код исключения должен быть EXCEPTION_BREAKPOINT или EXCEPTION_SINGLE_STEP. Если исключение EXCEPTION_BREAKPOINT то обработчик обнуляет DWORD по адресу 0040B04D. Далее идет дешифровка тела основного кода на основе функции расчета хеша (0040B051). Если продолжить исполнение без обработчика ошибки то DWORD (0040B04D) не обнулится и хеш будет неверный. Для передачи управления обработчику необходимо к примеру нажать Ctrl+F9. Далее есть еще место где генерируется исключение EXCEPTION_SINGLE_STEP через POPFD, этот обработчик также изменяет DWORD (0040B04D), что влияет на хеш. Чтобы декодировать код можно просто поставить хардварный бряк на 0040B0B1 и пропускать все исключения.
SGWW, Вот активность семпла. Посмотри где в реестре ключ через процмон или через отладчик, на сервис останов.
Thetrik, спасибо большое за пояснение, очень помогло. --- Сообщение объединено, 24 апр 2020 --- Indy_, спасибо, похоже в реесте должен находиться ключ с помощью которого расшифровывается код который и показывает ,что crackme успешно решен. Но как подобрать этот ключ, ведь нам неизвестно какой код зашифрован, а известно только что он рабочий. Немного облегчило ситуацию следующая часть Код (Text): 0040B140 E8 3C000000 CALL fp2.0040B181 ; Key retrival? 0040B145 85C0 TEST EAX,EAX ; EAX should be zero, EAX = EBX from CALL 0040B181 0040B147 0F85 DD000000 JNZ fp2.0040B22A ; If jump -> wrong key message 0040B14D 85C9 TEST ECX,ECX ; ECX should be zero 0040B14F 0F85 D5000000 JNZ fp2.0040B22A CALL fp2.0040B181 как раз генерирует EAX и ECX на основе того значения ключа который берется из реестра. Я думаю нужно пербрать все возможные значения и найти такое при котором EAX = ECX = 0x0 --- Сообщение объединено, 24 апр 2020 --- А тут еще и анти дебаг на основе RDTSC есть красота 0040B1D8 0F31 RDTSC ; rdtsc anti-debuggin
SGWW, Крэкми нужен для тебя, не для решения. Могу найти автоматикой решение, но зачем. Это всё для обучения, что бы поработать с окружением. > 0040B1D8 0F31 RDTSC ; rdtsc anti-debugging занопь.
Ключ легко подобрать перебором. Просто извлечь процедуру подсчета и написать код перебора. Просто извлек процедуру в decrypt.bin и написал процедуру перебора (FASM): Код (ASM): format PE mov edi, 0 .next: push edi push 0x2FB8813C push 0x3C9DB5D6 call calc test eax, eax jne @f test ecx, ecx jne @f int 3 @@: inc edi jmp .next calc: file "decrypt.bin" Через несколько минут упал на бряке, ключ 459f6937 должен лежать в HKCU\Software\Aalto раздел FP2KEY тип REG_DWORD.
Thetrik, я тоже сделал перебор, но только на питоне, выглядит вот так: Код (Text): ESI = 0x00000000 ECX = 0x2FB8813C EBX = 0x3C9DB5D6 # yep, this is a brute-force for ESI in range(0x459f6937, 0x0,-1): ECX = 0x2FB8813C EBX = 0x3C9DB5D6 EAX = 0xC6EF3720 EDI = 0x9E3779B9 EDX = 0x0 i = 0x20 while i > 0: """ MOV EDX, EBX SHL EDX,4 SUB ECX,EDX """ ECX = ECX - ((EBX << 4) & 0xFFFFFFFF)& 0xFFFFFFFF # print(hex(ECX).upper()) """ MOV EDX,ESI XOR EDX,EBX """ EDX = (ESI ^ EBX) & 0xFFFFFFFF # print(hex(EDX).upper()) """ SUB ECX, EDX """ ECX = (ECX - EDX) & 0xFFFFFFFF # print(hex(ECX).upper()) """ MOV EDX,EBX SHR EDX,5 XOR EDX,EAX """ EDX = (((EBX >> 5) & 0xFFFFFFFF) ^ EAX) & 0xFFFFFFFF # print(hex(EDX).upper()) """ SUB ECX,EDX """ ECX = (ECX - EDX) & 0xFFFFFFFF # print(hex(ECX).upper()) """ SUB ECX, ESI """ ECX = (ECX - ESI) & 0xFFFFFFFF # print(hex(ECX).upper()) """ MOV EDX, ECX SHL EDX,4 """ EDX = (ECX << 4) & 0xFFFFFFFF # print(hex(EDX).upper()) """ SUB EBX, EDX """ EBX = (EBX - EDX) & 0xFFFFFFFF # print(hex(EBX).upper()) """ MOV EDX, ESI XOR EDX, ECX """ EDX = (ESI ^ ECX) & 0xFFFFFFFF # print(hex(EDX).upper()) """ SUB EBX,EDX """ EBX = (EBX - EDX) & 0xFFFFFFFF # print(hex(EBX).upper()) """ MOV EDX, ECX SHR EDX,5 """ EDX = (ECX >> 5) & 0xFFFFFFFF # print(hex(EDX).upper()) """ XOR EDX, EAX """ EDX = (EDX ^ EAX) & 0xFFFFFFFF # print(hex(EDX).upper()) """ SUB EBX,EDX """ EBX = (EBX - EDX) & 0xFFFFFFFF # print(hex(EDX).upper()) """ SUB EBX, ESI """ EBX = (EBX - ESI) & 0xFFFFFFFF # print(hex(EBX).upper()) """ Sub EAX, EDI """ EAX = (EAX - EDI) & 0xFFFFFFFF # print(hex(EAX).upper()) """ print("Iteration :", i) print("EBX: ",hex(EBX).upper()) print("ECX: ",hex(ECX).upper()) print("ESI: ",hex(ESI).upper()) print("EAX: ",hex(EAX).upper()) print("EDI: ",hex(EDI).upper()) """ i = i - 1; if ESI % 10000000 == 0: print(hex(ESI).upper()) if EBX == 0x0: print("This is the right key ", hex(ESI).upper()) break Всю ночь считало)) Большое вам спасибо за советы@Thetrik, Indy_,
Indy_, ты шутишь, я два дня с этим crackme сидел и разбирался в куче механик, узнал как работает SEH и как установить свои обработчик исключений, логику работы INT3, узнал что RDTSC может использоваться как анти-дебаг. В конце концов я полностью разобрался в том как сделан первый стаб и почему поначалу код расшифровывался не корректно, в чем мне помог Thetrik. Что ключ храниться в реестре стало сразу очевидно после работы первого стаба, так как в расшифрованном коде есть обращения к RegOpenKeyExW и RegQueryValueExW. Возможно подбор на питоне не самая изящная вещь и в следующий раз я постараюсь использовать ASM, но как если не перебором найти этот ключ?
Шутит, щутит - Инди, вообще, любитель пошутить. А, вообще, тред получился неплохой, многим новичкам будет интересно. Я даже вспомнил какую-то статью К. Касперски, где я подставлял через FS свой адрес и программа на исключении, перескакивала туда, а отладчик на этом месте "спотыкался". Правда, это было чуть проще, чем в этом треде, но все же родственная тема. Сюда бы еще дополнение SReg'а с exelab'а добавить и, вообще, тема была бы полностью раскрытой. На exelab'е был же, вроде, подобный вопрос? P.S. За саму методику объяснения, я бы поставил Indy_ тройку, а Thetrik пятерку (по пятибальной системе). Ждем новых тем и объяснений.
SGWW, Но ты ничего не написал, что узнал из семпла. Тебе дали место для брута, цель же не ключ. Такие простые крэкми это обучающий материал для самостоятельной работы, а не для конечного решения. Если же брут, то можно решить так - создать карту исполнения(кэш инструкций для обнаружения новой обработки, кл пока не доступен я бы ссылку дал), выделить крипто функцию, затем на ней начать брутфорс. Это тупо автоматическое решение.
extravert, да был вопрос, но у меня что-то exelab не открывается, сервер не отвечает, поэтому не могу сказать что там ответили. Indy_, тут же главное понять, что именно нужно брутфорсить, а это целая история. А как создать эту карту исполнений, с помощью какого инструмента?
Так ведь режим самоизоляции же, поэтому БэдГай и все его модераторы, кроме одного, - уехали на шашлыки, от греха подальше и во имя спасения exelab'а. Коронавирус - это же такая зараза, что и сайт может сожрать и не подавиться.