Linux 内核获取函数size

方式一:通过objdump -t直接从目标文件中获取函数size

bash 复制代码
#objdump -t file_unread.o | grep hook
0000000000000030 l     F .text  000000000000012f hook_vfs_read

0000000000000030 l F .text 000000000000012f hook_vfs_read各个字段说明

  • 0000000000000030:符号的地址或值,这里是一个相对地址,表示该符号在.text节中的偏移量。
  • l:符号的绑定属性,这里是小写字母l,表示该符号是一个局部符号,只在当前目标文件中可见。
  • F:符号所在的节(section)的类型,这里是大写字母F,表示该符号所在的节是一个函数节(function section)。
  • .text:符号所在的节的名称,这里是.text,表示该符号所在的节是代码节。
  • 000000000000012f:符号的大小,以字节为单位,这里是0x12f,表示该函数的大小为303字节。
  • hook_vfs_read:符号的名称,这里是hook_vfs_read,表示该符号是一个名为hook_vfs_read的函数。

vfs_read+0xe6/0x2c0 表示什么意思呢?

其中vfs_read 是函数名,+0xe6/0x2c0 是函数在代码中的偏移量。具体来说,+0xe6 表示函数内部代码的偏移量,/ 后面的 0x2c0 表示函数的总大小(以字节为单位)。

hook_vfs_read+0xe4/0x130 [fi_file]为动态打印进程调用栈信息。

方式二:通过objdump -D反汇编后计算函数size

objdump对目标文件进行反汇编,从汇编代码中计算hook_vfs_read的函数size

bash 复制代码
0000000000000030 <hook_vfs_read>:
  30:   e8 00 00 00 00          callq  35 <hook_vfs_read+0x5>
  35:   41 57                   push   %r15
  37:   41 56                   push   %r14
  39:   31 c0                   xor    %eax,%eax
  ......
  14f:   eb c3                   jmp    114 <hook_vfs_read+0xe4>
  151:   48 c7 c7 00 00 00 00    mov    $0x0,%rdi
  158:   e8 00 00 00 00          callq  15d <hook_vfs_read+0x12d>
  15d:   eb a3                   jmp    102 <hook_vfs_read+0xd2>
  15f:   90                      nop

hook_vfs_read_size = 0x15f - 0x30 = 0x12f + 1,从计算的结果来看,加上偏移与调用栈打印的函数size是一致的。

bash 复制代码
-t, --syms               Display the contents of the symbol table(s)
-D, --disassemble-all    Display assembler contents of all sections

方式三:通过内核函数动态获取函数size

c 复制代码
/**
 * sprint_symbol - Look up a kernel symbol and return it in a text buffer
 * @buffer: buffer to be stored
 * @address: address to lookup
 *
 * This function looks up a kernel symbol with @address and stores its name,
 * offset, size and module name to @buffer if possible. If no symbol was found,
 * just saves its @address as is.
 *
 * This function returns the number of bytes stored in @buffer.
 */
int sprint_symbol(char *buffer, unsigned long address)
{
        return __sprint_symbol(buffer, address, 0, 1);
}
EXPORT_SYMBOL_GPL(sprint_symbol);

路径1:

获取到的字符串信息示例为hook_vfs_read+0x0/0x130 [fi_file],这种方式需要从数组str中解析出函数大小0x130

解析字符串中的函数size,还是要骚操作获取,在内核中没有看到直接获取函数size的函数。

c 复制代码
#include <linux/string.h>

static int __init my_init(void)
{
    const char *str = "hook_vfs_read+0x0/0x130 [fi_file]";
    const char *prefix = "/0x";
    const char *suffix = " [";
    char *start, *end;
    unsigned long value;

    // 查找前缀字符串
    start = strstr(str, prefix);
    if (!start) {
        printk(KERN_ERR "Failed to find prefix string\n");
        return -EINVAL;
    }
    start += strlen(prefix);

    // 查找后缀字符串
    end = strstr(start, suffix);
    if (!end) {
        printk(KERN_ERR "Failed to find suffix string\n");
        return -EINVAL;
    }

    // 截取字符串
    *end = '\0';
    value = simple_strtoul(start, NULL, 16);

    printk(KERN_INFO "Value: 0x%lx\n", value);

    return 0;
}

路径二:

内核代码v4.18.20sprint_symbol最终调用kallsyms_lookup获取函数size,因此也可以直接调用该函数获取函数size,免去上述复杂的字符串解析过程。

c 复制代码
/*
 * Lookup an address
 * - modname is set to NULL if it's in the kernel.
 * - We guarantee that the returned name is valid until we reschedule even if.
 *   It resides in a module.
 * - We also guarantee that modname will be valid until rescheduled.
 */
const char *kallsyms_lookup(unsigned long addr,
			    unsigned long *symbolsize,
			    unsigned long *offset,
			    char **modname, char *namebuf)
{
	const char *ret;

	namebuf[KSYM_NAME_LEN - 1] = 0;
	namebuf[0] = 0;

	if (is_ksym_addr(addr)) {
		unsigned long pos;

		pos = get_symbol_pos(addr, symbolsize, offset);
		/* Grab name */
		kallsyms_expand_symbol(get_symbol_offset(pos),
				       namebuf, KSYM_NAME_LEN);
		if (modname)
			*modname = NULL;
		return namebuf;
	}

	/* See if it's in a module or a BPF JITed image. */
	ret = module_address_lookup(addr, symbolsize, offset,
				    modname, namebuf);
	if (!ret)
		ret = bpf_address_lookup(addr, symbolsize,
					 offset, modname, namebuf);

	if (!ret)
		ret = ftrace_mod_address_lookup(addr, symbolsize,
						offset, modname, namebuf);
	return ret;
}
相关推荐
二十雨辰2 小时前
[linux]docker基础
linux·运维·docker
饮浊酒3 小时前
Linux操作系统 ------(3.文本编译器Vim)
linux·vim
lihuhelihu3 小时前
第3章 CentOS系统管理
linux·运维·服务器·计算机网络·ubuntu·centos·云计算
矛取矛求3 小时前
Linux系统性能调优技巧
linux
One_Blanks3 小时前
渗透测试-Linux基础(1)
linux·运维·安全
Perishell3 小时前
无人机避障——大疆与Airsim中的角速度信息订阅获取
linux·动态规划·无人机
爱吃喵的鲤鱼3 小时前
linux进程的状态之环境变量
linux·运维·服务器·开发语言·c++
dessler3 小时前
Linux系统-ubuntu系统安装
linux·运维·云计算
荒Huang4 小时前
Linux挖矿病毒(kswapd0进程使cpu爆满)
linux·运维·服务器
hjjdebug6 小时前
linux 下 signal() 函数的用法,信号类型在哪里定义的?
linux·signal