1

I'm disassembling one SO library for fun and was wondering what's purpose of following procedure:

sub_3699 proc near
mov     ebx, [esp+0]
retn
sub_3699 endp

It's widely used across entire library with very confusing pattern (at least to me) like this:

push    ebp
mov     ebp, esp
push    esi
push    ebx
call    sub_3699        ; Here it is called
add     ebx, 1DD5Ch     ; And this magic number is something I'm trying to understand
...                     ; rest of caller body
pop     ebx
pop     esi
pop     ebp
retn

Please note magic number 1DD5Ch added to EBX after procedure invocation. It seems that this number is unique across all invocations, but I have no idea of its purpose. Even EBX register itself doesn't seem to be read/write within caller code.

Any idea?

If that matters, here is output of readelf -s ...

ELF Header:
  Magic:   7f 45 4c 46 01 01 01 00 00 00 00 00 00 00 00 00 
  Class:                             ELF32
  Data:                              2's complement, little endian
  Version:                           1 (current)
  OS/ABI:                            UNIX - System V
  ABI Version:                       0
  Type:                              DYN (Shared object file)
  Machine:                           Intel 80386
  Version:                           0x1
  Entry point address:               0x14340
  Start of program headers:          52 (bytes into file)
  Start of section headers:          275660 (bytes into file)
  Flags:                             0x0
  Size of this header:               52 (bytes)
  Size of program headers:           32 (bytes)
  Number of program headers:         5
  Size of section headers:           40 (bytes)
  Number of section headers:         25
  Section header string table index: 24

Section Headers:
  [Nr] Name              Type            Addr     Off    Size   ES Flg Lk Inf Al
  [ 0]                   NULL            00000000 000000 000000 00      0   0  0
  [ 1] .hash             HASH            000000d4 0000d4 00221c 04   A  2   0  4
  [ 2] .dynsym           DYNSYM          000022f0 0022f0 0047e0 10   A  3   1  4
  [ 3] .dynstr           STRTAB          00006ad0 006ad0 00825f 00   A  0   0  1
  [ 4] .gnu.version      VERSYM          0000ed30 00ed30 0008fc 02   A  2   0  2
  [ 5] .gnu.version_r    VERNEED         0000f62c 00f62c 000020 00   A  3   1  4
  [ 6] .rel.dyn          REL             0000f64c 00f64c 000eb8 08   A  2   0  4
  [ 7] .rel.plt          REL             00010504 010504 0014b0 08   A  2   9  4
  [ 8] .init             PROGBITS        000119b4 0119b4 00001c 00  AX  0   0  1
  [ 9] .plt              PROGBITS        000119d0 0119d0 002970 04  AX  0   0 16
  [10] .text             PROGBITS        00014340 014340 02b324 00  AX  0   0  4
  [11] .fini             PROGBITS        0003f664 03f664 000017 00  AX  0   0  1
  [12] .rodata           PROGBITS        0003f680 03f680 001fa0 00   A  0   0  8
  [13] .eh_frame_hdr     PROGBITS        00041620 041620 00002c 00   A  0   0  4
  [14] .eh_frame         PROGBITS        0004164c 04164c 0000f8 00   A  0   0  4
  [15] .ctors            PROGBITS        00042000 042000 000018 00  WA  0   0  4
  [16] .dtors            PROGBITS        00042018 042018 000018 00  WA  0   0  4
  [17] .jcr              PROGBITS        00042030 042030 000004 00  WA  0   0  4
  [18] .data.rel.ro      PROGBITS        00042038 042038 0006d8 00  WA  0   0  8
  [19] .dynamic          DYNAMIC         00042710 042710 0000d0 08  WA  3   0  4
  [20] .got              PROGBITS        000427e0 0427e0 0001b4 04  WA  0   0  4
  [21] .got.plt          PROGBITS        00042994 042994 000a64 04  WA  0   0  4
  [22] .data             PROGBITS        000433f8 0433f8 000010 00  WA  0   0  4
  [23] .bss              NOBITS          00043408 043408 000bb0 00  WA  0   0  8
  [24] .shstrtab         STRTAB          00000000 043408 0000c3 00      0   0  1
Key to Flags:
  W (write), A (alloc), X (execute), M (merge), S (strings), I (info),
  L (link order), O (extra OS processing required), G (group), T (TLS),
  C (compressed), x (unknown), o (OS specific), E (exclude),
  p (processor specific)

Program Headers:
  Type           Offset   VirtAddr   PhysAddr   FileSiz MemSiz  Flg Align
  LOAD           0x000000 0x00000000 0x00000000 0x41744 0x41744 R E 0x1000
  LOAD           0x042000 0x00042000 0x00042000 0x01408 0x01fb8 RW  0x1000
  DYNAMIC        0x042710 0x00042710 0x00042710 0x000d0 0x000d0 RW  0x4
  GNU_EH_FRAME   0x041620 0x00041620 0x00041620 0x0002c 0x0002c R   0x4
  GNU_STACK      0x000000 0x00000000 0x00000000 0x00000 0x00000 RW  0x4

 Section to Segment mapping:
  Segment Sections...
   00     .hash .dynsym .dynstr .gnu.version .gnu.version_r .rel.dyn .rel.plt .init .plt .text .fini .rodata .eh_frame_hdr .eh_frame 
   01     .ctors .dtors .jcr .data.rel.ro .dynamic .got .got.plt .data .bss 
   02     .dynamic 
   03     .eh_frame_hdr 
   04     
2

The purpose of sub_3699 is to return address of the next instruction after the one that called that procedure. call sub_3699 pushes address of the next instruction onto the stack and jumps to the first instruction of sub_3699.

That instruction takes the value at the top of the stack (without removing it) and writes it to ebx. Such a mechanism is usually used when position independent code is generated to avoid accessing data using harcoded addresses (though combination of call $+5, pop reg is more popular).

So instead of accessing some addr directly, you compute it relatively to the address of the next instruction. So, you first compute addr - address_of_next_instruction = addr - sub_3699() = 0x1DD5C (in your case) and to obtain addr you have to simply add sub_3699() to that number. After this addition, ebx contains addr.

1
  • Thank you, it make sense now
    – rkosegi
    Oct 22 at 1:48

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.