方式一:通过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.20
中sprint_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;
}