欢迎来到 嗅灵易学

零基础也能上手的脚本技术课,一对一答疑带你入门

[原创]Linux 下内核shell实现

[原创]Linux 下内核shell实现

老早写的,现在估计没啥用了,有用得上的同学可以借鉴一下

/* 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");
}

上传的附件 shell.PNG

注意:上传附件及图片大小不得大于30M。

⚠️ 版权声明:
本博客所有内容(含教程、源码、工具)仅供个人技术学习与研究交流使用,严禁商用、倒卖、二次分发及非法用途。
未经作者书面授权,任何组织或个人不得转载、复制或用于其他平台,违者将追究相关责任。

0 0 0 举报
复制成功