здесь, сейчас, этот момент - Умный мусор: построение логики [entries|archive|friends|userinfo]
pr0mix

[ userinfo | ljr userinfo ]
[ archive | journal archive ]

Умный мусор: построение логики [Oct. 1st, 2013|03:50 pm]
Previous Entry Add to Memories Tell A Friend Next Entry
[Tags|, , , , , ]

Основной задачей мусорных инструкций является скрытие/защита полезного кода (от аверов, зорких глаз реверсера и других любопытных). Однако, "неправильный" трэш может стать причиной обнаружения вирусного кода, сводя на нет все наши старания.


Этот текст о том, как улучшить качество генерируемого мусора...




---------------------------------------------------------------------------------------------------------------------------------



				      xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
				      x					    	 x
				      x      Умный мусор: построение логики	 x
				      x					         x
				      xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx




[x]	Введение 


	Основной задачей мусорных инструкций является скрытие/защита полезного кода (от аверов, зорких глаз реверсера 
	и других любопытных). Однако, "неправильный" трэш может стать причиной обнаружения вирусного кода, сводя на 
	нет все наши старания. 
	
	Этот текст о том, как улучшить качество генерируемого мусора. 



[x]	Кто противник


	Допустим, что происходит проверка файла, заражённого нашим вирусом. Антивирус может действовать так: 

		  --------------------------------------------------------------------------------------------------------------
		- запустит поверхностный анализ файла: его структуры и каких-то участков кода (сбор информации для 
		  начала работы эмулятора, кодо-анализатора, эвристика, может чего-то ещё);
		  --------------------------------------------------------------------------------------------------------------
		- далее, запускается эмуль, в процессе работы которого могут быть вызваны: анализатор кода (сбор 
		  данных для (дальнейшей работы) эмулятора и эвристика), а также сигнатурный анализ (поиск известных 
		  сигнатур в уже проэмулированном коде);
		  --------------------------------------------------------------------------------------------------------------
		- после отработки эмуля в дело вступает эвристический анализ собранных данных (сигнатур для нашего 
		  виря ещё не сделали=)), где происходит подсчёт баллов "опасности". Если полученный результат больше 
		  заданного предела - получите клеймо heur-virus'a. 
		  --------------------------------------------------------------------------------------------------------------
	
	От эмуля нам поможет рабочая антиэмуль, от сигнатур - генератор мусора, который (как оказывается xD) использует 
	наш вирь. Но если созданный трэш-код окажется слабым, то нас накроет эвристика. 



[x]	План наступления


	Итак, для построения более качественного трэш-кода, вначале я предлагаю выбрать, под генерируемый код какого 
	компилятора мы будем "косить": ms, borland etc. После того, как выбран компилер (например, ms), можно ещё 
	определить, под какой режим генерации/оптимизации мы будем подстраиваться ("min size"/"max speed"). Это всё, 
	конечно же, не обязательно, но желательно. Так как под разными режимами код генерируется по-другому. Например, 	 
	для ms-компилера, команда занесения единицы в регистр в режиме "max speed" (в основном) будет такая:
		
		----------------------------------------------------------------------------------------------------------------
		mov	eax, 1		;0xB8 0x01 0x00 0x00 0x00
		----------------------------------------------------------------------------------------------------------------
	
	А для "min size" 

		----------------------------------------------------------------------------------------------------------------
		xor	eax, eax	;0x33 0xC0
		inc	eax		;0x40
		----------------------------------------------------------------------------------------------------------------

	Трэшген может генерировать оба эти варианта, но более правдоподобно смотрится, если держаться одной тактики 
	(неизвестно, какие извращения будут в новых версиях эвристиков). 

	Далее, помимо разных фичезов, которые вы встроите в трэшген, он также должен уметь генерировать "реалистичный" 
	код (похожий на обычный код стандартных программ, написанных на ЯВУ), а именно:

		  --------------------------------------------------------------------------------------------------------------
		+ "правильные" инструкции (опкоды и операнды - например, команда "mov eax, ecx" может быть закодирована 
		  с помощью двух разных опкодов: 0x8B 0xC1 и 0x89 0xC8 -> ms-компилер юзает первый вариант; некоторые 
		  команды с использованием регистра EAX, имеющие "оптимизированные" варианты опкодов; etc);
		  --------------------------------------------------------------------------------------------------------------
		+ "правильные" конструкции (например, test/cmp без последующей инструкции jmp/jxx - очевидное палево);
		  --------------------------------------------------------------------------------------------------------------
		+ множество различных команд (использующих регистры, адреса памяти и т.п.), функции (с прологами/
		  резервированием стэка/эпилогами etc и команды с использованием локальных переменных, входящих 
		  параметров), winapi и другие; 
		  --------------------------------------------------------------------------------------------------------------
		+ "правильная" статистика частоты встречаемости опкодов (собираем стату в обычных прогах и используем её; 
		  для более точного результата можно собирать стату только в файлах, собранных выбранным ранее 
		  компилером); 
		  --------------------------------------------------------------------------------------------------------------
		+ нормальная энтропия (в битах ~ [5.5; 6.8]; кстати, энтропия будет примерно в заданном диапазоне, если 
		  генерить "правильный" код + использовать стату встречи опкодов (сюда можно добавить и логику команд));  
		  --------------------------------------------------------------------------------------------------------------
		+ только живой код (который может выполниться);
		  --------------------------------------------------------------------------------------------------------------
		+ etc; 
		  --------------------------------------------------------------------------------------------------------------

	Но даже такой трэш-код, построенный с учётом данных пунктов, может легко ловиться эвристикой. 



[x]	Полезный мусор


	Основная засада в том, что мусорный код - это только мусор, набор бесполезных инструкций. В этом и кроются причины 
	гнева эвристиков. А раз так, значит трэш должен стать полезным. Для этого надо реализовать ещё 2 задачи:

		   -------------------------------------------------------------------------------------------------------------
		1. полезный код должен использовать результат работы трэш-кода (или наоборот, трэш-код должен как-либо 
		   повлиять на работу полезного кода) aka "псевдо-цель"; 
		   -------------------------------------------------------------------------------------------------------------
		2. "LOGICAL TRASH" technique; 
		   -------------------------------------------------------------------------------------------------------------

	Первый пункт в общем случае реализуется довольно просто: генерируем мусор, запускаем его на выполнение, и после 
	отработки трэша полученный результат используем в полезном коде. Например, сгенерировали такой код:
		
		----------------------------------------------------------------------------------------------------------------
		mov	eax, 100
		mov	ecx, eax
		sub	ecx, 95
		----------------------------------------------------------------------------------------------------------------

	После его выполнения ECX = 5. И данное значение можно добавить к ключу для расшифровки вирусного кода (применений 
	куча). 
	Однако, сгенерированный трэш-код может быть и таким:
	
		----------------------------------------------------------------------------------------------------------------
		mov	eax, 100	;1
		mov	ecx, eax	;2
		mov	ecx, eax	;3
		mov	ecx, eax	;4
		sub	ecx, 95		;5
		----------------------------------------------------------------------------------------------------------------

	После его выполения ECX также равно 5. Но команды 2 и 3 выдают себя с потрохами, за что будут наказаны эвристикой. 
	Решение состоит в построении "логичного" мусора.  
	


[x]	"LOGICAL TRASH" technique


	Идея заключается в том, чтобы мусорный код сделать логичным, подобно логике кода обычных программ. Нормальный код 
	вначале инициализирует параметры (регистры, локальные переменные etc); затем выполняет команды, использующие и/или 
	как-либо влияющие на эти параметры. Причём команды являются одним целым - выполняют общую задачу, и среди них нет 
	лишних - мусорных. Нет повторных инициализаций, использования и обращения к (значениям) неинициализированным 
	параметрам. Все инструкции связаны друг с другом, каждая влияет на дальнейший ход выполнения кода. 

	Примерно такую логику я реализовал в новой версии своего движка xTG (v2.0.0), который работает по следующей 
	схеме: 





		 ________
		|	 |
		| начало |
		|________|
		    |
		    |
		    |
		    |					+--------------------------------------------+
		    |					|					     |
		    |					|					     |
		    |	 ________________________	|		 ________________________    |
		    |	|			 |	|		|			 |   |
		    |	| модуль генерации	 |	|		| модуль логики		 |   |
		    |	| команд		 |	|		|			 |   |
		    |	|			 |	|		|			 |   |
		    |	|			 |	|		|			 |   |
		    |	| ---------------------- | 	|		| ---------------------- |   |
		    +-->| генерация 		 |<-----+	   +--->| парсер команд	      	 |   |
		+------>| правильно		 |		   |    |			 |   |
		|	| построенной		 | адрес команды   |	| ---------------------- |   |
		|	| команды		 |-----------------+ 	| эмулятор команд	 |   |
		|	| 			 |  			| 			 |   |
		|	| ---------------------- |			| ---------------------- | 0 |
		|	| корректировка адреса	 |<----------------+	| анализатор команд/     |---+
		|     1	| для генерации новой	 |		   |  1 | корректор логики       |
		+-------| команды		 | 0		   +----| команд		 |
			|			 |------+		|			 |
			| ---------------------- |	|		| ---------------------- |
			|________________________|	|		|________________________|
							|
							|
		   					|			
		 ________				|
		|	 |				|
		| конец	 |<-----------------------------+
		|________|





	Распишем подробно:

		      ----------------------------------------------------------------------------------------------------------
		I.    вначале, конечно же, вызываем модуль генерации команд;
		      ----------------------------------------------------------------------------------------------------------
		II.   генерируем "правильную" команду: правильные опкоды и остальные байты. Да, кстати, если разработанный 
		      двигатель логики (ДЛ) будет применяться к сторонним трэшгенам (или другим движкам), то ДЛ также 
		      должен проверять, правильно ли построена команда (aka проверка на уровне байтов); 
		      ----------------------------------------------------------------------------------------------------------
		III.  вызываем модуль логики, передавая в него адрес только что созданной команды; 
		      ----------------------------------------------------------------------------------------------------------
		IV.   за дело принимается парсер команд. Парсер может быть функцей, являющейся частью модуля логики, а может 
		      быть и отдельным самодостаточным движком (дизасм). Первый случай хорошо подходит, если модуль логики 
		      является частью генератора мусора. Тогда в функции парсера будут разобраны только те команды, которые 
		      может генерировать двигл. Второй случай хорошо подходит, если модуль логики является самостоятельным 
		      движком. И при этом мы не знаем, какие команды могут генерироваться. 

		      Парсер выясняет, какая перед ним команда, и получает её параметры (операнды: регистры, адреса etc) - в 
		      соответствии с этим сохраняет в некоторую структуру данные параметры и выставляет определённые флаги. 
		      Заполненная структура будет использоваться анализатором команд (об этом ниже). 
		      Также, например, если встретилась команда mov ecx, dword ptr [403008h] и т.п., тогда парсер заменит 
		      адрес 403008h на другой, соответствующий ему адрес в выделенной памяти для корректной эмуляции команды;  
		      ----------------------------------------------------------------------------------------------------------
		V.    затем эмулируем (скорректированную) команду. Эмуль, по аналогии с парсером, может быть как встроенной 
		      функцией в модуле логики, так и полноценным движком-пирожком; 
		   
		      Эмуль получает адрес команды, подготавливает специальную среду, копирует туда команду и эмулирует. Причём 
		      эмуляция может быть как минимум 3-х видов: прямой запуск в специальной среде, полная имитация выполнения 
		      команды и сочетание этих двух методов (для большинства команд хватает 1-ого метода). Результат эмуляции 
		      (текущие значения параметров команды и др.) сохраняем в переменных: виртуальных регистрах и др.

		      Кстати, эмуль - козырная технология для вирей, с помощью которой можно творить очень интересные темы 
		      (для UEP'a, виртуальных машин, "logical trash" tech, морфинга и прочих вкусностей); 
		      ----------------------------------------------------------------------------------------------------------		
		VI.   и после вызываем анализатор команд/корректор логики. Анализатор, по аналогии с парсером и эмулем, может 
		      быть как встроенной функцией в модуле логике, так и полноценным двиглом;

		      Анализатор, на основе данных от парсера (заполненная структура) и эмуля, решает, подходит ли команда по 
		      логике или нет. 

		      Анализ команды проходит в 2 этапа: 
			
			1. Проверка параметров команды. 

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

			--------------------------------------------------------------------------------------------------------
			LGC_INSTR_INIT	equ	00000000000000000000000000000001b	;команда инициализации параметров; 
			LGC_INSTR_CHG	equ	00000000000000000000000000000010b	;команда изменения параметров; 
			LGC_P1_DST	equ	00000000000000000000000000000100b	;первый парам - приёмник
			LGC_P1_SRC	equ	00000000000000000000000000001000b	;первый парам - источник
			LGC_P2_DST	equ	00000000000000000000000000010000b	;второй парам - приёмник
			LGC_P2_SRC	equ	00000000000000000000000000100000b	;второй парам - источник
			LGC_P1_REG	equ	00000000000000000000000001000000b	;первый парам - регистр
			LGC_P1_ADDR	equ	00000000000000000000000010000000b	;первый парам - адрес
			LGC_P1_NUM	equ	00000000000000000000000100000000b	;первый парам - число
			LGC_P2_REG	equ	00000000000000000000001000000000b	;второй парам - регистр
			LGC_P2_ADDR	equ	00000000000000000000010000000000b	;второй парам - адрес
			LGC_P2_NUM	equ	00000000000000000000100000000000b	;второй парам - число
			--------------------------------------------------------------------------------------------------------

			Этими флагами можно описать почти все инструкции (некоторые флаги можно убрать). Если инструкция 
			содержит больше 2 параметров, тогда остальные хранятся в отдельном поле. 
			
			Далее, по флагам определяется, что и как чекать: проверки на возможность инициализации параметров, 
			на изменение их значений, на использование их в других командах и многое другое. Результат каждой 
			проверки заносится в маски. Их 2: regs_init & regs_used. Грубо говоря, это 2 dword'a, где каждый бит 
			соответствует определённому параметру (например, какому-то регистру). Причём, биты в regs_init 
			показывают, можно ли инициализировать параметр или нет (защита от повторной инициализации). А по 
			битам в regs_used узнаём, можно ли вообще использовать параметр в командах или нет. 

			2. Проверка состояний параметров команды

			Итак, если первый этап пройден, то это означает, что параметры годные. Продолжим. 
			
			Состояние - это некоторое сохранённое значение, которое принимал параметр. Состояния всех параметров 
			хранятся в таблице состояний, которая представляет собой буфер определённого размера. 
			
			Значит, анализатор берёт текущее значение параметра, которое мы получили с помощью эмуляции (и 
			сохранили, например, в виртуальном регистре), и сверяет его со всеми накопленными состояниями данного 
			параметра. 
			Если совпадение найдено, тогда команду считаем мусорной, проверка не пройдена. В этом случае 
			из таблицы состояний берём последнее сохранённое состояние данного параметра и делаем его текущим 
			(сохраняем это состояние в виртуальном регистре); а также восстановим маски на предыдущие значения.
			Если совпадение не найдено - значит это новое состояние параметра, команда прошла проверку. Добавляем 
			это значение в таблицу состояний; 
		      ----------------------------------------------------------------------------------------------------------
		VII.  переходим снова в модуль генерации команд. Смотрим, какое значение вернул нам модуль логики: 
		      если 0, тогда команда не подходит по логике - по её же адресу сгенерируем новую команду (перезапишем). 
		      Прыгаем на пункт II. 
		      если 1, тогда команда подходит по логике. Прыгаем на пункт VIII. 
		      ----------------------------------------------------------------------------------------------------------
		VIII. увеличиваем адрес (для генерации новой команды) на размер проверенной команды. И выясним: если мы 
		      сгенерировали нужное количество байтов, тогда прыгаем на пункт IX. Если не все, тогда на пункт II.
		      ----------------------------------------------------------------------------------------------------------
		IX.   выходим; 
		      ----------------------------------------------------------------------------------------------------------



[x]	Примеры генерации простого мусора


	 ---------------------------------------	 ------------------------------------
	| такой код не пройдёт проверку логики, |	| такой код пройдёт проверку логики, | 
	| а значит не сгенерируется  		|	| а значит сгенерируется	     |
	 ---------------------------------------	 ------------------------------------

		----------------------------------------------------------------------------------------------------------------
		;1.1						;2.1
		mov	edi, 1000				mov	edi, 1000
		mov	esi, edi				mov	esi, edi
		mov	edi, esi				mov	edi, 2000
		----------------------------------------------------------------------------------------------------------------
		;1.2						;2.2
		mov	eax, 1000				mov	eax, 1000
		mov	ecx, 2000				mov	ecx, 2000
		xchg	eax, ecx				xchg	eax, ecx
		xchg	eax, ecx				inc	ecx
								xchg	eax, ecx
		----------------------------------------------------------------------------------------------------------------
		;1.3						;2.3
		xor	edi, edi				xor	edi, edi
		xor	ebx, ebx				xor	ebx, ebx
		add	edi, ebx				inc	ebx
								add	edi, ebx
		----------------------------------------------------------------------------------------------------------------
		;1.4						;2.4
		mov	edx, 1000				mov	edx, 1000
		mov	eax, edx				mov	eax, edx
		dec	edx					mov	edx, 2000
		mov	edx, 2000				
		----------------------------------------------------------------------------------------------------------------
		;1.5						;2.5
		mov	eax, 1000				mov	eax, 1000
		mov	ecx, 1001				mov	ecx, 1001
		sub	ecx, eax				sub	ecx, eax
		sub	eax, ecx				sub	eax, ecx
		inc	eax					dec	eax
		----------------------------------------------------------------------------------------------------------------

	------------------------------------------------------------------------------------------------------------------------
	В примере 1.1 первые 2 команды нормальные, а третья - мусорная. Регистр EDI инициализировать можно, но он примет 
	такое же значение, какое имеет сейчас - ненужная инициализация. Пример 2.1 (и все остальные в дальнейшем) показывает 
	правильный вариант инициализации. 
	------------------------------------------------------------------------------------------------------------------------
	В примере 1.2 первые 3 команды правильные, а 4-ая - мусорная. Регистры EAX & ECX снова примут значения, которые уже 
	имели (проверка состояний параметров). 
	------------------------------------------------------------------------------------------------------------------------
	В примере 1.3 первые 2 команды правильные, а 3-я - мусорная. EDI += 0 (проверка состояний);
	------------------------------------------------------------------------------------------------------------------------
	в примере 1.4 первые 3 команды правильные, а 4-я - мусорная. Регистр EDX нельзя инициализировать, если он прежде не 
	повлиял на значение другого параметра (проверка параметров); 
	------------------------------------------------------------------------------------------------------------------------
	в примере 1.5 первые 4 команды правильные, а 5-ая - мусорная. После выполнения первых 4-x команд состояния регистра 
	EAX будут такие: 1000, 999. А после выполнения 5-ой команды EAX = 1000. Такое состояние уже было (проверка состояний 
	параметров). 
	------------------------------------------------------------------------------------------------------------------------ 


	 	
[x]	Позитив =)


	Реализацию техники "логичного мусора", наглядные примеры генерации трэш-кода, а также более полное понимание 
	задумки - всё это ты найдешь в сорцах xTG v2.0.0. 

	Следует сказать, что в xTG наиболее лучшее качество логики получается при генерации линейного трэш-кода без 
	winapi-функций. 
	В остальных случаях логика будет нечёткой, но будет: на ветвлениях и с винапишками. Это связано с модулем 
	логики - чем он мощнее, тем качественней выхлоп. 

	Если же не устраивает логика каких-либо инструкций, достаточно просто установить другие флаги. 

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

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



[x]	Используемая инфа


	1. beauty on the fire "Эмуляция программного кода", 2004, http://uinc.ru/articles/47/
	2. beauty on the fire "Анализаторы кода в антивирусах", 2004, http://uinc.ru/articles/45/
	3. Sl0n "Полиморфизм. Новые техники", 2004, http://vx.netlux.org/lib/vsl05.html


								
					[+]
						август, 2011

						m1x
						pr0mix@mail.ru
						EOF

										вирмэйкинг для себя...искусство вечно 







LinkLeave a comment