Как шестнадцатеричная последовательность переводится в сборку без двусмысленности? - PullRequest
4 голосов
/ 12 октября 2010
8B EC 56 8B F4 68 00 70 40 00 FF 15 BC 82 40   

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

UPDATE

Ответ с большинством голосов фактически не отвечает на мой вопрос.

Ответы [ 9 ]

13 голосов
/ 12 октября 2010

Зная вашу отправную точку.

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

С более математической точки зрения не существует допустимой инструкции, байты которой являются префиксом другой действительной инструкции. Таким образом, если ab допустимо, то вы знаете, что ab cd не может быть допустимым, поэтому ab должна быть одной инструкцией, а cd - началом следующей инструкции.

3 голосов
/ 13 октября 2010

Если я правильно понимаю ваш вопрос, вы пытаетесь понять, почему

8B EC 56 8B F4 68 00 70 40 00 FF 15 BC 82 40

Можно разделить, например, как

8BEC 568BF4 68007040 00FF 15BC 8240

Вместо того, чтобы сказать,

8B EC568B F4 68007040 00FF 15BC 8240

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

Для правильного формирования ISA, одна серия байтов может соответствовать не более чем одной серии декодированныхинструкции (может быть меньше, если есть недопустимые инструкции).

Чтобы получить более конкретный пример, давайте возьмем пример x86: если вы хотите знать, что соответствует каждому байту, посмотрите здесь .

Вы увидите, что, например, инструкция, начинающаяся с 00, является добавлением (дополнительные параметры находятся в следующем байте с определенной кодировкой).

Вы такжевидим, что некоторые значения на самом деле являются префиксами, которые изменяют следующую инструкцию (0F - префикс для расширения пространства кода операции, 26, 2E, 36, 3E, 64, 65, 66, 67, F0, F2, F3), и что некоторые из нихпринять другое значение на основе точной следующей инструкции.Это не коды операций, но они могут изменить кодировку аргументов кода операции или ввести совершенно новое пространство кода операции (например, SSE использует 0F).

В целом, кодирование x86 очень сложное, спасибо за дизассемблеры.

3 голосов
/ 13 октября 2010

Прежде всего необходимо различать архитектуры RISC и CISC .

В архитектуре RISC у вас обычно есть инструкции одинакового размера, поэтому двусмысленность не может быть представлена. Ваш ЦП будет получать, например, 4 байта для каждой инструкции, и, поскольку он должен будет начинать откуда-то (у вашего ЦП нет последовательности, подобной той, которую вы представили, у него наверняка будет начальная точка), как только он получит При правильном выравнивании никаких проблем возникнуть не может.

То, что происходит с набором команд CISC, по сути то же самое: начиная с точки входа в программу, оно будет извлекать инструкции в соответствии с вашими кодами операций. Ему не нужно знать, как математически различать неоднозначности, поскольку не случится так, что он просто не знает, как долго длится следующая инструкция или где завершена последняя.

Поэтому спрашивать, как отделить каждую инструкцию, все равно, что спрашивать, как отделить каждое слово в

thepenisonthetable

Нет математического доказательства, но вы знаете, какие буквы правильны вместе, а какие не имеют смысла. Предыдущее предложение содержит «сын», но вы знаете, что оно получено из «включен». Вы не сможете сказать это, не имея значащей фразы, но ваш процессор выполняет только значимые программы, в чем смысл?

Так что, если процессор может работать с предыдущим предложением, он найдет первую содержательную инструкцию «the», затем «pen», «is», «on», и «son» никогда не сможет быть распознан в любом случае.

EDIT

Для очистки в архитектурах CISC единственным ограничением, которое вы должны быть уверены, чтобы не было двусмысленности, является недопущение наличия инструкции, которая является префиксом другой. Давайте предположим, что конечный алфавит состоит из букв a-z вместо шестнадцатеричных чисел (только для практических целей).

Если счетчик программы указывает на

abbcbcaabdeffabd

вы можете иметь, что abb - это целая инструкция. В этом случае ab не будет действительной инструкцией, иначе процессор не сможет знать, где остановиться, в то же время abbc тоже не может быть инструкцией, или это может создать проблемы. Если оставить его, например, ca - следующая инструкция, c - и cbc.

Вы можете распространить эту аргументацию на всю строку. Вы увидите, что, если ЦП находится в состоянии, в котором следующий байт двоичного кода указывает на ПЕРВЫЙ байт инструкции, и нет инструкции, которая является префиксом другой инструкции, то в следующем состоянии счетчик программы будет указывать на ПЕРВЫЙ байт следующей правильной инструкции.

1 голос
/ 14 октября 2010

Если вы откроете двоичный файл в шестнадцатеричном редакторе, скопируете часть данных и вставите в дизассемблер, вы, скорее всего, не скопируете полную инструкцию.Давайте использовать ваш пример ... в Windows XP 32bits SP3 на английском, если я соберу 8B EC 56 8B F4 68 00 70 40 00 FF 15 BC 82 40, я получу:

Hex dump          Command
8BEC              mov ebp,esp
56                push esi
8BF4              mov esi,esp
68 00704000       push 407000
FF15 BC824000     call dword ptr ds:[4082bc]

Как вы видите, он собран совершенно иначе, чем ответ других парней ниже...

Теперь давайте представим, что вместо сборки 8B EC 56 8B F4 68 00 70 40 00 FF 15 BC 82 40
вы добавили C0 код операции в начале C0 8B EC 56 8B F4 68 00 70 40 00 FF 15 BC 82 40

Теперь проверьте ниже, что сделал один байт в нашемИнструкции:

Hex dump             Command
C08B EC568BF4 68     ror byte ptr ds:[ebx+f48b56ec],68
0070 40              add byte ptr ds:[eax+40],dh
00FF                 add bh,bh
15 BC824000          adc eax,4082bc

Как вы можете видеть, он был полностью изменен!

Процессор знает, с чего начать и какие аргументы инструкции использовать командой opcode,В первом примере наш первый код операции был 8B, поэтому он знает, что за ним может следовать другой байт.Если этот байт равен EC, значит, инструкция на этом заканчивается, и это означает mov ebp, esp.

В нашем втором примере он начинается с C0, и за ним может следовать другой байт, означающий другую инструкцию.Тогда C08B - это инструкция, а EC568BF4 68 - это аргумент.

Представьте, что внутри процессора имеется огромная (но нано) схема, которая ведет себя как цепочка условий (условий), которая зависит отзначение шестнадцатеричного кода (или кода операции), которое он знает «куда идти» или «как себя вести».

1 голос
/ 12 октября 2010

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

8B EC 56 8B F4 68 00 70 40 00 FF 15 BC 82 40

И отправная точка (допустим, 8В - ваша отправная точка), существует только одна возможная интерпретация байтов.

Итак, скажем, одна операция - 8B EC 56 8B (в зависимости от длины операции и т. Д.), Затем СЛЕДУЮЩАЯ операция - F4 68 ... В этом случае устройство не может попытаться интерпретировать операцию 56 8B F4 68, потому что в этот момент ни одна операция не завершилась.

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

Структура вашей памяти очень специфична, а начальные точки / точки перехода точны и неумолимы - они необходимы так же точно, как и сам код.

1 голос
/ 12 октября 2010

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

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

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

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

1 голос
/ 12 октября 2010

В указанной вами последовательности отображается ровно 1 число.В двоичном виде это 100010111110110001010110100010111111010001101000000000000111000001000000000000001111111100010101101111001000001001000000.В десятичном виде это 726522768938664460674442126658667072.Это просто разные способы написания одного и того же значения.ISA конкретной архитектуры разделит биты на поля и назначит им значение.Большинству процессоров легко получить руководства, описывающие значение, присвоенное каждому из битов в этих полях.

0 голосов
/ 14 октября 2010

Может быть, вам интересно подумать о другом направлении: как бы вы разработали свой код, чтобы его можно было легко сегментировать для других? Вы могли бы потребовать, чтобы самый старший бит байта, начинающего последовательность, был нулевым, а те, что находятся в середине последовательности, равными единице, как это делает UTF-8. Затем, если вы начнете со случайной позиции - если вы знаете, где находятся байты - легко найти следующую последовательность. Если сделать еще один шаг вперед, как бы вы кодировали чистый битовый поток, чтобы легко было найти начало последовательности. Сколько битов было потрачено на такое кодирование?

Поскольку вы спрашивали о математике, я думаю, что соответствующие темы - «Теория кодирования», «Коды переменной длины» или «Коды префиксов».

Как найти ген в последовательности пар оснований?

0 голосов
/ 13 октября 2010

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

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

...