Проблема изучения байт-кода в MSVC ++ - PullRequest
2 голосов
/ 07 февраля 2009

Я бездельничал с бесплатным компилятором Digital Mars на работе (я знаю, шалость), и создал некоторый код для проверки скомпилированных функций и просмотра байт-кода в учебных целях, чтобы узнать, смогу ли я узнать что-нибудь ценное из компилятор строит свои функции. Однако воссоздание того же метода в MSVC ++ с треском провалилось, и результаты, которые я получаю, весьма запутаны. У меня есть такая функция:

unsigned int __stdcall test()
{
  return 42;
}

Потом я делаю:

unsigned char* testCode = (unsigned char*)test;

Я не могу заставить C ++ static_cast работать в этом случае (он выдает ошибку компилятора) ... отсюда приведение в стиле C, но это не главное ... Я также пытался использовать справка и тест, но это не помогает никому.

Теперь, когда я проверяю содержимое памяти, на которую указывает testCode, я запутался, потому что то, что я вижу, даже не похоже на допустимый код, и даже там застряла точка останова отладки ... это выглядит так ( цель - IA-32):

0xe9, 0xbc, 0x18, 0x00, 0x00, 0xcc ...

Это явно неверно, 0xe9 - инструкция относительного перехода, а если смотреть 0xbc байтов, это выглядит так:

0xcc, 0xcc, 0xcc ...

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

Где то, что я ожидаю от функции, возвращающей 42, будет примерно таким:

0x8b, 0x2a, 0x00, 0x00, 0x00, 0xc3

или, по крайней мере, какой-то вариант mov с последующим отступом (0xc2, 0xc3, 0xca или 0xcb) чуть ниже

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

У меня также возникают проблемы при переходе в другую сторону (выполнение байтов), но я подозреваю, что основная причина та же.

Любая помощь или советы будут с благодарностью.

Ответы [ 4 ]

2 голосов
/ 07 февраля 2009

Для вашего броска вы хотите:

unsigned char* testCode = reinterpret_cast<unsigned char*>( test );

Переключите формат информации отладки с «Программная база данных для редактирования и продолжения (/ ZI)» на «Программная база данных (/ Zi)» в Project -> Properties -> C / C ++ -> General. Я полагаю, что именно этот параметр заставляет компилятор вставлять код перехода, чтобы отладчик мог перестроить функцию и выполнить оперативное исправление во время выполнения программы. Возможно, отключите также «Включить минимальное перестроение».

Гораздо более простой способ проверки кода в MSVC - просто установить точку останова и проверить разборку (щелкните правой кнопкой мыши строку и выберите «Перейти к разборке» во всплывающем меню. Это аннотирует разборку источником код, чтобы вы могли видеть, к чему каждая строка компилируется.

2 голосов
/ 07 февраля 2009

Я могу только догадываться, но я уверен, что вы проверяете отладочную сборку. В режиме отладки компилятор MSVC ++ заменяет все вызовы вызовами переходов-заглушек. Это означает, что каждая функция начинается с перехода к реальной функции, и это именно то, с чем вы сталкиваетесь здесь.
Окружающие байты 0xCC на самом деле являются инструкциями точки останова, чтобы запустить возможно подключенный отладчик на случай, если вы выполняете код там, где вы не должны.
Попробуйте то же самое с выпуском сборки. Это должно работать как ожидалось.

Edit: Это на самом деле зависит от настройки компоновщика / INCREMENTAL. Причина, по которой описываемый вами эффект не проявляется в сборках релизов, заключается в том, что эти переходные заглушки просто оптимизируются, если включен какой-либо вид оптимизации (что, конечно, обычно имеет место для сборок релизов).

1 голос
/ 07 февраля 2009

Если это с включенной инкрементной связью, то вы видите jmp [destination]. Вы можете запустить отладчик и посмотреть, что также нужно проверить при разборке.

1 голос
/ 07 февраля 2009

Если вы хотите посмотреть на ассемблер и машинный код для данной скомпилированной функции, вам будет проще предоставить компилятору параметр командной строки / FAcs и посмотреть на следующий файл .asm.

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

...