Суть проблемы: есть код который пишет другой программист, код этот нужно зашифровать в бинарнике, а расшифровку организовать во время выполнения в стеке. Ради бога, не спрашивайте зачем это нужно, просто так нужно)) (это один из способов защиты от отладки). А сама проблема вот в чем, когда мы из секции кода копируем его(код) на стек то, т.к. call'ы в скопированном коде могут быть near(E8, т.е. адрес указан не абсолютный, а относительно адреса самой инструкции call) и соответственно адреса коллов начинают указывать в неправильное место. Как с этим можно справиться? Я думаю о варианте заставить компилятор генерить все call'ы как far(FF15), либо искать инструкции E8 и править их адреса на нужные, но как их можно отличить от других данных/команд я хз. Может немного сумбурно, но суть вроде понятна. Ниже пример: Код (Text): int main() { char Tmp[500]; void* start; int len; // получаем размер и адрес кода между метками __asm { pushad mov ebx, lab_1 mov start, ebx mov eax, lab_2 sub eax, lab_1 mov len, eax popad } memcpy(Tmp, start, len); // расшифровываем код в Tmp (как, пофиг) goto lab_2; lab_1: // код написаный другим человеком который должен выполняться на стеке. Может быть что угодно например std::cin >> a; std::cout << a << std::endl; lab_2: // передаем управление коду в буфере __asm{ lea ecx,Tmp call $ + 5 pop eax add eax,7 push eax jmp ecx } return 0; }
Код (Text): mov EAX,offset MessageBoxA CALL EAX http://asm.shadrinsk.net/arh/res1.php?par5=129502&par4=0&n=32
Это должно отключить DEP Код (Text): .code start: mov dword ptr ds:[xxx], 2 push 4 push offset xxx push 22h push -1 call NtSetInformationProcess
дизассемблер длин (помоему это так называется) + ищем E8 в первом байте инструкций (всех, или только которые по 5 байт)... такая вобщем идея
не лучше ли сразу писать базонезависимый код? Код (Text): либо искать инструкции E8 и править их адреса на нужные, но как их можно отличить от других данных/команд я хз. Дизассеблером пройтись, добавляя дельту к смещениям, не забывая про jxx и тд. Но релочить так свой же код имхо несколько не правильно =\
В общем я как раз и думал о дизассемблере и корректировке адреса на вычисленную дельту. Хотя целый дизассемблер это весьма долго. Про дизассемблер длин тоже думал, вот только как его заюзать в данном случае дошло только сейчас. Спасибо всем за ответы. Может есть еще какие-либо идеи? p.s. DEP больше маркетинг чем защита. Подробности http://www.insidepro.com/kk/063/063r.shtml
чем компилилось/линкилось? если есть релоки, то ищите их на ваш код/данные и релочьте их куда угодно. дизасм менее надежен. для пик-кода надо будет писать все или на асме или компилить спец-компилером. кстати, чтото мой склероз подсказывает, что в гцц была опция -fPIC. может, стоит попробовать ваш код под перемещение компилить с ей?
qqwe Может использовать поиск по терминам "пермутация" и "морфинг". Тогда поймёте что дизасм не менее надёжен.
Clerk ну а объем кода идущий на дизасм и разбор всех возможных вариантов переходов и доступа к переменным и объем кода идущий на прохождение по списку из 10тка релоков? кроме того, как вы надежно и просто разберете нечто вроде mov eax,[ebp + 8] ;.. некоторые преобразования над eax jmp eax ; или [eax] похожий код мсвс часто делает на оптимизации свитчей. или ; вычисления и push адрес перехода ; код ; опять код ret ; переход такое тоже бывает. кроме того, сколько дизасм будет расковыривать все цепочки библиотечных вызовов, что навпихает С++ (если писано на нем) на каждом шагу? в то же время, релоки компилер и линкер все равно сгенерируют.
qqwe У вас в оси есть экзешник содержащий релоки ? Не нужны никакие релоки, если компилируемый код готовится к мутации, он пишется особым образом, при котором в этом коде нет данных и абсолютных ветвлений.
Clerk вопрос звучал так те осевые ехешники тут ни при чем. а любой объектник содержит релоки на все абсолютные адреса. можно и так. только это ведь уже затруднительно сделать даже на Цэ (хотя и возможно). те объем и сложность такого кода при средних трудозатратах будет очень ограничена. а чем вам релоки так не нравятся? они ведь занимают совсем немного места и могут кодироваться как угодно. и при этом здорово упрощают работу
qqwe Мне они нравятся, значительно упрощают инфект, так что хватает базового дизасма и столь глубоко, что вы и под дизасмом внедрённый код врятле найдёте Более того вы сказали: Если есть сурцы, то мутация превращается из пермутации в обфускацию. Задачи пермутации - интеграция в бинарный код. Задачи мутации - изменение графа, который заранее описан(как бинарный упрощённый так и в сурцах, может быть морфинг в исходном коде), в вашем случае это позволяют исполнить фиксапы. Частный случай морфинга - перенос кода в памяти, тоесть изменение базы загрузки без изменения графа. Универсальный двиг содержит полноценный дизасм. Мутатор своего кода содержит только дизасм длин и базовый дизассемблер, который различает только ветвления, этого достаточно для пересборки бинарного кода.