![]() |
![]() |
![]() | |
Содержание Сам себе Дизассемблер Наверняка любой, прочитав название статьи, мог буквально на секунду задуматься: "А зачем собстно Дизассемблер? Ведь по любому так много отличный дизассемблеров, отладчиков и тому подобных инструментов.", все более чем просто, уже сейчас Ассемблер как язык программирования считается языком экзотичным, иногда сложным не совсем понятным и в целом для обычного пролетариата кажется экономически не самым выгодным. Оставим пролетариат с С++ и Delphi, а сами моментально двинемся... хе-хе, вовсе не вперед, а туда, где были наши далекие предки, которые еще десятки лет назад циркулем протыкали дырочки на перфокартах, может быть там мы найдем ответы на многие важные вопросы, которые помогут нам достичь того, о чем мы раньше совершенно и думать не могли :) Совершенно точно, что буквально все проблемы, связанные с кодингом и в целом с компьютерами мы сами себе создаем, ведь мы сами говорим себе "Ой, это так сложно...", "На это так много времени надо потратить, а у меня работы еще до ушей и выше", "Пойду посплю, как-нибудь потом попробую", а не хватает часто одного, осознания того, что все на компьютере, совершенно все: эта статья, наш любимый сайт, наша аська, даже мы сами тут, в сети только нули и единицы, вот только подумай, совершенно все, это только лишь 0 и 1, а мы собстно и занимаемся тем, что переставляем нулики и единички, была, скажем у нас цифра 2 [ASCII = 32h], поставили всего лишь одну маленькую единицу, получилось 3 [ASCII = 33h] и так везде, совершенно все это просто преставление нуликов и единичек, ведь совершенно точно, что эти микроскопические 0 и 1 мы можем перемещать легко? ;) Самое важное знать уверенно, что мы можем это сделать очень просто, ведь нет никаких препятствий нами, все препятствия мы сами выдумываем, а перед нами только цифры и что самое интересное не просто цифры, а самые простые цифры: 0 и 1 и если понять как их перемещать, как они работают, мы определенно сможем даже то, о чем и не задумывались раньше, и мощный, совершенно не видимый троян, который вытащит все данные из любой машины и будет жить в системе годами, совершенно реактивный червь, который базируется на мощном эксплойте, позволяет легко просочиться везде и создать миллионные ботнеты, вот все, на что только способна наша фантазия. В этой статье я хочу дать начало увлекательному и совершенно огромному миру "0-1", который даст возможность просто убрать перед собой все препятствия, открыть нам глаза и знать, что это очень просто. На самом деле все более чем просто, многие, даже опытные програмеры, что иногда бывает забавно, от чего-то думают, что не только Ассемблер сложен для них, а что даже под ним ничего нету, такая так сказать не понятная пустота. Это, конечно же, совершенно не так, там есть именно то, что открывает нам глаза, делает нас хозяевами компьютера и не одного компьютера, а любого, там есть цифры 0 и 1, большей частью совершенно элементарные мнемоники, которые может составить даже первоклассник, потому как по сути там нужно просто знать несколько цифр и несколько правил и только :) =1 Из 16 в 2 Определенно каждого из нас, у кого была информатика, в школе грузили какими-то совершенно не понятными многим формулами по переведению чисел в системах счисления, мол, чтобы скажем число из 16й системы перевести в двоичную, надо что-то там долго делить, складывать, потом записывать в обратном порядке и тд - забудьте об этом потому как все более чем просто и на самом деле это совершенно без всяких усилий можно проделать в уме еще легче чем 2х2. Но наверняка почти всем из того самого курса информатики удалось понять, что байт это 8 бит и собстно из себя он представляет две цифры, скажем 11, это на самом деле используется просто для того, чтобы было удобно записывать числа, скажем не записывать 8 нулей и единиц, а записать просто две половинки и легко идти дальше. Не трудно догадаться, что каждая из этих половинок, скажем число 1 из байта 11 занимает 4 бита, это условно называется ТЕТРАДА, ну собстно 4 нулика или единички называется тетрада. Одна шестнадцатеричная цифра может содержать цифры от 0 до 15, но после 9 цифры записывают буквами для компактности, что собстно очень просто значит 9 = 9, A = 10, B = 11, C = 12, D = 13, E = 14, F = 15. Это так сказать базовые сведения, вот в целом просто для тех, кто не знает, ведь по любому каждому, даже самому новичку надо дать возможность читать статью легко. ВОт наверняка многие задумаются над вопросом, а зачем поднимать тему про шестнадцатеричную систему счисления? Очень просто, только для удобства, потому как везде используется 16я система счисления, и в HEX редакторах и в отладчиках и тд, все это шестнадцатеричная система счисления, а нам нужно научиться видеть больше, легко спускаться на уровень ниже, чем HEX, к тем самым 0 и 1, это очень просто. На самом деле, чтобы совершенно без всяких проблем переводить и 16 в 2ю систему счислении и обратно, надо буквально только выучить небольшую таблицу, которая сама по себе очень простая, цифры идут по порядку, от этого, если просто запомнить несколько, так сказать "контрольных точек", можно легко все сделать. Вот скажем если я знаю, как записывается число 7, но не знаю, как записывается 8, просто добавляю единицу и вот у меня уже 8, а вот та самая таблица, там записано соответствие 16го кода с двоичным распределенным по тетрадам, ну собстно 4 бита, та самая половина байта, одна хекс цифра 1 = 0001 2 = 0010 3 = 0011 - 4 = 0100 5 = 0101 6 = 0110 7 = 0111 - 8 = 1000 9 = 1001 A = 1010 B = 1011 C = 1100 D = 1101 E = 1110 F = 1111 Как видим все ну просто элементарно, наглядно и просто, я специально разделил все цифры на три блока, потому как по сути тут даже учить эту таблицу не надо, все идет в определенной простой последовательности: 1, 10, 11 или 100, 101, 110, 111, и если скажем я знаю как записывается цифра 1 [0001], но не знаю как цифра 2, то легко могу вспомнить обо всем этом и просто по логике понять, что 2 = 0010. Точно так же легко можно переводить в обратную сторону из двоичной в хекс, скажем если у меня есть двоичное число 0101, моментально вспоминаем, что это 7 в хексе, есть, скажем, 1111, моментально вспоминаем, что это F. А собстно байт из себя представляет два вот этих самых числа, две тетрады по 4 двоичных бита, и скажем если есть хекс байт AA, то моментально нам приходит в голову, что это 1010 1010, а если есть скажем байты 7ВАE, то это просто 0101(7) 1011(B) 1010(A) 1110(E). И вот что самое приятное, это все, что нужно знать, по сути, буквально выучить несколько цифр, вот даже не всю таблицу из 15 элементов, а просто несколько так сказать контрольных цифр, скажем 1, 4, С, E и легко можно уже сказать, что мы можем прямо в уме переводить числа из хекса, что мы видем в хекс редакторе в двоичную систему, двоичную в хекс, теперь мы можем легко понять из чего что состоит ;) =2 Спускаемся на уровень атомов Часто многие задумываются, а что такое в целом понятие, зачем мне скажем знать, что табуретка называется табуреткой, а не стулом без спинки и для чего мне знать слово "Хронология", если я знаю слово "Последовательность событий", все это придумано просто ради удобства, чтобы можно было сказать больше, сделать больше. Ровно так же и с Ассемблером, это только условное обозначение тех самых нулей и единиц, той самой последовательности, из которой состоит все то, что мы можем сделать и уже сделали и знать как эти нули и единицы управляют системой может нам легко помочь начать понимать все то, что раньше было за гранью понимания, мы сможем делать вещи совершенно удивительные, скажем взять открыть файл в хекс редакторе и моментально понять, что делает эта программа, внести в нее изменения, самому написать такую программу без всякого компилятора, взглянуть на шелл код для эксплойта и сразу же понять как он работает, это очень просто на самом деле и то, что мы уже умеем переводить числа из хекс (шестнадцатеричной системы счисления) в двоичную систему счисления, это уже половина дела и буквально дочитав эту статью мы сможем легко начать понимать все это. Те совершенно микроскопические цифры, которые создает ассемблер - это просто операции и какой-то небольшой последовательностью нулей и единиц он говорит процессору скажем сложить два числа, вычесть, умножить, переместить данные оттуда сюда, обменять данные из того места и чтобы знать это изнутри нам потребуется буквально только знать несколько кодов операций и буквально пару правил, ровно так же как мы запомнили ту таблицу перевода из 16й системы в двоичную. Вот так и получается, что выучив несколько цифр, которые по сути легче выучить чем стихотворение и поняв как элементарно строятся команды, можно уже легко понимать из чего все состоит, знать все изнутри. С самого начала надо представлять из чего может состоять команда процессора, это небольшая схема легко может показать какие компоненты могут быть в команде, конечно же это просто целиком почти все, что может присутствовать в команде, а на самом деле в одной команде может присутствовать только один компонент, два компонента и тд, обязательным является только код операции. префикс | код операции | mod r/m | SIB | смещение | операнд ==[Префикс] - это можно сказать дополнительные данные к команде, префикс может значить условно "работать с данными как в MS DOS", "Повторять команду много раз" и т.д. С этим можно совершенно не заторачиваться потому как нам они почти не понадобятся и на самом деле даже в реальном коде префиксы встречаются не часто. Для удобства назову четыре префикса, потому как по сути остальные несколько могут вообще не встретиться, для удобства пишу в хекс и двоичной системе, чтобы можно было легко встретив в коде увидеть этот префикс REPNZ условно повторять операцию пока в регистре ECX не 0, после каждого повторения ECX уменьшается на один, ну можно сказать легко, что ECX просто как счетчик повторений. А команда может выглядеть примерно, как скажем REPNZ MOVSB, собстно команда повторять перемещение байтов, тут префикс REPNZ, а команда MOVSB. префикс F2 в хекс и соответственно 1111 0010 в двоичной REP аналогично предыдущей команде, повторение, но с другим условием префикс F3 в хекс и соответственно 1111 0011 в двоичной Переопределение размера операндов, скажем, если был полный регистр EAX, то с префиксом 66, он будет как половина регистра EAX - AX префикс 66 в хекс и соответственно 0110 0110 в двоичной Конечно же есть еще и несколько других префиксов, это переопределение размера адреса, блокировка шины и переопределение сегмента, но они по любому пока не понадобятся. ==[Код операции] - вот это собстно самое главное, сама команда, скажем, сложить числа, вычесть, переместить числа и тд, тут буквально запомнить несколько кодов, а там уже все будет очень легко, а я пока составлю небольшую таблицу кодов операций и если их выучить, дальше будет более чем просто. Сразу же записываю в формате HEX [двоичная система счисления] 8B [1000 1011] - команда MOV, перемещение данных, скажем MOV EAX, EBX переместить данные из EBX, в EAX, а собстно что перемещать надо именно EBX в EAX говорит байт moв r/m, о котором расскажу следующим пунктом, там все так же очень просто. 3B [0011 1011] - команда CMP, сравнение двух операндов, скажем CMP EAX, EBX, я сравниваю EAX с EBX. 33 [0011 0011] - команда XOR, исключающее логическое или, скажем XOR EAX, EAX обнуляет EAX 03 [0000 0011] - команда ADD, сложение, скажем ADD EAX, EBX, складывает EAX с EBX и результат попадает в EAX 2B [0010 1011] - команда SUB, вычитание аналогично сложению ADD 0B [0000 1011] - команда OR, логическое или, ну собстно OR EAX, EBX сравнивает данные в EAX и EBX и записывает в EAX по таблицу истинности числа, скажем если 1 или 1, будет 1, если 1 или 0 будет 0 и тд, все просто, тут все идет, конечно же, в двоичном виде, сравниваются только 0 и 1 23 [0010 0011] - команда AND, логическое И, ну собстно аналогично OR из прошлого пункта, только таблица истинности другая, скажем если 1 и 0 будет 1, если 1 и 1 будет 1 и если 0 и 0 будет 0 74 [0111 0100] - команда JZ, условный переход, если результат равен нулю, скажем cmp eax, ebx / jz addr 75 [0111 0101] - команда JNZ, аналогичный переход, если результат не равен нулю. 90 [1001 0000] - команда NOP или XCHG EAX, EAX, команда ничего не делает, собстно как просто заглушка Конечно же, это только самые простые и элементарные команды, по сути, эти команды составляют большую часть обычного кода и зная эти коды операций мы легко сможем найти знакомую нам команду в собстно бинарном коде. ==[MOD R/M] - это грубо говоря, управляющий командой байт, есть скажем, код операции "переместить данные" [MOV], этот байт определяет что куда перемещать, в какой регистр или брать что-то не из регистра а из памяти и тд, тут все так же очень просто, байт делится на три поля mod | Reg/Code | r/m 2 бита 3 бита 3 бита MOD определяет режим операции, если оно равно 11, то мы работаем только с регистрами, если это 10, то работаем с регистром и адресом + регистр, а собстно адрес находится в поле "Операнд", уже за пределами MOD R/M, вариантов всего четыре и если составить таблицу, можно легко представить как все работает. Код:
mod | Reg/Code | r/m Конечно же [РЕГИСТРЫ] обозначаются тут такими же трехбитными кодами, а которые помимо всего еще и очень просто запомнить, они идут вот в таком порядке EAX = 000 ECX = 001 EDX = 010 EBX = 011 - ESP = 100 EBP = 101 ESI = 110 EDI = 111 Тут точно так же легко все запомнить, как и ту таблицу, которую мы писали для перевода из хекс в двоичную систему счисления, тут самое главное запомнить, порядок EAX, ECX, EDX, EBX, или просто значимые буквы ACDB или по сути можно легко составить из этих регистров слово ACDBS BSD и сразу же вспомнить, что это собстно новая инопланетная версия BSD %) И вот тут уже мы можем легко составить свой первый MOD r/m и что самое главное свою первую команду в двоичном коде! Вот скажем, мы хотим переместить данные из регистра EBX в регистр EAX, смело пишем код команды перемещение MOV 10001011 и собстно составляем сам байт MOD R/M. Мы хотим переместить из регистра в регистр, от этого MOD у нас 11 ну и пишем код регистра EAX 000 и код регистра EBX 011 и получаем отличную команду MOV EAX, EBX в двоичном коде 1000 1011 11 000 011, что в хекс легко получается 8BC3, вот собстно эти два байта мы уже можем найти в бинарном коде или хекс редакторе и вот мы уже на один шаг ближе к дзену :) Есть отличный вариант себя проверить с помощью отладчика OllyDbg, загружаем в него любую программу, моментально щелкаем на нижнее левое окно с дампом памяти, нажимаем простую комбинацию CTRL+G для перехода к стартовому адресу кода и вводим стартовый адрес кода, ну собстно адрес, на котором у нас стоит метка выполнения кода в левом верхнем окне с дизасемблеными командами, обводим в дампе памяти слева внизу два байта и записываем туда нашу команду 8BC3 и вот теперь в окне отладчика мы видим наши первые MOV EAX, EBX! А остальные команды строятся так же просто, вот попробуем переместить в регистр EAX 4 байта (вместительность регистра EAX) из адреса EBX + адрес, скажем MOV EAX, DWORD PTR[EBX+00403000], если бы в есть содержалось число 2 а адрес был бы 00403000, то мы перемещали бы 4 байта начиная с адреса 00403002, попробуем составить такую команду. Пишем код операции MOV 1000 1011 и составляем байт MOD R/M, у нас тут будет 4 байтовое смещение, от этого MOD у нас будет 10, регистр EAX как и прежде 000, а EBX 011, получаем байт MOD R/M 10 000 011 и добавляем в поле данных адрес смещения 00403000, записывать конечно же надо в перевернутом виде и собстно в хекс это получается 00304000, а в двоичном виде 00000000 00110000 01000000 00000000, моментально совмещаем все байты в одно и получаем отличную команду MOV EAX, DWORD PTR[EBX+00403000] 10001011 / 10 000 011 / 00000000001100000100000000000000 или в хексе 8B8300304000 По сути мы уже знаем большую часть и точно так же легко можем оперировать и другими командами, моментально распознавать их в чистом бинарном коде и более того, самим писать программы в бинарном виде, что даст определенно отличные возможности :) ==[SIB, смещение и операнд] - с этими полями все более чем просто, на самом деле пока нам надо знать только поле "Операнд", определенно мы уже знаем, что там хранится те самые адреса в операциях вроде наших смещений 00403000 и AA, ну собстно это просто данные, с которыми работает процессор, это может быть адрес памяти, смещение, просто операнд в операциях. Про смещение можно так же не упоминать, потому как оно нам совершенно не пригодится. Поле SIB расшифровывается как Scale-Index Base и представляет из себя все тот же байт по структуре напоминающий MOD R/M, только вместо полей MOD | REG | R/M там будет Scale 2 бита | Index 3 бита | Base 3 бита, а сам процессор ждет этот байт после MOD R/M всякий раз, как поле R/M равно 100, и если у нас MOD R/M равен, скажем 11 000 100, то дальше должен стоять байт SIB. Изучать подробно нам пока что его не нужно потому как на первых началах изучения совершенно не понадобится, в целом можно сказать, что SIB помогает обращаться с адресацией намного лучше, получать адреса через умножение, сложение с индексным регистром и тд. Ну и небольшое разъяснение полей SIB: два бита [SCALE] это степень двойки для умножения базового регистра, так сказать масштабирования адреса, [INDEX] индексный регистр, там все так же как и в прошлом разделе, вот для примера 000 - EAX, 001 - ECX и тд, а три последних бита [BASE] это базовый регистр. И уже сейчас моментально можно привести пример команды , которая может получиться с байтом SIB. MOV EAX, [EAX*4][EСX+00403000] КОд операции / MOD R/M / SIB / Операнд 1000 1011 10 000 100 11 000 001 hex(00304000) От этого у нас получается, что базовый регистр ECX код 001, а индексный EAX, код получился 000. =Перед нами уже нету препятствий И совершенно правильно, ведь уже прямо сейчас мы можем видеть все изнутри, вот ровно так же, как Нео в в фильме "Матрица" и совершенно точно, что если знать все изнутри можно управлять этим, перед нами нет препятствий, самое важное знать точно, что все это очень просто и все получится только в самом лучшем виде ;) Приложения к статье .../soft/10/hex2bin.exe - Отличная программа, которая позволит потренироваться в переводе из хекс в двоичную систему счисления, по любому она сможет помочь дойти до такого уровня, что не произвольно числа будут сами переводиться в те самые нули и единицы :) .../soft/10/1regfield.exe - Объясняет простейший однобайтовый формат xxxxxreg (inc reg,dec reg,push,pop,xhch (e)ax,reg). Размером 5:3 bit. Служит вводной частью изучения того как байт внутри делится на битовые поля и помогает запомнить значения кодов регистров поля reg. Поля очень важного и использующегося в подавляющем большинстве других форматов опкода, как для регистровой адресации, так и для адресации памяти. .../soft/10/2mod11bitsdw.exe - Вводная программа для понимания важнейшей составляющей инструкций - байта modr/m и бит d,w. Основополагающие знания по размещению и кодированию операндов, их размера а также первый пример того как кодирование может проводиться двумя различными способами с получением одинаковых результатов. .../soft/10/3muldivopcode.exe - Продолжает тему mod11bitsdw.exe (т.е. байт modrm, переключение между полными и частичными регистрами и т.д.)а также расширяет его - показывает как поле Code может хранить не только операнд но и ID опкода. .../soft/10/4movrimm.exe - Вводная программа для обучения\тестирования по теме кодирования непосредственных операндов в памяти. Даётся простейший формат подобного кодирования - с отсутствием бита S. .../soft/10/5Modrm1op.exe - Программа завершающая тему кодирования адреса (но только эту тему, ещё остаётся много нераскрытых тем по опкоду). Единственная программа, которая не содержит тестирующей части. Программа содержит демонстрационную часть показывающую в мельчайших деталях как кодируются адреса, вы можете убедится, что адрес может быть закодирован до 7и разными способами. Кодирование может быть продемонстрировано и по правилам 32х битного эффективного адреса и (при включении префикса 67) по правилам 16и битного эффективного адреса. Specially for TGBR E-ZinoS #2 from Dr.Cert |
||
![]() |
![]() |
|