Оригинальный DVD-ROM: eXeL@B DVD !
eXeL@B ВИДЕОКУРС !

Видеокурс программиста и крэкера 5D 2O17
(актуальность: декабрь 2O17)
Свежие инструменты, новые видеоуроки!

  • 400+ видеоуроков
  • 800 инструментов
  • 100+ свежих книг и статей

УЗНАТЬ БОЛЬШЕ >>
Домой | Статьи | RAR-cтатьи | FAQ | Форум | Скачать | Видеокурс
Новичку | Ссылки | Программирование | Интервью | Архив | Связь


Взлом трёх виртуальных машин (crackme с VM), часть 3




Хорошая подборка видеоуроков, инструментов крэкера, книг и статей - здесь.

CrackMe#0 by 0x00786F72:

Исследование, декомпиляция и создание генератора серийных номеров.

ev1l^4 стр. 23 15.02.2011



CrackMe#0 by 0x00786F72:

Исследование, декомпиляция и создание генератора серийных номеров.


Автор: ev1l^4


/* Вступление */


Эта статья про взлом crackme от 0x00786F72, основной защитой является виртуальная машина, это по совокупности своей интерпретатор псевдо-кода(байт-кода) или же эмулятор, в нашем случае последнее. На форуме CRACKL@B, автор писал, что это вымышленный эмулятор RISC-процессора, похожий на среднее семейство микроконтроллеров PIC. Поэтому заранее нужно обзавестись справочником по системе команд PIC, чтобы можно было давать свои имена функциям – исполнителям псевдо-кода.





/* Обзор crackme */


Листинг 1:

 					mov	eax, 			[ebp+wParam]
 					rol	eax, 			10h
 					cmp	ax, 			0
 					jnz	loc_40114A
 					push	20h		; 			cchMax
 					push	offset 			szName ; lpString
 					push	3E9h		; 			nIDDlgItem
 					push	[ebp+hDlg]	; 			hDlg
 					call	ds:GetDlgItemTextA
 					test	eax, 			eax
 					jz	short 			_end
 					mov	esi, 			offset szName
 					mov	edi, 			eax
 					call	GetCRC
 					mov	dwCRC, 			eax
 			


В листинге номер 1 приведено получение имени из “edit” контрола и, если длина имени будет больше или равна одному символу, то в регистр ESI будет записано значение адреса с полученным именем, а в EDI длина, для подсчёта CRC суммы и записи её в переменную dwCRC.


Листинг 2:

 					push	20h		; 			cchMax
 					push	offset 			szSerial	; lpString
 					push	3EAh		; 			nIDDlgItem
 					push	[ebp+hDlg]	; 			hDlg
 					call	ds:GetDlgItemTextA
 					test	eax, 			eax
 					jz	short 			_end
 					call	CheckSerial
 			


В листинге номер 2 приведено получение серийного номера, в случае если длина будет не равна нулю, то будет выполнена процедура проверки серийного номера.

Листинг 3:

 					rol	eax, 			10h
 					xor	ax, 			ax
 					rol	eax, 			10h
 					cmp	ax, 			1Ah
 					jz	short 			False_Message
 					cmp	ax, 			1Bh
 					jz	short 			True_Message
 					push	0		; 			uType
 					push	offset 			Caption	
 					push	offset 			Caption	
 					push	0		; 			hWnd
 					call	ds:MessageBoxA
 					jmp	short 			_end
 			False_Message:
 					mov	esi, 			offset Text ; "I think that you're loser..."
 					push	0		; 			uType
 					push	esi		; 			lpCaption
 					push	esi		; 			lpText
 					push	0		; 			hWnd
 					call	ds:MessageBoxA
 					jmp	short 			_end
 			True_Message:	
 					mov	esi, 			offset Text ; "I think that you're loser..."
 					add	esi, 			6Eh
 					push	esi
 					push	0		; 			uType
 					push	esi		; 			lpCaption
 					push	esi		; 			lpText
 					push	0		; 			hWnd
 					call	ds:MessageBoxA
 					pop	edi
 					mov	ecx, 			13h
 					xor	eax, 			eax
 					rep 			stosw
 					jmp	short 			_end
 			



В листинге номер 3 приведён код реагирующий на результат процедуры CheckSerial, если в регистр AX будет возвращено значение 1Bh, то будет отображено сообщение с расшифрованным текстом, в противном случае будет с не желаемым текстом.


Листинг 4:

 					popa
 					call	CharToHex
 					mov	esi, 			Serial
 					mov	edi, 			0Ah
 					call	GetCRC
 			; 			---------------------------------------------------------------------------
 					call	sub_40130D
 			; 			---------------------------------------------------------------------------
 					cmp	ax, 			0FFFFh
 					jnz	short 			locret_401392
 					mov	esi, 			offset DecryptedData
 					mov	ax, 			1Bh
 					retn
 			


В листинге номер 4 приведена процедура вызываемая из интерпретатора, функция не имеющая отношения к делу эмулятора подменяет адрес возврата. Эта процедура переводит символы в hex-числа. Далее из 10 байт от полученного массива будет посчитана CRC сумма, после идёт вызов процедуры с использованием интерпретатора, именно там происходит вызов расшифровки зашифрованного текста, если длина серийного номера совпала с фиксированной и 2 байта CRC суммы от имени, последние два байта от серийного номера, CRC 10 байт серийного номера будут в ходе каких-то действий получать 0, то значение расшифруется и будет сообщение с поздравлением.


Листинг 5:

 					mov	esi, 			offset CryptedData
 					mov	edi, 			offset  			
 					push	esi
 					mov	ecx, 			26h
 					shr	ecx, 			1
 			loc_4013A5:
 					mov	ax, 			[esi]
 					xor	ax, 			dx
 					mov	[edi], 			ax
 					add	edi, 			2
 					add	esi, 			2
 					loop	loc_4013A5
 					pop	esi
 					retn
 			


В листинге номер 5 приведена процедура расшифровки зашифрованного текста, значение расшифровывающее текст будет инициализировано в регистр DX, в случае если данные серийного номера будут подходить под условия.




Листинг 6:

 			CharToHex	proc 			near	
 					mov	esi, 			offset szSerial
 					mov	edi, 			Serial
 					mov	ecx, 			0Eh
 			loc_4013C7:
 					mov	ah, 			[esi]
 					inc	esi
 					sub	ah, 			30h
 					cmp	ah, 			9
 					jbe	short 			loc_4013D5
 					sub	ah, 			7
 			loc_4013D5:
 					shl	ah, 			4
 					mov	al, 			[esi]
 					inc	esi
 					sub	al, 			30h
 					cmp	al, 			9
 					jbe	short 			loc_4013E3
 					sub	al, 			7
 			loc_4013E3:
 					and	al, 			0Fh
 					or	ah, 			al
 					mov	[edi], 			ah
 					inc	edi
 					loop	loc_4013C7
 					retn
 			CharToHex	endp
 			


В листинге номер 6 приведена процедура перевода строки в hex-числа.


На данном этапе можно предположить, что длина серийного номера будет 28 символов, т.к. процедура берёт по два символа, а цикл будет выполняться 0Eh кол-во раз, что 14d*2=28.




/* Обзор VM */


Листинг 7:

 			struct_1	struc
 			FLAGS	dw 			?
 			W		dw 			?
 			reg16_02	dw 			?
 			reg16_03	dw 			?
 			IP		dw 			?
 			reg16_05	dw 			?
 			reg16_06	dw 			?
 			reg16_07	dw 			?
 			reg16_08	dw 			?
 			reg16_09	dw 			?
 			reg16_10	dw 			?
 			reg16_11	dw 			?
 			reg16_12	dw 			?
 			reg16_13	dw 			?
 			reg16_14	dw 			?
 			reg16_15	dw 			?
 			reg16_16	dw 			?
 			reg16_17	dw 			?
 			reg16_18	dw 			?
 			reg16_19	dw 			?
 			reg16_20	dw 			?
 			reg16_21	dw 			?
 			reg16_22	dw 			?
 			reg16_23	dw 			?
 			reg16_24	dw 			?
 			reg16_25	dw 			?
 			reg16_26	dw 			?
 			reg16_27	dw 			?
 			reg16_28	dw 			?
 			reg16_29	dw 			?
 			reg16_30	dw 			?
 			struct_1	ends
 			


В листинге номер 7 приведена структура контекста VM.

Листинг 8:

 			sub_4011D3	proc 			near
 					mov	VM_CONTEXT.reg16_09, 			ax
 					mov	VM_CONTEXT.reg16_10, 			bx
 					mov	VM_CONTEXT.IP, 			0
 					pusha
 					call	VM
 					popa
 					mov	ax, 			VM_CONTEXT.reg16_09
 					mov	bx, 			VM_CONTEXT.reg16_10
 					retn
 			


В листинге номер 8 приведена процедура вызова VM, здесь показана инициализация регистров VM и регистров x86, после выполнения псевдо-кода.


Листинг 9:

 			VM		proc 			near
 					call	GetOP
 					call	Unknown_func
 					call	Switch
 					jmp	short 			VM
 			VM		endp
 			


В листинге номер 9 приведен основной цикл VM, состоящий из процедуры получения оператора в регистр EAX, специфичной функции для эмулятора, которая в crackme не используется и переключателя функций – исполнителей байт-кода.


Листинг 10:

 			GetOP		proc 			near
 					movzx	eax, 			VM_CONTEXT.IP
 					add	VM_CONTEXT.IP, 			4
 					lea	eax, 			PCODE[eax]
 					mov	eax, 			[eax]
 					retn
 			GetOP		endp
 			


В листинге номер 10 приведена функция получения оператора в виде псевдо-кода и увеличивающая значение указателя на инструкцию.


Листинг 11:

 			Switch		proc 			near
 					mov	ebx, 			eax
 					and	ebx, 			0FFh
 					call	dword 			ptr ds:funcs-4[ebx*4]
 					retn
 			Switch		endp
 			


В листинге номер 11 приведена процедура переключателя функций.




Листинг 12:

 			funcs:	dd 			offset ADDWF
 					dd 			offset ANDWF
 					dd 			offset CLRF
 					dd 			offset CLRW
 					dd 			offset XORLWF
 					dd 			offset DECF
 					dd 			offset DECFSZ
 					dd 			offset INCF
 					dd 			offset INCFSZ
 					dd 			offset IOWRF
 					dd 			offset MOVF
 					dd 			offset MOVWF
 					dd 			offset NOP
 					dd 			offset SHLLW
 					dd 			offset SHRLW
 					dd 			offset SUBWF
 					dd 			offset RLLW
 					dd 			offset XORWF
 					dd 			offset ANDLF
 					dd 			offset IORLF
 					dd 			offset TFSS
 					dd 			offset TFSC
 					dd 			offset ADDLW
 					dd 			offset ANDLW
 					dd 			offset CALL
 					dd 			offset INIT_DATA
 					dd 			offset GOTO
 					dd 			offset IORLW
 					dd 			offset MOVLW
 					dd 			offset GOTO_x86_code
 					dd 			offset RETLW
 					dd 			offset RETURN
 					dd 			offset GOTO_Decrypt_M
 					dd 			offset SUBLW
 					dd 			offset XORLW
 					dd 			offset EXIT
 			

В листинге номер 12 приведен список адресов функций, которые выполняются в зависимости от PCODE. Имена для функций были взяты, часть из справочника по среднему семейству микроконтроллеров PIC, часть была выдумана на основе описаний из справочника и часть на основе действий процедур.


Листинг 13:

 			DECFSZ:	
 					cmp	ah, 			0
 					jnz	short 			loc_4016B9
 					shr	eax, 			10h
 					mov	dx, 			word ptr VM_CONTEXT.FLAGS[eax]
 					dec	dx
 					jnz	short 			loc_4016B1
 					add	VM_CONTEXT.IP, 			4
 			loc_4016B1:
 					mov	VM_CONTEXT.W, 			dx
 					retn
 			loc_4016B9:
 					shr	eax, 			10h
 					dec	word 			ptr VM_CONTEXT.FLAGS[eax]
 					jnz	short 			locret_4016CD
 					add	VM_CONTEXT.IP, 			4
 			locret_4016CD:
 					retn
 			


В листинге номер 13 приведена функция – исполнитель псевдо-кода, выполняющая: Уменьшение значения регистра f, пропуск следующей инструкции если результат равен нулю.


Полный листинг crackme, включая функции VM, приведен в приложении.




/* Декомпиляция */


Идеальным решением разбора VM – написание декомпилятора, целью которого будет получение близкого к оригиналу кода в бинарном виде.

Например, листинг номер 13 можно сократить до такого состояния кода, который приведен в листинге номер 14 или 15, в зависимости от состояния значения регистра AH.


Листинг 14:

 			mov	dx, 			word ptr VM_CONTEXT.FLAGS[eax]
 			dec	dx
 			mov	VM_CONTEXT.W, 			dx
 			jz 			relativity offset
 			


Листинг 15:

 			dec	word 			ptr VM_CONTEXT.FLAGS[eax]
 			jz 			relativity offset
 			


Декомпилятор подобного рода VM делается на основе самой VM, а именно интерпретация. Только вместо исполнения кода используя псевдо-код, будет минимальное сохранение команд, как было приведено выше.

Эта VM имеет:
- Процедуры, а значит и возврат из них.

- Переходы, как условные, так и безусловные.

- Команды для выполнения каких-либо действия – xor, and, mov, etc..

- Имеет функции не имеющие отношения к эмуляции RISC-процессора.




Учитывая все факторы, нужно позаботиться:

- О инициализации оператора в виде байт-кода, увеличении указателя на псевдо-код, причём до тех пор, пока псевдо-код не закончится.

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

- О сохранении адреса начала декомпилированного кода перед вызовом функции декомпиляции и после в соответствующий указатель, чтобы было известно куда следующей функции-декомпилятору декомпилировать код.

- О сохранении адреса, куда будет записано относительное смещение для вызова процедуры или перехода.


Листинг 16:

 			.data?
 			CODE			db	128*144	dup(?) 			; Recalled code
 			PointersOperators	dd	144	dup(?) 			; Pointers on operators
 			pCODE			dd	? 			; Указатель на восстановленный код
 			pPointersOperators	dd	? 			; Указатель на массив указателей команд
 			pPCODE			dd	? 			; Указатель на PCODE
 			PointersJCC		dd	144	dup(?) 			; Pointers on jcc
 			pPointersJCC		dd	? 			; Указатель на массив адресов адреса перехода
 			


В листинге номер 16 приведены основные неинициализированные данные.


Листинг 17:

 			.code
 			mov 			pCODE, offset CODE
 			mov 			pPointersOperators, offset PointersOperators
 			mov 			pPCODE, offset PCODE
 			mov 			pPointersJCC, offset PointersJCC
 			.while 			1
 			call 			GetOP
 			.break 			.if (!eax)
 			call 			@xor_crac_00401426
 			call 			@switch
 			.endw
 			


В листинге номер 17 приведена инициализация переменных и основной цикл декомпилятора.


Листинг 18:

 			mov 			esi, offset PointersJCC
 			.while 			1
 			lodsd
 			mov 			edi, eax
 			xor 			eax,eax
 			lodsw
 			.break 			.if dword ptr [esi-6] == 0
 			mov 			eax, dword ptr [PointersOperators+eax]
 			sub 			eax, edi
 			sub 			eax, 4
 			stosd
 			.endw
 			


В листинге номер 18 приведен цикл записи относительных смещений для процедур и переходов.




Листинг 19:

 			DECFSZ 			proc
 			mov 			ebx, eax
 			cmp 			ah, 0
 			jnz 			@ah_not_0
 			shr 			ebx, 010h
 			mov 			ax, 066A1h ; MOV AX, WORD PTR [0]
 			xchg 			ah, al
 			stosw
 			mov 			eax, offset VM_CONTEXT
 			stosd
 			add 			dword ptr [edi-4], ebx
 			mov 			ecx, code1_end-code1
 			mov 			esi, offset code1
 			rep 			movsb
 			movzx 			eax, word ptr [VM_CONTEXT.VM_STRUCT.reg16_03]
 			add 			ax, 4
 			SavePointerJCC
 			retn
 			;---------------------------
 			code1:
 			dec 			ax
 			mov 			word ptr [VM_CONTEXT.VM_STRUCT.W], ax
 			db 			0fh, 84h, 0, 0, 0, 0
 			code1_end:
 			;---------------------------
 			@ah_not_0:
 			shr 			ebx, 010h
 			mov 			ax, 066FFh ; DEC WORD PTR [0]
 			xchg 			ah, al
 			stosw
 			mov 			al, 0Dh
 			stosb
 			mov 			eax, offset VM_CONTEXT
 			stosd
 			add 			dword ptr [edi-4], ebx
 			mov 			ecx, code2_end-code2
 			mov 			esi, offset code2
 			rep 			movsb
 			movzx 			eax, word ptr [VM_CONTEXT.VM_STRUCT.reg16_03]
 			add 			ax, 4
 			SavePointerJCC
 			retn
 			;---------------------------
 			code2:
 			db 			0fh, 84h, 0, 0, 0, 0
 			code2_end:
 			;---------------------------
 			DECFSZ 			EndP
 			


В листинге номер 19 приведена функция декомпиляции команды DECFSZ, которая приведена в листинге номер 13.


Полный исходный код находится в приложении, также там дописана часть crackme, которая вызывает уже декомпилированный код, который будет гораздо интереснее отлаживать в отладчике, вместо того, чтобы рассматривать огромные листинги в статье.





/* Упрощённый вариант crackme и keygen */


Листинг 20:

 			invoke	GetDlgItemText,hWin,IDC_NAME,addr 			szName,31
 			.if	eax
 				mov 			edi, eax
 				mov 			esi, offset szName
 				call 			GetCRC
 				mov 			[dwCRC],eax
 				invoke	GetDlgItemText,hWin,IDC_SERIAL,addr 			szSerial,31
 				.if 			eax
 					mov 			ebx, eax
 					and 			ebx, 3
 					.if 			zero?
 						shl 			ax, 1
 						xor 			ax, 38h
 							.if 			zero?
 							call 			CharToHex
 							mov 			edi, 10
 							mov 			esi, offset Serial
 							call 			GetCRC
 							add 			ax, word ptr [Serial+10]
 							xor 			ax, word ptr [dwCRC]
 							ror 			eax, 16
 							add 			ax, word ptr [Serial+12]
 							xor 			ax, word ptr [dwCRC+2]
 								.if 			zero?
 								mov 			dx, 5417h
 								mov 			esi, offset CryptedData
 								mov 			edi, offset DecryptData
 								mov 			ecx, 026h shr 1
 								@@:
 								mov 			ax, word ptr [esi]
 								xor 			ax, dx
 								mov 			word ptr [edi], ax
 								add 			edi, 2
 								add 			esi, 2
 								loopd 			@b
 								mov 			esi, offset DecryptData
 								invokeMessageBox,hWin,esi,esi,MB_OK
 								invoke	RtlZeroMemory,offset 			DecryptData, sizeof DecryptData
 								.endif
 							.endif
 						.endif
 					.endif
 				.endif
 			


В листинге номер 20 приведена проверка серийного номера и расшифровка зашифрованного сообщения. Как оказалось, длина на самом деле 28 символов, а проверка очень примитивна, HIWORD(CRC of 10 bytes of serial)+word ptr [Serial+12]^ word ptr [dwCRC+2] == 0.


В листинге номер 21 приведен исходный код генератора серийных номеров. 12 байт заполняются случайными данными, остальные два байта вычисляются следующим выражением:

word ptr [Serial+12] = word ptr [dwCRC+2] - HIWORD(CRC of 10 bytes of serial).


Листинг 21:

 				invoke	GetDlgItemText,hWin,IDC_NAME,addr 			szName,31
 					.if	eax
 						mov 			edi, eax
 						mov 			esi, offset szName
 						call 			GetCRC
 						mov 			[dwCRC],eax
 						mov 			esi, offset Serial
 						mov 			edi, esi
 						invoke 			GetTickCount
 						xor 			[dwTemp],eax
 						rol 			word ptr [dwTemp], 9
 						rol 			word ptr [dwTemp+2], 7
 						add 			eax, [dwTemp]
 						xor 			eax, [dwCRC]
 						stosd
 						not 			eax
 						stosd
 						xor 			ax, 'zA'
 						rol 			eax, 16
 						xor 			ax, 'Az'
 						stosd
 						mov 			edi, 10
 						call 			GetCRC
 						ror 			eax, 16
 						sub 			word ptr [dwCRC+2], ax
 						xchg 			word ptr [dwCRC+2], ax
 						mov 			word ptr [Serial+12], ax
 						call 			HexToChar
 						invoke	SetDlgItemText,hWin,IDC_SERIAL,addr 			szSerial
 					.elseif
 						invoke	SetDlgItemText,hWin,IDC_SERIAL,0
 					.endif
 			


/* Заключение */


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

Сама статья получилась обзором проделанной работы, она послужит введением в исследование исходного кода декомпилятора и crackme, как в декомпилированном варианте, так и в оригинальном.


/* Приложение */


#xor_crackme0.zip – Оригинальный crackme от xor.

#xor_crackme0_asm_listing.zip – Дизассемблерный листинг IDA Pro.

#decompile.zip – Декомпилятор VM, вместе с crackme.

#crackme.zip – Упрощённый вариант crackme.

#keygen.zip – Keygen для crackme от xor.

#PIC.zip - Сводная таблица команд семейства PIC18XXXX.



Скачать статью "Взлом трёх виртуальных машин (crackme с VM), часть 3" в авторском оформление + файлы.



Оригинальный DVD-ROM: eXeL@B DVD !


Вы находитесь на EXELAB.rU
Проект ReactOS