Тема: Solutions
Показать сообщение отдельно

  #7  
Старый 05.02.2007, 01:32
Ra$cal
Постоянный
Регистрация: 16.08.2006
Сообщений: 640
Провел на форуме:
1354067

Репутация: 599


По умолчанию

Ra$cal KeyGenMe 2
Написал как автор крякми. То есть на что я делал ставку. Ждём солюшена человека, взломавшего сей крякми...
Итак. Основная фича этого творения - антиотладка используется с интересной целью. Не просто выдаёт месаджбокс и выходит. А данные получаемые от проверок изменяют внутренний ключ, который участвует в проверке. Таким образом если программа отлаживается и хоть одна проверка обнаружила отладчик, то найденный ключ не подойдёт при запуске программы без отладчика. Теперь про реализацию...

Сначала была придумана константа - 0x7DA3EF10. Далее вспомнил какие проверки знаю. Всего решил сделать 5 штук. Соотв решил какие биты в константе должны быть изменены. 3 бита должны быть изменены, 2 должны остаться. 0x7923EF14. Далее для сложности обнаружения обращений к этому дворду (в олли очень просто найти всю работу с конкретным адресом в секции данных. то есть легко локализовать и изменения и использование этой константы) сделал так. Дворд был разбит на 2: ControlDW_L = 0x5ca3ef14 и ControlDW_H = 0xba617923. В сладших словах находятся части константы. А для собсно скрытия обращений сделал ещё две переменные. Итог - static DWORD DummyBefore = 0x547298f0, ControlDW_L = 0x5ca3ef14, ControlDW_H = 0xba617923, DummyAfter = 0xba45910f;
Компилятор сделает так что эти переменные в памяти находятся последовательно. Следовательно чтоб обратиться к нужной достаточно получить адрес подставной переменной и и прибавить/удалить 4. Или 8. Вобщем как это работает:

Код:
_asm{
lea eax, DummyBefore;
add eax, 8;
mov eax, [eax]; получили ControlDW_H
}
Дальше...
Для реализации идеи пришлось придумывать, как связать возвращаемое значение проверки (причём не просто cmp jnz, а именно то значение, которое возвращает апи(например) при наличии отладчика и при его отсутствии). Так для IsDebuggerPresent задумано, что при отсутствии отладчика он вернёт 0. То есть если вернёт 1 константа будет испорчена...
Код:
temp = IsDebuggerPresent();
_asm{
	lea eax, DummyBefore;
	add eax, 8;
	mov ecx, temp;
	xor [eax], ecx;
}
Ещё из применённых проверок.

Код:
004016D0 >/$  55               PUSH EBP
004016D1  |.  8BEC             MOV EBP,ESP
004016D3  |.  83EC 08          SUB ESP,8
004016D6  |.  8D45 FC          LEA EAX,DWORD PTR SS:[EBP-4]
004016D9  |.  50               PUSH EAX                                                          ; /pThreadId
004016DA  |.  6A 00            PUSH 0                                                            ; |CreationFlags = 0
004016DC  |.  6A 00            PUSH 0                                                            ; |pThreadParm = NULL
004016DE  |.  68 30164000      PUSH KeyGenMe.CheckMemoryCrc                                      ; |ThreadFunction = KeyGenMe.CheckMemoryCrc
004016E3  |.  6A 00            PUSH 0                                                            ; |StackSize = 0
004016E5  |.  6A 00            PUSH 0                                                            ; |pSecurity = NULL
004016E7  |.  FF15 1C104000    CALL NEAR DWORD PTR DS:[<&KERNEL32.CreateThread>]                 ; \CreateThread
004016ED  |.  C745 F8 00000000 MOV DWORD PTR SS:[EBP-8],0
004016F4  |.  EB 09            JMP SHORT KeyGenMe.004016FF
004016F6  |>  8B4D F8          /MOV ECX,DWORD PTR SS:[EBP-8]
004016F9  |.  83C1 01          |ADD ECX,1
004016FC  |.  894D F8          |MOV DWORD PTR SS:[EBP-8],ECX
004016FF  |>  817D F8 88130000  CMP DWORD PTR SS:[EBP-8],1388
00401706  |.  7D 30            |JGE SHORT KeyGenMe.00401738
00401708  |.  817D F8 D0070000 |CMP DWORD PTR SS:[EBP-8],7D0
0040170F  |.  7E 25            |JLE SHORT KeyGenMe.00401736
00401711  |.  817D F8 C4090000 |CMP DWORD PTR SS:[EBP-8],9C4
00401718  |.  7D 1C            |JGE SHORT KeyGenMe.00401736
0040171A  |.  C705 18214000 01>|MOV DWORD PTR DS:[CanCheck],1
00401724  |.  6A 00            |PUSH 0                                                           ; /Timeout = 0. ms
00401726  |.  FF15 18104000    |CALL NEAR DWORD PTR DS:[<&KERNEL32.Sleep>]                       ; \Sleep
0040172C  |.  C705 18214000 00>|MOV DWORD PTR DS:[CanCheck],0
00401736  |>^ EB BE            \JMP SHORT KeyGenMe.004016F6
00401738  |>  8BE5             MOV ESP,EBP
0040173A  |.  5D               POP EBP
0040173B  \.  C3               RETN
Вот пример. Цикл в 5000 итераций. Причём с сомнительным содержимым. Тут надежда что поставят бряк на 3 последние команды цикла. А суть простая - Sleep (0) приводит к прекращению обработки текущего потока (его квант процессорного времени обнуляется) и начале работы другого потока процесса (если такой есть). А он, как видим немного выше, как раз создаётся. Как видно из инфы в pdb, функцию я назвал CheckMemoryCrc.Так вот Sleep (0) приведёт к тому, что второй поток будет сработывать при вызове Sleep(0)...

Код:
00401630 >/.  55               PUSH EBP
00401631  |.  8BEC             MOV EBP,ESP
00401633  |>  813D 14214000 C7>/CMP DWORD PTR DS:[ThreadCounter],0C7
0040163D  |.  75 32            |JNZ SHORT KeyGenMe.00401671
0040163F  |.  8B0D 28204000    |MOV ECX,DWORD PTR DS:[MemoryCrc]
00401645  |.  BA 01000000      |MOV EDX,1
0040164A  |.  22D1             |AND DL,CL
0040164C  |.  C1C9 10          |ROR ECX,10
0040164F  |.  22D1             |AND DL,CL
00401651  |.  C1C9 08          |ROR ECX,8
00401654  |.  22D1             |AND DL,CL
00401656  |.  C1C2 06          |ROL EDX,6
00401659  |.  8D05 18204000    |LEA EAX,DWORD PTR DS:[DummyBefore]
0040165F  |.  83C0 08          |ADD EAX,8
00401662  |.  C1C2 04          |ROL EDX,4
00401665  |.  3110             |XOR DWORD PTR DS:[EAX],EDX
00401667  |.  C705 28204000 00>|MOV DWORD PTR DS:[MemoryCrc],0
00401671  |>  813D 14214000 C8>|CMP DWORD PTR DS:[ThreadCounter],0C8
0040167B  |.  7E 0B            |JLE SHORT KeyGenMe.00401688
0040167D  |.  833D 18214000 01 |CMP DWORD PTR DS:[CanCheck],1
00401684  |.  75 02            |JNZ SHORT KeyGenMe.00401688
00401686  |.  EB 40            |JMP SHORT KeyGenMe.004016C8
00401688  |>  A1 14214000      |MOV EAX,DWORD PTR DS:[ThreadCounter]
0040168D  |.  83C0 01          |ADD EAX,1
00401690  |.  A3 14214000      |MOV DWORD PTR DS:[ThreadCounter],EAX
00401695  |.  C705 28204000 00>|MOV DWORD PTR DS:[MemoryCrc],0
0040169F  |.  833D 14214000 0F |CMP DWORD PTR DS:[ThreadCounter],0F
004016A6  |.  75 05            |JNZ SHORT KeyGenMe.004016AD
004016A8  |.  E8 03010000      |CALL KeyGenMe.HRChecker
004016AD  |>  B9 D0164000      |MOV ECX,KeyGenMe.MemoryCheckFaker
004016B2  |.  8B51 68          |MOV EDX,DWORD PTR DS:[ECX+68]
004016B5  |.  8915 28204000    |MOV DWORD PTR DS:[MemoryCrc],EDX
004016BB  |.  6A 00            |PUSH 0                                                           ; /Timeout = 0. ms
004016BD  |.  FF15 18104000    |CALL NEAR DWORD PTR DS:[<&KERNEL32.Sleep>]                       ; \Sleep
004016C3  |.^ E9 6BFFFFFF      \JMP KeyGenMe.00401633
004016C8  |>  5D               POP EBP
004016C9  \.  C3               RETN
Здесь тоже цикл и Sleep (0). То есть тут просто синхронизированы потоки. Далее.

Установка бряка в олли осуществляется просто. В целевой процесс пишут байт 0xCC по нужному адресу. Поэтому я читаю 4 байта с конца функции (куда я ожидаю установки бряка).

Код:
004016B2  |.  8B51 68          |MOV EDX,DWORD PTR DS:[ECX+68]
004016B5  |.  8915 28204000    |MOV DWORD PTR DS:[MemoryCrc],EDX
Прикинув как выглядят байты 0x8B, 0x5D, 0xC3 и 0xCC я нашёл, что СС - нечётное число, остальные чётные. Соответственно первый бит равен единице, если бряк не установлен, и 0, если установлен. Эта проверка должна изменить константу. Если хоть один бряк установлен бит будет обнулён.

Код:
0040163F  |.  8B0D 28204000    |MOV ECX,DWORD PTR DS:[MemoryCrc]
00401645  |.  BA 01000000      |MOV EDX,1
0040164A  |.  22D1             |AND DL,CL
0040164C  |.  C1C9 10          |ROR ECX,10
0040164F  |.  22D1             |AND DL,CL
00401651  |.  C1C9 08          |ROR ECX,8
00401654  |.  22D1             |AND DL,CL
00401656  |.  C1C2 06          |ROL EDX,6
00401659  |.  8D05 18204000    |LEA EAX,DWORD PTR DS:[DummyBefore]
0040165F  |.  83C0 08          |ADD EAX,8
00401662  |.  C1C2 04          |ROL EDX,4
00401665  |.  3110             |XOR DWORD PTR DS:[EAX],EDX
Плюс ещё есть вызов 004016A8 |. E8 03010000 |CALL KeyGenMe.HRChecker
Эта проверка проверят, установлен ли хардбряк. Для этого вызывает исключение и проверяет переданнвый контекст потока в функцию обработчик исключения, в котором было исключение. Отладчик для полноты картины ставит в контекст ВСЕХ потов хард бряки, заданные пользователем в каком-то одном. Чтобы мы не пропустили никаких интересующих нас событий. Следовательно, даже если бряк поставлен в другом потоке, в нашем созданном потоке он тоже будет. Поэтому в контексте можно проверить их наличие.

Код:
00401748  |.  33DB             XOR EBX,EBX
0040174A  |.  0B59 04          OR EBX,DWORD PTR DS:[ECX+4]
0040174D  |.  0B59 08          OR EBX,DWORD PTR DS:[ECX+8]
00401750  |.  0B59 0C          OR EBX,DWORD PTR DS:[ECX+C]
00401753  |.  0B59 10          OR EBX,DWORD PTR DS:[ECX+10]
00401756  |.  0B59 18          OR EBX,DWORD PTR DS:[ECX+18]
00401759  |.  8D05 24204000    LEA EAX,DWORD PTR DS:[DummyAfter]
0040175F  |.  83E8 04          SUB EAX,4
00401762  |.  3118             XOR DWORD PTR DS:[EAX],EBX
Эта проверка тоже не должна изменить никакой бит, ибо используется значение регитсров dr, и если хоть 1 задан, то константа опять не будет валидной.

Собсно есть ещё проверки, но логика их такая же. Перечислю только их суть - проверка отладочного порта и фича с обработкой CloseHandle с неправильным параметром.

Теперь что с алгоритмом. Как я и говорил он простой. Напишу тока код моего генератора

Код:
DWORD	Control = 0x7DA3EF10;

void Generate(char* UserName, char* Password)
{
	DWORD	Hash = 0;
	long	Summ = 0;
	DWORD	Pass = 0;
	for (int i  = strlen (UserName); i >= 0; i--){
		Hash ^= UserName[i];
		_asm{
			rol Hash, 8;
		}
		Summ += UserName[i];
	}
	Pass = (Summ + Hash) ^ Control;
 	_itoa(Pass, Password, 16);
	int len = strlen (Password);
	if (len < 8){
		int d;
		d = 8 - len;
		memcpy (Password + d, Password, len);
		memset (Password, '0', d);
		Password [8] = '\0';
	}
}
 
Ответить с цитированием