Excuse the ads! We need some help to keep our site up.

List

Shellcode

The basics of shellcode(ubuntu-16.04)

C → ASM → Machine code

C code

Assembly code

Machine code

#include<stdio.h>
int main(){
    printf("Hello world!\n");
    return 1;
}
0000000000400526 <main>:
  400526:	push   %rbp
  400527:	mov    %rsp,%rbp
  40052a:	mov    $0x4005c4,%edi
  40052f:	callq  400400 <puts@plt>
  400534:	mov    $0x1,%eax
  400539:	pop    %rbp
  40053a:	retq   
400526:	55
400527:	48 89 e5 
40052a:	bf c4 05 40 00
40052f:	e8 cc fe ff ff 
400534:	b8 01 00 00 00
400539:	5d
40053a:	c3

Assembly code

Instructions

Meaning
mov destination, source목표 피연산자에 소스 피연산자를 복사합니다.
PUSH valuestack에 Value 값을 저장합니다.
POP registerstack 상위의 값을 레지스터에 저장합니다.
CALL function_name(address)리턴을 위해 CALL 명령어의 다음 명령주소를 스택에 저장한 후 함수의 위치로 점프를 합니다.
ret스택으로 부터 리턴 주소를 팝하고 그 곳으로 점프하여 함수에서 리턴 합니다.
inc destination목표 피연산자를 1증가 시킵니다.
dec destination목표 피연산자를 1 감소 시킵니다.
add destination, value목표 피연산자에 value 값을 더합니다.
sub destination, value목표 피연산자에 value 값을 뺍니다.
or destination, value비트 or 논리 연산을 한다. 최종 결과는 목표 피연산자에 저장됩니다.
and destination, value비트 and 논리 연산을 한다. 최종 결과는 목표 피연산자에 저장됩니다.
xor destination, value비트 xor 논리 연산을 한다. 최종 결과는 목표 피연산자에 저장됩니다.
lea destination, source목표 피연산자에 소스 피연산자의 유효 주소를 로드합니다.
InstructionsMeaningArchitecture
INT <Operand 1>Call to interruptx86, x86_64
SYSCALLSYStem call

x86_64

Linux system call in assembly

lazenca0x0@ubuntu:~/$ cat /usr/include/x86_64-linux-gnu/asm/unistd_32.h
#ifndef _ASM_X86_UNISTD_32_H
#define _ASM_X86_UNISTD_32_H 1

#define __NR_restart_syscall 0
#define __NR_exit 1
#define __NR_fork 2
#define __NR_read 3
#define __NR_write 4
#define __NR_open 5
#define __NR_close 6
#define __NR_waitpid 7
#define __NR_creat 8
#define __NR_link 9
#define __NR_unlink 10
#define __NR_execve 11
#define __NR_chdir 12
#define __NR_time 13
#define __NR_mknod 14
#define __NR_chmod 15
#define __NR_lchown 16
#define __NR_break 17
#define __NR_oldstat 18
#define __NR_lseek 19
#define __NR_getpid 20
...
lazenca0x0@ubuntu:~/$ cat /usr/include/x86_64-linux-gnu/asm/unistd_64.h
#ifndef _ASM_X86_UNISTD_64_H
#define _ASM_X86_UNISTD_64_H 1

#define __NR_read 0
#define __NR_write 1
#define __NR_open 2
#define __NR_close 3
#define __NR_stat 4
#define __NR_fstat 5
#define __NR_lstat 6
#define __NR_poll 7
#define __NR_lseek 8
#define __NR_mmap 9
#define __NR_mprotect 10
#define __NR_munmap 11
#define __NR_brk 12
#define __NR_rt_sigaction 13
#define __NR_rt_sigprocmask 14
#define __NR_rt_sigreturn 15
#define __NR_ioctl 16
#define __NR_pread64 17
#define __NR_pwrite64 18
#define __NR_readv 19
#define __NR_writev 20
...
  • 32bit : /usr/include/x86_64-linux-gnu/asm/unistd_32.h
  • 64bit : /usr/include/x86_64-linux-gnu/asm/unistd_64.h

Save argument value in registers

-Register(32bit)Register(64bit)
System callEAXRAX
Argument 1EBX

RDI

Argument 2ECX

RSI

Argument 3EDX

RDX

Argument 4ESI

R10

Argument 5EDI

R8

Argument 6

EBP

R9

Assembly Code Example("Hello, world!")

Build assembly code(32 bit)

section .data							; 데이터 세그먼트
	msg	db	"Hello, world!",0x0a, 0x0d	; 문자열과 새 줄 문자, 개행 문자 바이트
 
section	.text							; 텍스트 세그먼트
	global	_start						; ELF 링킹을 위한 초기 엔트리 포인트
 
_start:
	; SYSCALL: write(1,msg,14)
	mov	eax, 4		; 쓰기 시스템 콜의 번호 '4' 를 eax 에 저장합니다. 
	mov ebx, 1		; 표준 출력를 나타내는 번호 '1'을 ebx에 저장합니다.
	mov ecx, msg	; 문자열 주소를 ecx에 저장니다.
	mov edx, 14		; 문자열의 길이 '14'를 edx에 저장합니다.
	int 0x80		; 시스템 콜을 합니다.
 
	; SYSCALL: exit(0)
	mov eax, 1		; exit 시스템 콜의 번호 '1'을 eax 에 저장합니다.
	mov ebx, 0 		; 정상 종료를 의미하는 '0'을 ebx에 저장 합니다.
	int 0x80		; 시스템 콜을 합니다.
lazenca0x0@ubuntu:~/ASM$ nasm -f elf ASM32.asm 
lazenca0x0@ubuntu:~/ASM$ ld -m elf_i386 -o hello ASM32.o 
lazenca0x0@ubuntu:~/ASM$ ./hello 
Hello, world!
lazenca0x0@ubuntu:~/ASM$ file ./hello 
./hello: ELF 32-bit LSB executable, Intel 80386, version 1 (SYSV), statically linked, not stripped
lazenca0x0@ubuntu:~/ASM$ 


sudo apt-get install nasm

Build assembly code(64 bit)

section .data								; 데이터 세그먼트
	msg db      "hello, world!",0x0a, 0x0d	; 문자열과 새 줄 문자, 개행 문자 바이트

section .text								; 텍스트 세그먼트
    global _start							; ELF 링킹을 위한 초기 엔트리 포인트

_start:
	; SYSCALL: write(1,msg,14)
    mov     rax, 1		; 쓰기 시스템 콜의 번호 '1' 를 rax 에 저장합니다. 
    mov     rdi, 1		; 표준 출력를 나타내는 번호 '1'을 rdi에 저장합니다.
    mov     rsi, msg	; 문자열 주소를 rsi에 저장니다.
    mov     rdx, 14		; 문자열의 길이 '14'를 rdx에 저장합니다.
    syscall				; 시스템 콜을 합니다.

	; SYSCALL: exit(0)
    mov    rax, 60		; exit 시스템 콜의 번호 '60'을 eax 에 저장합니다.
    mov    rdi, 0		; 정상 종료를 의미하는 '0'을 ebx에 저장 합니다.
    syscall				; 시스템 콜을 합니다.
lazenca0x0@ubuntu:~/ASM$ nasm -f elf64 ASM64.asm 
lazenca0x0@ubuntu:~/ASM$ ld -o hello64 ASM64.o 
lazenca0x0@ubuntu:~/ASM$ ./hello64 
hello, world!
lazenca0x0@ubuntu:~/ASM$ file ./hello64 
./hello64: ELF 64-bit LSB executable, x86-64, version 1 (SYSV), statically linked, not stripped
lazenca0x0@ubuntu:~/ASM$ 

Change to Shellcode format

Code

BITS 32    						; nasm에게 32비트 코드임을 알린다
 
call helloworld 				; 아래 mark_below의 명령을 call한다.
db "Hello, world!", 0x0a, 0x0d 	; 새 줄 바이트와 개행 문자 바이트
 
helloworld:
	; ssize_t write(int fd, const void *buf, size_t count);
	pop ecx			; 리턴 주소를 팝해서 exc에 저장합니다.
	mov eax, 4		; 시스템 콜 번호를 씁니다.
	mov ebx, 1		; STDOUT 파일 서술자
	mov edx, 15		; 문자열 길이
	int 0x80		; 시스템 콜: write(1,string, 14)
 
	; void _exit(int status);
	mov eax,1		;exit 시스템 콜 번호
	mov ebx,0		;Status = 0
	int 0x80		;시스템 콜: exit(0)
lazenca0x0@ubuntu:~/ASM$ nasm ASM32.s 
lazenca0x0@ubuntu:~/ASM$ ndisasm -b32 ASM32
00000000  E80F000000        call dword 0x14
00000005  48                dec eax
00000006  656C              gs insb
00000008  6C                insb
00000009  6F                outsd
0000000A  2C20              sub al,0x20
0000000C  776F              ja 0x7d
0000000E  726C              jc 0x7c
00000010  64210A            and [fs:edx],ecx
00000013  0D59B80400        or eax,0x4b859
00000018  0000              add [eax],al
0000001A  BB01000000        mov ebx,0x1
0000001F  BA0F000000        mov edx,0xf
00000024  CD80              int 0x80
00000026  B801000000        mov eax,0x1
0000002B  BB00000000        mov ebx,0x0
00000030  CD80              int 0x80
lazenca0x0@ubuntu:~/ASM$ hexdump -C ASM32
00000000  e8 0f 00 00 00 48 65 6c  6c 6f 2c 20 77 6f 72 6c  |.....Hello, worl|
00000010  64 21 0a 0d 59 b8 04 00  00 00 bb 01 00 00 00 ba  |d!..Y...........|
00000020  0f 00 00 00 cd 80 b8 01  00 00 00 bb 00 00 00 00  |................|
00000030  cd 80                                             |..|
00000032
lazenca0x0@ubuntu:~/ASM$

Test

lazenca0x0@ubuntu:~/ASM$ python
Python 2.7.12 (default, Nov 20 2017, 18:23:56) 
[GCC 5.4.0 20160609] on linux2
Type "help", "copyright", "credits" or "license" for more information.
>>> f = open('ASM32','r')
>>> data = f.read()
>>> data
'\xe8\x0f\x00\x00\x00Hello, world!\n\rY\xb8\x04\x00\x00\x00\xbb\x01\x00\x00\x00\xba\x0f\x00\x00\x00\xcd\x80\xb8\x01\x00\x00\x00\xbb\x00\x00\x00\x00\xcd\x80'
>>> 
#include<stdio.h>
#include<string.h>

unsigned char shellcode [] = "\xe8\x0f\x00\x00\x00Hello, world!\n\rY\xb8\x04\x00\x00\x00\xbb\x01\x00\x00\x00\xba\x0f\x00\x00\x00\xcd\x80\xb8\x01\x00\x00\x00\xbb\x00\x00\x00\x00\xcd\x80";
unsigned char code[];

void main(){
	int len = strlen(shellcode);
	printf("Shellcode len : %d\n",len);
	strcpy(code,shellcode);
	(*(void(*)()) code)();
}
lazenca0x0@ubuntu:~/ASM$ gcc -o shellcode -fno-stack-protector -z execstack --no-pie -m32 shellcode.c
test.c:5:15: warning: array 'code' assumed to have one element
 unsigned char code[];
               ^
lazenca0x0@ubuntu:~/ASM$ ./shell 
Shellcode len : 2
Segmentation fault (core dumped)
lazenca0x0@ubuntu:~/ASM$ 
sudo apt-get install libx32gcc-5-dev libc6-dev-i386

Debugging

lazenca0x0@ubuntu:~/ASM$ gdb -q ./shell
Reading symbols from ./shell...(no debugging symbols found)...done.
gdb-peda$ disassemble main
Dump of assembler code for function main:
   0x0804846b <+0>:	lea    ecx,[esp+0x4]
   0x0804846f <+4>:	and    esp,0xfffffff0
   0x08048472 <+7>:	push   DWORD PTR [ecx-0x4]
   0x08048475 <+10>:	push   ebp
   0x08048476 <+11>:	mov    ebp,esp
   0x08048478 <+13>:	push   ecx
   0x08048479 <+14>:	sub    esp,0x14
   0x0804847c <+17>:	sub    esp,0xc
   0x0804847f <+20>:	push   0x804a040
   0x08048484 <+25>:	call   0x8048340 <strlen@plt>
   0x08048489 <+30>:	add    esp,0x10
   0x0804848c <+33>:	mov    DWORD PTR [ebp-0xc],eax
   0x0804848f <+36>:	sub    esp,0x8
   0x08048492 <+39>:	push   DWORD PTR [ebp-0xc]
   0x08048495 <+42>:	push   0x8048550
   0x0804849a <+47>:	call   0x8048320 <printf@plt>
   0x0804849f <+52>:	add    esp,0x10
   0x080484a2 <+55>:	sub    esp,0x8
   0x080484a5 <+58>:	push   0x804a040
   0x080484aa <+63>:	push   0x804a074
   0x080484af <+68>:	call   0x8048330 <strcpy@plt>
   0x080484b4 <+73>:	add    esp,0x10
   0x080484b7 <+76>:	mov    DWORD PTR [ebp-0x10],0x804a074
   0x080484be <+83>:	mov    eax,DWORD PTR [ebp-0x10]
   0x080484c1 <+86>:	call   eax
   0x080484c3 <+88>:	nop
   0x080484c4 <+89>:	mov    ecx,DWORD PTR [ebp-0x4]
   0x080484c7 <+92>:	leave  
   0x080484c8 <+93>:	lea    esp,[ecx-0x4]
   0x080484cb <+96>:	ret    
End of assembler dump.
gdb-peda$ b *0x080484af
Breakpoint 1 at 0x80484af
gdb-peda$ r
Starting program: /home/lazenca0x0/ASM/shell 
Shellcode len : 2
Breakpoint 1, 0x080484af in main ()
gdb-peda$ x/64bx 0x804a074
0x804a074 <code>:	0x00	0x00	0x00	0x00	0x00	0x00	0x00	0x00
0x804a07c:	0x00	0x00	0x00	0x00	0x00	0x00	0x00	0x00
0x804a084:	0x00	0x00	0x00	0x00	0x00	0x00	0x00	0x00
0x804a08c:	0x00	0x00	0x00	0x00	0x00	0x00	0x00	0x00
0x804a094:	0x00	0x00	0x00	0x00	0x00	0x00	0x00	0x00
0x804a09c:	0x00	0x00	0x00	0x00	0x00	0x00	0x00	0x00
0x804a0a4:	0x00	0x00	0x00	0x00	0x00	0x00	0x00	0x00
0x804a0ac:	0x00	0x00	0x00	0x00	0x00	0x00	0x00	0x00
gdb-peda$ 
gdb-peda$ x/64bx 0x804a040
0x804a040 <shellcode>:	0xe8	0x0f	0x00	0x00	0x00	0x48	0x65	0x6c
0x804a048 <shellcode+8>:	0x6c	0x6f	0x2c	0x20	0x77	0x6f	0x72	0x6c
0x804a050 <shellcode+16>:	0x64	0x21	0x0a	0x0d	0x59	0xb8	0x04	0x00
0x804a058 <shellcode+24>:	0x00	0x00	0xbb	0x01	0x00	0x00	0x00	0xba
0x804a060 <shellcode+32>:	0x0f	0x00	0x00	0x00	0xcd	0x80	0xb8	0x01
0x804a068 <shellcode+40>:	0x00	0x00	0x00	0xbb	0x00	0x00	0x00	0x00
0x804a070 <shellcode+48>:	0xcd	0x80	0x00	0x00	0xe8	0x0f	0x00	0x00
0x804a078:	0x00	0x00	0x00	0x00	0x00	0x00	0x00	0x00
gdb-peda$ 
gdb-peda$ ni

0x080484b4 in main ()
gdb-peda$ x/32bx 0x804a074
0x804a074 <code>:	0xe8	0x0f	0x00	0x00	0x00	0x00	0x00	0x00
0x804a07c:	0x00	0x00	0x00	0x00	0x00	0x00	0x00	0x00
0x804a084:	0x00	0x00	0x00	0x00	0x00	0x00	0x00	0x00
0x804a08c:	0x00	0x00	0x00	0x00	0x00	0x00	0x00	0x00
gdb-peda$ 

Remove null byte

lazenca0x0@ubuntu:~/ASM$ ndisasm -b32 ASM32
00000000  E80F000000        call dword 0x14
00000005  48                dec eax
00000006  656C              gs insb
00000008  6C                insb
00000009  6F                outsd
0000000A  2C20              sub al,0x20
0000000C  776F              ja 0x7d
0000000E  726C              jc 0x7c
00000010  64210A            and [fs:edx],ecx
00000013  0D59B80400        or eax,0x4b859
00000018  0000              add [eax],al
0000001A  BB01000000        mov ebx,0x1
0000001F  BA0F000000        mov edx,0xf
00000024  CD80              int 0x80
00000026  B801000000        mov eax,0x1
0000002B  BB00000000        mov ebx,0x0
00000030  CD80              int 0x80
lazenca0x0@ubuntu:~/ASM$

Call instruction

BITS 32			; nasm에게 32비트 코드임을 알린다

jmp short last	; 맨 끝으로 점프한다.
helloworld:
	; ssize_t write(int fd, const void *buf, size_t count);
	pop ecx 	; 리턴 주소를 팝해서 exc에 저장합니다.
	mov eax, 4	; 시스템 콜 번호를 씁니다.
	mov ebx, 1	; STDOUT 파일 서술자
	mov edx, 15	; 문자열 길지
	int 0x80	; 시스템 콜: write(1,string, 14)

	; void _exit(int status);
	mov eax,1	;exit 시스템 콜 번호
	mov ebx,0	;Status = 0
	int 0x80	;시스템 콜: exit(0)
 
last:
	call helloworld	; 널 바이트를 해결하기 위해 위로 돌아간다.
	db "Hello, world!", 0x0a, 0x0d	; 새 줄 바이트와 개행 문자 바이트
lazenca0x0@ubuntu:~/ASM$ nasm ASM32-2.s 
lazenca0x0@ubuntu:~/ASM$ ndisasm -b32 ASM32-2
00000000  EB1E              jmp short 0x20
00000002  59                pop ecx
00000003  B804000000        mov eax,0x4
00000008  BB01000000        mov ebx,0x1
0000000D  BA0F000000        mov edx,0xf
00000012  CD80              int 0x80
00000014  B801000000        mov eax,0x1
00000019  BB00000000        mov ebx,0x0
0000001E  CD80              int 0x80
00000020  E8DDFFFFFF        call dword 0x2
00000025  48                dec eax
00000026  656C              gs insb
00000028  6C                insb
00000029  6F                outsd
0000002A  2C20              sub al,0x20
0000002C  776F              ja 0x9d
0000002E  726C              jc 0x9c
00000030  64210A            and [fs:edx],ecx
00000033  0D                db 0x0d
lazenca0x0@ubuntu:~/ASM$ 

Register

64 bitRAXRBXRCXRDXRSPRBPRSIRDI
32 bitEAXEBXECXEDXESPEBPESIEDI
16 bitAXBXCXDXSPBPSIDI
8 bitAH/ALBH/BLCH/CLDH/DL



  • L :하위 바이트(Low byte),H :상위 바이트(High byte)
Assessmbly codeMachine code
mov eax,0x4B8 04 00 00 00
mov ax,0x466 B8 04 00
mov al,0x4B0 04

CodeEBX
shellcode 호출 이전-0xdeaddead
shellcodemov bl,0x40xdeadde04
Assessmbly codeMachine code
sub eax, eax29 C0
xor eax,eax31 C0
BITS 32			; nasm에게 32비트 코드임을 알린다

jmp short last	; 맨 끝으로 점프한다.
helloworld:
	; ssize_t write(int fd, const void *buf, size_t count);
	pop ecx 	; 리턴 주소를 팝해서 exc에 저장합니다.
	xor eax,eax	; eax 레지스터의 값을 0으로 초기화합니다.
	mov al, 4	; 시스템 콜 번호를 씁니다.
	xor ebx,ebx	; ebx 레지스터의 값을 0으로 초기화합니다.
	mov bl, 1	; STDOUT 파일 서술자
	xor edx,edx	; edx 레지스터의 값을 0으로 초기화합니다.
	mov dl, 15	; 문자열 길지
	int 0x80	; 시스템 콜: write(1,string, 14)

	; void _exit(int status);
	mov al,1	;exit 시스템 콜 번호
	xor ebx,ebx	;Status = 0
	int 0x80	;시스템 콜: exit(0)
 
last:
	call helloworld	; 널 바이트를 해결하기 위해 위로 돌아간다.
	db "Hello, world!", 0x0a, 0x0d	; 새 줄 바이트와 개행 문자 바이트
lazenca0x0@ubuntu:~/ASM$ nasm RemoveNullbyte.s 
lazenca0x0@ubuntu:~/ASM$ ndisasm RemoveNullbyte
00000000  EB15              jmp short 0x17
00000002  59                pop cx
00000003  31C0              xor ax,ax
00000005  B004              mov al,0x4
00000007  31DB              xor bx,bx
00000009  B301              mov bl,0x1
0000000B  31D2              xor dx,dx
0000000D  B20F              mov dl,0xf
0000000F  CD80              int 0x80
00000011  B001              mov al,0x1
00000013  31DB              xor bx,bx
00000015  CD80              int 0x80
00000017  E8E6FF            call word 0x0
0000001A  FF                db 0xff
0000001B  FF4865            dec word [bx+si+0x65]
0000001E  6C                insb
0000001F  6C                insb
00000020  6F                outsw
00000021  2C20              sub al,0x20
00000023  776F              ja 0x94
00000025  726C              jc 0x93
00000027  64210A            and [fs:bp+si],cx
0000002A  0D                db 0x0d
lazenca0x0@ubuntu:~/ASM$ 
#include<stdio.h>
#include<string.h>

unsigned char shellcode [] = "\xeb\x15\x59\x31\xc0\xb0\x04\x31\xdb\xb3\x01\x31\xd2\xb2\x0f\xcd\x80\xb0\x01\x31\xdb\xcd\x80\xe8\xe6\xff\xff\xffHello, world!\n\r";
unsigned char code[] = "";

void main()
{
	int len = strlen(shellcode);
	printf("Shellcode len : %d\n",len);
	strcpy(code,shellcode);
	(*(void(*)()) code)();
}
lazenca0x0@ubuntu:~/ASM$ gcc -o shellcode2 -fno-stack-protector -z execstack --no-pie -m32 shellcode2.c
lazenca0x0@ubuntu:~/ASM$ ./shellcode2 
Shellcode len : 43
Hello, world!
lazenca0x0@ubuntu:~/ASM$ 

Related site

Comments