#!/bin/sh
# ragg2-cc : a shellcode compiler -- pancake - 2011-2013
#
# Supported operating systems:
#  - GNU/Linux
#  - OSX
#  - BSD
# Supported compilers
#  - gcc
#  - clang
# TODO
#  remove temporary files
#  add support for arm
#  add support for nested shellcodes

if [ -z "${CC}" ]; then
	for a in llvm-gcc clang gcc ; do
		$a --version >/dev/null 2>&1
		if [ $? = 0 ]; then
			CC="$a"
			break
		fi
	done
	if [ -z "${CC}" ]; then
		echo "Cannot find CC" > /dev/stderr
		exit 1
	fi
fi
A=x86
SFLIBPATH=`ragg2 -v | cut -d ' ' -f 3-`
if [ ! -d "${SFLIBPATH}" ]; then
	echo "Cannot find ${SFLIBPATH}"
	exit 1
fi
case "`uname -m`" in
"x86_64")
	B=64
	;;
*)
	B=32
	;;
esac


dohelp() {
	echo "Usage: ragg2-cc [-dabkoscxv] [file.c]"
	echo "  -d       enable debug mode"
	echo "  -a x86   set arch (x86, arm)"
	echo "  -b 32    bits (32, 64)"
	echo "  -k linux set kernel (darwin, linux)"
	echo "  -o file  set output file"
	echo "  -s       generate assembly"
	echo "  -c       generate compiled shellcode"
	echo "  -x       show hexpair bytes"
	echo "  -v       show version"
}

case "`uname`" in
Darwin)
	K=darwin
	;;
*)
	K=linux
	;;
esac
X=0
C=""
D=""
O=""
F=""
ASM=0
while : ; do
	[ -z "$1" ] && break
	F=$1
	case "$F" in
	-a)
		shift
		A=$1
		[ -z "$A" ] && { echo "Missing argument for -a" ; exit 1; }
		;;
	-b)
		shift
		B=$1
		[ -z "$B" ] && { echo "Missing argument for -b" ; exit 1; }
		;;
	-k)
		shift
		K=$1
		[ -z "$K" ] && { echo "Missing argument for -k" ; exit 1; }
		;;
	-x)
		X=1
		;;
	-c)
		C=1
		;;
	-d)
		D=1
		;;
	-s)
		ASM=1
		;;
	-o)
		shift
		O=$1
		if [ -z "$O" ]; then
			echo "Missing argument for -o"
			exit 1
		fi
		;;
	-h)
		dohelp
		exit 0
		;;
	-v)
		ragg2 -v | sed -e 's,2,2-cc,'
		exit 0
		;;
	esac
	shift
done
if [ -z "$F" ]; then
	dohelp
	exit 1
fi

if [ "`uname`" = Darwin ]; then
	OBJCOPY=gobjcopy
	case "$B" in
	32)
		CFLAGS="-arch i386 "
		LDFLAGS="-arch i386 -shared -c"
		ARCH=darwin-x86-32
		;;
	64)
		CFLAGS="-arch x86_64"
		LDFLAGS="-arch x86_64 -shared -c"
		ARCH=darwin-x86-64
		;;
	esac
	SHDR="
.text
jmp _main"
else
	OBJCOPY=objcopy
	CFLAGS="-fPIC -fPIE -pie"
	SHDR="
.section .text
.globl  main
.type   main, @function
jmp main
"
	case "$B" in
	64)
		ARCH=linux-x86-64
		;;
	*) 
		ARCH=linux-x86-32
		;;
	esac
fi

[ "$A$K" ] && ARCH="$K-$A-$B"

OPT=-Os

#/usr/include/libr/sflib/
#CFLAGS="-shared -fPIC -fPIE -pie "
#CFLAGS="${CFLAGS} -shared -fPIC -fPIE -pie "
CFLAGS="${CFLAGS} -nostdinc -include ${SFLIBPATH}/${ARCH}/sflib.h"
CFLAGS="${CFLAGS} -fomit-frame-pointer -finline-functions -fno-zero-initialized-in-bss"
LDFLAGS="${LDFLAGS} -nostdlib"

case "$K" in
darwin)
	#TEXT="__TEXT.__text"
	TEXT="0.__text"
	;;
*|linux)
	TEXT=".text"
	;;
esac

rmtemps() {
	[ -z "$D" ] && rm -f $F.tmp $F.text $F.s $F.o
}

fail() {
	rmtemps
	exit 1
}

if [ "$D" ]; then
	echo "==> Compile"
	echo "${CC} ${CFLAGS} -o $F.tmp -S ${OPT} $F"
fi
rm -f $F.bin
${CC} ${CFLAGS} -o $F.tmp -S ${OPT} $F || fail
echo "${SHDR}" > $F.s
cat $F.tmp \
	| sed -e s,rdata,text, -e s,rodata,text, -e 's,get_pc_thunk.bx,__getesp__,g' \
	| grep -v .cstring | grep -v size | grep -v ___main | grep -v section \
	| grep -v __alloca | grep -v zero | grep -v cfi >> $F.s
rm -f $F.tmp
if [ $ASM = 1 ]; then
	echo $F.s
	exit 0
fi

if [ "$D" ]; then
	echo "==> Assemble"
	echo "${CC} -c ${LDFLAGS} -Os -o $F.o $F.s"
fi
${CC} ${LDFLAGS} ${OPT} -o $F.o $F.s || fail

if [ "$D" ]; then
	echo "==> Link"
	#echo "${OBJCOPY} -j .text -O binary $F.o $.text"
	echo "rabin2 -o '$F.text' -O d/S/${TEXT} $F.o"
fi
rabin2 -o "$F.text" -O d/S/${TEXT} $F.o
if [ "`du $F.text|awk '{print $1}'`" = 0 ]; then
	# use objcopy as falback for rabin2
	${OBJCOPY} -j .text -O binary $F.o $F.text || fail
fi
if [ "$C" = 1 ]; then
	if [ "$O" ]; then
		mv $F.text $O
	else
		O="$F.text"
	fi
	echo "$O"
	exit 0
fi

[ "$X" = 1 ] && exec rax2 -S < $F.text

if [ "$D" ]; then
#	hexdump -C $F.text
	rax2 -S - < $F.text
	ls -l $F.text
fi
[ -z "$O" ] && O=$F.bin
ragg2 -C $F.text -F -o $O || fail
echo $O
rmtemps
exit 0
