;::::::::::::::::::::::::::::::::::::::::::::::::::::::
;                Simple Morpher v.0.1                 :
;                                                     :
;                    x0man © 2008                     :
;                                                     :
;               http://www.virustech.org/             :
;::::::::::::::::::::::::::::::::::::::::::::::::::::::
;-----------------------------------------------------------------------------------------:
;                                                                                         :
;В Кратце расскажу алгоритм, который у меня реализован.                                   :
;                                                                                         :
;Есть массив в который заносятся данные о инструкциях                                     :
;Одним эллементом массива является:                                                       :
;                                                                                         :
;_OPCODE struct;                                                                          :
;	dwOldAddress	dd	?	; Старый адрес инструкции (до морфинга)               :
;	dwNewAddress	dd	?	; Новый адрес инструкции  (после морфинга)            :
;	dwJumpAddress	dd	?	; Адрес куда должен указывать прыжок(или вызов)       :
;                                   ; (если инструкция им является)                       :
;	dwLength		dd	?	; в данном примере эта переменная не используется     :
;	 					; Надеюсь вам она пригодиться больше чем мне :)       :
;_OPCODE ends                                                                             :
;                                                                                         :
;В "мой" Первый этап входит                                                               :
;  1. Парсинг кода, и выделение на каждую инструкцию одного                               :
;     эллемента массива, заполняя данные в структуре _OPCODE.                             :
;  2. Расширение кода для будущих изменений (замена коротких прыжков на длинные)          :
;  3. Если инструкция является прыжком или вызовом, нам нужно                             :
;     подсчитать скажем так EIP при выполнении оной.                                      :
;  4. И есстественно разбавление кода (инструкцией NOP)                                   :
;                                                                                         :
;Второй этап                                                                              :
;  1. Проходимся по массиву с описаниями инструкций                                       :
;  2. Если инструкция является вызовом, то вычисляем параметр у прыжка                    :
;    или вызова, и меняем его на корректный(уже в изменённом коде).                       :
;                                                                                         :
;                                                                                         :
;Вроде бы всё... для более точных описаний смотрим код!                                   :
;                                                                                         :
;Дизассемблер длинн инструкций я взял Catchy_32, который можно скачать с                  :
; http://www.wasm.ru, он также прилагается к исходникам в приложении.                     :
;                                                                                         :
;GreeTz:                                                                                  :
;        Osen                                                                             :
;        izee [ EOF-Project ] http://eof-project.net/                                     :
;                                                                                         :
;  tPORt  (http://www.tport.org/)                                                         :
;  REVENGE(http://www.revenge-crew.com/)                                                  :
;  TLG    (http://tlg.astalavista.ms/)                                                    :
;  TSRh   (http://tsrh.org.ua/)                                                           :
;  TPOC   (http://vx.netlux.org/tpoc/)                                                    :
;                                                                                         :
;                                                                                         :
;  Спасибо за внимание!                                                                   :
;                                                                                         :
;                                                                  10.05.2008             :
;                                                           x0man [VirusTech]             :
;                                                    http://www.virustech.org             :
;-----------------------------------------------------------------------------------------:

	.386
	.model flat, stdcall
	option casemap :none

	include \MASM32\INCLUDE\windows.inc
	include \MASM32\INCLUDE\kernel32.inc
	include \MASM32\INCLUDE\user32.inc
	
	includelib \MASM32\LIB\kernel32.lib
	includelib \MASM32\LIB\user32.lib
		
; #########################################################################

_OPCODE struct
	dwOldAddress	dd	?	; Старый адрес инструкции (до морфинга)
	dwNewAddress	dd	?	; Новый адрес инструкции  (после морфинга)
	dwJumpAddress	dd	?	; Адрес куда должен указывать прыжок(или вызов)
						; (если инструкция им является)
	dwLength		dd	?	; в данном примере эта переменная не используется
	 					; Надеюсь вам она пригодиться больше чем мне :)
_OPCODE ends
	
; #########################################################################

.code

;:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
; Код для теста :)
;:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
test_code:
	@@:
	jmp @F
	mov eax, edx
	pop eax
	push eax
	call @F
	cmp eax, 0
	jne @B
	jmp @B
	add ecx, edx
	add eax, edx
	xchg edx, ecx
	call @B
	jne @F
	db	0,0,0,0,0,0,0,0,0,0
	db	0,0,0,0,0,0,0,0,0,0
	db	0,0,0,0,0,0,0,0,0,0
	db	0,0,0,0,0,0,0,0,0,0
	db	0,0,0,0,0,0,0,0,0,0
	db	0,0,0,0,0,0,0,0,0,0
	db	0,0,0,0,0,0,0,0,0,0
	db	0,0,0,0,0,0,0,0,0,0
	db	0,0,0,0,0,0,0,0,0,0
	db	0,0,0,0,0,0,0,0,0,0
	db	0,0,0,0,0,0,0,0,0,0
	db	0,0,0,0,0,0,0,0,0,0
	db	0,0,0,0,0,0,0,0,0,0
	db	0,0,0,0,0,0,0,0,0,0
	db	0,0,0,0,0,0,0,0,0,0
	jne @B
	ret
	@@:
	ret
	int 3
;:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
;:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
;:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::




;::::::::::::::::::::::::::::::::::
; Подключим дизасм длинн инструкций
include Catchy32\Catchy32.inc



;:::::::::::::::::::::::::::::::::::::::::::::::::::::::::|
; Функция для подсчёта числа, куда должен указывать прыжок|
; Пример: dwCurrentAddress - указывает на код             |
;                                                         |
;                     00000000: 74 30  JE imm8            |
;  эта функция высчитывает "imm8"                         |
;  в нашем примере imm8 = 00000000 + 30 + 2 = 00000032    |
;   т.е.                                                  |
;   00000000 - Текущий адрес                              |
;         30 - Параметр                                   |
;          2 - Длинна инструкции JE imm8                  |
;:::::::::::::::::::::::::::::::::::::::::::::::::::::::::|
;                     00000000: 74 30  JE 00000032 --.    |
;                     00000002:                      |    |
;                                                    |    |
;                     00000032:                <-----°    |
; Это описание для "положительных" прыжков                |
; Для отрицательных, попробуйте разобрать сами            |
;                                там не сложно ;-)        |
;:::::::::::::::::::::::::::::::::::::::::::::::::::::::::|:::|
; IN  dwCurrentAddress : текущий адрес предпологаемого прыжка |
; OUT EAX              : Адрес куда прыжок указывает          |
;:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::|
get_jump_address proc dwCurrentAddress : DWORD

	push ecx
	push edi

	mov edi, dwCurrentAddress
	mov al, byte ptr [edi]
	
	;:::::::::::::::::::::::::::::::::::::
	; XX imm8
	cmp al, 070h
	jl @F
	cmp al, 07Fh
	jna @_jump_imm8_
	
@@:
	;:::::::::::::::::::::::::::::::::::::
	cmp al, 0EBh
	je @_jump_uncond_imm8_
	;:::::::::::::::::::::::::::::::::::::
	; 0F XX imm32
	cmp al, 00Fh
	jne @F
	mov ah, byte ptr [edi + 1]
	cmp ah, 080h
	jl @F
	cmp ah, 08Fh
	jna @_jump_imm32_
	;:::::::::::::::::::::::::::::::::::::

@@:	
	;:::::::::::::::::::::::::::::::::::::
	; JMP imm32
	cmp al, 0E9h
	je @_jump_uncond_imm32_
	

	;:::::::::::::::::::::::::::::::::::::
	; CALL
	cmp al, 0E8h
	je @_call_imm32_
	

	;:::::::::::::::::::::::::::::::::::::
	jmp @_exit_
	;:::::::::::::::::::::::::::::::::::::
	
@_jump_imm8_:
@_jump_uncond_imm8_:
	;:::::::::::::::::::::::::::::::::::::
	; Короткие прыжки
	movzx eax, byte ptr [edi + 1]
	mov cl, al
	test cl, 10000000b ; isNegative?
	
	jnz @_neg_1
		add edi, eax
		add edi, 2
		xchg eax, edi
		jmp @_exit_
	
	@_neg_1:
		neg al
		sub al, 2
		sub edi, eax
		xchg eax, edi
		jmp @_exit_	

	
@_jump_imm32_:
	;:::::::::::::::::::::::::::::::::::::
	; Длинные прыжки
	mov eax, dword ptr [edi + 2]
	mov ecx, eax
	shr ecx, 24d
	test ecx, 10000000b ; isNegative?
	
	jnz @_neg_2
		add eax, edi
		add eax, 6
		jmp @_exit_
		
	@_neg_2:
		neg eax
		sub eax, 6
		sub edi, eax
		xchg eax, edi
		jmp @_exit_
	;:::::::::::::::::::::::::::::::::::::
	

		
@_jump_uncond_imm32_:
@_call_imm32_:
	;:::::::::::::::::::::::::::::::::::::
	; JMP imm32 & CALL imm32
	mov eax, dword ptr [edi + 1]
	mov ecx, eax
	shr ecx, 24d
	test ecx, 10000000b ; isNegative?
	
	jnz @_neg_3
		add edi, eax
		add edi, 5
		xchg eax, edi
		jmp @_exit_
		
	@_neg_3:
		neg eax
		sub eax, 5
		sub edi, eax
		xchg eax, edi
	;///////////////////////////////////////
@_exit_:
	
	pop edi
	pop ecx

	ret
get_jump_address endp


;::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::|
; Функция для поиска в массиве описаний инструкций             |
; Нового адреса для прыжка....                                 |
;                                                              |
; IN  dwAddress - Адрес прыжка                                 |
; IN  pOpcodes  - Указатель на массив с описаниями опкодов     |
; OUT EAX       - Новый адрес прыжка...                        |
;::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::|
get_new_jump_address proc dwAddress:DWORD, pOpcodes : DWORD
	push ecx
	
	assume ecx : ptr _OPCODE
	mov ecx, pOpcodes
	mov eax, dwAddress
	
	@@:
	cmp [ecx].dwOldAddress, eax
	je @F
	add ecx, sizeof _OPCODE
	cmp [ecx].dwOldAddress, 0
	jne @B
	xor eax, eax
	@@:
	mov eax, [ecx].dwNewAddress
	
	pop ecx
	ret
get_new_jump_address endp


;:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::|
; Функция разбавляет определённый код, инструкцией NOP            |
; ВАЖНО! Код должен заканчиваться опкодом 0CCh                    |
;:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::|
; IN  dwCodeAddress  - Адрес кода для морфинга                    |
; IN  dwOutputBuffer - Адрес куда будет занесён проморфленный код |
; OUT EAX            - Размер проморфленного кода                 |
;:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::|
MorphCode proc dwCodeAddress	: DWORD, dwOutputBuffer : DWORD
local pOpcodes			: DWORD
local dwTotalCodeSize		: DWORD

	;::::::::::::::::::::::::::::::::::::::::::::::::::
	; pOpcodes       - Указатель на описания опкодов ::
	; dwOutputBuffer - Проморфленный код             ::
	;::::::::::::::::::::::::::::::::::::::::::::::::::
	
	;::::::::::::::::::::::::::::::::::::::::::::::::::::::::
	; Зарезервируем памяти для массива с описаниям инструкций
	invoke VirtualAlloc, NULL, 1024*1024, MEM_COMMIT + MEM_RESERVE, PAGE_READWRITE
	mov pOpcodes, eax
	
	;::::::::::::::::::::::::::::::::::::::::::::
	; Обнулим переменную в которую будем заносить
	; Общий размер кода
	push 0
	pop dwTotalCodeSize

	;:::::::::::::::::::::::::::::::::::::::::
	; Без комментариев
	assume ecx : ptr _OPCODE
	mov esi, dwCodeAddress	; Code Address
	mov edi, dwOutputBuffer	; New Code Address
	mov ecx, pOpcodes		; array of _OPCODES
	;::::::::::::::::::::::::::::::::::::::::	
	

	
;::::::::::::::::::::::::::::::::::::::::
; Первый этап :::::::::::::::::::::::::::
;::::::::::::::::::::::::::::::::::::::::

	;::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
	; Укажем для первой инструкции её новый адрес что в EDI
	mov [ecx].dwNewAddress, edi

	;::::::::::::::::::::::::::::::::::::
	; Начинаем цикл
	; Loop 1
	@_loop_1:

	;::::::::::::::::::::::::::::::::::::
	; Узнаем длинну у инструкции
	; IN  ESI == Current Code Offset
	; OUT EAX == Instruction Length
	call c_Catchy

	;:::::::::::::::::::::::::::::::::::::
	; Занесём данные в массив с описаниями
	mov [ecx].dwOldAddress, esi
	mov [ecx].dwLength, eax

	;::::::::::::::::::::::::::::::::::::
	; Короткие условные прыжки меняем на длинные	
	; Они отличаются от длинных префиксом 00Fh
	; в начале и +10h к текущему опкоду прыжка
	; Пример:
	;    Было :00000000:     74 30
	;                    0F +10 30 00 00 00
	;    Стало:00000000: 0F  84 30 00 00 00
	cmp byte ptr [esi], 070h
	jl @F
	cmp byte ptr [esi], 07Fh
	ja @F
		push eax
		mov al, 00Fh
		stosb
		
		movzx eax, byte ptr [esi]
		add eax, 10h
		stosd	
		
		;:::::::::::::::::::::::::::::::::::::::
		; Подсчитаем адрес куда указывает прыжок
		push esi
		call get_jump_address
		
		;::::::::::::::::::::::::
		; Занесём данные в массив
		mov [ecx].dwJumpAddress, eax
		
		pop eax
		
		;::::::::::::::::::::::::::::::::::::
		; Прибавим к общему размеру кода
		; Длинну 00Fh XXh imm32, т.е. число 6
		; где XX » [80h..8Fh]
		add dwTotalCodeSize, 6
	;::::::::::::::::::::::::::::::::::::
	; Пропустим перенос маленького прыжка
	; Переходим к следующей инструкции
	jmp @_next_inst_	
	
	@@:
	;::::::::::::::::::::::::::::::::::::
	; Меняем...
	; JMP imm8 -> JMP imm32
	; Пример
	; Было : 00000000: EB 33
	; Стало: 00000000: E9 33 00 00 00
	cmp byte ptr [esi], 0EBh
	jne @F
		push eax
		
		mov al, 0E9h
		stosb
		xor eax, eax
		stosd

		;:::::::::::::::::::::::::::::::::::::::
		; Подсчитаем адрес куда указывает прыжок
		push esi
		call get_jump_address
		
		;::::::::::::::::::::::::
		; Занесём данные в массив
		mov [ecx].dwJumpAddress, eax

		pop eax		
		;:::::::::::::::::::::::::::::::
		; Прибавим к общему размеру кода
		; Длинну E9 imm32, т.е. число 5
		add dwTotalCodeSize, 5		
	jmp @_next_inst_
	;::::::::::::::::::::::::::::::::::::
	
	
	@@:
	;::::::::::::::::::::::::::::::::::::
	; Проверим на JMP imm32
	cmp byte ptr [esi], 0E9h
	jne @F

	push eax
	push esi
	call get_jump_address
	mov [ecx].dwJumpAddress, eax
	pop eax
	jmp @_replace_instr_
	;::::::::::::::::::::::::::::::::::::
	
	@@:	
	;::::::::::::::::::::::::::::::::::::
	; Проверим на CALL
	cmp byte ptr [esi], 0E8h
	jne @F
	
	push eax
	push esi
	call get_jump_address
	mov [ecx].dwJumpAddress, eax
	pop eax
	jmp @_replace_instr_
	;::::::::::::::::::::::::::::::::::::
	
	
	@@:
	;::::::::::::::::::::::::::::::::::::
	; 00Fh XX imm32
	cmp byte ptr [esi], 00Fh
	jne @F
	cmp byte ptr [esi + 1], 080h
	jl @F
	cmp byte ptr [esi + 1], 08Fh
	ja @F
		push eax
		push esi
		call get_jump_address
		mov [ecx].dwJumpAddress, eax
		pop eax		
	;::::::::::::::::::::::::::::::::::::

	@@:

@_replace_instr_:
	;::::::::::::::::::::::::::::::::::::::::::
	; Переносим инструкцию в новый буффер.
	; Этот код выполниться в том случае
	; Если мы не меняли оригинальные инструкции
	push esi
	push ecx
	
	mov ecx, eax
	rep movsb
	
	pop ecx
	pop esi
	
	; Добавим к общему размеру кода
	; длинну текущей инструкции
	add dwTotalCodeSize, eax

	;::::::::::::::::::::::::::::::::::::
	; сюда прыгаем если мы заменили текущую инструкцию на свою
@_next_inst_:
	;:::::::::::::::::::::::::::::::::::::::::::::::::::::::::
	; Разбавляем код инструкцией NOP
	; Тут можно разбавлять код
	; любыми "пустыми" инструкциями
	; вида:
	;--------------
	;      push eax
	;      pop  eax
	;--------------
	;      mov  eax, eax
	;--------------
	;      и т.д.
	push eax
	mov al, 90h
	stosb
	pop eax
	
	;:::::::::::::::::::::::::::::::::::::::::::::::::::::::::
	; Увеличим общий размер кода на 1 (Длинна инструкции NOP)
	; Так же если будете добавлять свой "пустой" код
	; Нужно будет прибавить к dwTotalCodeSize его общую длинну(!)
	add dwTotalCodeSize, 1
	
	;::::::::::::::::::::::::::::::::::::
	; Перейдём к следующей инструкции
	add esi, eax
	
	;::::::::::::::::::::::::::::::::::::
	; перейдём к следующему элементу
	; в нашей таблице с описаниями опкодов
	add ecx, sizeof _OPCODE
	
	;::::::::::::::::::::::::::::::::::::
	; Заносим новый адрес у следующей инструкции
	mov [ecx].dwNewAddress, edi
	

	;::::::::::::::::::::::::::::::::::::
	; Доходим до int 3
	; Это значит что код закончился....
	; и следовательно нам тут делать уже нечего.
	cmp byte ptr [esi], 0CCh
	jne @_loop_1
	; End Loop 1
	;::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::

	;::::::::::::::::::::::::::::::::::::
	; Укажем что массив с описаниями закончился
	mov [ecx].dwOldAddress, 0




;::::::::::::::::::::::::::::::::::::::::
; Второй этап :::::::::::::::::::::::::::
;::::::::::::::::::::::::::::::::::::::::
	mov ecx, pOpcodes
	@_loop_2:
	;::::::::::::::::::::::::::::::::::::::::
	; Если инструкция является прыжком или вызовом
	; то dwJumpAddress != 0 (!)
	cmp [ecx].dwJumpAddress, 0
	je @F
	;::::::::::::::::::::::::::::::::::::::::
	; Получим новый адрес адрес куда должен
	; указывать прыжок или вызов
	push pOpcodes
	push [ecx].dwJumpAddress
	call get_new_jump_address
	
	;::::::::::::::::::::::::::::::::::::::::
	; Проверим результат на нуль.
	; По идее тут проверки быть не должно
	; ибо EAX (если весь код по адресу dwCodeAddress был корректным)
	; никогда не будет равняться нулю
	; и эти 2 строчки лишние, но мне захотелось
	; всётаки их написать :)))
	cmp eax, 0
	je @F
	;::::::::::::::::::::::::::::::::::::::::
	
	;::::::::::::::::::::::::::::::::
	; Проверим адрес какой инструкции
	; нужно поменять на новый
	mov edx, [ecx].dwNewAddress
	;:::::::::::::::::::::::::
	; Длинные условные прыжки
	; 00Fh XXh imm32
	cmp byte ptr [edx], 00Fh
	je @_0F_XX_imm32
	;:::::::::::::::::::::::::::
	; Длинный безусловный прыжок
	; JMP imm32
	cmp byte ptr [edx], 0E9h
	je @_XXX_imm32_
	;::::::::::::::::::::::
	; Вызов (CALL imm32)
	cmp byte ptr [edx], 0E8h
	je @_XXX_imm32_
	;::::::::::::::::::::::
	jmp @F

	;::::::::::::::::::::::::::::::::::::::
	; 00Fh XXh imm32
	; Нам нужно заменить есстественно imm32
	; что находится по смещению [АДРЕС ПРЫЖКА + 2]
	; Пример:
	;   00Fh 84 imm32   ; JE  imm32
	;   00Fh 85 imm32   ; JNE imm32
@_0F_XX_imm32:
	cmp eax, [ecx].dwNewAddress
	jle @_less_or_equal_1
		push eax
		sub eax, [ecx].dwNewAddress
		sub eax, 6
		mov edx, [ecx].dwNewAddress
		mov dword ptr [edx + 2], eax
		pop eax
		jmp @F

	@_less_or_equal_1:
		push eax
		mov edx, [ecx].dwNewAddress
		sub edx, eax
		neg edx
		sub edx, 6
		mov eax, [ecx].dwNewAddress
		mov dword ptr [eax + 2], edx
		pop eax
		jmp @F
	
	;:::::::::::::::::::::::::::::::::::::::::::::
	; Смещение imm32 в инструкциях JMP и CALL
	; одно и тоже, следовательно код обработки
	; нового адреса у них можно сделать одним
	; imm32 находится по смещению [АДРЕС ПРЫЖКА + 1]
	; Пример:
	;   E9 imm32
	;   E8 imm32
	; :)
@_XXX_imm32_:
	cmp eax, [ecx].dwNewAddress
	jle @_less_or_equal_2
		push eax
		sub eax, [ecx].dwNewAddress
		sub eax, 5
		mov edx, [ecx].dwNewAddress
		mov dword ptr [edx + 1], eax
		pop eax
		jmp @F

	@_less_or_equal_2:
		push eax
		mov edx, [ecx].dwNewAddress
		sub edx, eax
		neg edx
		sub edx, 5
		mov eax, [ecx].dwNewAddress
		mov dword ptr [eax + 1], edx
		pop eax
		jmp @F
	;::::::::::::::::::::::::::::::::::::::::
	

	@@:	
	;::::::::::::::::::::::::::::::
	; Перейдём к следующем описанию
	; Следующего опкода :)
	add ecx, sizeof _OPCODE

	;:::::::::::::::::::::::::::::::::::::
	; Повторяем до тех пор пока
	; не наткнёмся на конец массива
	; На это указывает Нуль в dwOldAddress
	cmp [ecx].dwOldAddress, 0
	jne @_loop_2

	;::::::::::::::::::::::::::::::::::::::::
	; Освободим память что занимали
	invoke VirtualFree, pOpcodes, NULL, MEM_RELEASE
	
	@_exit_:

	mov eax, dwTotalCodeSize
	ret
MorphCode endp

.data
	; Буфер куда занесём проморфленный код
	dwOutputBuffer		dd	0
	
	; Размер буфера
	dwOutputBufferSize	dd	0
	
	; Кол-во байт записанных в фаил
	dwBytesWritten		dd	0
	
	; Имя файлика в который запишем проморфленный код
	szFileName			db	'morphed_code_dump_raw.bin',0
	
	; Текст для MessageBoxA
	szComplete			db	'Complete! :)', 0
.code

start:

	;:::::::::::::::::::::::::::::::::::::::::::::::::::::
	; Зарезервируем память для проморфленного кода
	; Размер можете выставить конечноже свой
	; Всё зависит от кол-ва кода что собираетесь Морфить
	invoke VirtualAlloc, NULL, 1024*1024, MEM_COMMIT + MEM_RESERVE, PAGE_READWRITE
	mov dwOutputBuffer, eax

	;::::::::::::::::
	; Морфим код.....
	invoke MorphCode, offset test_code, dwOutputBuffer
	mov dwOutputBufferSize, eax
	
	;::::::::::::::::::::::::::::::::
	; Дампим проморфленный код в фаил
	invoke CreateFile, offset szFileName, GENERIC_WRITE, FILE_SHARE_WRITE, NULL, CREATE_ALWAYS, 0, 0
	push eax
	invoke WriteFile, eax, dwOutputBuffer, dwOutputBufferSize, addr dwBytesWritten, NULL
	call CloseHandle
	
	;:::::::::::::::::
	; Освободим память
	invoke VirtualFree, dwOutputBuffer, NULL, MEM_RELEASE
	
	;:::::::::::::::::
	; Мессадж :)
	invoke MessageBoxA, 0, offset szComplete, 0, MB_ICONINFORMATION

	;:::::::::::::::::
	; Выход)
	xor eax, eax
	ret
end start

; #########################################################################