2010-12-29 2 views
2
char program[3] = { 0x6a, 0x0a, 0xC3 }; //push 10; ret; 
char (*p)(void) = program; 
printf("char = %c \n", p()); 

Этот код падает. Зачем?x86 asm crashing application

Следующий код работает нормально:

char program[3] = { 0x90, 0x90, 0xC3 }; //nop; nop; ret 
void (*p)(void) = program; 
p(); 
+1

нажимной + в отставке == перехода. – ruslik

ответ

3

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

Например, в C на большинстве платформ x86-32 существует одно общее правило: eax должно содержать возвращаемое значение. Также будет установлен набор значений, которые помещаются в стек (называемый фреймом стека для функции). Другое дело, что некоторые регистры должны быть сохранены, в то время как другие (иногда называемые регистры царапин) могут быть оставлены с вашей мусором в них.

Короче говоря, если вы нарушите эти правила, все пойдет не так, и OS пригодится для вас. Это зависит не только от вашего процессора, но и от вашей операционной системы; например, linux и windows на x64.

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

И, наконец, nop, nop, ret ничего не делает. Поэтому вы должны выполнить эти инструкции и вернуться, потому что на самом деле вы ничего не сделали для фрейма стека.

+0

Я не знаю, откуда у меня появилась идея, что возвращаемые значения были добавлены в стек из. Спасибо –

+0

Входные значения добавляются в стек; Я предполагаю, что вы используете «nop», вы изучаете идею nop sled, способ использовать функции, которые не проверяют правильность размеров параметров, обычно называемых переполнением буфера ... –

+0

Нет, я был просто показывая, что это не проблема с кодом C, а сами инструкции. –

5

Потому что вам нужно очистки стек, выталкивая из него. Подробнее о инструкции ret - он загрузит cs: ip со значениями из стека!

+0

Технически только «retf» загружает как CS, так и EIP (отдаленный возврат). '' Retn' только выдает EIP (и это то, что большинство ассемблеров будет выводить для 'ret' в эти дни). Игнорирование инструкций, таких как 'iret', которые вы редко видите в пользовательском пространстве). –

3

Я настоятельно рекомендую вам узнать о вызовах. Для 32-битной x86, функция, которая модифицирует стек должен выглядеть следующим образом (Cdecl):

push ebp 
mov ebp, esp ; or ENTER instruction for the push+mov 

; Function code! 

mov esp, ebp 
pop ebp ; or LEAVE instruction for the mov+pop 
ret 

mov esp, ebp обеспечивает ваши возвращения стека к, как это было в начале функции, что означает возвращение адрес находится в стеке для ret для загрузки и перехода.

Локальные переменные в функции помещаются в стек (sub esp, 0x4 выделяет пространство для одной 32-битной переменной, которую можно использовать как [esp + 0]).

64-разрядный x86 (не Itanium) имеет свое собственное соглашение о вызовах. Тогда есть fastcall и т. Д.

Wikipedia Article on x86 Calling Conventions стоит взглянуть на.

+0

+1 для фактического цитирования соглашения о вызовах. –

0

И кроме очистки стека, на основе намерений (ожидается выход 10) вашей программы ...

char program[3] = { 0x6a, 0x0a, 0xC3 }; //push 10; ret; 
char (*p)(void) = program; 
printf("char = %c \n", p()) 

..., вы не должны нажимать 10, возвращаемое значение из функций сохраняется в AX (16 бит), EAX, если 32 бит

Если параметр передачи предназначен, очистка стека зависит от соглашения о вызове функции, если это __fastcall (или pascal), это вызванная процедура, которая должна выполнить очистку; если это C соглашение о вызовах (_cdecl), то абонент, который должен очистить стек

1
char program[] = { 
    0x55,    /* push %ebp */ 
    0x89, 0xe5,  /* mov %esp, %ebp */ 
    0x6a, 0x0a,  /* push $0xa */ 
    0x8b, 0x04, 0x24, /* mov (%esp), %eax */ 
    0x89, 0xec,  /* mov %ebp, %esp */ 
    0x5d,    /* pop %ebp */ 
    0xc3,    /* ret */ 
}; 

Это будет работать *. Ваша программа выйдет из строя, потому что вы испортили структуру стека.

* пока program находится в исполняемой части памяти, которая не всегда верно, и вы работаете на x86-32 с типичной C вызовах

+0

+1 для обозначения выполнения данных как кода. –

Смежные вопросы