4a9368455e
o initial work by brad@, polishing by me o idea for MP detection shellcode from drahn@ for those with MP systems, pay attention to the MESSAGE shellcode example, and make sure to modify any existing shellcode you may have in /etc/rc.securelevel lest you try to load a UP lkm on a MP kernel and have unpleasant consequences.
367 lines
7.4 KiB
C
367 lines
7.4 KiB
C
/*
|
|
* Copyright (c) 2007 Enache Adrian <3n4ch3@gmail.com>
|
|
*
|
|
* Permission to use, copy, modify, and distribute this software for any
|
|
* purpose with or without fee is hereby granted, provided that the above
|
|
* copyright notice and this permission notice appear in all copies.
|
|
*
|
|
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
|
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
|
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
|
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
|
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
|
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
|
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
|
*/
|
|
|
|
#include <sys/param.h>
|
|
#include <sys/systm.h>
|
|
#include <sys/conf.h>
|
|
#include <sys/exec.h>
|
|
#include <sys/fcntl.h>
|
|
#include <sys/ioccom.h>
|
|
#include <sys/lkm.h>
|
|
#include <sys/malloc.h>
|
|
#include <sys/signalvar.h>
|
|
#include <sys/namei.h>
|
|
#include <sys/vnode.h>
|
|
|
|
#include "kqemu-kernel.h"
|
|
|
|
#define KQEMU_DEV "/dev/kqemu"
|
|
#define KQEMU_MODE 0660
|
|
#define KQEMU_GID 597
|
|
|
|
#ifdef __amd64__
|
|
#define want_resched (curcpu()->ci_want_resched)
|
|
#define vtophys(VA) ((*vtopte(VA) & PG_FRAME) | ((unsigned)(VA) & ~PG_FRAME))
|
|
#endif
|
|
|
|
struct kqemu_page* CDECL
|
|
kqemu_alloc_zeroed_page(unsigned long *ppage_index)
|
|
{
|
|
vaddr_t va;
|
|
paddr_t pa;
|
|
|
|
if (!(va = uvm_km_zalloc(kernel_map, PAGE_SIZE)))
|
|
return (0);
|
|
|
|
pmap_extract(pmap_kernel(), va, &pa);
|
|
*ppage_index = pa >> PAGE_SHIFT;
|
|
|
|
return (struct kqemu_page*)va;
|
|
}
|
|
|
|
void CDECL
|
|
kqemu_free_page(struct kqemu_page *page)
|
|
{
|
|
uvm_km_free(kernel_map, (vaddr_t)page, PAGE_SIZE);
|
|
}
|
|
|
|
void* CDECL
|
|
kqemu_io_map(unsigned long page_index, unsigned int size)
|
|
{
|
|
return (0); /*XXX*/
|
|
}
|
|
|
|
void CDECL
|
|
kqemu_io_unmap(void *ptr, unsigned int size)
|
|
{
|
|
/*XXX*/
|
|
}
|
|
|
|
struct kqemu_user_page *CDECL
|
|
kqemu_lock_user_page(unsigned long *ppage_index, unsigned long user_addr)
|
|
{
|
|
vaddr_t va = (vaddr_t)user_addr;
|
|
vm_map_t map = &curproc->p_vmspace->vm_map;
|
|
paddr_t pa;
|
|
|
|
if (uvm_map_pageable(map, va, va + PAGE_SIZE, FALSE, 0))
|
|
return (0);
|
|
|
|
pmap_extract(vm_map_pmap(map), va, &pa);
|
|
*ppage_index = pa >> PAGE_SHIFT;
|
|
|
|
return (struct kqemu_user_page*)va;
|
|
}
|
|
|
|
void CDECL
|
|
kqemu_log(const char *fmt, ...)
|
|
{
|
|
va_list va;
|
|
|
|
printf("kqemu: ");
|
|
va_start(va, fmt); vprintf(fmt, va); va_end(va);
|
|
}
|
|
|
|
void* CDECL
|
|
kqemu_page_kaddr(struct kqemu_page *page)
|
|
{
|
|
return page; /*XXX*/
|
|
}
|
|
|
|
int CDECL
|
|
kqemu_schedule(void)
|
|
{
|
|
if (want_resched)
|
|
yield();
|
|
|
|
return CURSIG(curproc);
|
|
}
|
|
|
|
void CDECL
|
|
kqemu_unlock_user_page(struct kqemu_user_page *page)
|
|
{
|
|
vaddr_t va = (vaddr_t)page;
|
|
vm_map_t map = &curproc->p_vmspace->vm_map;
|
|
|
|
if (uvm_map_pageable(map, va, va + PAGE_SIZE, TRUE, 0))
|
|
printf("kqemu: failed to unwire page at 0x%08lx\n", va);
|
|
}
|
|
|
|
void CDECL kqemu_vfree(void *ptr)
|
|
{
|
|
free(ptr, M_TEMP);
|
|
}
|
|
|
|
void* CDECL
|
|
kqemu_vmalloc(unsigned int size)
|
|
{
|
|
return malloc(size, M_TEMP, M_WAITOK);
|
|
}
|
|
|
|
unsigned long CDECL
|
|
kqemu_vmalloc_to_phys(const void *vaddr)
|
|
{
|
|
paddr_t pa = vtophys((vaddr_t)vaddr);
|
|
|
|
if (!pa)
|
|
return (-1);
|
|
|
|
return (pa >> PAGE_SHIFT);
|
|
}
|
|
|
|
/* /dev/kqemu device operations */
|
|
|
|
#define QEMU_MAGIC 0x554d4551
|
|
struct kqemu_instance {
|
|
int magic;
|
|
struct kqemu_state *state;
|
|
};
|
|
|
|
static struct kqemu_global_state *kqemu_gs;
|
|
|
|
static int
|
|
kqemuopen(dev_t dev, int flag, int devtype, struct proc* p)
|
|
{
|
|
struct kqemu_instance *ks;
|
|
|
|
if ((flag & (FREAD|FWRITE)) == FREAD)
|
|
return (EPERM);
|
|
|
|
if (p->p_emuldata)
|
|
return (EBUSY);
|
|
|
|
if (!(ks = malloc(sizeof *ks, M_EMULDATA, M_WAITOK)))
|
|
return (ENOMEM);
|
|
|
|
ks->magic = QEMU_MAGIC;
|
|
ks->state = 0;
|
|
p->p_emuldata = ks;
|
|
|
|
return (0);
|
|
}
|
|
|
|
static int
|
|
kqemuclose(dev_t dev, int flag, int devtype, struct proc* p)
|
|
{
|
|
struct kqemu_instance *ks = p->p_emuldata;
|
|
|
|
if (!ks || ks->magic != QEMU_MAGIC) {
|
|
printf("kqemu: the kqemu instance was lost\n");
|
|
return (EIO);
|
|
}
|
|
|
|
if (ks->state)
|
|
kqemu_delete(ks->state);
|
|
free(ks, M_EMULDATA);
|
|
p->p_emuldata = 0;
|
|
|
|
return (0);
|
|
}
|
|
|
|
static int
|
|
kqemuioctl(dev_t dev, u_long cmd, caddr_t data, int flags, struct proc *p)
|
|
{
|
|
struct kqemu_cpu_state *ctx;
|
|
struct kqemu_instance *ks = p->p_emuldata;
|
|
|
|
if (!ks || ks->magic != QEMU_MAGIC) {
|
|
printf("kqemu: the kqemu instance was lost\n");
|
|
return (EIO);
|
|
}
|
|
|
|
switch(cmd){
|
|
case KQEMU_INIT:
|
|
if (ks->state)
|
|
return (EIO);
|
|
ks->state = kqemu_init((struct kqemu_init*)data, kqemu_gs);
|
|
if (!ks->state)
|
|
return (ENOMEM);
|
|
break;
|
|
case KQEMU_EXEC:
|
|
if (!ks->state)
|
|
return (EIO);
|
|
ctx = kqemu_get_cpu_state(ks->state);
|
|
*ctx = *(struct kqemu_cpu_state*)data;
|
|
KERNEL_PROC_UNLOCK(p);
|
|
kqemu_exec(ks->state);
|
|
KERNEL_PROC_LOCK(p);
|
|
*(struct kqemu_cpu_state*)data = *ctx;
|
|
break;
|
|
case KQEMU_GET_VERSION:
|
|
*(int*)data = KQEMU_VERSION;
|
|
break;
|
|
default:
|
|
return (ENOTTY);
|
|
}
|
|
|
|
return (0);
|
|
}
|
|
|
|
static struct cdevsw kqemu_cdevsw = {
|
|
kqemuopen,
|
|
kqemuclose,
|
|
(dev_type_read((*)))enodev,
|
|
(dev_type_write((*)))enodev,
|
|
kqemuioctl,
|
|
(dev_type_stop((*)))enodev,
|
|
0,
|
|
seltrue,
|
|
(dev_type_mmap((*)))enodev,
|
|
};
|
|
|
|
#ifdef MULTIPROCESSOR
|
|
#define _KQ_NAME "kqemu_mp"
|
|
#else
|
|
#define _KQ_NAME "kqemu"
|
|
#endif
|
|
|
|
MOD_DEV(_KQ_NAME, LM_DT_CHAR, -1, &kqemu_cdevsw);
|
|
int lkmexists(struct lkm_table *);
|
|
|
|
static int
|
|
kqemu_mknod(const char *path, dev_t dev)
|
|
{
|
|
struct nameidata nd;
|
|
struct vattr vattr;
|
|
int error;
|
|
|
|
NDINIT(&nd, CREATE, LOCKPARENT, UIO_SYSSPACE, path, curproc);
|
|
|
|
if ((error = namei(&nd)) != 0)
|
|
return (error);
|
|
|
|
if (nd.ni_vp != NULL) {
|
|
VOP_ABORTOP(nd.ni_dvp, &nd.ni_cnd);
|
|
if (nd.ni_dvp == nd.ni_vp)
|
|
vrele(nd.ni_dvp);
|
|
else
|
|
vput(nd.ni_dvp);
|
|
vrele(nd.ni_vp);
|
|
return (EEXIST);
|
|
}
|
|
|
|
VATTR_NULL(&vattr);
|
|
vattr.va_rdev = dev;
|
|
vattr.va_mode = KQEMU_MODE;
|
|
vattr.va_type = VCHR;
|
|
|
|
return (VOP_MKNOD(nd.ni_dvp, &nd.ni_vp, &nd.ni_cnd, &vattr));
|
|
}
|
|
|
|
static int
|
|
kqemu_chgrp(const char *path)
|
|
{
|
|
struct nameidata nd;
|
|
struct vattr vattr;
|
|
int error;
|
|
|
|
NDINIT(&nd, LOOKUP, FOLLOW, UIO_SYSSPACE, path, curproc);
|
|
|
|
if ((error = namei(&nd)) != 0)
|
|
return (error);
|
|
|
|
vn_lock(nd.ni_vp, LK_EXCLUSIVE | LK_RETRY, curproc);
|
|
VATTR_NULL(&vattr);
|
|
vattr.va_gid = KQEMU_GID;
|
|
|
|
error = VOP_SETATTR(nd.ni_vp, &vattr, curproc->p_ucred, curproc);
|
|
vput(nd.ni_vp);
|
|
|
|
return (error);
|
|
}
|
|
|
|
static int
|
|
kqemu_unlink(const char *path)
|
|
{
|
|
struct nameidata nd;
|
|
int error;
|
|
|
|
NDINIT(&nd, DELETE, LOCKPARENT | LOCKLEAF, UIO_SYSSPACE,
|
|
path, curproc);
|
|
|
|
if ((error = namei(&nd)) != 0)
|
|
return (error);
|
|
|
|
uvm_vnp_uncache(nd.ni_vp);
|
|
|
|
return (VOP_REMOVE(nd.ni_dvp, nd.ni_vp, &nd.ni_cnd));
|
|
}
|
|
|
|
int
|
|
#ifdef MULTIPROCESSOR
|
|
kqemu_mp_lkmentry(struct lkm_table *lkmtp, int cmd, int ver)
|
|
#else
|
|
kqemu_lkmentry(struct lkm_table *lkmtp, int cmd, int ver)
|
|
#endif
|
|
{
|
|
int error = 0;
|
|
int max_locked_pages = physmem / 2;
|
|
|
|
if (ver != LKM_VERSION)
|
|
return (EINVAL);
|
|
|
|
if (cmd == LKM_E_LOAD)
|
|
lkmtp->private.lkm_any = (struct lkm_any *)&_module;
|
|
|
|
if ((error = lkmdispatch(lkmtp, cmd)) == 0) {
|
|
switch(cmd){
|
|
case LKM_E_LOAD:
|
|
if (!(kqemu_gs = kqemu_global_init(max_locked_pages)))
|
|
return (ENOMEM);
|
|
printf("kqemu: kqemu version 0x%08x loaded,"
|
|
" max locked mem=%dkB\n",
|
|
KQEMU_VERSION, max_locked_pages << (PAGE_SHIFT - 10));
|
|
kqemu_unlink(KQEMU_DEV);
|
|
kqemu_mknod(KQEMU_DEV, makedev(_module.lkm_offset, 0));
|
|
kqemu_chgrp(KQEMU_DEV);
|
|
break;
|
|
case LKM_E_UNLOAD:
|
|
/* kqemu_global_delete() should return an
|
|
* error if kqemu_gs->nb_kqemu_states > 0.
|
|
* then this could be changed to:
|
|
*
|
|
* if (kqemu_global_delete(kqemu_gs))
|
|
* return (EBUSY);
|
|
*/
|
|
kqemu_global_delete(kqemu_gs);
|
|
kqemu_unlink(KQEMU_DEV);
|
|
break;
|
|
}
|
|
}
|
|
|
|
return (error);
|
|
}
|