Затем на свет появляется ASProtect - мощная защита Win32 программ, которая также с версии 1.1 включает в себя этот полиморфик. Вот ее основные функции - сжатие и шифровка бинарного кода программы, добавление антиотладочного кода, а также кода противодействия дизассемблерам и программам снятия дампа памяти. ASProtect использует сильные криптографические алгоритмы для генерации регистрационных ключей - RSA 1024, BlowFish, TwoFish и так далее.
Взаимодействие защиты с целевым приложением осуществляется путем перехвата API вызовов. Особенности ASProtect - большое количество мутаций и защищенных ею условно бесплатных программ. Версия 1.11 вобрала в себя код противодействия перехвату собственных API из вируса Win32.Crypto и полиморфный движок из BPE32. Самая поздняя версия, которую я встречал "в диком виде" (здесь столько из кода вирусов, что думаю такое выражение будет уместно) - 1.23.
Name VS VO RS RO Char до: .text 000038F8 00001000 00003A00 00000400 60000020 .rdata 00000FB4 00005000 00001000 00003E00 40000040 .data 000007F8 00006000 00000400 00004E00 C0000040 после: 00004000 00001000 00002200 00000400 С0000040 00001000 00005000 00000600 00002600 С0000040 00001000 00006000 00000200 00002E00 C0000040 .data 0000F000 00007000 0000E400 00002E00 C0000040 .adata 00001000 00016000 00000000 00011200 C0000040
Если внимательно посмотреть на границы секций в файле и в памяти (или разобрать заголовки), то видно, что выравнивание на диске 0х200 и 0х1000 в памяти защита не тронула. После заголовка в 0х400 байт в защищенной программе три исходных секции расположены в том же порядке, но без имен и слегка измельчав после шифрации. Зато появились две дополнительные секции .data и .adata. Обратите внимание, что атрибуты секций в защищенном варианте пытаются убедить нас, что кода здесь нет.
адрес содержимое инструкция 401000 6801704000 push 00407001 401005 E801000000 call 0040100B 40100A C3 ret 40100B C3 ret
Этот код находится в самом начале первой безымянной секции. Она же бывшая .text исходной программы. Смысл этого двенадцати байтового "нехилого молочного коктейля за пять долларов" - jmp 407001. Вам кажется, что это довольно извращенный переход? Дальше будет еще интереснее. Смотрим, что творится по адресу 407001 (Эта ячейка находится по смещению 1 от начала добавленной ASProtect’ом .data. В первом байте стоит nop.):
407001 60 pushad
Сохранения состояния регистров.
407002 E803000000 call 40700A 407007 E9EB04 <пока не важно> 40700A 9D pop ebp 40700B 45 inc ebp 40700C 55 push ebp 40700D C3 ret
Здесь напрямую меняется адрес возврата. Если бы вместо строк 0A-0C было бы что-то более стандартное, то вернулись бы мы на 407007 E9. А так попадем на 407008 EB.
407008 EB04 jmp 0040700E
И опять, все, что сделал этот код - передал управление дальше без каких бы то ни было полезных действий. Длина хитрого перехода опять равна двенадцати байтам, но на этот раз код содержит мусорный байт, мешающий дизассемблеру верно интерпретировать инструкции.
40700E call 00407014 407013 EB 407014 pop ebp
Подобный код с мусорным байтом нам только что встречался. Здесь таким образом определяется адрес инструкции 407013. В принципе количество байт между call и pop могло быть абсолютно произвольным.
407015 mov ebx, FFFFFFED 40701A add ebx, ebp 40701C sub ebx, 00007000
В три инструкции получили в ebx адрес начала проекции файла в память отняв сначала от 407013 смещение ячейки от начала секции, а затем смещение секции от начала проекции.
407022 cmp dword ptr [ebp+25], 00 407026 mov [ebp+25], ebx
Сохранили полученный адрес.
407029 jnz 0040703c 40702b lea eax, [ebp+2a] 40702E push eax 40702F push ebx 407030 push dword ptr [ebp+91d]
На стек заносятся три параметра - ничем не примечательный адрес недалеко от начала секции .data ASProtect’а - 407039, адрес начала проекции файла в памяти 400000 и адрес чего-то напоминающего таблицу импорта для трех функций GetProcAddress, GetModuleHandleA, LoadLibraryA.
407036 jmp 407057
Вот как раз следующий свободный адрес за этим переходом - 407039. При переходе на 407057 мы оставили за собой 1Е свободных байт.
407057 mov eax, 166906B1 40705C call 40706C ... 40706C push eax 40706D push edi 40706E push ecx 40706F pop ebx 407070 pop ebx 407071 pop eax
Манипуляции со стеком - полнейший мусор. Опять никакой полезной работы.
407072 pop ecx
Теперь в ecx адрес следующей за вызовом 40706С ячейки памяти. Помните, я об этом говорил - количество байт между call и pop при определении адреса следующей за call инструкции может быть любым. И ничего страшного, если эти байты представляют собой не выполняющие реальной работы инструкции.
407073 jmp 407082 ... 407082 add ecx, 814 407088 sub ax, 06FC 40708D mov esi, 1DE 407092 mov bx, ax
Несмотря на то, что много инструкций было потрачено на два запутанных перехода и два определения адресов и просто мусорные инструкции (на этом участке, допустим, никакой нагрузки не несут операции над eax и ebx) мы закончили инициализацию регистров и памяти нужными значениями и с этого момента начинает работу непосредственно полиморфный движок, распаковывающий код самого ASProtect’а.
С этого момента установка программных точек останова для контроля работы программы теряет смысл. Можно либо пользоваться аппаратными (если ставящий защиту программист не предусмотрел работу с отладочными регистрами в коде защиты), либо через определенные промежутки времени контролировать инициализацию нужного байта нужным значением. Но при этом остановка произойдет не на требуемой инструкции, как бы ни был мал заданный промежуток времени.
407095 mov edx, [ecx]
Получили в edx очередные (на этом шаге первые) четыре закодированных байта.
407097 mov bx, BC32 40709B sub edx, 46F4DA7D 4070A1 mov ebx, edx 4070A3 xor edx, 0A814D72 4070A9 jmp 4070BB
Первая и третья инструкции - хлам. (Я буду выделять инструкции, выполняющие реальную работу жирным шрифтом.) Остальные расшифровывают порцию данных:
4070BB sub edx, 36D2E5C3
Все, на этом трехшаговая (sub - xor - sub) расшифровка окончена. Дальше идет подготовка к следующему шагу цикла:
4070C1 mov edi, esi 4070C3 mov [ecx], edx
Теперь эти расшифрованные байты готовы к выполнению.
4070C5 mov bl, 59 4070C7 sub ecx, 01 4070CA mov ebx, eax 4070CC dec ecx 4070CD dec ecx 4070CE dec ecx 4070CF mov ax, 9789 4070D3 sub esi, 1 4070D6 jnz 4070F0
Полезная работа этого участка - уменьшение на 4 ecx, теперь он указывает на следующий нерасшифрованный блок, и декремент счетчика цикла esi, куда до начала работы мы записали 1DE.
4070F0 mov bl, ah 4070F1 jmp 407095
Вот и все. 1DE раз программа будет брать по 4 байта, применять к ним sub - xor - sub и
записывать полученный результат обратно. Еще обратите внимание на переход с 4070A9 на
4070BB. Если дизассемблер не поддерживает интерактивный режим, то вы не увидите инструкции
с началом в 4070BB. Ближайшая будет начинаться в 4070BA. Этот переход запутывает
дизассемблер.
Выйдем мы из цикла вот здесь:
4070DC mov ebx, 22AFF0FBC 4070E1 jmp 407101
Переход в очередной раз осуществляется на неудобный для дизассемблирования адрес. Теперь становится понятно, что содержимое первой секции по адресам памяти 407000 - 407100 отвечает за самораспаковку, далее следует распакованный код. Этим циклом мы сделали исполняемым участок памяти 407101 - 407878.
407101 sbb si, 246C 407105 call 407115 ... 407115 call 40712D ... 40712D jmp 40713F
Это для нас уже не ново - sbb для отвода глаз, занос двух адресов на стек и переход дальше (опять с фокусом против дизассемблирования)
40713F pop esi 407140 pop edi
Достали в esi 407117 и 407107 в edi.
407141 push 3FA33F01 407146 movzx ebx, dx 407149 pop ecx
Здесь нет ничего, что бы использовалось в дальнейшем.
40714A add edi, 76A 407150 sub ecx, 6D8AE018 407156 sub edx, edx 407158 push esi 407159 jnp 407165 40715F push 2915342E 407164 pop ebx 407165 pop ecx
Только две полезные инструкции - занулен edx, значение edi увеличено на 76A. Переход не будет выполнен никогда.
407166 push dword ptr [edi+edx] 407169 sub esi, 4F039B06 40716F pop eax 407170 add ch, DE 407173 add eax, 2B105828 407179 mov esi, ebx 40717B sub eax, 27C1E941 407181 sbb bx, 158D 407186 sub eax, 6BE84EE6 40718C mov cl, 69 40718E push eax 40718F call 40719F
edi - база расшифровываемого участка, edx - смещение, равное на первой итерации расшифровки нулю.
40719F mov ebx, eax 4071A1 pop ecx 4071A2 pop dword ptr [edx+edi] 4071A5 jae 4071B3 4071AB jle 4071B3
Этот участок можно было бы заменить одной инструкцией безусловного перехода.
4071B3 mov bx, BDC6 4071B7 sub edx, 401D08B4 4071BD push 0DD37F23 4071C2 jmp 4071DA
Модификация смещения и переход дальше.
4071DA pop ecx 4071DB add edx, 401D08B0 4071E1 jg 4071EB 4071E7 mov cx, 4B03 4071EB cmp edx, FFFFF984 4071F0 jnz 407166
В результате этого шаманизма смещение уменьшается на 4 (т.е. на второй итерации оно будет -4d, затем -8d и так далее).
Давайте аккуратно вспомним, что на данный момент успело произойти с программой. В начале первой секции (бывшей секции кода) ASProtect вставил двенадцатибайтный переход на собственную первую секцию. В ней первые 0х100 байт уже находились в виде инструкций процессора (причем до смещения 0х95 находится код инициализации регистров нужными значениями, а дальше цикл расшифровки) и при выполнении этот код распаковал содержимое памяти по адресам 407101 - 407875. Распаковал-то он весь этот участок, но реально для расшифровки первичного распаковщика используются инструкции до смещения 1F0. Остальное вторично распаковывается от 407875 до 4071F9, после чего управление передается на
[c] wirepuller, (wirepuller@tut.by)
29.08.02