[Assembler] Самомодифицирущийся код

Corrector

Имеется исходник программы, написанная на си под винду. Одна из её функций - на чистом ассемблере. Нужно до вызова этой функции заменить команды этой функции на другие, опкоды которых берутся из массива. Как записать поверх одного кода другой?

banderon

А можно получить адрес этой на ассемблере написанной проги? Попробуй писать по тому адресу, естественно с необходимым смещением. Однако сразу оговорюсь, сам именно этого не пробовал, да и винда может это запретить. Но попробовать можно

Slavaga

Это во время работы программы нужно делать?
Если да, то под виндами по-моему нельзя этого делать.

Corrector

да, нужно заменить код runtime

Corrector

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

Chupa

на стеке можно попробовать, хотя тут NX помешать может

Slavaga

Содержимое массива будет меняться динамически так понимаю?
Кажись нельзя. Это типа одна из защит в виндовс.
Щас поищу, где-то у меня на эту тему было несколько статей.

Helga87

Это во время работы программы нужно делать?
Если да, то под виндами по-моему нельзя этого делать

Ну да, конечно. А как по-твоему работает JIT-компилятор у Java и .Net?

banderon

Переформулирую задачу: нужно выполнить код, находящийся в массиве. Необязательно записывать его поверх чего-либо.
Вот такую задачу я решал, правда на Дельфи под ВинХР64. Я тестил как у них DEP работает. Действительно работает
То есть если он включен, то шансов - около нуля, но ведь DEP можно и выключить, тогда все работает как по маслу.

Slavaga

Ну да, конечно. А как по-твоему работает JIT-компилятор у Java и .Net?
Понятия не имею, с ними никогда не работал.
Вот выдержка из одного довольно старого документа о вирусах.
Самая главная фишка.
Заключается в том, что для каждой страницы существует так называемый уровень доступа. В win32 всего два уровня защиты: ring-3 (юзер) и ring-0 (ядро). Находясь в ring-0 свершенно наплевать какой уровень доступа у какой страницы -- все их (подгруженные) можно без проблем читать и писать.
А вот для ring-3 есть несколько вариантов:
1. страницы для чтения и записи (read-write)
2. страницы только для чтения (read-only)
3. хуй (ни читать ни писать в такие страницы нельзя)
4. комбинации вышеперчисленных с испольнябельностью (executable а также guard, writecopy и еще хер знает что -- скорее всего ничего их этого вам не встретится.
Идея в том, что кодовые страницы в системе помечаются как read-only, и поэтому их можно исполнять и читать, а вот писать в них нельзя. Это в основном страницы кода в kernel'е и в ваших PE-файлах. Проблема с PE-файлами решается добавленим нужного бита в ObjectTable при заражении файла, либо через VirtualProtect/WriteProcessMemory; проблема с kernel'ами и системными хернями решается (под маздаем) несколько более хитрыми приемами.

Olyalyau

Под POSIX вот:

#include <stdio.h>
#include <sys/mman.h>
typedef int function_t ;
int
main
{
char * sample_code = "\xB8\x01\x00\x00\x00\xC3";
function_t * function;
char * code;
function = code = mmap (0, 6, PROT_EXEC | PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS, 0, 0);
memcpy (code, sample_code, 6);
printf ("%d\n", (* function) ;
code [1] = 2;
printf ("%d\n", (* function) ;
}

под виндой наверняка есть аналог mmap'а.

Dasar

> Кажись нельзя. Это типа одна из защит в виндовс.
и как по твоему всякие exepack-и работают? или asprotect-ы? или хотя бы полиморфные вирусы?
ps
VirtualAlloc
VirtualProtect

bleyman

Ты можешь более внятно объяснить, что именно тебе нужно?
Если у тебя есть ИСХОДНИК программы, почему бы не заменить код в исходнике и перекомпилить?
Если тебе важно генерить код динамически - все вызовы исходной функции заменяешь на вызовы специальной заглушки, которая передаст управление в массив, в который ты динамически напихаешь всё что нужно. Если ты ещё хочешь, чтобы DEP не сругнулся, читай советы выше.
В некоторых случаях наиболее простым будет вынести изменяемый код в дллку, которую грузить динамически.
Если ты хочешь устроить кому-нибудь смешное западло, есть гораздо более простые способы, типа грамотного #DEFINE.

Corrector

нужно защитить программу с помощью электронного ключа. Я хочу хранить часть кода на этом ключе.

Andbar

Вот выдержка из одного довольно старого документа о вирусах.
это не документ времён 9х винды?
Вроде NT работает в 3х или 4х кольцах.

Slavaga

и как по твоему всякие exepack-и работают? или asprotect-ы? или хотя бы полиморфные вирусы?
ладно, ладно... уговорили =)

Slavaga

так и есть

mysha

В этом нет смысла - такое действие не улучшит криптографических свойств ключа (если ключ простой, то есть не реализует криптоалгоритмов).
Можно заменить асмовскую процедуру на написанную на скриптовом языке и сложить программу на нем в ключ. Но все эти пути - потенциальная дырка в программе, не зря же все эти NX придумывали.

bleyman

Смысл есть вот какой: при таком подходе кулхацкер уже не сможет найти все вызовы функции "проверить валидность ключа" и заменить их на нопы (что как правило и делается). Так же не будет никакого смысла в реверс-инжиниринге фунцкии "проверить валидность ключа" с целью написать кейген - ведь фактически в таком случае проверка валидности оказывается неявно размазана по всей программе.
Таким образом, у кулхацкера остаётся всего две возможности -
1) угадать, что именно делает отсутствующая процедура и написать её заново (нереально, скорее всего)
2) купить программу и выдрать из ключа процедуру.
Причём с пунктом 2 можно побороться вообще забавным образом - написать генератор процедур (ну, там, если используются какие-то константы, то их можно как-то хитроумно менять, не меняя семантики) так, чтобы по появившемуся вдруг в сети крэку/кейгену можно было определить, какой именно зарегистрированный пользователь отдал свой ключ на поругание.
Правда, в таком случае опять-таки возникает соблазн не мучаться со всей этой фигнёй и оформить "ключ" в виде дллки. Почему бы и нет?

NataNata

такая реализация - не лучший вариант. большую процедуру ты на асме в ключ не засунешь, тем более что-то серьезное. и ее можно будет поймать, если отслеживать, как прога работает с памятью

mysha

Избыточность asm кода довольно велика, поэтому не разумно его использовать в качестве ключа
В качестве секретных данных можно использовать либо достаточно большой набор данных, необходимых для работы программы, которые надо расшифровывать ключом из "ключа", либо некоторый псевдокод, который легко контролировать и придавать ему специальные свойства.
что касается кул-хацкеров:
1) реверс инжиниринг нормально реализованного криптоалгоритма ничего не даст
2) с помощью пассивных флешек здесь бороться не получится - нужно чтобы этот код не только хранился в флешке но еще и исполнялся там, кстати - видел проекты обеспечения DRM с помощью таких чипов, вклеивающихся в лицензионные диски

laki

под винды можно так.
 

char * sample_code = "\x33\B\xF7\xFB\xC3";
unsigned long address = (unsigned longsample_code);
__asm
{
xor eax,eax
mov eax, [address]
call eax
}
//---33DB xor ebx, ebx
//---F7FB idiv ebx
//---C3 ret
//Unhandled exception at 0x004240ca in test2.exe: 0xC0000094: Integer division by zero.
 

ибо исполняться в стеке никто не запрещал

Olyalyau

ибо исполняться в стеке никто не запрещал
А вот это зря. Допускает security violation при любом срыве стека.
К сожалению, большинство операционок обладает такой багой.

lovriat

Где-то полтора года назад была статья Криса Касперского на сайте void.ru по защите программ от отладчиков и интерактивных дизассемблеров - там прога, если не ошибаюсь, проверяла свою целостность и модифицировала свой код. Может поможет.

Marinavo_0507

Это фича. Трамплины можно делать.
Можно, понятно, и в куче, но это дольше, и срыв кучи немногим лучше срыва стека.

Olyalyau

Это фича. Трамплины можно делать.

Поясни
Можно, понятно, и в куче, но это дольше, и срыв кучи немногим лучше срыва стека.

Конечно, срыв кучи тоже плохо. Но, если куча (или стек) без права выполнения, то arbitrary code при срыве не запустишь и взломать до рутового шела становится сложнее. А то, что срыв -- это вина автора проги, так с этим никто не спорит. Просто более секъюрны системы, которые запрещают выполнение в стеке.

Marinavo_0507

Что пояснить? Что такое трамплин?
> Просто более секъюрны системы, которые запрещают выполнение в стеке.
Только пока они в меньшинстве.
Если в мейнстрим пройдёт такое (а сейчас проходит уже не факт.

Olyalyau

Что пояснить? Что такое трамплин?

Да, я единственное упоминание видел в исходниках linux'а. Там так называется процедура, которая стартует всё остальное.

Marinavo_0507

GCC умеет вложенные функции. Если взять на такую функцию указатель, получается фактически closure. Для его получения на стек кладётся динамически сгенерированный код, который и называется трамплином.
В языках с GC тоже можно, в принципе, реализовывать так closure, но тогда в куче придётся делать, а не на стеке (хотя, если escape analisys показывает, что можно на стеке - то будет эффективнее именно там).

Olyalyau

Прикольно, я правда не придумал для чего вложенные функции и передача наружу указателей на них могли бы мне пригодиться, но всё равно прикольно. А точно нельзя это реализовать без executable stack?
Оставить комментарий
Имя или ник:
Комментарий: