方式一:通过objdump -t
直接从目标文件中获取函数size
#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
0000000000000030 <hook_vfs_read>:30: e8 00 00 00 00 callq 35 <hook_vfs_read+0x5>35: 41 57 push %r1537: 41 56 push %r1439: 31 c0 xor %eax,%eax......14f: eb c3 jmp 114 <hook_vfs_read+0xe4>151: 48 c7 c7 00 00 00 00 mov $0x0,%rdi158: 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
是一致的。
-t, --syms Display the contents of the symbol table(s)
-D, --disassemble-all Display assembler contents of all sections
方式三:通过内核函数动态获取函数size
/*** 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
的函数。
#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.20
中sprint_symbol
最终调用kallsyms_lookup
获取函数size
,因此也可以直接调用该函数获取函数size
,免去上述复杂的字符串解析过程。
/** 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;
}