ELF 프로그램 헤더의 p_vaddr이 실제 메모리 주소인지 아니면 공유 라이브러리 기본 주소에 상대적인 오프셋인지 확인하는 방법은 무엇입니까?

ELF 프로그램 헤더의 p_vaddr이 실제 메모리 주소인지 아니면 공유 라이브러리 기본 주소에 상대적인 오프셋인지 확인하는 방법은 무엇입니까?

프로그램 헤더를 읽어 프로그램 메모리에서 libc 프로그램 세그먼트의 위치를 ​​찾으려고 합니다.

Centos 6에서 libc.so.6 파일에 대해 readelf를 사용하면 VirtAddr에는 프로세스 메모리에 로드된 프로그램 세그먼트의 올바른 주소가 포함됩니다.

[user@centos6 src]$ readelf -l /lib64/libc.so.6 --wide

Elf file type is DYN (Shared object file)
Entry point 0x3032c1ee30
There are 10 program headers, starting at offset 64

Program Headers:
  Type           Offset   VirtAddr           PhysAddr           FileSiz  MemSiz   Flg Align
  PHDR           0x000040 0x0000003032c00040 0x0000003032c00040 0x000230 0x000230 R E 0x8
  INTERP         0x15aab0 0x0000003032d5aab0 0x0000003032d5aab0 0x00001c 0x00001c R   0x10
      [Requesting program interpreter: /lib64/ld-linux-x86-64.so.2]
  LOAD           0x000000 0x0000003032c00000 0x0000003032c00000 0x18a00c 0x18a00c R E 0x200000
  LOAD           0x18a700 0x0000003032f8a700 0x0000003032f8a700 0x004f58 0x009228 RW  0x200000
  DYNAMIC        0x18db40 0x0000003032f8db40 0x0000003032f8db40 0x0001f0 0x0001f0 RW  0x8
  NOTE           0x000270 0x0000003032c00270 0x0000003032c00270 0x000044 0x000044 R   0x4
  TLS            0x18a700 0x0000003032f8a700 0x0000003032f8a700 0x000010 0x000068 R   0x8
  GNU_EH_FRAME   0x15aacc 0x0000003032d5aacc 0x0000003032d5aacc 0x0065ec 0x0065ec R   0x4
  GNU_STACK      0x000000 0x0000000000000000 0x0000000000000000 0x000000 0x000000 RW  0x8
  GNU_RELRO      0x18a700 0x0000003032f8a700 0x0000003032f8a700 0x003900 0x003900 R   0x1

따라서 이 예에서 DYNAMIC 세그먼트는 0x0000003032f8db40에 있습니다.

그러나 Centos 7에서 VirtAddr에는 세그먼트가 메모리에 있는 위치를 찾기 위해 libc 기본 주소를 추가해야 하는 오프셋이 포함되어 있습니다.

[user@centos7 src]$ readelf -l /usr/lib64/libc.so.6 --wide

Elf file type is DYN (Shared object file)
Entry point 0x22660
There are 10 program headers, starting at offset 64

Program Headers:
  Type           Offset   VirtAddr           PhysAddr           FileSiz  MemSiz   Flg Align
  PHDR           0x000040 0x0000000000000040 0x0000000000000040 0x000230 0x000230 R E 0x8
  INTERP         0x18eb00 0x000000000018eb00 0x000000000018eb00 0x00001c 0x00001c R   0x10
      [Requesting program interpreter: /lib64/ld-linux-x86-64.so.2]
  LOAD           0x000000 0x0000000000000000 0x0000000000000000 0x1c3170 0x1c3170 R E 0x200000
  LOAD           0x1c36f0 0x00000000003c36f0 0x00000000003c36f0 0x0051b0 0x009b10 RW  0x200000
  DYNAMIC        0x1c6b60 0x00000000003c6b60 0x00000000003c6b60 0x0001f0 0x0001f0 RW  0x8
  NOTE           0x000270 0x0000000000000270 0x0000000000000270 0x000044 0x000044 R   0x4
  TLS            0x1c36f0 0x00000000003c36f0 0x00000000003c36f0 0x000010 0x0000a0 R   0x10
  GNU_EH_FRAME   0x18eb1c 0x000000000018eb1c 0x000000000018eb1c 0x006aec 0x006aec R   0x4
  GNU_STACK      0x000000 0x0000000000000000 0x0000000000000000 0x000000 0x000000 RW  0x10
  GNU_RELRO      0x1c36f0 0x00000000003c36f0 0x00000000003c36f0 0x003910 0x003910 R   0x1

이 경우 동적 세그먼트는 0x00000000003c6b60 + libc 기본 주소에 위치합니다.

아마도 Centos 7의 ASLR로 인해 libc 라이브러리가 매번 다른 주소로 로드되기 때문일 수 있습니다. Centos 6에서는 libc가 항상 같은 주소에 로드되는 것 같습니다.

메모리에서 프로그램 세그먼트의 실제 위치를 얻기 위해 ELF 헤더를 읽는 것만으로 libc 기본 주소를 VirtAddr에 추가해야 하는지 판단할 수 있는 방법이 있습니까?

답변1

제 생각에는 모든 PT_LOAD 세그먼트를 확인하고 p_vaddr이 가장 ​​낮은 세그먼트를 찾아야 합니다(저는 lower_pt_load라고 부르겠습니다).

그런 다음 메모리 위치를 파악합니다.libc_base + segment.p_addr - lowest_pt_load.p_vaddr

Centos 6에서 일어나는 일은 lower_pt_load가 libc 기본 주소와 동일하여 서로를 취소하게 한다는 것입니다.

원천:https://docs.oracle.com/cd/E19683-01/816-1386/6m7qcoblk/index.html#chapter6-83432

기본 주소

실행 파일과 공유 객체 파일에는 프로그램 객체 파일의 메모리 이미지와 연관된 가장 낮은 가상 주소인 기본 주소가 있습니다. 기본 주소의 용도 중 하나는 동적 연결 중에 프로그램의 메모리 이미지를 재배치하는 것입니다.

실행 파일 또는 공유 개체 파일의 기본 주소는 실행 중에 메모리 로드 주소, 최대 페이지 크기, 프로그램 로드 가능 세그먼트의 가장 낮은 가상 주소 등 세 가지 값에서 계산됩니다. 프로그램 헤더의 가상 주소는 프로그램 메모리 이미지의 실제 가상 주소를 나타내지 않을 수 있습니다. "프로그램 로딩(프로세서별)"을 참조하십시오.

기본 주소를 계산하려면 PT_LOAD 세그먼트의 가장 낮은 p_vaddr 값과 관련된 메모리 주소를 결정해야 합니다. 그런 다음 메모리 주소를 최대 페이지 크기의 가장 가까운 배수로 잘라서 기본 주소를 얻습니다. 메모리에 로드된 파일 유형에 따라 메모리 주소가 p_vaddr 값과 일치하지 않을 수 있습니다.

관련 정보