kubo / plthook

Hook function calls by replacing PLT(Procedure Linkage Table) entries.
762 stars 156 forks source link

Fix missing hook for a same symbol in both PLT and GOT #16

Closed maoenpei closed 4 years ago

maoenpei commented 5 years ago

Hi kubo, thanks for your code that helps me to hook symbols on Linux. However I've met an issue that it will miss hooking a symbol if that symbol exists in both PLT and GOT. Such library can be made by compling one obj file with "-fPIC" but another without, and finally link them together.

tntljc commented 5 years ago

@kubo , does this change seem reasonable to you? plthook is powerful, we really would like to contribute to its robustness and keep improve it.

maoenpei commented 5 years ago

@kubo Hi Kubo, I have more changes for plthook library. This library is really useful and I've met different issues while using it. The rest changes are about:

  1. Optimize symbol searching with hash table.
  2. Add support for "R_X86_64_64" / "R_386_32"
  3. Add support for "R_X86_64_PC32" / "R_386_PC32" Leave comment if you have any thoughts. Thanks.
kubo commented 5 years ago

@maoenpei Sorry for my late reply.

Such library can be made by compiling one obj file with "-fPIC" but another without, and finally link them together.

I could not reproduce it. I have tested it on Ubuntu 14.04 (gcc 4.8.4), Ubuntu 16.04 (gcc 5.4.0) and Ubuntu 19.04 (gcc 8.3.0). Could you post your environment?

I made a test program and put it on gist. When a same symbol is in both PLT and GOT, it should be found twice while iterating by plthook_enum. However it is found only once as follows.

$ wget https://gist.github.com/kubo/e456246df93b4cc95c9e47e9c3db8835/archive/7b147be9436a2b6860ee67d8463bae3b35ba030c.zip
$ unzip 7b147be9436a2b6860ee67d8463bae3b35ba030c.zip 
$ cd e456246df93b4cc95c9e47e9c3db8835-7b147be9436a2b6860ee67d8463bae3b35ba030c/
$ make check
cc    -c -o main.o main.c
cc   -c addone_no_pic.c -fno-pic
cc   -c addone_pic.c -fPIC
cc    -c -o plthook_elf.o plthook_elf.c
cc -shared -o libaddone.so libaddone.c
cc   -o prog main.o addone_no_pic.o addone_pic.o plthook_elf.o -L. -laddone -Wl,-rpath,'$ORIGIN' -ldl
====================================
./prog
call from pic: 11
call from no_pic: 11
0x7f7024866650:addone
====================================
objdump -d prog | grep '<addone_.*:' -A10
0000000000400e53 <addone_no_pic>:
  400e53:   55                      push   %rbp
  400e54:   48 89 e5                mov    %rsp,%rbp
  400e57:   48 83 ec 10             sub    $0x10,%rsp
  400e5b:   89 7d fc                mov    %edi,-0x4(%rbp)
  400e5e:   8b 45 fc                mov    -0x4(%rbp),%eax
  400e61:   89 c7                   mov    %eax,%edi
  400e63:   e8 08 fd ff ff          callq  400b70 <addone@plt>
  400e68:   c9                      leaveq 
  400e69:   c3                      retq   

0000000000400e6a <addone_pic>:
  400e6a:   55                      push   %rbp
  400e6b:   48 89 e5                mov    %rsp,%rbp
  400e6e:   48 83 ec 10             sub    $0x10,%rsp
  400e72:   89 7d fc                mov    %edi,-0x4(%rbp)
  400e75:   8b 45 fc                mov    -0x4(%rbp),%eax
  400e78:   89 c7                   mov    %eax,%edi
  400e7a:   e8 f1 fc ff ff          callq  400b70 <addone@plt>
  400e7f:   c9                      leaveq 
  400e80:   c3                      retq   

====================================
maoenpei commented 5 years ago

@kubo Thanks for reply. Here's a test to build an executable with both PLT and GOT, I've put source code / makefile / built binary in it: https://raw.githubusercontent.com/maoenpei/UploadFiles/master/hello.tar.gz

I'm using ubuntu 1604.

"g++ --version" g++ (Ubuntu 5.4.0-6ubuntu1~16.04.11) 5.4.0 20160609 Copyright (C) 2015 Free Software Foundation, Inc. This is free software; see the source for copying conditions. There is NO warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.

"readelf -r hello.out" Relocation section '.rela.dyn' at offset 0x450 contains 2 entries: Offset Info Type Sym. Value Sym. Name + Addend 000000600ff0 000300000006 R_X86_64_GLOBDAT 0000000000000000 \_gmon_start__ + 0 000000600ff8 000600000006 R_X86_64_GLOB_DAT 00000000004004f0 dlopen@GLIBC_2.2.5 + 0 Relocation section '.rela.plt' at offset 0x480 contains 2 entries: Offset Info Type Sym. Value Sym. Name + Addend 000000601018 000200000007 R_X86_64_JUMP_SLO 0000000000000000 __libc_start_main@GLIBC_2.2.5 + 0 000000601020 000600000007 R_X86_64_JUMP_SLO 00000000004004f0 dlopen@GLIBC_2.2.5 + 0

kubo commented 5 years ago

Thanks! It seems that he key point is assignment of function to local variables. I'll make a test program and then merge your code.

maoenpei commented 5 years ago

@kubo Forgot to mention, please don't add test for "plthook_enum" in this version, I'm going to pull new request that optimizes the searching progress with a hash table, and "plthook_enum" will be removed. I'm not familiar with github so I don't know if it is safe to pull requests before this change is merged.

Feel free to view my change in my forked version "https://github.com/maoenpei/plthook". I've passed your test with only "test_plthook_enum" commented out.

And of course, feel free to leave comments about the optimization. My purpose is to finally unify plthook in your github and that in my project. There are some more fixes (besides hash table change) I've made in my project. And my project is really performance sensitive so I cannot use unoptimized version.

Good luck :)

maoenpei commented 5 years ago

@kubo Sorry I've pushed a new commit to master branch of my forked repository, as I'm not very familiar with github mechanism. Do you need me to undo my last change? Or this time review them together? You see, I'd like all my changes be part of your repository eventually so I won't sync your changes manually.

kubo commented 4 years ago

@maoenpei Sorry for my too late reply again. I have tried to make test cases which fails without your patch several times with no luck. I concluded that there are no need to hook a GOT entry when the corresponding PLT entry is hooked. So I don't merge this PR.

The followings uses hello.out you created.

$ readelf -r hello.out

Relocation section '.rela.dyn' at offset 0x450 contains 2 entries:
  Offset          Info           Type           Sym. Value    Sym. Name + Addend
000000600ff0  000300000006 R_X86_64_GLOB_DAT 0000000000000000 __gmon_start__ + 0
000000600ff8  000600000006 R_X86_64_GLOB_DAT 00000000004004f0 dlopen@GLIBC_2.2.5 + 0

Relocation section '.rela.plt' at offset 0x480 contains 2 entries:
  Offset          Info           Type           Sym. Value    Sym. Name + Addend
000000601018  000200000007 R_X86_64_JUMP_SLO 0000000000000000 __libc_start_main@GLIBC_2.2.5 + 0
000000601020  000600000007 R_X86_64_JUMP_SLO 00000000004004f0 dlopen@GLIBC_2.2.5 + 0

The PLT entry is at 0x601020. The GOT entry is at 0x600ff8.

$ gdb -q hello.out
Reading symbols from hello.out...(no debugging symbols found)...done.
(gdb) b main
Breakpoint 1 at 0x40060a
(gdb) run
Starting program: /home/kubo/Public/winshare/plthook/hello/hello.out 

Breakpoint 1, 0x000000000040060a in main ()
(gdb) x/1gx 0x600ff8
0x600ff8:   0x00000000004004f0

Function calls using the GOT entry jump to 0x00000000004004f0.

(gdb) disassemble 0x00000000004004f0
Dump of assembler code for function dlopen@plt:
   0x00000000004004f0 <+0>: jmpq   *0x200b2a(%rip)        # 0x601020
   0x00000000004004f6 <+6>: pushq  $0x1
   0x00000000004004fb <+11>:    jmpq   0x4004d0
End of assembler dump.

The function at 0x00000000004004f0 is dlopen@plt jumping to the address at 0x601020, which is the address of the PLT entry. Therefore function calls using the GOT entry jump to the address in the hooked PLT entry after all when the PLT entry is hooked and the GOT entry isn't.