Встроенная сборка и перезапись функций, приводящая к segfault - PullRequest
0 голосов
/ 03 октября 2009

Кто-то из SO опубликовал вопрос, спрашивающий, как он может «скрыть» функцию. Это был мой ответ:

#include <stdio.h>
#include <stdlib.h>

int encrypt(void)
{
  char *text="Hello World";
  asm("push text");
  asm("call printf");
  return 0;
}

int main(int argc, char *argv[])
{
  volatile unsigned char *i=encrypt;
  while(*i!=0x00)
    *i++^=0xBE;
  return EXIT_SUCCESS;
}

но есть проблемы:

encode.c: In function `main':
encode.c:13: warning: initialization from incompatible pointer type
C:\DOCUME~1\Aviral\LOCALS~1\Temp/ccYaOZhn.o:encode.c:(.text+0xf): undefined reference to `text'
C:\DOCUME~1\Aviral\LOCALS~1\Temp/ccYaOZhn.o:encode.c:(.text+0x14): undefined reference to `printf'
collect2: ld returned 1 exit status

Мой первый вопрос: почему происходит сбой встроенной сборки ... что было бы правильным способом сделать это? Другое дело - код "ret" или "retn" - 0x00, верно ... мой код xor, пока он не достигнет возврата ... так почему он SEGFAULTing?

Ответы [ 3 ]

1 голос
/ 03 октября 2009

Как точка высокого уровня, я не совсем уверен, почему вы пытаетесь использовать встроенную сборку для простого вызова printf, так как все, что вы сделали, - это создали неверную версию вызова функции (ваш встроенный помещает что-то в стек, но никогда не выталкивает его, что, скорее всего, вызывает проблемы, потому что GCC не знает, что вы изменили указатель стека в середине функции. Это нормально в тривиальном примере, но может привести к очевидные ошибки в более сложной функции)

Вот правильная реализация вашей главной функции:

int encrypt(void)
{
  char *text="Hello World";
  char *formatString = "%s\n";
  // volatile really isn't necessary but I just use it by habit
  asm volatile("pushl %0;\n\t"
               "pushl %1;\n\t"
            "call printf;\n\t"
               "addl $0x8, %%esp\n\t"          
               : 
               : "r"(text), "r"(formatString)
               );

  return 0;
}

Что касается вашего последнего вопроса, обычный код операции для RET - "C3", но есть много вариантов, посмотрите на http://pdos.csail.mit.edu/6.828/2009/readings/i386/RET.htm Ваша идея поиска RET также ошибочна из-за того факта, что когда вы видите байт 0xC3 в случайном наборе инструкций, это НЕ означает, что вы встретили ret. Поскольку 0xC3 может просто представлять собой данные / атрибуты другой инструкции (в качестве примечания, особенно трудно попытаться проанализировать инструкции x86, поскольку вы делаете это из-за того факта, что x86 является архитектурой CISC с длинами команд между 1-16 байтами )

Как еще одно примечание, не все ОС допускают изменение сегмента текста / кода (где хранятся исполняемые инструкции), поэтому код, который вы используете в main, может не работать независимо.

1 голос
/ 03 октября 2009

Встроенный ассемблер GCC использует синтаксис AT & T (если не выбраны конкретные опции для использования с Intel).

Вот пример:

 int a=10, b;
 asm ("movl %1, %%eax; 
       movl %%eax, %0;"
      :"=r"(b)        /* output */
      :"r"(a)         /* input */
      :"%eax"         /* clobbered register */
      );       

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

См. здесь для справки.

Более того, ваш код не переносим между 32 и 64 битными средами. Скомпилируйте его с флагом -m32, чтобы обеспечить надлежащий анализ (GCC в любом случае будет жаловаться на ошибку).

Полное решение вашей проблемы находится на этой записи в списке рассылки GCC. Вот фрагмент кода:

for ( i = method->args_size - 1; i >= 0; i-- ) {
    asm( "pushl %0": /* no outputs */: \
         "g" (stack_frame->op_stack[i]) );
}

asm( "call *%0" : /* no outputs */ : "g" (fp) :
     "%eax", "%ecx", "%edx", "%cc", "memory" );

asm ( "movl %%eax, %0" : "=g" (ret_value) : /* No inputs */ );

В системах Windows есть также дополнительный asm ( "addl %0, %%esp" : /* No outputs */ : "g" (method->args_size * 4) );. Google для более подробной информации.

0 голосов
/ 21 октября 2009

Это не printf, а _printf

...