拷贝机器码的一种inline hook方式

   将需要inline hook的函数机器码先拷贝到一块内存中, 然后动态修正这块内存的call,jmp对应的offset(相对值), 然后给原始函数做个inline hook, 跳到新函数去执行, 新函数做一些过滤判断后, 调用新分配的内存保存的机器码, 想当于调用原始函数。这种技术在实战中没什么用, 拓展下思路还是不错的。

#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/init.h>
#include <linux/version.h>
#include <linux/string.h>
#include <linux/list.h>
#include <asm/processor.h>
#include <asm/uaccess.h>
#include <asm/unistd.h>
#include <asm/cacheflush.h>
#include <linux/slab.h>

MODULE_LICENSE("GPL");
MODULE_AUTHOR("wzt");

#define OPCODE_LEN      38

unsigned char 	orig_opcode[5];
void (*new_fn)(char *, int n);		/// point to opcodes in heap space.
unsigned char 	*s;


void test(char *str, int n)
{
	int i = 0;

	for (;i < n ; i++) {
		printk("%s\n", str);
	}
}

void test1(char *str, int n)
{
	int i;
	if (n > 3) {
		test("aa", 1);
	}
	test(str, n);
}


void new_test1(char *str, int n)
{
	printk("in new_test1.\n");
	new_fn(str,n);			/// heap space ,function test1.
}

void test2(char *str, int n)
{
	test("aaa", 2);
}

void copy_opcode(void)
{
	unsigned char *p;
	unsigned char buf[4] = "\x00\x00\x00\x00";
	unsigned int orig_func_addr = 0;
	int orig_offset, new_offset;
	int i;

	printk("orig func addr: 0x%x\n", test);

	p = (unsigned char *)test1;		/// print the opcode of function test1.
	for (i = 0; i < OPCODE_LEN; i++) {
		printk("0x%02x", p[i]);
	}

	s = kmalloc(100, GFP_KERNEL);		/// s point to a heap space.		
	memset(s, 0x0, 100);
	printk("\ns: 0x%x\n", s);

	for (i = 0; i < OPCODE_LEN; i++) {		
		s[i] = p[i];
		if (p[i] == 0xe8 || p[i] == 0xe9) {		/// opcode of call jmp instruction.
			printk("\nfound 0x%x: 0x%x\n", p[i], &p[i]);
			buf[0] = p[i + 1];
			buf[1] = p[i + 2];
			buf[2] = p[i + 3];
			buf[3] = p[i + 4];

			orig_offset = *(unsigned int *)buf;	/// relative address 
			printk("orig offset: 0x%x\n", orig_offset);

			orig_func_addr = orig_offset + (unsigned int)&p[i] + 5;	///absolute address
			printk("orig func addr: 0x%x\n", orig_func_addr);

			new_offset = orig_func_addr - (unsigned int)&s[i] - 5;	
			printk("new_offset: 0x%x\n", new_offset);

			i++;
			s[i++] = (new_offset & 0x000000ff);		///because some calls use relative  
			s[i++] = (new_offset & 0x0000ff00) >> 8;	///address.But the call instruction's 
			s[i++] = (new_offset & 0x00ff0000) >> 16;	///absolute address has been changed
			s[i++] = (new_offset & 0xff000000) >> 24;	///so the relative address should be
		}						///changed too.(very important!)
	}

	printk("\n");
	for (i = 0; i < OPCODE_LEN; i++) {
		printk("0x%02x", s[i]);
	}
	printk("\n");
	new_fn = s;
}



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

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

	offset = (unsigned int)new_func - (unsigned int)old_func - 5;
	*p++ = 0xe9;
	//memcpy(buf + 1, &p, 4);
	*p++ = (offset & 0x000000ff);
	*p++ = (offset & 0x0000ff00) >> 8;
	*p++ = (offset & 0x00ff0000) >> 16;
	*p++ = (offset & 0xff000000) >> 24;
	*p++ = 0x90;

	return 0;
}

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);

	return 0;
}

static int opcode_test_init(void)
{
	copy_opcode();
	inline_hook_func(test1, new_test1, orig_opcode);
	test1("hello,world", 3);		///when to here ,call test1 equals new_test1.


	return 0;
}

static void opcode_test_exit(void)
{
	printk("unload opcode test module.\n");
}

module_init(opcode_test_init);
module_exit(opcode_test_exit);


上面程序首先调用copy_opcode,将函数test1的机器码拷贝到s指向的堆空间,并修改相对条转指令。

然后在用inline_hook_func 把test1的开始opcode改变为跳向 new_test1的机器码。

当调用test1的时候调用 new_fn ,也就是test1,来回折腾还是那个函数!!!!


源代码意图如上所示,但是根据以前的经验 内核模块中的代码是不可以修改的(内核中的代码才可以修改)。内核模块中代码 即使等完全把内核模块加载上之后再修改也不行!!


因此上面程序中 inline_hook_func 执行到  *p++ = 0xe9;  就会导致内核崩溃。


原文链接:

http://hi.baidu.com/wzt85/item/0c79f4e110ae6ff22a09a4d3



参考内核调试文章 http://blog.csdn.net/xsckernel/article/details/8159560  对 CONFIG_DEBUG_SET_MODULE_RONX 编译选项的说明

将这个编译选项去掉后可以执行过函数  inline_hook_func(修改可加载模块的代码),但是到 new_func这一行

则执行错误,内核数据不可已被执行(不管这些数据是在全局区还是在堆区)




则执行错误,内核数据不可已被执行(不管这些数据是在全局区还是在堆区)则执行错误,内核数据不可已被执行(不管这些数据是在全局区还是在堆区)则执行错误,内核数据不可已被执行(不管这些数据是在全局区还是在堆区)









  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值