· Начало · Отвђтить · Статистика · Поиск · FAQ · Правила · Установки · Язык · Выход · WASM.RU · Noir.Ru ·

 WASM Phorum —› WASM.VIROLOGY —› Помогите пожалуйста разобраться с delta-смещением пожалуйста :(((

. 1 . 2 . >>

Посл.отвђт Сообщенiе


Дата: Мар 18, 2004 17:50:38

Извините за непонятливость, я уже задавал этот вопрос. Но все-таки рискну еще раз. Я пишу программку, которая подменяет Entry Point (типа вирус). При подмене используем поиск дельта смещения. Все работает нормально.
Вопрос: как получается дельта смещение ?

call delta
delta:
pop ebp
sub ebp, offset delta

(Спешу отметить , что вопрос задавал уже Zervide в http://www.wasm.ru/forum/index.php?action=vthread&forum=6&topic=2134 но конкретного ответа не получи, может сам дошел? )


Мне ответили так :

1) call пихает в стек адрес следующей инструкции,(pop ebp) pop его вынимает, а sub вычитает смещение этой самой инструкции. вот тебе и дельта...

Sickle
смотри:

00000000: E80000 call 000000003 ; Next
Next:
00000003: 665D pop ebp
00000005: 6683ED03 sub ebp,003 ;offset Next
в ebp будет 0 только если начало кода находится по адресу 0. загрузи в любой другой адрес - он и окажется у тебя в ebp. понятно?

… но такой код тоже даст 0 :

.CODE

Main:
NOP
NOP
;…
NOP
NOP

call delta
delta:
pop ebp
sub ebp, offset delta


mov eax,ebp
push OFFSET cBuff
push eax
call dwtoa

push MB_ICONINFORMATION OR MB_SYSTEMMODAL
push OFFSET szCap
push OFFSET cBuff
push NULL
call MessageBox
invoke ExitProcess,0
end Main

Grenader
Все очень просто. Вирусу нужно узнать адрес, по которому он загрузился. Адрес этот лежит в EIP - и указывает на текущую команду. Взять бы его оттдуа, и нет проблем. Но доступа к EIP у программы нету (спасибо Интелу). Команда CALL делает слежующее: кладет в стек значение EIP, указывающее на следующую команду, и изменяет значение EIP - передавая управление в соотв. часть кода. Нормальная функция завершается командой RET, которая достанет из стека адрес возврата, и передаст управление по нему. А вирус делает вместо RET команду POP EBP (или любой другой регистр) - и получает значение EIP, которое сохранила команда CALL. Ну, а дальше вычисляется дельта-смещение: разница между текущим адресом и тем, который "планировался" при компиляции.


… что значит - который "планировался" при компиляции.

в программе код которой выше в OllyDbg имеем :

00401000 CALL _file.00401005
00401005 POP EBP
00401006 SUB EBP,_file.00401005 ---> EBP=00000000



… в зараженной программе имеем :

00403200 PUSHAD
00403201 CALL _file.00403206
00403206 POP EBP
00403207 SUB EBP,_file.00412042 ---> EBP=00001FC4


В зараженной программе Entry Point = 00403200 , а delta: имеет адрес = 00412042.

Как и где взялся этот 00412042 не пойму ни как. Смотрел зараженный файл в HIEW–е. Такого смещения в файле вообще нет, там прыжек. Как заговор какой-то J. Объясните пожалуйста сам не пойму. Помогите !!!!


Дата: Мар 18, 2004 21:29:34

Вообще-то Zervide получил вполне конкретный ответ! Сказано было (сказал Sten), что при компиляции этот код всегда даст ноль. А если воткнуть его в файл, то ты получишь другое смещение по месту расположения call, как следствие, загнаный в стек EIP будет другим и дельта станет другой.
Мне очень понравилась эта статья:
http://www.lwfug.org/~abartoli/virus-writing-HOWTO/_html/i386-redhat8. 0-linux/doing.it.in.c.html

А вообще, мог бы в том треде свои проблемы изливать, а не новый создавать!


Дата: Мар 19, 2004 00:33:23

Вообще-то я не умею читать, но мне кажется что вопрос про то, как это будет в PE-экзешнике??? На счёт exe могу ошибаться, но скорее всего аналогично объектному COFF-формату. Там это смещение по заверениям того же Sten всегда 0, за исключением одного интересного момента. Когда я писал свой компилято для асма, я ковырялся долго с этим, но уже успел подзабыть. Так что если я правильно понял вопрос, пши, попробую вспомнить. Вообще помимо скомпиленного кода, в объектник добавляется таблица релокаций (relocation table), которая содержит поправки к адресам такого рода. С линкером я не работал, но если я не ошибаюсь, он делает следующее: берёт поправку из таблицы и прибавляет её к базовому адресу загрузки exe файла.


Дата: Мар 19, 2004 09:14:39

volodya... извините за лишний тред, но хотелось по-своему вопрос задать. Тем более что ответ не простой.

Kirk...спасибо за ответ. Вопрос (в кратце): как, в зараженном exe, одна и та-же инструкция (POP EBP) имеет два разных адреса:
и 00403206 и _file.00412042 одновременно.

00403200 PUSHAD
00403201 CALL _file.00403206
00403206 POP EBP
00403207 SUB EBP,_file.00412042 ---> EBP=00001FC4.
И еще вопрос к Вам куда писать вопросы-то :)

Спасибо!


Дата: Мар 19, 2004 09:45:06

volodya ... да, статья она может и не плохая, но для меня (начинающего) записка специалиста со стажем в linux-е на английском языке, все равно что отчет академика по дифференциальным неоднородным уравнениям на беларусском. Инфа окрыляет. Хотя этож ты статью про распаковку писал, объяснил бы молодежи :)))). Ну ничего, правда она всегда была проста и где-то рядом. :) Прорвемся ))))


Дата: Мар 22, 2004 11:28:33

Честно сказать, не понял я вопроса:-? Почему "одна и та же инструкция POP"??? Либо я чего-то не понял, либо я вижу только одну, SUB в данном случае не делает по-моему ничего похожего на POP EBP.


Дата: Мар 22, 2004 12:17:06

Извините если я что то не доходчиво спросил.
Вспрограмме, в которой я определил delta, я установил EP=00403200. Поместил в программу код, перешел на код по 00403200, тут же лежит следующая команда POP EBP так у нее адрес не то 00403200+6, не то 00412042.
Как и кто определил этот 00412042 ... простите может за тупые вопросы, но хочется знать.


Дата: Мар 22, 2004 16:06:13

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

call delta 
...
sub ebp, offset delta


Очевидный вывод, если есть сложности - с понятиями о природе этих операндов ты незнаком.
2. Знание механизмов, как компилятор определяет точные адреса операндов ещё до стадии того как они загружены в память, если угодно, "протоколов" между записями в откомпилированном файле и операционной системой когда то, что "предположил" компилятор по поводу адреса операнда (в твоём случае смещения) будет совпадать с фактом когда этот операнд уже окажется в памяти по определённому загруженному адресу. Когда эти адреса совпадут точно, когда произойдёт "штатная поправка" (релокейшн) а когда вообще компилятор ну никак не может "договорится" с операционнкой (случай помещения по произвольному адресу о котором никто не знает пока это помещение не произойдёт - как раз случай такого тошнотворного вида деятельности как вирусология).
Опять же видно тут отсутсвие знаний, прямым подтверждением этому является твои вопросы связанные с тем что ты увидел в Hiew - т.е. в дизассемблере, хотя процесс правильного вычисления можно увидеть только в дебагере.

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

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

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

Понятия:
sub ebp, offset delta
offset delta это просто число-непосредственный операнд.
Результатом например может быть такой опкод:
81ED 00 10 40 00
81ED - начало опкода которое говорит процессору что нужно вычесть из ebp непосредственный операнд, который находится
непосредственно после этих байтов. Размер операнда в данном случае 4е байта (DWORD) т.е. процессор возьмёт эти 4е байта находящиеся сразу после 81ED и вычтет из ebp.
Это байты 00 10 40 00, которые представляют собой двойное слово 00401000h.
Откуда взялись эти байты и как компилятор "догадался" или "решил", что именно это число 401000h нужно вставить вместо слов "offset delta"? Вещь это простая но невзирая на простоту важная.
Компилятор предпологает, что то что он компилирует будет использовано как отдельная программа. Учитывая этот факт и то, что алгоритм загрузки в память из файла операционнкой работает по детерминированным правилам и ему эти правила известны он может вычислить по какому адресу будет находится код обозначенный как метка delta.
Т.е. например, он возьмёт базу (число в заголовке) прибавит смещение секции в которой находится код по смещению delta, прибавит смещение от начала секции кода байтов помеченных delta и получит предпологаемый линейный адрес. Т.е. произойдёт просто ряд сложений. Но!
Важно понимать, что число это будет соответсвовать действительности тогда и только тогда, когда выполнятся условия на которые "пологается" процессор. А имменно скомпилированный код будет использоваться как отдельная независимая программа, а не будет вставляться как бинарный дамп в чужие программы по каким то произвольным адресам.
Это просто. Как и все основные понятия в математике.
То, что на этом этапе нам нужно отметить это тот факт, что
даже если этот адрес, будучи вставлен в чужую программу, не будет совпадать с адресом по которому он реально вставлен, то узнав разность между лишь одним адресом реальным и соответсвующим ему предположенным компилятором мы можем узнать все уже реальные адреса в коде вставленном в другую программу.
Арифметика тут простая, порядковая нумерология.
Пусть элементы x_1,x_2,x_3
имеют порядковые номера 6,7,8. Потом были смещены так что
x_1 оказался с номером 12. Разность прежнего и нового номера 12-6=6. Значит элемент с прежним номером 8(т.е x_3) стал 8+6=14ым. Будь этих иксов не 3 а хоть миллион, нам достаточно узнать только для одного из них "новый" номер.
И получив разность с прежним номером мы сможем простым арифметическим сложением получить для любого элемента "новый номер" или в нашем случае "новый адрес" прибавляя к старому эту разность, которая у соответсвующих пар всегда однинакова.
Я столь подробно излогаю это только затем, чтобы показать следующее:
Код сгенерирован компилятором исходя из того, что он будет работать отдельно, все "оффсеты" внутри этого кода будут (за исключением случайного совпадения) неверны, но все их можно "поправать" узнав разность между "старым" и "новым" адресом хотя бы для одного элемента.
Этот то элемент и есть метка "delta" в приведённом тобой коде. И "старый" адрес и есть "offset delta". Осталось найти каким оказался "новый" адрес - и телемаркет - эту разность можно прибавить ко всем нужным оффсетам и они окажутся будто "родные" в инфицированной программе, т.е. будто и сгенерированны компилятором который делал оригинальную программу до инфекции.

Тут мы плавно перейдём к понятиям связанных с тем "а как узнать "новый адрес""?
Итак тело твоей программы (испытываю гадливое ощущение при слове вирус) оказалось в теле кода другой программы по какому-то неизвестному изначально "новому адресу" к тому моменту когда она начёт исполняться этот "новый адрес" окажется в EIP. EIP это как известно указатель на команду и он и содержит адрес (всегда реальный-"настоящий") команды которая выполняется.
Таким образом узнав какое EIP соответсвует коду обазначенному меткой delta в реальности мы узнаем "новый порядковый номер для этого участка или новый адрес" и зная старый порядковый номер (который вставил компилятор как "offset delta"- мы определим разность для всех смещений в нашем коде для используем оффсеты кода и переменных.
Разберёмся для этого с другим типом переменных - тех что используются в командах управления.
Во первых отметим тот факт, что во многих книгах пишется, что регистр EIP неподвластен программисту и он не может на него воздействовать или адресовать.
Это полная чушь, программы сплошь и рядом воздействуют на EIP иначе невозможно было бы создать ни одного управляющего блока.
Воздействие на EIP по характеру арифметики можно исскуственно разделить на две части
1. Прибавление к EIP непосредственного операнда.
2. Загрузка в EIP (и заодно в CS) непосредственного операнда.
3. Сохранение EIP (а иногда и CS одновременно) в стеке.
Нас интересует пункты 1 и 3 выполняемые одним из вариантов
инструкции которые в ассемблерных мнемониках называются CALL. Почему я говорю "одним из.." - другая инструкция в мнемониках тоже обозначается CALL - тем не менее инструкция эта выполняет не прибавление (1) а загрузку (2). Нужно помнить, что ассемблер всего лишь язык и реальность мира инструкций процессора как и любой язык отражает не абсолютно точно.

Сначала разберёмся как вообще меняется EIP (это важно)в машинной реальности (а не той программисткой реальности которую описывают книжки). В программисткой реальности процессор выполняет текущую инструкцию и если эта инструкция не является управляющей (call, ret, jmp и т.д)
то он переходит к "следующей".
1.Сначала об инструкциях, не являющихся управляющими:
В машинной реальности - процессор пытается декодировать байты по адрессу CS:EIP как опкод команды. В частности во время декодирования он определяет размер текущей инструкции.
Этот размер прибавляется к EIP, таким образом EIP начинает показывать на байты "после" байт текущей инструкции.
2. Управляющие инструкции.
Возьмём из этих инструкций те, которые "прибавляют" к EIP.
Такими инструкциями являются например jcc, jmp near/short
call near...
Эти инструкции имеют в своём теле непосредственный операнд. Посмотрим как этот операнд применяется.
Сначала делается то, что и в пункте 1.
Т.е. если CS:EIP указывает на команду call near, jcc и т.п. он начинает декодировать её и так же как и в первом случае определяет в частности и размер инструкции и прибавляет его к временной переменной "будущего EIP".
Например:
00401005: E8 03000000 CALL 0040100D
Код инструкции здесь E8 и за ним следует 4х байтовый (DWORD) непосредсвенный операнд.
Начало инструкции по адресу 00401005.
Процессор декодирует её и узнаёт что размер инструкции здесь 5 байт. Условно скажем (реальность слишком сложна чтобы сейчас я загружал ею без угрозы что читатель потеряется) что процессор создаёт временную переменную =EIP+5. Т.е. получает число 401005+5=40100A.
Чтобы ещё жёстче привлечь внимание к арифметике скажу, что
число 40100Ah не адрес следующей команды (там вообще может быть мусор) а адрес первого байта после байтов текущей инструкции.
Далее процессор выполняет команду call а именно
а) первым делом он помещает в стек 401005+5=40100A число
(будущее)EIP
б) вторым делом он прибавляет к этому EIP непосредственный
операд, который находится за байтами E8. В приведённом примере там находятся байты 03 00 00 00 т.е. двойное слово
00000003h.
Таким образом в EIP к началу выполнения команды было
401005
После декодированния вычислилось что размер инструкции 5 байт и "будущее EIP" получилось 401005+5=40100A
Это значение запушилось в стек.
Потом во время исполнения к этому значению прибавился непосредственный операнд 00000003. Получилось 401005+5+3=40100D и это окончательное значение загружается в EIP. После чего в EIP уже находится 40100D и всё начинается снова - процессор декодирует байты по новому адресу в EIP 40100D как команду и т.д.
Обратим внимание в конце, что при опкоде данном в примере
как E8 03 00 00 00, при вычислениях и
EIP+5 и EIP+5+3. 5 и 3 это "известные" компилятору константы. Т.е. размер инструкции call near метка (5), и смещение и разность между оффсетами метка+5 и оффсетом вызываемого кода (3) а вот EIP которая будет участвовать в этих формулах будет реальной, той которая будет во время выполнения этого кода. Различные анализаторы кода, статичные, типа дизассемлеров, могут лишь "предпологать" адресс который будет в EIP на данный момент, показывая в мнемониках что то типа call 40100D, на самом деле если это код окажется в другом месте не по адресу 401005 то и прибавлятся будет не к 401005 а другому, текущему значению EIP. Например кто то "вставил" E8 03 00 00 00 по адресу
402005 результатом будет уже 402005+5 в стеке и 402005+5+3=40200D в адресе загрузки в EIP. Т.е. получится
уже call 40200D а не call 40100D как в случае когда инструкция находилась по адресу 401005

Теперь после того как мы познакомились с необходимыми
представлениями вернёмся к вычислению разницы между "старым" адресом метки delta и "новым" адресом её когда код который она представляет поместили в произвольное, "неизвестное" место.
1. call delta 
2. delta: pop ebp 
3. sub ebp, offset delta 

Первая инструкция вызывает код находящийся сразу после самой инструкцией.
Компилятор должен будет сформировать опкод E8 xx xx xx xx
поставив вместо xx xx xx xx непосредственный операнд который процессор должен будет прибавить к EIP+5 чтобы в EIP оказался адрес вызоваемого кода. Но сам вызываемый код и находится по адресу +5 относительно инструкции поэтому ничего прибавлять уже не нужно, или иначе - прибавляется 0. Поэтому эта инструкция будет всегда E800000000.
Процессор выполнив эту инструкции поместит EIP+5 в стек.
Чтобы стало абсолютно ясно определим термины как
EIP_s - это EIP о которое "предпологал" компилятор
оно совпадает с предпологаемым компилятором оффсетом команды call delta
EIP_r - это реальное EIP которое будет при выполнении кода. Оно совпадёт с реальнымоффсетом команды
call delta.
Таким образом EIP_s+5 это предпологаемый компилятором оффсет delta. Или иначе то число что генерировалось компилятором при указаниях "offset delta"
А EIP_r+5 - это реальный оффсет кода определённого меткой delta в реальности.
Итак команда
call delta
помещает в стек EIP_r+5
прибавляет к EIP_r+5 непосредственный операнд 0
загружает в EIP полученное значение т.е. EIP_r+5
pop ebp находится по адресу EIP_r+5 и начинает выполнятся
загружает в EBP EIP_r+5 т.е. в ebp оказывается реальный
адрес по которому находится команда pop ebp.
sub ebp,offset delta =
= sub EIP_r+5,EIP_s+5 =
= EIP_r-EIP_s
Что нам и требовалось, т.е. разность между предпологаемым компилятором адрессом одной из бинарных команд и реальным адресом в которой оказалась эта команда. Используя эту разность можно поправить все остальные адреса которые вставил в код компилятор, все они будут отличаться от реальных на эту разность.

Надеюсь мне не зашлют злобный вирусняк в "благодарность" :)
Вообще слабо, что то полезное писать? На кой маятся вирусами, нормального программиного обеспечения нет ни фига толком, на этой шаткости добавлять ещё больше неустойчивости. Не понятно мне это...
Впрочем кто я такой, чтобы учить кого-то, просто как у обычного пользователя какая-то тоска по поводу происходящего.


Дата: Мар 22, 2004 16:27:35

Спасибо больное! ТАКОЕ внимание моей скромной персоне!

Надеюсь мне не зашлют злобный вирусняк в "благодарность" :)

Нет во-первых я не умею этого делать (их писать) мал еще;
Во-вторых это предосудительно (ст. 273,274 УК РФ );
В-третьих заниматься этим в направлении на wasm.ru (этого храма науки) это кощунство в высшей степени своего проявления. ( Последние события с сайтом - это просто "ни в какие ворота".) Статью читаю вникаю. Спасибо.


Дата: Мар 22, 2004 18:28:31

The Svin

Мда... 8-()


Дата: Мар 22, 2004 18:48:15

... а че Мда... 8-() volodya, человек любит детей, и учит их, приятно ...


Дата: Мар 23, 2004 00:46:24

Меня просто удивляет и поражает, как The Svin под столь простой вопрос смог подвести такую теоретическую базу, что она перешла в формат статьи ;-)
Остаётся только сожалеть про два накрывшихся винта(кстати, интересно какой марки???) с тем материалом, который мы не увидим.
Я бы например с удовольствием почитал про процесс дизассемблирования, но на русском, ведь в сети кроме статьи/книги? К.Касперски ничего нет..


Дата: Мар 23, 2004 04:37:42

Ей-богу, Крис в плане дизассемблирования не авторитет. Свин как-то раз взял на себя труд посмотреть, что там в книге Криса написано. Мама... Там ошибка на ошибке...


Дата: Апр 8, 2004 14:29:54

В продолжении темы, возможно, The Svin опять не поленится клаву топтать?
Этот вопрос уже мусировался в форумах, но такого как здесь развернутого ответа он не получил. После того как смещение определено, как проге получить право писать в свою секцию кода? Еще конкретнее: откуда взять параметр pflOldProtect для функции API VirtualProtectEx. Ведь заставить masm линковать секцию кода с характеристикой write-enable: \masm32\bin\Link /SUBSYSTEM:WINDOWS /section:.text,RWE %1.obj - это не выход, когда мы внедряемся в чужой код...


Дата: Апр 8, 2004 15:53:00

DirectOr
pflOldProtect это указатель на dword в который запишутся старые атрибуты первой страницы региона.

. 1 . 2 . >>


Powered by miniBB 1.6 © 2001-2002
Время загрузки страницы (сек.): 0.045