Ошибка Сегментации Shellcode при запуске из эксплуатируемой программы
BITS 64
section .text
global _start
_start:
jmp short two
one:
pop rbx
xor al,al
xor cx,cx
mov al,8
mov cx,0755
int 0x80
xor al,al
inc al
xor bl,bl
int 0x80
two:
call one
db 'H'`
Это мой ассемблерный код. Тогда я использовал две команды. "nasm -f elf64 newdir.s -o newdir.o" и "ld newdir.o -o newdir". Я запускаю./newdir и работал нормально, но когда я извлек оп-код и попытался проверить этот шелл-код, используя следующую программу на языке c. Это не работает (нет ошибки сегментации). Я скомпилировал, используя cmd gcc newdir -z execstack
#include <stdio.h>
char sh[]="\xeb\x16\x5b\x30\xc0\x66\x31\xc9\xb0\x08\x66\xb9\xf3\x02\xcd\x80\x30\xc0\xfe\xc0\x30\xdb\xcd\x80\xe8\xe5\xff\xff\xff\x48";
void main(int argc, char **argv)
{
int (*func)();
func = (int (*)()) sh;
(int)(*func)();
}
objdump -d newdir
newdir: file format elf64-x86-64
Disassembly of section .text:
0000000000400080 <_start>:
400080: eb 16 jmp 400098 <two>
0000000000400082 <one>:
400082: 5b pop %rbx
400083: 30 c0 xor %al,%al
400085: 66 31 c9 xor %cx,%cx
400088: b0 08 mov $0x8,%al
40008a: 66 b9 f3 02 mov $0x2f3,%cx
40008e: cd 80 int $0x80
400090: 30 c0 xor %al,%al
400092: fe c0 inc %al
400094: 30 db xor %bl,%bl
400096: cd 80 int $0x80
0000000000400098 <two>:
400098: e8 e5 ff ff ff callq 400082 <one>
40009d: 48 rex.W
когда я бегу./a.out, я получаю что-то вроде на фотографии. Я прилагаю фото, потому что я не могу объяснить, что происходит. образ
PS- Моя проблема решена. Но я хотел знать, где что-то пошло не так. Поэтому я использовал отладчик, и результат ниже
(gdb) list
1 char shellcode[] = "\xeb\x16\x5b\x30\xc0\x66\x31\xc9\xb0\x08\x66\xb9\xf3\x02\xcd\x80\x30\xc0\xfe\xc0\x30\xdb\xcd\x80\xe8\xe5\xff\xff\xff\x48";
2 int main (int argc, char **argv)
3 {
4 int (*ret)();
5 ret = (int(*)())shellcode;
6
7 (int)(*ret)();
8 } (gdb) disassemble main
Dump of assembler code for function main:
0x00000000000005fa <+0>: push %rbp
0x00000000000005fb <+1>: mov %rsp,%rbp
0x00000000000005fe <+4>: sub $0x20,%rsp
0x0000000000000602 <+8>: mov %edi,-0x14(%rbp)
0x0000000000000605 <+11>: mov %rsi,-0x20(%rbp)
0x0000000000000609 <+15>: lea 0x200a20(%rip),%rax # 0x201030 <shellcode>
0x0000000000000610 <+22>: mov %rax,-0x8(%rbp)
0x0000000000000614 <+26>: mov -0x8(%rbp),%rdx
0x0000000000000618 <+30>: mov $0x0,%eax
0x000000000000061d <+35>: callq *%rdx
0x000000000000061f <+37>: mov $0x0,%eax
0x0000000000000624 <+42>: leaveq
0x0000000000000625 <+43>: retq
End of assembler dump.
(gdb) b 7
Breakpoint 1 at 0x614: file test.c, line 7.
(gdb) run
Starting program: /root/Desktop/Progs/shell/a.out
Breakpoint 1, main (argc=1, argv=0x7fffffffe2b8) at test.c:7
7 (int)(*ret)();
(gdb) info registers rip
rip 0x555555554614 0x555555554614 <main+26>
(gdb) x/5i $rip
=> 0x555555554614 <main+26>: mov -0x8(%rbp),%rdx
0x555555554618 <main+30>: mov $0x0,%eax
0x55555555461d <main+35>: callq *%rdx
0x55555555461f <main+37>: mov $0x0,%eax
0x555555554624 <main+42>: leaveq
(gdb) s
(Control got stuck here, so i pressed ctrl+c)
^C
Program received signal SIGINT, Interrupt.
0x0000555555755048 in shellcode ()
(gdb) x/5i 0x0000555555755048
=> 0x555555755048 <shellcode+24>: callq 0x555555755032 <shellcode+2>
0x55555575504d <shellcode+29>: rex.W add %al,(%rax)
0x555555755050: add %al,(%rax)
0x555555755052: add %al,(%rax)
0x555555755054: add %al,(%rax)
Вот отладочная информация. Я не могу найти, где управление идет не так. Если вам нужна дополнительная информация, пожалуйста, спросите.
2 ответа
Если int 0x80
в 64-битном коде была единственная проблема, построение вашего C-теста с gcc -fno-pie -no-pie
сработало бы, потому что тогда char sh[]
будет в низком 32-битном виртуальном адресном пространстве, поэтому системные вызовы, которые усекают указатели до 32-битных, все равно будут работать.
Запустите вашу программу под strace
чтобы увидеть, какие системные вызовы он на самом деле делает. (Кроме этого strace
декодирует int 0x80
Системные вызовы неправильно в 64-битном коде, декодирование, как если бы вы использовали 64-битный syscall
ABI. Номера вызовов и регистры arg разные. Но, по крайней мере, вы можете видеть значения, возвращаемые системным вызовом (которые будут -EFAULT
для 32-битных creat
с усеченным 64-битным указателем.)
Вы также можете просто gdb
в один шаг и проверить возвращаемые значения системного вызова. имеющий strace
однако декодировать входные данные системного вызова действительно приятно, поэтому я бы порекомендовал перенести ваш код для использования 64-битного ABI, и тогда он будет работать.
Кроме того, он фактически сможет использовать 64-битные процессы, в которых переполнение буфера находится в памяти по адресу за пределами младших 32 бит. (например, как стек). Так что да, вы должны действительно прекратить использование int 0x80
или придерживаться 32-битного кода.
Вы также зависите от того, будут ли регистры обнуляться перед выполнением кода, как будто они запускаются при запуске процесса, но не при вызове из других источников.
xor al,al
до mov al,8
совершенно бессмысленно, потому что xor-zeroing al
не очищает верхние байты. Запись 32-битных регистров очищает верхние 32, но не запись 8 или 16-битных регистров. И если это произойдет, вам не понадобится обнуление xor перед использованием mov
который также только для записи.
Если вы хотите установить RAX=8 без нулевых байтов в машинном коде, вы можете
push 8
/pop rax
(3 байта)xor eax,eax
/mov al,8
(4 байта)- Или с нулевым
rcx
регистр,lea eax, [rcx+8]
(3 байта)
Настройка CX на 0755
не так просто, потому что константа не вписывается в imm8. Ваш 16-битный mov
хороший выбор (или был бы, если бы вы обнулили rcx
первый.
xor ecx,ecx
lea eax, [rcx+8] ; SYS_creat = 8 from unistd_32.h
mov cx, 0755 ; mode
int 0x80 ; invoke 32-bit ABI
xor ebx,ebx
lea eax, [rbx+1] ; SYS_exit = 1
int 0x80
Ниже приведен рабочий пример использования x86-64; который может быть дополнительно оптимизирован для размера. Последний 0x00 null подходит для выполнения шеллкода.
собрать и ссылку:
$ nasm -felf64 -g -F dwarf pushpam_001.s -o pushpam_001.o && ld pushpam_001.o -o pushpam_001
Код:
BITS 64
section .text
global _start
_start:
jmp short two
one:
pop rdi ; pathname
xor rax, rax
add al, 85 ; creat syscall 64-bit Linux
xor rsi, rsi
add si, 0755 ; mode - octal
syscall
xor rax, rax
add ax, 60
xor rdi, rdi
syscall
two:
call one
db 'H',0
objdump:
pushpam_001: file format elf64-x86-64
0000000000400080 <_start>:
400080: eb 1c jmp 40009e <two>
0000000000400082 <one>:
400082: 5f pop rdi
400083: 48 31 c0 xor rax,rax
400086: 04 55 add al,0x55
400088: 48 31 f6 xor rsi,rsi
40008b: 66 81 c6 f3 02 add si,0x2f3
400090: 0f 05 syscall
400092: 48 31 c0 xor rax,rax
400095: 66 83 c0 3c add ax,0x3c
400099: 48 31 ff xor rdi,rdi
40009c: 0f 05 syscall
000000000040009e <two>:
40009e: e8 df ff ff ff 48 00
.....H.
извлечение кодировки: есть много других способов сделать это.
$ for i in `objdump -d pushpam_001 | grep "^ " | cut -f2`; do echo -n '\x'$i; done; echo
\xeb\x1c\x5f\x48\x31\xc0\x04\x55\x48\x31\xf6\x66\x81\xc6\xf3\x02\x0f\x05\x48\x31\xc0\x66\x83\xc0\x3c\x48\x31\xff\x0f\x05\xe8\xdf\xff\xff\xff\x48\x00\x.....H.
C shellcode.c - частичный
...
unsigned char code[] = \
"\xeb\x1c\x5f\x48\x31\xc0\x04\x55\x48\x31\xf6\x66\x81\xc6\xf3\x02\x0f\x05\x48\x31\xc0\x66\x83\xc0\x3c\x48\x31\xff\x0f\x05\xe8\xdf\xff\xff\xff\x48\x00";
...
окончательный:
./shellcode
--wxrw---t 1 david david 0 Jan 31 12:25 H