首页
社区
课程
招聘
[原创]Linux 下内核shell实现
发表于: 2013-12-19 10:08 9090

[原创]Linux 下内核shell实现

2013-12-19 10:08
9090
老早写的,现在估计没啥用了,有用得上的同学可以借鉴一下


/* by boywhp@126.com */
#include <linux/module.h>
#include <linux/version.h>
#include <linux/cdev.h>
#include <linux/syscalls.h>
#include <linux/file.h>
#include <linux/fs.h>
#include <asm/uaccess.h>
#include <asm/unistd.h>
#include <linux/sched.h>
#include <linux/delay.h>
#include "../x_shell.h"

#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 33)
#include <linux/autoconf.h>
#else
#include <generated/autoconf.h>
#endif

#ifndef DBG
#define printk
#endif

#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,25)
#include <linux/fdtable.h>
#endif

#if (defined CONFIG_X86_32) || (defined CONFIG_X86)

#define _syscall2(type,name,type1,arg1,type2,arg2) \
        type xsys_##name(type1 arg1,type2 arg2) \
{ \
        long __res; \
        __asm__ volatile ("int $0x80" \
                        : "=a" (__res) \
                        : "0" (__NR_##name),"b" ((long)(arg1)),"c" ((long)(arg2)) \
                        ); \
        return (type) (__res); \
}

#define _syscall3(type,name,type1,arg1,type2,arg2,type3,arg3) \
        type xsys_##name(type1 arg1,type2 arg2,type3 arg3) \
{ \
        long __res; \
        __asm__ volatile ("int $0x80" \
                        : "=a" (__res) \
                        : "0" (__NR_##name),"b" ((long)(arg1)),"c" ((long)(arg2)),"d" ((long)(arg3)) \
                        : "memory"); \
        return (type) (__res); \
}

_syscall3(int, execve, const char*, filename, const char**, argv, const char**, envp);
_syscall3(long, mknod, const char*, filename, int, mode, unsigned, dev);
_syscall2(long, kill, int, pid, int, sig);

#else

#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,24))
#include <asm/msr.h>
#else
#include <asm-x86_64/msr.h>
#endif

int (*xsys_mknod)(const char* filename, int mode, unsigned dev);
int (*xsys_execve)(const char* filename, const char** argv, const char** envp);
long (*xsys_kill)(int pid, int sig);

static void init_x64_syscalls(void)
{
        int i;
        void* system_call_addr = 0;
        unsigned char* lpbin;

        rdmsrl(MSR_LSTAR, system_call_addr);
        printk(KERN_ALERT "X64 system_call_addr %x \n", system_call_addr);

        for (lpbin = (char*)system_call_addr, i = 0; i < 255; i++) {
                /* 
                 * 测试发现 call指令对应特征码为 ff 14 c5 
                 */
                if (lpbin[i] == 0xff && lpbin[i+1]== 0x14){
                        unsigned long long* sys_call_table = *(int*)(lpbin + i + 3);
                        printk(KERN_ALERT "sys_call_table %p\n", sys_call_table);

                        xsys_mknod = sys_call_table[__NR_mknod];
                        xsys_execve = sys_call_table[__NR_execve];
                        xsys_kill = sys_call_table[__NR_kill];

                        break;
                }
        }
} 

#endif

struct linux_shell_interface linux_shell_context = {0};
struct pid * old_pid = NULL;
struct task_struct * current_task = NULL;

typedef struct _execv_info
{
        char *path;
        char **argv;
        char **envp;
        struct file *std_handle;
        struct work_struct work;
        struct completion *complete;
} execv_info, *pexecv_info;

#define MAX_STDIN_BUF           2048 

/* 
 *  环形stdin缓冲区 
 *  rp == wp 说明缓冲区完全空闲,写缓冲区时注意-1 
 *  始终留 rp - 1 位置不写,以防止模糊现象
 */
static char stdin_buf[MAX_STDIN_BUF];
static char* stdin_rp = stdin_buf;
static char* stdin_wp = stdin_buf;

wait_queue_t in_wq, out_wq;

#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,36)
DECLARE_MUTEX(in_sem);
DECLARE_MUTEX(out_sem);
#else
DEFINE_SEMAPHORE(in_sem);
DEFINE_SEMAPHORE(out_sem);
#endif

/*
 * stdout环形缓冲区
 */
#define MAX_STDOUT_BUF          2048

static char stdout_buf[MAX_STDOUT_BUF];
static char* stdout_rp = stdout_buf;
static char* stdout_wp = stdout_buf;



static struct cdev shell_dev;
static dev_t sheLL_dev_no;

static void linux_shell_buf_reset(void)
{
        printk(KERN_ALERT "linux_shell_reset\n");
        stdin_rp = stdin_buf;
        stdin_wp = stdin_buf;

        stdout_rp = stdout_buf;
        stdout_wp = stdout_buf;
}

static int get_stdinbuf_free_space(void)
{
        if (stdin_rp == stdin_wp)
                return MAX_STDIN_BUF - 1;
        return ((stdin_rp + MAX_STDIN_BUF - stdin_wp) % MAX_STDIN_BUF) - 1;
}

static int get_stdoutbuf_free_space(void)
{
        if (stdout_rp == stdout_wp)
                return MAX_STDOUT_BUF - 1;
        return ((stdout_rp + MAX_STDOUT_BUF - stdout_wp) % MAX_STDOUT_BUF) - 1;
}

static int do_kernel_mknod(const char *filename, int mode, unsigned dev)
{
        long ret;
        mm_segment_t old_fs;
        //注意使用set_fs切换内核参数检查模式
        old_fs = get_fs();
        set_fs(KERNEL_DS);
        ret = xsys_mknod(filename, mode, dev);
        set_fs(old_fs);
        return ret;
}

static int shell_dev_release(struct inode *inode, struct file *filp)
{
        printk(KERN_ALERT "shell_dev_release...%p\n", current);

        if (current_task == current) {
                if (linux_shell_context.shell && linux_shell_context.shell->on_exec_done)
                        linux_shell_context.shell->on_exec_done(linux_shell_context.shell);
        }

        return 0;
}

static int shell_dev_open(struct inode *inode, struct file *filp)
{
        printk(KERN_ALERT "shell_dev_open...\n");
        return 0;
}

/*
 *	重定向输入:一次读取一个字节
 */
static ssize_t shell_dev_read(struct file *filp, char __user *buf, size_t size, loff_t *ppos)
{
        size_t count = size;

        //printk(KERN_ALERT "shell_dev_read entry...\n");
        /*
         * 首先检查缓冲数据是否准备好
         */
        while (stdin_rp == stdin_wp)
                msleep(10);

        if (current_task != current)
                return -1;

        if (down_interruptible(&in_sem))
                return -1; 

        if (stdin_wp > stdin_rp)
                count = min(count, (size_t)(stdin_wp - stdin_rp));
        else
                count = min(count, (size_t)(stdin_buf + MAX_STDIN_BUF - stdin_rp));

        /*
         * 测试发现STDIN重定向时,一次读取多字符好像不能执行。这里强制读取一个
         */
        count = 1;

        if (copy_to_user(buf, stdin_rp, count)){
                up(&in_sem);
                return -EFAULT;
        }

        stdin_rp += count;
        if (stdin_rp == stdin_buf + MAX_STDIN_BUF)
                stdin_rp = stdin_buf;

        //printk(KERN_ALERT "shell_dev_read: %d bytes %c %d pos\n", count, *buf, (int)*ppos);

        up(&in_sem);

        return count;
}

/*
 *	重定向输出
 */
static ssize_t shell_dev_write(struct file *filp, const char __user *buf, size_t size, loff_t *ppos)
{
        int count, remain = size;

        //printk(KERN_ALERT "linux_redirect_stdout entry...\n");

        while (get_stdoutbuf_free_space() == 0)
                msleep(10);

        if (down_interruptible(&out_sem))
                return 0;

        count = min(remain, get_stdoutbuf_free_space());
        //printk(KERN_ALERT "linux_redirect_stdout << %d bytes %c\n", count, buf[0]);
        if (stdout_wp >= stdout_rp)
                count = min(count, stdout_buf + MAX_STDOUT_BUF - stdout_wp);
        else
                count = min(count, stdout_rp - stdout_wp - 1);

        memcpy(stdout_wp, (char*)buf + size - remain, count);

        remain -= count;
        stdout_wp += count;
        if (stdout_wp == stdout_buf + MAX_STDOUT_BUF)
                stdout_wp = stdout_buf;

        up(&out_sem);

        //printk(KERN_ALERT "shell_dev_write: %s \n", buf);

        return count;
}

static const struct file_operations shell_fops =
{
        .owner = THIS_MODULE,
        //.llseek = shell_dev_llseek,
        //.ioctl = shell_dev_ioctl,
        .read = shell_dev_read,
        .write = shell_dev_write,
        .open = shell_dev_open,
        .release = shell_dev_release
};

/*
 *	设置当前进程文件据柄的重定向处理
 */
static int setup_file_handler(int fd, struct file *file)
{
        struct files_struct *f = current->files;

        if ((IS_ERR(file) || file == NULL))
                return -1;

        sys_close(fd);
        fd_install(fd, file);
        /*
         *	open_fds为当前进程已经打开的文件的描述符的集合
         *	close_on_exec为当进程调用exec()是,新进程需要关闭的文件描述符集合中所指的文件
         */
        spin_lock(&f->file_lock);
#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,25)
        {
                struct fdtable *fdt = files_fdtable(f);
                FD_SET(fd, fdt->open_fds);
                FD_CLR(fd, fdt->close_on_exec);
        }
#else
        FD_SET(fd, f->open_fds);
        FD_CLR(fd, f->close_on_exec);
#endif
        spin_unlock(&f->file_lock);
        return 0;
}

/*
 *	执行do_kernel_execve用户模式进程
 *	参考kmod.c ____call_usermodehelper
 */
static int _kernel_execv_thread(void *data)
{
        pexecv_info ts_info = (pexecv_info)data;

        if (ts_info == NULL)
                return -1;

        /* setup stdin stdout stderr */
        setup_file_handler(0, ts_info->std_handle);

        get_file(ts_info->std_handle);
        setup_file_handler(1, ts_info->std_handle);

        get_file(ts_info->std_handle);
        setup_file_handler(2, ts_info->std_handle);

        allow_signal(SIGKILL);	

        printk(KERN_ALERT "_kernel_execv_thread:ready for do_kernel_execve %s!\n", ts_info->path);

        xsys_execve(ts_info->path, (const char**)ts_info->argv, (const char**)ts_info->envp);

        /*正常情况,以下代码不会被执行*/
        printk(KERN_ALERT "_kernel_execv_thread:failed!\n");

        complete_and_exit(NULL, 0);        
        //do_exit(0);
}

/*
 *	execv工作队列执行函数、运行于特殊内核进程上下文
 */
static void _kernel_execv_work(struct work_struct *work)
{
        pexecv_info ts_info = container_of(work, struct _execv_info, work);

        if (old_pid){
                /* 首先kill掉先前创建的进程 */
#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,20))
                kill_pid(old_pid, SIGKILL, 1);
#else
                xsys_kill(old_pid, SIGKILL);
#endif
        }

        ts_info->std_handle = filp_open("/dev/nsdev", O_RDWR , 0);
        if (IS_ERR(ts_info->std_handle)){
                printk(KERN_ALERT "filp_open /dev/nsdev failed!\n");
        }else{
                pid_t id;
                printk(KERN_ALERT "_kernel_execv_work:ready to _kernel_execv_thread %s!\n", ts_info->path);

                linux_shell_buf_reset();

                id = kernel_thread(_kernel_execv_thread, ts_info, CLONE_VFORK | SIGCHLD);

                /*
                 * 以下函数需要针对具体版本编译 
                 * pid_task 从2.6.25开始导出
                 * find_get_pid 从2.6.20开始导出
                 * find_task_by_pid_type(PIDTYPE_PID, id) 2.6.23以前版本
                 */
#if (LINUX_VERSION_CODE <= KERNEL_VERSION(2,6,23))
                current_task = find_task_by_pid_type(PIDTYPE_PID, id);
#else
                old_pid = find_get_pid(id);
                current_task = pid_task(old_pid, PIDTYPE_PID);
#endif
                printk(KERN_ALERT "current run:%p\n", current_task);

                //如何等待执行完毕?
                //filp_close(file, current->files);
        }

        complete(ts_info->complete);
}

int linux_kernel_execv(char *path, char **argv, char **envp)
{
        execv_info ts_info;
        struct workqueue_struct *kexecv_wq = NULL;
        DECLARE_COMPLETION(done);

        printk(KERN_ALERT "linux_kernel_execv start...!\n"); 

        kexecv_wq = create_singlethread_workqueue("kthread");
        if (!kexecv_wq){
                printk(KERN_ALERT "linux_kernel_execv:create workqueue fail!\n");
                return -1;
        }

        /* 初始化进程参数 */
        ts_info.path = path;
        ts_info.argv = argv;
        ts_info.envp = envp;
        ts_info.complete = &done;

#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,20))
        INIT_WORK(&ts_info.work, _kernel_execv_work);
#else
        INIT_WORK(&ts_info.work, _kernel_execv_work, &ts_info.work);
#endif

        printk(KERN_ALERT "linux_kernel_execv queue_work...!\n"); 

        queue_work(kexecv_wq, &ts_info.work);
        wait_for_completion(&done);

        destroy_workqueue(kexecv_wq);

        printk(KERN_ALERT "call_kernel_execv:completed!\n");

        return 0;
}

/*
 * 重定向stdin数据写入设备缓冲区
 */

int linux_redirect_stdin(void *context, void* buf, int size)
{
        int count, remain = size;

        while (remain > 0){
                if (get_stdinbuf_free_space() == 0){
                        msleep(10);
                        continue;
                }

                if (down_interruptible(&in_sem))
                        return -1; 

                count = min(remain, get_stdinbuf_free_space());
                //printk(KERN_ALERT "linux_redirect_stdin >> %d bytes\n", count);
                if (stdin_wp >= stdin_rp)
                        count = min(count, stdin_buf + MAX_STDIN_BUF - stdin_wp);
                else
                        count = min(count, stdin_rp - stdin_wp - 1);

                memcpy(stdin_wp, (char*)buf + size - remain, count);

                remain -= count;
                stdin_wp += count;
                if (stdin_wp == stdin_buf + MAX_STDIN_BUF)
                        stdin_wp = stdin_buf;

                up(&in_sem);
        }

        return 0;
}

/*
 * 读取设备stdout缓冲区并重定向
 */
int linux_redirect_stdout(void *context, void* buf, int size)
{
        size_t count = size;
        if (stdout_rp == stdout_wp)
                return 0;

        if (down_interruptible(&out_sem))
                return -1; 

        if (stdout_wp > stdout_rp)
                count = min(count, (size_t)(stdout_wp - stdout_rp));
        else
                count = min(count, (size_t)(stdout_buf + MAX_STDOUT_BUF - stdout_rp));

        memcpy(buf, stdout_rp, count);

        stdout_rp += count;
        if (stdout_rp == stdout_buf + MAX_STDOUT_BUF)
                stdout_rp = stdout_buf;

        //printk(KERN_ALERT ">>%c\n", *(char*)buf);
        up(&out_sem);

        return count;
}

int init_linux_shell(void)
{
        int result;

#if (!defined CONFIG_X86_32) && (!defined CONFIG_X86)
        init_x64_syscalls();
#endif

        /*
         * 创建一个字符设备/dev/nsdev,以便在shell重定向时候使用
         */
        result = alloc_chrdev_region(&sheLL_dev_no, 0, 1, "nsdev");
        if (result >= 0){
                memset(&shell_dev, 0, sizeof(shell_dev));

                shell_dev.owner = THIS_MODULE;
                shell_dev.ops = &shell_fops;
                cdev_init(&shell_dev, &shell_fops);

                result = cdev_add(&shell_dev, sheLL_dev_no, 1);
                printk(KERN_ALERT "cdev_add ret %d! dev %08x\n", result, sheLL_dev_no);

                if (result == 0){
                        result = do_kernel_mknod("/dev/nsdev", S_IFCHR | 0666, new_encode_dev(sheLL_dev_no));
                        printk(KERN_ALERT "create shell device:/dev/nsdev %d\n", result);
                }
        }

        /*
         * 初始化shell接口
         */
        linux_shell_context.read_stdout = linux_redirect_stdout;
        linux_shell_context.write_stdin = linux_redirect_stdin;
        linux_shell_context.exec = linux_kernel_execv;

        return 0;
}

void cleanup_shell_test(void)
{
        cdev_del(&shell_dev);
        unregister_chrdev_region(sheLL_dev_no, 1);

        printk(KERN_ALERT "cleanup_shell_test ok!\n");
}

[课程]Android-CTF解题方法汇总!

上传的附件:
收藏
免费 5
支持
分享
最新回复 (9)
雪    币: 1411
活跃值: (692)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
2
沙发~~~
2013-12-19 10:30
0
雪    币: 4
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
3
好东西不错
2013-12-19 10:56
0
雪    币: 185
活跃值: (25)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
4
内核shell是怎么一个概念? 以模块方式 运行于内核态,所以可以执行任意命令?
2013-12-19 11:17
0
雪    币: 7
活跃值: (56)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
5
同问,内核shell是个什么概念。。。
2013-12-19 12:08
0
雪    币: 350
活跃值: (27)
能力值: ( LV4,RANK:50 )
在线值:
发帖
回帖
粉丝
6
请问是哪个CVE?没有漏洞描述,没有适用条件,能难最终成功root的。
2013-12-19 13:00
0
雪    币: 19
活跃值: (25)
能力值: ( LV3,RANK:20 )
在线值:
发帖
回帖
粉丝
7
一直认为楼主应该是一个吊炸天的人物,今天一看帖子果然印证了我的猜想,我太TM机智了。
2013-12-19 13:08
0
雪    币: 12
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
8
Mark
2014-9-25 14:00
0
雪    币: 3
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
9
技术贴 支持1
2014-12-2 17:16
0
雪    币: 101
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
10
LZ使用的vim和什么插件搭配的linux编程环境啊
2014-12-2 17:39
0
游客
登录 | 注册 方可回帖
返回
//