;:::::::::::::::::::::::::::::::::::::::::::::::::::::: ; 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 e