方式一:通過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;
}