老早写的,现在估计没啥用了,有用得上的同学可以借鉴一下
/* 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解题方法汇总!