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

ВИДЕОКУРС ВЗЛОМ
выпущен 10 декабря!


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


Декомпиляция кода виртуальной машины




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


Reversing crackme with VM



Автор: ev1l^4

[ Вступление ]


Эта статья про исследование и декомпиляцию псевдо-кода VM в CrackMe N3 by Rascal. Под VM подразумевается некий виртуальный процессор со своей архитектурой и системой команд, который реализован в виде интерпретатора PCODE, на основе которого выполняются функции содержащие выражения.




[ Исследование CrackMe и VM ]


.text:00401243 			                call    InitVars

Процедура инициализации переменных, а именно выделение памяти под виртуальные регистры, под сохранение значений регистров x86 процессора используемых самой VM, под стек VM, под код, который VM не эмулирует и получение адреса с размером секции псевдо-кода VM.

  	 	 		 			.text:004012BA 			                push  			  offset szKey
 			.text:004012BF 			                push    offset szName
 			.text:004012C4 			                call    CheckOfKey
 			

Процедура проверки ключа, судя по количеству инструкций NOP в процедуре и строке приведённой ниже, можно сделать вывод, что основной код процедуры был скомпилирован в псевдо-код.

 			.text:004011AC 			aLCrackme_3Pcode_compilerCrackme_3_pdb db 			'¦-¦-TË\CrackMe_3\PCode_Compiler\CrackMe_3.pdb',0
 			


 			00000000 			VM          			    struc ; (sizeof=0x24)
 			00000000 			.ESI	dd 			?
 			00000004 			.ESI.	dd 			?
 			00000008 			.EBP	dd 			?
 			0000000C 			.ESP	dd 			?
 			00000010 			.EBX	dd 			?
 			00000014 			.EDX	dd 			?
 			00000018 			.ECX	dd 			?
 			0000001C 			.EAX	dd 			?
 			00000020 			.EFLAGS	dd 			?
 			00000024 			VM              ends
 			

Структура виртуальных регистров.




 			.text:004014F8 			                mov     dwTempEAX, 			eax
 			.text:004014FD 			                mov     eax, pVM_REGS
 			.text:00401502 			                pushf
 			.text:00401503 			                pop     [eax+VM..EFLAGS]
 			.text:00401506 			                mov     [eax+VM..ESP], 			esp
 			.text:00401509 			                mov     [eax+VM..ESI], 			esi
 			.text:0040150B 			                mov     [eax+VM..ESI.], 			esi
 			.text:0040150E 			                mov     [eax+VM..EBP], 			ebp
 			.text:00401511 			                mov     [eax+VM..EBX], 			ebx
 			.text:00401514 			                mov     [eax+VM..EDX], 			edx
 			.text:00401517 			                mov     [eax+VM..ECX], 			ecx
 			.text:0040151A 			                mov     dwTempECX, 			ecx
 			.text:00401520 			                mov     ecx, eax
 			.text:00401522 			                mov     eax, dwTempEAX
 			.text:00401527 			                mov     [ecx+VM..EAX], 			eax
 			.text:0040152A 			                mov     ecx, dwTempECX
 			

Инициализация виртуальных регистров.


 			.text:0040152A 			                mov     ecx, dwTempECX
 			.text:00401530 			                push    eax
 			.text:00401531 			                mov     eax, pVM_REGS
 			.text:00401536 			                add     [eax+VM..ESP], 			4
 			.text:0040153A 			                mov     eax, offset End_CheckOfKeyInVM
 			.text:0040153F 			                mov     dwOffset, 			eax
 			.text:00401544 			                mov     eax, [esp+4]
 			.text:00401548 			                sub     eax, 5
 			.text:0040154B 			                mov     dwOffsetCheckOfKey, 			eax
 			.text:00401550 			                pop     eax
 			.text:00401551 			                add     esp, 4
 			

Инициализация адреса вызова процедуры VM для идентификации начала PCODE в секции .rvmpc.


 			.text:00401559 			                mov     eax, pREGS4VM
 			.text:0040155E 			                pushf
 			.text:0040155F 			                pop     [eax+VM..EFLAGS]
 			.text:00401562 			                mov     [eax+VM..ESP], 			esp
 			.text:00401565 			                mov     [eax+VM..ESI], 			esi
 			.text:00401567 			                mov     [eax+VM..ESI.], 			esi
 			.text:0040156A 			                mov     [eax+VM..EBP], 			ebp
 			.text:0040156D 			                mov     [eax+VM..EBX], 			ebx
 			.text:00401570 			                mov     [eax+VM..EDX], 			edx
 			.text:00401573 			                mov     [eax+VM..ECX], 			ecx
 			.text:00401576 			                mov     dwTempECX, 			ecx
 			.text:0040157C 			                mov     ecx, eax
 			.text:0040157E 			                mov     [ecx+VM..EAX], 			eax
 			.text:00401581 			                mov     eax, dwTempEAX
 			.text:00401586 			                mov     ecx, dwTempECX
 		 	  
// Инициализация регистров для VM. .text:0040158C push eax .text:0040158D mov eax, pVM_REGS .text:00401592 pop eax .text:00401593 mov esp, pStackOfVM .text:00401599 call VM_Proc

Лишний кусок кода. Инициализация регистра стека и вызов ядра VM.


 			.text:0040159E 			End_CheckOfKeyInVM: 			                    ; DATA XREF: InVM+4Ao
 			.text:0040159E 			                mov     eax, pVM_REGS
 			.text:004015A3 			                push    [eax+VM..EFLAGS]
 			.text:004015A6 			                popf
 			.text:004015A7 			                mov     esp, [eax+VM..ESP]
 			.text:004015AA 			                mov     esi, [eax+VM..ESI]
 			.text:004015AC 			                mov     esi, [eax+VM..ESI.]
 			.text:004015AF 			                mov     ebp, [eax+VM..EBP]
 			.text:004015B2 			                mov     ebx, [eax+VM..EBX]
 			.text:004015B5 			                mov     edx, [eax+VM..EDX]
 			.text:004015B8 			                mov     ecx, [eax+VM..ECX]
 			.text:004015BB 			                mov     eax, [eax+VM..EAX]
 			.text:004015BE 			                retn
 			

Инициализация регистров процессора.





 			.text:004015C0 			            			    push    ebp
 			.text:004015C1 			                mov     ebp, esp
 			.text:004015C3 			                sub     esp, 1Ch
 			.text:004015C6 			                mov     [ebp+dwTempLabelDword], 			0
 			.text:004015CD 			                mov     eax, address_sec_rvmpc
 			.text:004015D2 			                mov     pPCODE, 			eax
 			.text:004015D7 			                cmp     [ebp+dwTempLabelDword], 			0
 			.text:004015DB 			                jnz     short START_LOOP_VM
 			.text:004015DD 			                mov     ecx, dwDelta
 			.text:004015E3 			                mov     [ebp+lpDelta], 			ecx
 			.text:004015E6 			                mov     edx, dwOffsetCheckOfKey
 			.text:004015EC 			                and     edx, 0FFFFh
 			.text:004015F2 			                mov     [ebp+dwLoWordOffset], 			edx
 			.text:004015F5 			                mov     [ebp+i], 			0
 			.text:004015FC 			                jmp     short loc_401607
 			.text:004015FE 			loc_4015FE:
 			.text:004015FE 			                mov     eax, [ebp+i]
 			.text:00401601 			                add     eax, 1
 			.text:00401604 			                mov     [ebp+i], 			eax
 			.text:00401607 			loc_401607:
 			.text:00401607 			                cmp     [ebp+i], 			100h
 			.text:0040160E 			                jge     short loc_401636
 			.text:00401610 			                mov     ecx, [ebp+dwLoWordOffset]
 			.text:00401613 			                xor     ecx, [ebp+lpDelta]
 			.text:00401616 			                mov     [ebp+dwLoWordOffset], 			ecx
 			.text:00401619 			                mov     eax, [ebp+dwLoWordOffset]
 			.text:0040161C 			                xor     edx, edx
 			.text:0040161E 			                mov     ecx, 100h
 			.text:00401623 			                div     ecx
 			.text:00401625 			                add     edx, [ebp+lpDelta]
 			.text:00401628 			                mov     [ebp+lpDelta], 			edx
 			.text:0040162B 			                mov     eax, [ebp+dwLoWordOffset]
 			.text:0040162E 			                rol     eax, 5
 			.text:00401631 			                mov     [ebp+dwLoWordOffset], 			eax
 			.text:00401634 			                jmp     short loc_4015FE
 			.text:00401636 			loc_401636:
 			.text:00401636 			                mov     edx, [ebp+dwLoWordOffset]
 			.text:00401639 			                mov     [ebp+dwTempLabelDword], 			edx
 			

Получение метки в виде двойного слова для идентификации начала PCODE.



 	 				 			 				.text:0040163C 				START_LOOP_VM:
 				.text:0040163C 				                mov     eax, 				pPCODE
 				.text:00401641 				                mov     ecx, [ebp+dwTempLabelDword]
 				.text:00401644 				                cmp     ecx, [eax+2]
 				.text:00401647 				                jnz     INC_IP
 				.text:0040164D 				                mov     edx, pPCODE
 				.text:00401653 				                movzx   eax, word ptr [edx]
 				.text:00401656 				                mov     [ebp+idCommand], 				eax
 				.text:00401659 				                cmp     [ebp+idCommand], 				19h
 				.text:0040165D 				                ja      ERROR
 				.text:00401663 				                mov     ecx, [ebp+idCommand]
 				.text:00401666 				                jmp     ds:VM_FUNCS[ecx*4] 				; Non in PCODE  				
 				.text:004017C7 				INC_IP:
 				.text:004017C7 				                mov     edx, pPCODE
 				.text:004017CD 				                mov     [ebp+lpPCODE], 				edx
 				.text:004017D0 				                mov     eax, pPCODE
 				.text:004017D5 				                movzx   ecx, byte ptr [eax+6]
 				.text:004017D9 				                mov     edx, [ebp+lpPCODE]
 				.text:004017DC 				                lea     eax, [edx+ecx+0Fh]
 				.text:004017E0 				                mov     [ebp+lpPCODE], 				eax
 				.text:004017E3 				                mov     ecx, [ebp+lpPCODE]
 				.text:004017E6 				                mov     pPCODE, 				ecx
 				.text:004017EC 				                mov     edx, [ebp+lpPCODE]
 				.text:004017EF 				                sub     edx, address_sec_rvmpc
 				.text:004017F5 				                cmp     edx, raw_size_sec_rvmpc
 				.text:004017FB 				                jb      short no_above_IP
 				.text:00401818 				no_above_IP:
 				.text:00401818 				                jmp     START_LOOP_VM
 				.text:0040182D 				END_LOOP_VM:
 				.text:0040182D 				                mov     eax, CheckRemoveDecryptString
 				.text:00401832 				                and     eax, 2
 				.text:00401835 				                jz      short No_Remove_String
 				.text:00401837 				                call    RemoveDecryptString
 				.text:0040183C 				No_Remove_String:
 				.text:0040183C 				                cmp     JMP_DWORD, 				0
 				.text:00401843 				                jz      short No_Jmp
 				.text:00401845 				                mov     ecx, JMP_DWORD
 				.text:0040184B 				                mov     [ebp+dwTempLabelDword], 				ecx
 				.text:0040184E 				                mov     JMP_DWORD, 				0
 				.text:00401858 				                mov     edx, address_sec_rvmpc
 				.text:0040185E 				                mov     pPCODE, 				edx
 				.text:00401864 				                jmp     START_LOOP_VM
 				.text:00401869 				No_Jmp:
 				.text:00401869 				                mov     eax, pPCODE
 				.text:0040186E 				                cmp     dword ptr [eax+7], 0
 				.text:00401872 				                jz      short end_pcode
 				.text:00401874 				                mov     ecx, pPCODE
 				.text:0040187A 				                mov     edx, [ecx+7]
 				.text:0040187D 				                mov     [ebp+dwTempLabelDword], edx
 				.text:00401880 				                mov     eax, address_sec_rvmpc
 				.text:00401885 				                mov     pPCODE, 				eax
 				.text:0040188A 				                jmp     START_LOOP_VM
 				


Ядро VM, без неважного кода и функций.

Выполнение VM по шагам:

  1. Проверка метки, если метка совпала, то получение идентификатора, выполнение функции и переход на 3 шаг.

  2. Переход на следующую инструкцию, если количество инструкций не исчерпано, то переход на первый пункт, иначе сообщение об ошибке и выход.

  3. Очистка строки, если требуется. Проверка метки команды для перехода, если есть, то переход на команду с этой меткой, в противном случае переход на следующую команду за предыдущей. В обоих случаях указатель на PCODE устанавливается в начальное положение, чтобы переход мог осуществляться в обратное направление, однако во втором случае нет необходимости начинать поиск метки сначала.

И так до тех пор, пока позволяет условие.


 			.text:0040188F 			end_pcode:
 			.text:0040188F 			                call    RemoveData
 			.text:00401894 			                mov     ecx, pVM_REGS
 			.text:0040189A 			                mov     edx, [ecx+VM..ESP]
 			.text:0040189D 			                sub     edx, 4
 			.text:004018A0 			                mov     eax, pVM_REGS
 			.text:004018A5 			                mov     [eax+VM..ESP], 			edx
 			.text:004018A8 			                mov     ecx, dwOffsetCheckOfKey
 			.text:004018AE 			                add     ecx, 5
 			.text:004018B1 			                mov     edx, pVM_REGS
 			.text:004018B7 			                mov     eax, [edx+VM..ESP]
 			.text:004018BA 			                mov     [eax], ecx
 			.text:004018BC 			                mov     eax, [ebp+dwResult]
 			.text:004018BF 			@END_VM_Proc:
 			.text:004018BF 			                mov     esp, ebp
 			.text:004018C1 			                pop     ebp
 			.text:004018C2 			                retn
 			

Очистка данных и восстановление значений в стеке.


 			.text:00401694 			Func3:
 			.text:00401694 			                call    PUSH_CONST
 			.text:00401699 			                mov     [ebp+dwResult], 			eax
 			.text:0040169C 			                jmp     END_LOOP_VM
 			
// Вызов функции с идентификатором номер 3. .text:00401D60 push ebp .text:00401D61 mov ebp, esp .text:00401D63 sub esp, 14h .text:00401D66 push ebx .text:00401D67 mov eax, pPCODE .text:00401D6C mov [ebp+lpPCODE], eax .text:00401D6F mov ecx, [ebp+lpPCODE] .text:00401D72 add ecx, 0Fh .text:00401D75 mov [ebp+lpPCODE], ecx .text:00401D78 mov edx, [ebp+lpPCODE] .text:00401D7B mov [ebp+.lpPCODE], edx .text:00401D7E mov eax, [ebp+.lpPCODE] .text:00401D81 mov ecx, [eax] .text:00401D83 mov [ebp+CryptedDword], ecx .text:00401D86 mov edx, pPCODE .text:00401D8C mov eax, [edx+2] .text:00401D8F mov [ebp+CryptedDword2], eax
// Инициализация переменных.
.text:00401D92 mov eax, [ebp+CryptedDword] .text:00401D95 mov ebx, [ebp+CryptedDword2] .text:00401D98 mov ecx, 4 .text:00401D9D loc_401D9D: .text:00401D9D xor eax, ebx .text:00401D9F ror eax, 7 .text:00401DA2 rol ebx, 10h .text:00401DA5 dec ecx .text:00401DA6 jnz short loc_401D9D .text:00401DA8 xor eax, ebx .text:00401DAA mov [ebp+CryptedDword], eax .text:00401DAD mov eax, [ebp+CryptedDword] .text:00401DB0 mov ebx, dwDelta .text:00401DB6 mov ecx, 4 .text:00401DBB loc_401DBB: .text:00401DBB xor eax, ebx .text:00401DBD ror eax, 7 .text:00401DC0 rol ebx, 10h .text:00401DC3 dec ecx .text:00401DC4 jnz short loc_401DBB .text:00401DC6 xor eax, ebx .text:00401DC8 mov [ebp+CryptedDword], eax // Расшифровка полученных значений из массива PCODE.


.text:00401DCB mov ecx, pVM_REGS .text:00401DD1 mov edx, [ecx+VM..ESP] .text:00401DD4 sub edx, 4 .text:00401DD7 mov eax, pVM_REGS .text:00401DDC mov [eax+VM..ESP], edx .text:00401DDF mov ecx, pVM_REGS .text:00401DE5 mov edx, [ecx+VM..ESP] .text:00401DE8 mov [ebp+lpESP], edx .text:00401DEB mov eax, [ebp+lpESP] .text:00401DEE mov ecx, [ebp+CryptedDword] .text:00401DF1 mov [eax], ecx // Эмуляция команды push const.
.text:004016C8 Func7: .text:004016C8 call JMP .text:004016CD mov [ebp+dwResult], eax .text:004016D0 jmp END_LOOP_VM

Вызов функции с идентификатором номер 7. Эта функция содержит выражение похожее по принципу из предыдущей.

Рассмотрим главную суть.


 			.text:0040246E 			                mov     eax, [ebp+dwDword]
 			.text:00402471 			                mov     JMP_DWORD, 			eax
 		 	  
// Инициализация метки команды в переменную перехода. .text:0040182D END_LOOP_VM: .text:0040183C cmp JMP_DWORD, 0 .text:00401843 jz short No_Jmp .text:00401845 mov ecx, JMP_DWORD .text:0040184B mov [ebp+dwTempLabelDword], ecx .text:0040184E mov JMP_DWORD, 0 .text:00401858 mov edx, address_sec_rvmpc .text:0040185E mov pPCODE, edx .text:00401864 jmp START_LOOP_VM

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


Все функции выполняются по следующему принципу:

1.Инициализация значений.

2.Расшифровка значений, не всегда.

3.Эмуляция команды или инициализация переменной.

[ Декомпиляция ]


Для написания декомпилятора необходимо знать архитектуру виртуальной машины и систему команд. На основе первого строится общий каркас декомпилятора, второе используется для написания функций создающих код. Функции можно добавлять постепенно, лучше всего начать с тех, которые вызываются первыми при использовании PCODE.


Необходимые детали VM, которые нужно учесть при написании декомпилятора:

  • Переходы.

  • Длину PCODE и реализацию перехода на следующую команду в нём.

  • Группы команд для эмуляции одной



На основе этого можно определить массивы:

- Массив для получаемого кода.

- Массив для адресов(и возможно другой информации) каждой восстановленной команды.

- Массив для адресов переходов.

А также переменные указатели для этих массивов и PCODE.


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




 			Decompile_LOOP 			proc
 				local 			idCommand, 			labelDWORD
 				.repeat
 				mov 			eax, [pPCODE]
 				movzx 			eax, word ptr [eax]
 				mov 			[idCommand], 			eax
 				mov 			eax, [pPCODE]
 				mov 			eax, dword ptr [eax+2]
 				mov 			[labelDWORD],eax
 				
 				invoke 			Decompile,idCommand,labelDWORD
 				
 				mov 			eax, [pPCODE]
 				movzx 			ecx, byte ptr [eax+6]
 				lea 			eax, [eax+ecx+15]
 				mov 			[pPCODE], 			eax
 				.until 			dword ptr [eax+7] == 0 && dword ptr [eax+11] == 0 && 			dword ptr [eax+15] == 0
 			Decompile_LOOP 			EndP
 			

Основной цикл декомпилятора, который передаёт необходимые данные функции декомпиляции, делает переход на следующую команду и проверяет конец PCODE.


 			Func06 			proc
 				movzx 			eax, byte ptr [edx]
 				shr 			al, 4
 				.if al 			== 0
 					mov 			ax, 04C7h
 					stosw
 					mov 			al, 24h
 					stosb
 					mov 			eax, dword ptr [edx+2]
 					stosd
 					; 			mov dword 			ptr [esp], const
 				.endif
 				ret
 			Func06 			EndP
 			

Пример декомпиляции команды.

Все другие команды декомпилируются подобным образом, исключение составляют 14h-19h и 10h-13h, которые взаимосвязаны.

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


[ Заключение ]


Защита могла сначала показаться большой и громоздкой, однако если разобраться и вникнуть в то, что хотел показать нам автор, даже если код на C, всёравно можно понять как это работает. Стоит поблагодарить автора за такой CrackMe, т.к. это ещё одно творение, которое создаётся не за пару часов и даже, возможно, дней.


[ Приложение ]


#crackme.zip – оригинальный CrackMe, без VM и CrackMe_3.idb.

#decompile.zip – декомпилятор PCODE в близкий к оригиналу бинарный код.

#keygenme.zip – упрощенный вариант CrackMe.

#keygen.zip – keygen для CrackMe.



Скачать статью "Декомпиляция кода виртуальной машины" в авторском оформление + файлы.
пароль архива на картинке



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


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