运维最全Linux 内核模块API之__module_address_linux内核增加api,2024年最新Linux运维开发必会技术

最后的话

最近很多小伙伴找我要Linux学习资料,于是我翻箱倒柜,整理了一些优质资源,涵盖视频、电子书、PPT等共享给大家!

资料预览

给大家整理的视频资料:

给大家整理的电子书资料:

如果本文对你有帮助,欢迎点赞、收藏、转发给朋友,让我有持续创作的动力!

网上学习资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。

需要这份系统化的资料的朋友,可以点击这里获取!

一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!

// linux-3.10/include/linux/module.h

static inline int within_module_core(unsigned long addr, const struct module *mod)
{
return (unsigned long)mod->module_core <= addr &&
addr < (unsigned long)mod->module_core + mod->core_size;
}

static inline int within_module_init(unsigned long addr, const struct module *mod)
{
return (unsigned long)mod->module_init <= addr &&
addr < (unsigned long)mod->module_init + mod->init_size;
}


within\_module\_core判断地址addr是否在模块的module\_core地址区间。  
 within\_module\_init判断地址addr是否在模块的module\_init 地址区间。



// linux-3.10/include/linux/rculist.h

/**
* list_for_each_entry_rcu - iterate over rcu list of given type
* @pos: the type * to use as a loop cursor.
* @head: the head for your list.
* @member: the name of the list_struct within the struct.
*
* This list-traversal primitive may safely run concurrently with
* the _rcu list-mutation primitives such as list_add_rcu()
* as long as the traversal is guarded by rcu_read_lock().
*/
#define list_for_each_entry_rcu(pos, head, member)
for (pos = list_entry_rcu((head)->next, typeof(*pos), member);
&pos->member != (head);
pos = list_entry_rcu(pos->member.next, typeof(*pos), member))


该函数逻辑非常简单,遍历内核模块链表,如果给定的地址在一个内核模块的module\_init和module\_core的范围之间,则找到所在地址属于该模块。


为了防止模块被释放,使用该函数时必须禁止内核抢占,或者加锁访问。注意禁止内核抢占或加锁是保护的struct module结构体数据。


### 1.2 is\_module\_address


is\_module\_address是对\_\_module\_address函数的一个简单封装,用来判断一个地址是否在模块内。宏preempt\_disable( )和preempt\_enable( )分别用来实现禁止内核抢占和允许内核抢占。



/*
* is_module_address - is this address inside a module?
* @addr: the address to check.
*
* See is_module_text_address() if you simply want to see if the address
* is code (not data).
*/
bool is_module_address(unsigned long addr)
{
bool ret;

preempt\_disable();
ret = \_\_module\_address(addr) != NULL;
preempt\_enable();

return ret;

}


## 二、使用例程



include <linux/init.h>

include <linux/kernel.h>

include <linux/module.h>

int test_module(void)
{
return 0;
}

//内核模块初始化函数
static int __init lkm_init(void)
{
struct module *mod;
unsigned long addr = (unsigned long)test_module;
preempt_disable();
mod = __module_address(addr);
preempt_enable();
if(mod){
printk(“module name = %s\n”, mod->name);
printk(“module core_size = %d\n”, mod->core_size);
printk(“module refcount = %ld\n”, module_refcount(mod));
}
return 0;
}

//内核模块退出函数
static void __exit lkm_exit(void)
{

}

module_init(lkm_init);
module_exit(lkm_exit);

MODULE_LICENSE(“GPL”);


test\_module函数的地址在该函数模块内,通过\_\_module\_address找到该模块,结果如下:  
 ![在这里插入图片描述](https://img-blog.csdnimg.cn/285ad5f67ef54d0f93e3e5d101ff2354.png)


## 三、其它类似函数


### 3.1 \_\_module\_text\_address



/*
* is_module_text_address - is this address inside module code?
* @addr: the address to check.
*
* See is_module_address() if you simply want to see if the address is
* anywhere in a module. See kernel_text_address() for testing if an
* address corresponds to kernel or module code.
*/
bool is_module_text_address(unsigned long addr)
{
bool ret;

preempt\_disable();
ret = \_\_module\_text\_address(addr) != NULL;
preempt\_enable();

return ret;

}

/*
* __module_text_address - get the module whose code contains an address.
* @addr: the address.
*
* Must be called with preempt disabled or module mutex held so that
* module doesn’t get freed during this.
*/
struct module *__module_text_address(unsigned long addr)
{
struct module *mod = __module_address(addr);
if (mod) {
/* Make sure it’s within the text section. */
if (!within(addr, mod->module_init, mod->init_text_size)
&& !within(addr, mod->module_core, mod->core_text_size))
mod = NULL;
}
return mod;
}
EXPORT_SYMBOL_GPL(__module_text_address);


\_\_module\_text\_address返回值是一个struct module类型的指针,如果内存地址addr在某一模块的代码段内,则返回指向该模块的指针,如果addr不在模块的代码段内,则返回NULL。


init\_text\_size是内核模块初始化部分代码段大小。  
 core\_text\_size是内核模块核心部分代码段大小。


### 3.2 is\_module\_percpu\_address



/**
* is_module_percpu_address - test whether address is from module static percpu
* @addr: address to test
*
* Test whether @addr belongs to module static percpu area.
*
* RETURNS:
* %true if @addr is from module static percpu area
*/
bool is_module_percpu_address(unsigned long addr)
{
struct module *mod;
unsigned int cpu;

preempt\_disable();

list\_for\_each\_entry\_rcu(mod, &modules, list) {
	if (mod->state == MODULE_STATE_UNFORMED)
		continue;
	if (!mod->percpu_size)
		continue;
	for\_each\_possible\_cpu(cpu) {
		void \*start = per\_cpu\_ptr(mod->percpu, cpu);

		if ((void \*)addr >= start &&
		    (void \*)addr < start + mod->percpu_size) {
			preempt\_enable();
			return true;
		}
	}
}

preempt\_enable();
return false;

}



struct module
{
#ifdef CONFIG_SMP
/* Per-cpu data. */
void __percpu *percpu;
unsigned int percpu_size;
#endif
}


percpu指向该内核模块的percpu数据,在模块加载时初始化。  
 percpu\_size是percpu数据大小。


## 总结


通过判断全局内核链表modules下的每一个模块,判断该地址是否在模块的内存范围内。


该函数可在安全领域中用来检测内核关键点是否被hook,可参考开源项目:  
 <https://github.com/bytedance/Elkeid/blob/main/driver/LKM/src/anti_rootkit.c>




**网上学习资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。**

**[需要这份系统化的资料的朋友,可以点击这里获取!](https://bbs.csdn.net/forums/4f45ff00ff254613a03fab5e56a57acb)**

**一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!**

nti_rootkit.c>




**网上学习资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。**

**[需要这份系统化的资料的朋友,可以点击这里获取!](https://bbs.csdn.net/forums/4f45ff00ff254613a03fab5e56a57acb)**

**一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!**

  • 13
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值