Я работаю над книгой Джона Эриксона «Взлом: искусство эксплуатации».
В одной части книги он дает код на C, а затем просматривает соответствующую сборку, используя gdb, объясняя инструкции и действия с памятью.
Я работаю вместе в Mac OS X, поэтому все немного отличается от того, что он представляет в книге (он использует Linux).
В любом случае, у меня есть эта программа на C:
1 #include <stdio.h>
2 #include <string.h>
3
4 int main()
5 {
6 char str_a[20];
7
8 strcpy(str_a, "Hello, world!\n");
9 printf(str_a);
10 }
Вот соответствующий дамп объекта otool (я только что включил main):
_main:
0000000100000ea0 pushq %rbp
0000000100000ea1 movq %rsp,%rbp
0000000100000ea4 subq $0x30,%rsp
0000000100000ea8 movq 0x00000189(%rip),%rax
0000000100000eaf movq (%rax),%rax
0000000100000eb2 movq %rax,0xf8(%rbp)
0000000100000eb6 leaq 0xd4(%rbp),%rax
0000000100000eba movq %rax,%rcx
0000000100000ebd movq $0x77202c6f6c6c6548,%rdx
0000000100000ec7 movq %rdx,(%rcx)
0000000100000eca movb $0x00,0x0e(%rcx)
0000000100000ece movw $0x0a21,0x0c(%rcx)
0000000100000ed4 movl $0x646c726f,0x08(%rcx)
0000000100000edb movq %rcx,0xe8(%rbp)
0000000100000edf xorb %cl,%cl
0000000100000ee1 movq %rax,%rdi
0000000100000ee4 movb %cl,%al
0000000100000ee6 callq 0x100000f1e ; symbol stub for: _printf
0000000100000eeb movl 0xf4(%rbp),%eax
0000000100000eee movq 0x00000143(%rip),%rcx
0000000100000ef5 movq (%rcx),%rcx
0000000100000ef8 movq 0xf8(%rbp),%rdx
0000000100000efc cmpq %rdx,%rcx
0000000100000eff movl %eax,0xd0(%rbp)
0000000100000f02 jne 0x100000f0d
0000000100000f04 movl 0xd0(%rbp),%eax
0000000100000f07 addq $0x30,%rsp
0000000100000f0b popq %rbp
0000000100000f0c ret
0000000100000f0d callq 0x100000f12 ; symbol stub for: ___stack_chk_fail
OK. Вы заметите вызов подпрограммы для printf ():
0000000100000ee6 callq 0x100000f1e ; symbol stub for: _printf
Но где же вызов strcpy ()?
Есть еще две аномалии. Прежде всего, если я установлю точку останова в gdp для strcpy ():
break strcpy
Программа выполняет выполнение без остановки. Кажется, что strcpy () на самом деле не вызывается.
Во-вторых, когда я скомпилировал код:
gcc -g -o char_array2 char_array2.c
Я получил предупреждение:
char_array2.c: In function ‘main’:
char_array2.c:9: warning: format not a string literal and no format arguments
char_array2.c:9: warning: format not a string literal and no format arguments
Я не уверен, связано ли это с отсутствующим вызовом подпрограммы, но я подумал, что все равно включу его в качестве точки данных.
Мне кажется, что компилятор решил, что strcpy () не нужен, и оптимизировал код для работы без него. Программа работает как положено, печатая «Привет, мир!» к стандартному выводу, но этот пропущенный вызов strcpy () заставляет меня задуматься, что именно происходит.
В примере Эриксона в книге - это вызов strcpy (), так что, возможно, есть разница в том, как работает его компилятор и мой компилятор. Я на LLVM:
i686-яблоко-darwin11-LLVM-GCC-4,2
Любые идеи будут с благодарностью приняты!
Заранее спасибо, и я надеюсь, что вы найдете этот интересным.
Tom