C函数分析(地址似乎是offseted)

jae*_*ong 7 c profiling function

我正在尝试使用-finstrument-functions选项来分析函数调用.基本上,我所做的是将以下内容写入任何已编译的源代码:

static int __stepper=0;
void __cyg_profile_func_enter(void *this_fn, void *call_site)
                              __attribute__((no_instrument_function));
void __cyg_profile_func_enter(void *this_fn, void *call_site) {
  int i=0;
  for( ; i<__stepper; i++ ) printf(" ");
  printf("E: %p %p\n", this_fn, call_site);
  __stepper ++;
} /* __cyg_profile_func_enter */

void __cyg_profile_func_exit(void *this_fn, void *call_site)
                             __attribute__((no_instrument_function));
void __cyg_profile_func_exit(void *this_fn, void *call_site) {
  int i=0;
  __stepper --;
  for( ; i<__stepper; i++ ) printf(" ");
  printf("L:  %p %p\n", this_fn, call_site);
} /* __cyg_profile_func_enter */
Run Code Online (Sandbox Code Playgroud)

并得到以下结果:

 E: 0xb7597ea0 0xb75987a8
  E: 0xb7597de0 0xb7597ef5
  L:  0xb7597de0 0xb7597ef5
 L:  0xb7597ea0 0xb75987a8
Run Code Online (Sandbox Code Playgroud)

所有函数调用地址都在该区域附近(0xb7 .......)但是,如果我尝试使用'readelf -s'读取函数符号,它会给出以下内容:

2157: 00101150   361 FUNC    LOCAL  DEFAULT   13 usb_audio_initfn
2158: 00100940   234 FUNC    LOCAL  DEFAULT   13 usb_audio_handle_reset
2159: 00100de0   867 FUNC    LOCAL  DEFAULT   13 usb_audio_handle_control
Run Code Online (Sandbox Code Playgroud)

二进制中所有函数的地址区域大约是0x00 ......所以,我无法从函数指针中获取函数名.看起来像函数指针如何获得偏移量或其他东西.

有人有什么想法吗?

Mic*_*kov 6

从问题看,你看起来正在分析库函数.

要了解被测量的功能,您有两个选择:

1运行使用库的程序,gdb然后停止main.此时,获取pid程序PID=...并执行`cat/proc/$ PID/maps'.你应该看到这样的东西:

?  ~  ps
  PID TTY          TIME CMD
18533 pts/4    00:00:00 zsh
18664 pts/4    00:00:00 ps
?  ~  PID=18533
?  ~  cat /proc/$PID/maps
00400000-004a2000 r-xp 00000000 08:01 3670052                            /bin/zsh5
006a1000-006a2000 r--p 000a1000 08:01 3670052                            /bin/zsh5
006a2000-006a8000 rw-p 000a2000 08:01 3670052                            /bin/zsh5
006a8000-006bc000 rw-p 00000000 00:00 0 
...
7fa174cc9000-7fa174ccd000 r-xp 00000000 08:01 528003                     /lib/x86_64-linux-gnu/libcap.so.2.22
7fa174ccd000-7fa174ecc000 ---p 00004000 08:01 528003                     /lib/x86_64-linux-gnu/libcap.so.2.22
7fa174ecc000-7fa174ecd000 r--p 00003000 08:01 528003                     /lib/x86_64-linux-gnu/libcap.so.2.22
7fa174ecd000-7fa174ece000 rw-p 00004000 08:01 528003                     /lib/x86_64-linux-gnu/libcap.so.2.22
...
Run Code Online (Sandbox Code Playgroud)

7fa174cc9000是库的基地址/lib/x86_64-linux-gnu/libcap.so.2.22.因此,您获得的所有地址都readelf -s将被该值抵消.知道基址后,您可以计算出文件中的原始偏移量.

即如果你得到了7fa174206370库的值和基址,7fa1741cf000那么偏移就是7fa174206370 - 7fa1741cf000 = 37370.在我的例子中,它sigsuspend来自GLIBC:

94: 0000000000037370   132 FUNC    WEAK   DEFAULT   12 sigsuspend@@GLIBC_2.2.5
Run Code Online (Sandbox Code Playgroud)

2运行gdb使用这些库的程序.它会立即在内存中找到加载的库,或者需要指向.text库的部分.

> gdb
(gdb) attach YOUR_PID
(a lot of output about symbols)
(gdb) x/i 0x00007fa174206386
=> 0x7fa174206386 <sigsuspend+22>:  cmp    $0xfffffffffffff000,%rax
Run Code Online (Sandbox Code Playgroud)

这样你就知道0x7fa174206386里面了sigsuspend.

如果gdb没有自己加载任何符号(没有像Reading symbols from ... Loading symbols for ...附加后的输出),你可以在选项1中查找库的基地址,然后添加.text部分的偏移量

?  ~  readelf -S /lib/x86_64-linux-gnu/libcap.so.2.22 | grep '.text.'
  [11] .text             PROGBITS         0000000000001620  00001620
Run Code Online (Sandbox Code Playgroud)

7fa174cc9000 + 0000000000001620以十六进制给出7FA174CCA620,然后gdb按上面的方式附加并执行

(gdb) add-symbol-file /lib/x86_64-linux-gnu/libcap.so.2.22 7FA174CCA620
Run Code Online (Sandbox Code Playgroud)

那么你应该能够找到符号(通过x/i ADDRESS选项1中的那个),即使gdb它们没有自己加载它们.

请问是否有任何不清楚的地方,我会试着解释一下.

澄清为什么会这样:

观察到的行为是由于库被编译为与位置无关的代码.它允许我们轻松支持动态库.PIC本质上意味着库的ELF具有.plt.got部分,并且可以在任何基地址加载.PLT是过程链接表,它包含用于调用位于其他模块中的函数的陷阱,这些函数首先转到程序解释器以允许它重新定位被调用的函数,然后在第一次调用后跳转到该函数.它的工作原理是程序解释器更新GOT(全局偏移表),它包含要调用的函数的地址.最初初始化GOT,以便在第一次函数调用时,执行跳转到程序解释器的功能,该程序解释器执行当前调用的函数的分辨率.

在x86-64上,PLT条目通常如下所示:

0000000000001430 <free@plt>:
    1430:       ff 25 e2 2b 20 00       jmpq   *0x202be2(%rip)        # 204018 <_fini+0x201264>
    1436:       68 00 00 00 00          pushq  $0x0
    143b:       e9 e0 ff ff ff          jmpq   1420 <_init+0x28>
Run Code Online (Sandbox Code Playgroud)

第一个jmpq是跳转到地址,存储在GOT的位置%rip + 0x202be2:

  [20] .got              PROGBITS         0000000000203fd0  00003fd0
       0000000000000030  0000000000000008  WA       0     0     8
Run Code Online (Sandbox Code Playgroud)

%rip + 0x202be2将被0x204012添加到库的基地址,以生成与实际加载库的位置相关的绝对地址.即如果它被加载0x7f66dfc03000,那么相应的GOT条目的结果地址将是0x7F66DFE07012.存储在该位置的地址是(在该示例中)free功能的地址.它是由程序解释器维护指向实际freelibc.

有关这方面的更多信息,请点击此处.