• 通过实例理解 .gnu.hash section


    Assume the file is libc.so.6, the system is of 64 bit. The file is in /lib/x86_64-linux-gnu/. This document is helpful to understand the gnu hash section through an example. The symbol to be checked is “printf”.

    Let’s go!

    $ objdump -s /lib/x86_64-linux-gnu/libc.so.6|grep .gnu.hash -A 5
    Contents of section .gnu.hash: (5 lines after the section start)
    02b8 f3030000 0a000000 00010000 0e000000 …
    02c8 00301044 a0200201 8803e690 c5458c00 .0.D. …E…
    02d8 c4000800 05840060 c080000d 8a0c0004 …`…
    02e8 10008840 32082a40 88543c2c 200e3248 …@2.*@.T<, .2H
    02f8 2684c08c 04080002 020ea1ac 1a0466c0 &…f.

    nbuckets: 03f3, hash buckets, 4 bytes
    symndx: 000a, Index of 1st dynsym, 4 bytes
    bloom: 0100, Bloom filter words, 4 bytes
    shift2: 000e, Bloom filter hash shift2, 4 bytes

    start of bloom filter: 02b8 + 0010 = 02c8
    start of bucket: 02c8 + 8 * 0100 = 0ac8 , each bloom 8 bytes
    start of hash value: 0ac8 + 4 * 03f3 = 1a94 , each bucket 4 bytes

    hash of “printf”: new_hash = 0x156b2bb8

     "printf"=70 72 69 6e 74 66
     h = 0x1505  // 5381
     t = 0x21    //33
     h = h * t + 70  //h * 33 = h >> 5 +h
     h = h * t + 72
     h = h * t + 69
     h = h * t + 6e 
     h = h * t + 74
     h = h * t + 66
     h = h & 0xffffffff
    

    Hash function
    The GNU hash function user the DJB hash (blogs.oracle.com/solaris/post/gnu-hash-elf-sections)

     uint32_t dl_new_hash (const char *s)
     {
         uint32 h = 5381;
         for (unsigned char c = *s; c != '\0'; c = *++s)
            h = h * 33 + c;  //or ((h = h >> 5) +h) +c
         return h; // h in uint32 means h = h & 0xffffffff
     }
    

    class64 = 0x40, 64
    hashbit1 = new_hash & (class64 - 1) = 56
    hashbit2 = (new_hash >> shift2) & (class64 - 1) = 44
    if a value is power of 2, & (value - 1) equals % value
    bits 56 and 44 of maskword N are set to 1
    N =((new_hash /class64) % maskwords), maskwords = bloom

    bloom word: (new_hash / 64) % bloom = 0xae
    bloom word address: 02c8 + 8 * ae = 0x0838 , one bloom word 8 bytes

    find the address 0838:
    $ objdump -s /lib/x86_64-linux-gnu/libc.so.6|grep " 0838 "
    0838 d0884a41 c0703429 10ec4303 92003103 …JA.p4)…C…1.

    the bloom word value at 0838 is 0x293470c0 414a88d0
    (0x293470c0 414a88d0 >> 56) & 1 = 0x29 & 1 =1
    (0x293470c0 414a88d0 >> 44) & 1 = 0x293447 & 1 =1
    “printf” pass the check

    determine bucket:
    bucket_id = new_hash % nbuckets = 0x127
    address of the bucket: 0ac8 + 4 * 0x127 = 0x0f64 , one bucket 4 bytes

    find the address 0f64
    $ objdump -s /lib/x86_64-linux-gnu/libc.so.6|grep " 0f58 " -A 2
    0f58 55020000 00000000 58020000 5a020000 U…X…Z…
    0f68 5f020000 62020000 63020000 66020000 _…b…c…f…
    0f78 67020000 6a020000 6c020000 70020000 g…j…l…p…

    bucket value at 0f64 is: 025a
    hash value of the bucket at:
    1a94 + 4*(025a -symndx) = 0x23d4

    find the address 23d4
    $ objdump -s /lib/x86_64-linux-gnu/libc.so.6|grep " 23c8 " -A 2
    23c8 ade8dbbb 142dcb13 bb86f85f e6952000 …-…_… .
    23d8 b82b6b15 0a05f1d5 deb6427f 856177fd .+k…B…aw.
    23e8 ec296fa8 1ae585e7 29ce248f e7d045cc .)o…).$…E.

    the new_hash 156b2bb8 at 23d8
    it’s the second hash value of the bucket, so the dynsym index is: 025a + 1=0x025b

    find the start of dynsym section:
    $ objdump -s /lib/x86_64-linux-gnu/libc.so.6|grep .dynsym -A 2
    Contents of section .dynsym:
    03d80 00000000 00000000 00000000 00000000 …
    03d90 00000000 00000000 00000000 03000d00 …

    the 0x025b dynsym at: 0x03d80+0x18*0x25b = 0x07608, one sym item 24 bytes
    docs.oracle.com/cd/E19120-01/open.solaris/819-0690/chapter6-79797/index.html

    type struct {
    	Elf64_Word    st_name;//4 bytes
    	unsigned char st_info;//1 byte
    	unsigned char st_others;//1 byte
    	Elf64_Half    st_shndx;// 2 bytes
    	Elf64_Addr    st_value;// 8 bytes
        Elf64_Xword   stsize;  // 8 bytes 
     } Elf6s_Sym
    

    find the dynsym at 0x07608:
    $ objdump -s /lib/x86_64-linux-gnu/libc.so.6|grep " 07600 " -A 2
    07600 12000000 00000000 c2040000 12000d00 …
    07610 10580500 00000000 a1000000 00000000 .X…
    07620 533c0000 12000d00 70e70f00 00000000 S<…p…

    st_name = 00000402
    st_info =12,
    ELF64_ST_BIND (info) = info >> 4 =1, STB_GLOBAL, Global symbols.
    ELF64_ST_TYPE (info) = info & 0xf =2, STT_FUNC, a function or executable
    st_other = 00, STV_DEFAULT, visibility specified by the symbol’s binding type
    st_shndx =000d, relevant section header table index,
    st_value = 00000000 00055810, virtual address
    st_size = 00000000 000000a1, object size in bytes

    the dynsym_name at 0x7608 points to 04c2 of section dynstr

    find the start of dynstr section:

    $ objdump -s /lib/x86_64-linux-gnu/libc.so.6|grep .dynstr -A 2
    Contents of section .dynstr:
    10ff8 00786472 5f755f6c 6f6e6700 5f5f7763 .xdr_u_long.__wc
    11008 746f6d62 5f63686b 00676574 6d6e7465 tomb_chk.getmnte

    the symbol start at: 10ff8 + 04c2 =0x114ba
    find the symbol at 0x114ba
    $ objdump -s /lib/x86_64-linux-gnu/libc.so.6|grep " 114b8 " -A 2
    114b8 4f5f7072 696e7466 00726567 69737465 O_printf.registe
    114c8 725f7072 696e7466 5f66756e 6374696f r_printf_functio
    114d8 6e00706f 7369785f 6f70656e 70740069 n.posix_openpt.i

    the symbol at 0x114ba is: 70 72 69 6e 74 66, that is “printf”

    look at the address specified by st_value = 55810
    $ objdump -S /lib/x86_64-linux-gnu/libc.so.6|grep "55810 " -A 2
    0000000000055810 <_IO_printf@@GLIBC_2.2.5>:
    55810: 48 81 ec d8 00 00 00 sub $0xd8,%rsp
    55817: 84 c0 test %al,%al

    It’s the start code of _IO_printf@@GLIBC_2.2.5

    End

  • 相关阅读:
    cmake实现Blib引用Alib, C.exe引用Blib
    TikTok Shop:年轻一代购物革命的未来之旅
    Android13 大屏设备底部显示TaskBar并NavagatonBar居右
    Vue 怎么调用ElementUI中的方法?
    VirtualKD-Redux 双机调试内驱驱动
    IIS Application Pool
    使用C#实现服务端与客户端的简陋聊天
    ceph简介
    C语言:union类型
    晚上下班之后做副业做什么好呢?
  • 原文地址:https://blog.csdn.net/wubyatseu/article/details/127100799