프로그램 헤더를 읽어 프로그램 메모리에서 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 값과 일치하지 않을 수 있습니다.