Форум: "Система";
Текущий архив: 2003.03.31;
Скачать: [xml.tar.bz2];
ВнизСамомодифицирующийся код Найти похожие ветки
← →
Александр К (2003-02-01 23:06) [0]Есть ли способ изменения кода приложения и нормально работающий под Windows 95/98/Me/NT/2000/XP?
Спасибо!
← →
Alex Konshin (2003-02-01 23:10) [1]Есть, но зачем?
← →
Александр К (2003-02-01 23:49) [2]Под MS-DOS широко использовался самомодифицирующийся код, без которого не обходилась практически ни одна мало-мальски серьезная защита. Хотелось бы узнать как это можно сделать в Windows.
← →
Alex Konshin (2003-02-02 00:22) [3]http://msdn.microsoft.com/library/default.asp?url=/library/en-us/memory/base/virtualprotect.asp
← →
Nick_N_A (2003-02-02 06:17) [4]ССылками, и поинтерами пользыватся умеем - вперед,
делфя линкует процедуры последовательно
Procedure A;
Begin
end;
Procedure B;
Begin
end;
Type TAByte=array[0..$FFFFFFFF] of byte;
proca:pointer;
size:integer;
size:=integer(@B)-integer(@A);
proca:=GetMemory(size);
move((@A)^,proca^,size);
теперь прога А находится в TAByte(proca^)
зная ассемблер ее можно подкорректировать
← →
Alex Konshin (2003-02-02 06:43) [5]2Nick_N_A: А ты попробуй это под NT.
← →
Ice (2003-02-03 01:07) [6]
> Alex Konshin © (02.02.03 00:22)
> Nick_N_A © (02.02.03 06:17)
Mда...
На самом деле – есть по крайней мере два легальных способа изменения кода приложений, вполне доступных гостевому пользователю и нормально работающих под Windows 95/98/Me/ NT/2000/XP.
Во-первых, kernel32.dll экспортирует функцию WriteProcessMemory, прямо предназначенную для модификации памяти процесса.
Во-вторых, практически все ОС (включая Windows и Linux) разрешают выполнение и модификацию кода, размещенного в стеке.
В принципе, можно создать самомодифицирующийся код исключительно средствами языков высокого уровня - таких как С++, Delphi - без применения ассемблера.
Для адресации 4 ГБ виртуальной памяти, выделенной в распоряжение процесса, Windows использует два селектора; один загружается в сегментный регистр CS, а другой – в регистры DS, ES и SS. Оба селектора ссылаются на один и тот же базовый адрес памяти (равный нулю), и имеют лимит в 4 ГБ. (Замечание: кроме перечисленных, Windows использует еще и регистр FS, в который загружает селектор сегмента, содержащего информационный блок потока).Фактически существует всего один сегмент, вмещающий в себя и код, и данные, и стек процесса. Благодаря этому передача управления коду, расположенному в стеке, осуществляется близким (near) вызовом или переходом, и для доступа к содержимому стека использование префикса "SS" совершенно необязательно. Несмотря на то, что значение регистра CS не равно значению регистров DS, ES и SS, команды MOV dest,CS:[src]; MOV dest,DS:[src] и MOV dest,SS:[src] в действительности обращаются к одной и той же ячейке памяти. Отличия между регионами кода, стека и данных заключаются в атрибутах принадлежащих им страниц: страницы кода допускают чтение и исполнение, страницы данных – чтение и запись, а стека – чтение, запись и исполнение одновременно.
Помимо этого, каждая страница имеет специальный флаг, определяющий уровень привилегий, необходимых для доступа к этой странице. Некоторые страницы – например, те, что принадлежат ОС, требуют наличия прав супервизора, которыми обладает только код нулевого кольца. Прикладные программы, исполняющиеся в кольце 3, таких прав не имеют, и при попытке обращения к защищенной странице вызывают исключение. Манипулировать атрибутами страниц, равно как и ассоциировать страницы с линейными адресами, может только операционная система (или код, исполняющийся в нулевом кольце), но, старая песня про маздай продолжается. Судя по всему кроме кернела и кодовых секций программ не предполагалось защищать вообще ничего. Таким образом ВО ВСЕХ Windows можно произвести запись В ЛЮБОЕ место памяти не переходя для этого в нулевое кольцо.
Замечание: среди начинающих программистов ходит совершенно нелепая байка о том, что, дескать, если обратится к коду программы командой, предваренной префиксом DS, Windows якобы беспрепятственно позволит его изменить. На самом деле это ерунда; обратиться-то она позволит, а вот изменить – нет, каким бы способом ни происходило обращение, т.к., защита работает на уровне физических страниц, а не логических адресов.
← →
Ice (2003-02-03 01:09) [7]1. Использование WriteProcessMemory:
Если требуется изменить некоторое количество байт своего (или чужого) процесса, самый простой способ сделать это – вызвать функцию WriteProcessMemory. Она позволяет модифицировать страницы памяти с неустановленным флагом супервизора (т.е. страницы, доступные из кольца 3, где выполняются прикладные приложения). Бесполезно пытаться изменить с ее помощью критические структуры данных ОС (например, page directory или page table) – они доступны лишь из нулевого кольца. Поэтому эта функция не представляет никакой угрозы для безопасности системы, и успешно вызывается независимо от уровня привилегий пользователя (WriteProcessMemory НЕ требует прав отладки приложений). Процесс, в память которого происходит запись, должен быть предварительно открыт функцией OpenProcess с атрибутами доступа "PROCESS_VM_OPERATION" и "PROCESS_VM_WRITE".
Поскольку Windows для экономии памяти разделяет код между процессами, возникает вопрос: а что произойдет, если запустить вторую копию самомодифицирующейся программы? Создаст ли ОС новые страницы или отошлет приложение к уже модифицируемому коду?
В Win’NT и 2000 поддерживают копирование при записи (copy on write), т.е. автоматически дублируют страницы кода при попытке их модификации. Напротив, Win95/98 не поддерживают такую возможность; однако, сама функция WriteProcessMemory создает копии всех модифицируемых страниц, распределенных между процессами. Поэтому самомодифицирующийся код одинаково хорошо работает во всех вариантах Windows. Но нужно учесть, что копии приложения, модифицируемые любым иным путем (например, командой mov кольца 0), под Win 95/98 будут разделять одни и те же страницы кода, со всеми вытекающими отсюда последствиями.
Теперь об ограничениях. Во-первых, использовать WriteProcessMemory разумно только в компиляторах, компилирующих в память, или распаковщиках исполняемых файлов; а в защитах – несколько наивно. Мало-мальски опытный взломщик сразу обнаружит подвох, заметив эту функцию в таблице импорта. Затем он отловит вызов WriteProcessMemory, и будет контролировать каждую операцию записи в память...
Другое ограничение WriteProcessMemory - невозможность создания новых страниц; ей доступны лишь уже существующие страницы. А если требуется выделить некоторое количество памяти - например, для кода, динамически генерируемого "на лету"? Ведь в динамической памяти выполнение кода запрещено...
← →
Ice (2003-02-03 01:12) [8]2. Выполнение кода в стеке:
Выполнение кода в стеке разрешено, поскольку исполняемый стек необходим многим программам (в том числе и самой ОС) для выполнения некоторых системных функций; к тому же, это упрощает генерацию кода компиляторами и компилирующими интерпретаторами.
Поэтому использование стека для выполнения самомодифицирующегося кода вполне законно более-менее системно независимо. Такое решение устраняет оба недостатка функции WriteProcessMemory:
Во-первых, выявить и отследить команды, модифицирующие заранее неизвестную ячейку памяти, чрезвычайно трудно; взломщику придется провести кропотливый анализ кода защиты без надежды на скорый успех.
Во-вторых, приложение в любой момент может выделить столько стековой памяти, сколько ему заблагорассудится, а затем, при исчезновении потребности – ее освободить. По умолчанию система резервирует 1 МБ стекового пространства; если этого недостаточно, нужное количество можно указать при компоновке программы.
Основное преимущество – в том, что код функции, исполняющийся в стеке, можно прямо "на лету" изменять (например, расшифровывать).
Шифрованный код чрезвычайно затрудняет дизассемблирование и усиливает стойкость защиты; хотя одна лишь шифровка кода – не очень-то серьезное препятствие для взломщика, снабженного отладчиком или продвинутым дизассемблером, наподобие IDA Pro. Но антиотладочные приемы – тема отдельного разговора.
Однако, программирование кода, выполняющегося в стеке, имеет ряд специфических особенностей.
При разработке кода, выполняющегося в стеке, надо учесть, что в Windows 9x, NT и 2000 стек размещается по-разному; поэтому код должен быть безразличен к адресу, по которому он будет загружен.
Если беретесь разрабатывать код, выполняемый в стеке - основательно изучите документацию по вашему компилятору. В большинстве случаев - код функции, скопированный в стек, с первой попытки запустить не получится (особенно если включена оптимизация кода). Вообще, на чистом языке высокого уровня (С, Паскаль), скопировать код функции в стек (или куда-то еще) принципиально невозможно - поскольку стандарты языка не оговаривают, как именно должна осуществляться компиляция. Программист может получить указатель на функцию, но стандарт не оговаривает, как ее интерпретировать. Еще одна проблема – как достоверно определить длину тела функции? Если возникнут проблемы, лучше отключите оптимизацию вообще (плохо, конечно, но надо).
И еще...
Пишите Ваш код так, как вы это делали в MS-DOS(Можете "перемешивать" код и данные). После сборки Вашего приложения, когда Вы получили exe файл, любым редактором PE файлов, в таблице секций (для Delphi это будет секция с именем CODE), измените DWord по смещению 28H от начала таблицы, присвоив ему соответствующие значения (см. документацию по формату PE файлов).
Многие считают использование самомодифицирующегося кода "дурным" примером программирования, обвиняя его в отсутствии переносимости, плохой совместимости с различными операционными системами, необходимости использования ассемблера и т.д.
С появлением Windows 95/NT этот список пополнился еще одним умозаключением: дескать "самомодифицирующийся код – только для MS-DOS; в нормальных же ОС он невозможен (и поделом!)".
все это, мягко выражаясь, неверно. Другой вопрос – так ли необходим самомодифицирующийся код? Низкая эффективность существующих защит (обычно программы ломаются быстрее, чем успевают дойти до легального потребителя) и огромное количество "программистов", стремящихся "топтанием клавиш" заработать себе на хлеб – говорят о необходимости усиления защитных механизмов любыми доступными средствами.
В том числе - и с помощью самомодифицирующегося кода.
← →
Alex Konshin (2003-02-03 02:16) [9]Все верно, только я не понимаю почему именно WriteProcess?VirtualProtect вполне должно быть достаточно, о чем я и написал.
← →
Suntechnic (2003-02-03 06:01) [10]>Ice ©
Ты бы хоть копирайт ради приличия поставил :)
Или мы имеем честь созирцать Криса Касперски собственной персоной? ;)
← →
perov (2003-02-03 08:36) [11]>Ice
Написали бы статью с примерами :)
← →
SV (2003-02-03 09:52) [12]1) Посмотри статью в RSDN#1 - перехват АПИ ф-ций (если ее модифицировать, то можно и код выполнять в отдельном потоке, который сам дописал)
2) Используй .NET - тут хоть вся прогр. сама себя напишет ;)
← →
Юрий Зотов (2003-02-03 15:36) [13]> perov © (03.02.03 08:36)
> Написали бы статью с примерами :)
А что ее писать, когда она уже давно написана? С примерами.
Секрет простой - Ice пересказал статью Криса Касперски в журнале "Программист". См. архивы журнала на www.programme.ru
← →
Юрий Зотов (2003-02-03 18:17) [14]Статья Криса:
http://www.programme.ru/archive/2001/3/032001_3.phtml
← →
Burmistroff (2003-02-03 18:49) [15]Не совсем понятна фраза
"А если требуется выделить некоторое количество памяти - например, для кода, динамически генерируемого "на лету"? Ведь в динамической памяти выполнение кода запрещено..."
Ведь есть отличная функция:
VirtualAllocEx( hProcess, nil, 512, MEM_COMMIT, PAGE_EXECUTE_READWRITE);
← →
Alex Konshin (2003-02-03 20:17) [16]VirtualProtect меняет защиту страниц. О чем я и говорю. И никаких WriteProcess не нужно. Хотя, конечно, надо проверить, но по идее должно работать.
← →
Ice (2003-02-04 00:09) [17]
> Юрий Зотов © (03.02.03 15:36)
> Секрет простой - Ice пересказал статью Криса Касперски в
> журнале "Программист". См. архивы журнала на www.programme.ru
Верно :), т.к. не вижу смысла "изобретать велосипед".
И как обычно, дальше каждый о своем, не вдаваясь в суть вопроса :)
> Burmistroff (03.02.03 18:49)
> Ведь есть отличная функция:
> VirtualAllocEx( hProcess, nil, 512, MEM_COMMIT, PAGE_EXECUTE_READWRITE);
Эта функция не реализована в Win 95. Вопрос был, как реализовать модификацию кода во всех Windows. Кроме того при ее использовании
возникают те же проблемы, что и при вызове WriteProcessMemory.
Поймите же наконец, что защищать свой код вызовами стандартных функций API по меньшей мере глупо, ломаются такие "защиты", "как два байта переслать", здесь нужны нестандартные решения, иначе какой тогда смысл в самомодификации?
> Alex Konshin © (03.02.03 20:17)
> VirtualProtect меняет защиту страниц. О чем я и говорю.
> И никаких WriteProcess не нужно.
Я не понимаю смысла предлагаемого Вами решения. Что дает смена атрибутов защиты страниц? Повторяться не буду.
Кстати, раньше применялся такой прием:
Стек определяется непосредственно в области исполняемых команд,
что приводит к затиранию команд при работе со стеком. Этот способ хорош, когда не требуется повторное исполнение
программного кода. Таким же способом можно генерировать исполняемые команды впереди вычислительного процесса.
Исходя из вышесказанного можно сделать вывод, что реализовывать защиту приложения самомодификацией кода средствами предоставляемыми языками высокого уровня очень проблематично. Наиболее верным решением остается ассемблер и... выполнение кода приложения в стеке.
И еще...
Если для Вас этот вопрос не принципиальный, или Вы не сотрудник Micro$oft :), существует способ (и даже не один), и работающий под всей линейкой Windows, перехода в RING0 средствами вызова стандартных API-функций третьего кольца. Все это возможно реализовать на Delphi без драйверов и
прочих "извращений". Помимо этого вследствии слабой защиты системы возможна запись В ЛЮБОЕ (хоть в Kernel) место памяти из третьего кольца в ЛЮБЫХ Windows.
ВСЕМ! ПРИМЕРОВ ПРИВОДИТЬ Я НЕ БУДУ!
← →
Alex Konshin (2003-02-04 00:58) [18]Смена атрибутов страниц дает возможность писать туда как обычно, а не через... WriteProcess. Может, я и заблуждаюсь, но я всегда думал, что VirtualProtect дает такую возможность. Не могу сейчас проверить.
← →
Digitman (2003-02-04 08:33) [19]
> Alex Konshin
> Смена атрибутов страниц дает возможность писать туда как
> обычно
Что значит "как обычно" ? В "своем" ВАП - да, можно писать непосредственно, но в "чужом" - только c через WriteProcessMemory() (из документированных). Т.е. кроме того, что страницы памяти "чужого" процесса должны иметь атрибут доступа по записи, этот процесс д.б. еще и открыт с соотв.привелегиями доступа, прежде чем можно будет пытаться модифицировать его ВАП с пом. вышеуказанной ф-ции.
← →
Alex Konshin (2003-02-04 09:35) [20]Ну ты про контекст вопроса-то не забывай. Если код "самомодифицирующий", то о каком другом процессе может идти речь-то? Чего вы меня-то учите, я и сам кого хочешь научу :)
Кстати, для чужого процесса есть VirtualProtectEx.
← →
Digitman (2003-02-04 14:05) [21]
> Alex Konshin
Меня вот что смутило :
> Смена атрибутов страниц дает возможность писать туда как
> обычно, а не через... WriteProcess
Из этого как бы следует, что, якобы, без установки атрибутов записи страница (в ВАП любого процесса !) становится доступной только только "через... WriteProcess"... А установка атрибута, якобы, дает возможность прямой записи (ну пусть это будет mov [VMAddr], value) в ВАП любого процесса
Если же речь идет о тек.процессе, то нет никакого резона вызывать WritreProcessMemory (хотя и так можно), достаточно только VirtualProtect() выполнить. Безо всяких Ex
← →
Ice (2003-02-05 20:19) [22]
> Alex Konshin © (04.02.03 00:58)
> Смена атрибутов страниц дает возможность писать туда как
> обычно, а не через... WriteProcess. Может, я и заблуждаюсь,
> но я всегда думал, что VirtualProtect дает такую возможность.
> Digitman © (04.02.03 14:05)
> Если же речь идет о тек.процессе, то нет никакого резона
> вызывать WritreProcessMemory (хотя и так можно), достаточно
> только VirtualProtect() выполнить.
О Господи!
ЭТОГО НЕ ДОСТАТОЧНО!</b
Для реализации подобного, нужно выполнить следующие шаги:
1. Вызвать VirtualProtect() с атрибутом модифицируемой страницы PAGE_WRITECOPY.
2. Модифицировать страницу.
3. Вызвать VirtualProtect() с атрибутом PAGE_EXECUTE.
4. ОБЯЗАТЕЛЬНО! вызвать FlushInstructionCache().
Про кеш процессора объяснять не нужно? Или нужно? ;)
Дело в том, что модифицируемая Вами кодовая страница уже может быть загружена в кеш процессора и без перезагрузки кеша, ВСЕ сделанные Вами изменения не будут выполнены.
ВСЕМ! Если ВЫ отвечаете на вопрос, то пожалуйста аргументируйте свои утверждения!
← →
zot (2003-02-05 21:43) [23]
Ice © (03.02.03 01:09)
Мало-мальски опытный взломщик сразу обнаружит подвох, заметив эту функцию в таблице
импорта. Затем он отловит вызов WriteProcessMemory, и будет контролировать каждую
операцию записи в память...
А что мешает написать код сканирующий память процесса на предмет поиска
загруженного образа kernel32 с последующим анализом секции экспорта и определением
реального адреса функции WriteProcessMemory , а потом вызывать ее напрямую , это
ведь никак не отобразится на нашей таблице импорта , а для того чтоб было проблематично
взломщику патчить первые байты функциии WriteProcessMemory (для перехвата) можна их
сохранять где-то и проверять при вызове функции.
А вообще то весь спор о WriteProcessMemory - спор ни о чем , так как если захотеть
можна работать с физической памятью напрямую (если знаь куда писать).
← →
Ketmar (2003-02-05 22:03) [24]>Юрий Зотов © (03.02.03 18:17)
причём следует признать, что Крис кое-где раскидал "ловушки для ламеров".
>Ice © (04.02.03 00:09)
"тяжело быть идиотом" (ц) добавлю: пальцующимя идиотом.
Satanas Nobiscum! 05-Feb-XXXVIII A.S.
← →
paul_shmakov (2003-02-06 00:11) [25]2 Ice:
a один раз вызвать VirtualProtect с PAGE_EXECUTE_READWRITE не проще, чем два раза ее вызывать ;)
ВСЕМ! Если ВЫ отвечаете на вопрос, то пожалуйста аргументируйте свои утверждения!
мы горячие финские парни ;)
← →
paul_shmakov (2003-02-06 00:14) [26]да и четвертый шаг обязателен только в отдельных случаях
Страницы: 1 вся ветка
Форум: "Система";
Текущий архив: 2003.03.31;
Скачать: [xml.tar.bz2];
Память: 0.55 MB
Время: 0.019 c