- 프로그램 로드
- 섹션은 어떻게 실제 메모리에 매핑되는가?
- 페이징, 페이지드 세그멘테이션
ELF
Executable and Linkable Format
실행 가능하거나, 링크할 수 있는 파일들의 사전 정의된 구조(Format)
ex) .o, .so, .out(실행파일)
ELF 구조
프로그램은 메모리에 올라가기 전, 디스크에 있는 바이너리를 말함.
(참고) 섹션 vs 세그먼트
프로세스 ELF 구조 (Segments)

- ELF 헤더
- 타입 - 얘가 EXEC(.out)인지, REL(.o, .a)인지, DYN(.so)인지…
- .text
- 사용자 작성 부분이 컴파일(.c→.o→asm→0101010)되어 담김
- .rodata
- 문자열 등 상수
- .data
- ‘초기화 된’ 전역 변수, 정적 변수
- .bss (’Block Started by’ Symbol)
- ‘초기화 되지 않은’ 전역 변수, 정적 변수
- .symtab
- 심볼 테이블: 함수, 변수의 심볼을 심볼 이름(str)과 매핑할 수 있는 정보가 있음
- num: 일종의 id인듯?
- value: 실제 함수가 있는 주소
- .strtab
- 스트링 테이블: 함수 이름이 있는 테이블
0x1부터 다음null까지 → “hello.c\0” (+8 bytes)0x9부터 다음null까지 → “main\0” (+5 bytes)0xE(014)부터 다음null까지 → “puts\0” (+ 5bytes)- 제거(
strip) - .symtab과 .strtab은 정적 링크가 완료된 이후에는 필요없는 내용.
- 참고
static int a = 123;은 .data에 담기는가?
그렇다. 함수 호출을 반복해도 유지해야 하기 때문이다. (전역변수와 생명주기가 비슷)
‘프로그램이 0으로 초기화를 하고 시작해야 하는가?’ 가 기준.
bss영역은 프로그램이 시작할 때 초기화하고 시작함.


총 18 bytes → 0x13 bytes

따라서 공유 라이브러리나 실행 파일에서는 직접 제거하곤 한다.

정의하기
정적 링킹 & 로딩이 무엇인가?
정적 링킹
= 심볼테이블 병합 및 재구성
링킹 시 참조 대상을 모두 resolve(symbol resolution) 후, 바이너리에 탑재.

정적 로딩
별거없고, 실행 시 메모리에 올림(로드)
동적 로딩 & 동적 링킹이 무엇인가?
정적 링킹 시 참조 대상의 링크 정보만을 탑재,
load-time에 필요한 라이브러리를 메모리에 로드 및 재배치,
실제 함수 호출시 다이나믹 링커가 라이브러리의 함수로 링크한다.
링크 및 로드 과정
정적 링킹 →
- (symbol resolution 과정을 통해) 바이너리에 함수 및 그 주소가 내장된다. (링킹)
- ex) 정적 링킹을 한 경우, 다른 목적 파일 의 함수 기계어는 .text에, 정적 변수는 .data/.bss에 이미 내장되어있다.
결과적으로 .text 부분에 해당 함수 주소로 바로 점프하도록 박혀있어 함수 호출에 별도 symbol resolution이 필요없다.
정적 로딩 →
- 로드 시 메모리에 그대로 적재한다. (일부 section 제외)

동적 로딩 →
*여기서부터는 동적 링킹을 사용한 경우에만 해당한다.(정적 링킹한다면, 이미 symbol resolution이 끝나있다)
- (참고) ‘동적 링킹을 사용할 때’의 정적 링킹
- 정적 링킹 시에 함수를 특정할 수 있는 정보만 삽입
- (참고)
동적 링킹이 필요한 경우 정적 링킹 과정에서 심볼들이 삽입된다.
→ .dynsym, .dynstr에 symbol resolution을 위한 정보 삽입
gcc는 별도로 옵션 (-static)을 지정하지 않으면 동적 링킹 방식을 사용한다.
그러나 이 때에도 지정한 목적 파일 (gcc add.o sub.o)는 정적 링킹된다.
(공유 라이브러리 .so가 아니므로, 동적 로드가 불가하기 때문이다)

- 라이브러리 로드
- 커널이 .interp를 참고해 ld.so를 로드한다(프로세스 실행 과정의 일부)
- ld.so의
dl_start()→dl_map_object()n회 호출 (라이브러리 갯수만큼) - .dynamic 세그먼트를 참고해 .so 로드
- 재배치
- ld.so의
dl_relocate_object()n회 호출 (라이브러리 갯수만큼)
세그먼트(로드된 섹션)에서 Read-only 세그먼트 할당

동적 링킹(lazy-binding)
- 함수 호출 시 → 정적 링킹에서 삽입된 정보를 통해 함수 호출로 lazy binding한다.
- 이어서 함수 호출이 일어남.
- 두번째 호출 부터는 바인딩이 필요 없음
gcc -static)이미 symbol resolution이 종료되었기 때문이다.
→ libc.so 등 외부 라이브러리도 이미 정적 링킹되었다. (자동임)
동적 링킹 과정(Detailed)
위의 내용 중 동적 로딩은 완료되었다고 보고, 그 이후 동적 링킹이 실제로 어떻게 수행되는지 살펴본다.
동적 링킹이 완료되지 않은 경우(첫 실행)

함수 호출하면 먼저 .plt의 해당 함수 영역으로 감
해당 영역은 .got.plt를 참조해 점프하도록 함
그러나 첫 호출시 .got.plt에는 함수@.plt 아래의 [ reloc offset 푸시 + [email protected]로 점프] 인스트럭션 부분 주소가 있음.
이걸 카운터 기준으로 정리하자면 함수@.plt에서 →(.got.plt 참조해서) 함수@.plt+6으로 갔다가 → .plt+0([email protected])으로 갔다가 → (.got.plt 참조해서) 실제 라이브러리의 dl_runtime_resolve 호출함
그러면 dl_runtime_resolve는 .rel.plt 참고해서 .got.plt의 해당 함수 주소를 실제 라이브러리의 주소로 수정하겠죠?
그러면 마침내 링킹이 끝나고, dl_runtime_resolve에서 .got.plt의 바뀐 주소 참조해서 점프시킴
외부에서 보면 알아서 링킹하고 함수 실행까지 한 것으로 보이겠죠?
- (0)
.text의 기계어 코드에 의해[email protected]로 점프(call) - (1)
[email protected]가 가리키는 곳으로 점프 - (2)
[email protected]+6로 점프 - (3)
.plt+0으로 점프 - (4)
0x8049ffc가 가리키는 곳(.got.plt 범위)으로 점프 - (5) _dl_runtime_resolve() 점프
- (6) 끝났으니까 .got.plt의 바뀐 주소 참조해서 점프
.text → .plt

.text → .plt — .got.plt 참조

[email protected] 참조
[email protected]을 봤더니 → [email protected]+6의 주소(0x0804845e)가 있음
아직 symbol resolution 이전이기 때문임.
동적 링킹이 끝난 상태라면, 라이브러리의 함수 주소가 있을것.
.text → .plt — .got.plt 참조 —> .plt
(1) → (1-a) 과정으로 프로그램 카운터는 [email protected]+6로 이동됨
.text → .plt — .got.plt 참조 —> .plt → .plt

여기서부터 reloc 오프셋을 푸시하고,
GOT 테이블에 할당된 주소를 라이브러리 함수 주소로 재할당
dl_fixup() 시 .rel.plt 참조용

_dl_runtime_resolve()를 호출하는 .plt+0으로 점프를 실행한다.
.text → .plt — .got.plt 참조 —> .plt → .plt → .got.plt

점프 전에 pushl 을 하는 것은 ‘링크맵 구조체 포인터’를 스택에 삽입하는 것(아직 몰라도 됨)
그 이후 0x8049ffc가 가리키는 곳(_dl_runtime_resolve()의 주소)으로 점프함
이렇게 하는 ASM은 ld.so:_dl_runtime_resolve()에 있음.
개쩌는 레퍼런스
- 자세히 말고, 전체적으로 참고하면 좋을 듯.
- ㅇㅇ
PLT와 GOT 자세히 알기 2 (with ‘yocto’)
이번 편에서는 Codegate 2015 본선 문제 였던 pwnable 분야의 ‘yocto’ 를 통해 PLT 와 GOT에 대해 자세히 알아보겠습니다. 과정이 조금 복잡하기 때문에, 보시면서 따라 해 보는 것이 이해 하는 데 조금 더 도움이 될 것 같습니다. :) PLT와 GOT 자세히 알기 1 그럼 본격적으로 함수의 호출 과정을 살…
bpsecblog.wordpress.com
Plt 와 Got
Plt와 Got 개념 PLTProcdedure Linkage Table의 약자로 프로시져들을 연결해 주는 테이블을 말합니다. 프로그램을 어떻게 구현 했느냐에 따라 달라지기도 합니다.실제 호출 코드를 담고 있으며 이 내용을 참조해 _dl_runtime_resolve가 수행되고, 실제 시스템 라이브러리 호출이 이루어 집니다. GOTGlobal Offset Table의 약자로 함수들의 주소를 담고 있는 테이블입니다. 라이브러리 함수를 호출할때 PLT가 GOT를 참조하게 됩니다.첫 호출시엔 코드의 영역의 벡터 주소가 들어가 있고, 두번째 호출부터 그 함수의 실제 주소가 들어가져 있다. 설명 위 C 코드를 보면 매우 간단하게 printf와 puts를 사용하고 있습니다. 그저 단순 출력 소스코드 입니다. 해당 파일..
kblab.tistory.com
Symbol Resolution — _dl_runtime_resolve()


이제 libcalc.so의 베이스에 더하면 → 함수 주소
.rel.plt를 참고해 .got.plt에 매핑해주면 됨.
.plt, .got, .got.plt는 함수 호출 시 항상 참고함
반면 .dynsym, .dynstr은 ld.so의 _dl_runtime_resolve() 가 symbol resolution 과정에서 참고하는 용도 (동적 링킹시에만 쓰임)
그래서 .text 영역의 코드를 호출하면 → .plt로 갔다가 → 바로 .got.plt로 점프시키고, .got.plt
의문점(주제 외)
Base Address & Entry Point

- Base address 자체는 ‘시작 주소’라는 의미. 여기에 offset을 더해서 상대 주소를 Virtual Address로 바꿈.
- 프로세스의 base address는 이미지(바이너리) 베이스가 시작하는 주소
- 보통 0x1000부터 시작된다고 하는데, 그 이전은 보안을 위해 비워놓는다 함.
- Entry point는 프로그램 진입점
- main() 근처

동적 링킹에 재배치 과정이 포함되나?
아니다. 재배치 과정은 동적 로딩의 일부.
동적 로딩(+재배치) 이후 동적 링킹이 수행된다
PIC(아직 잘모르겠음)
상대주소로 되어있음.
.dynsym, .dynstr은 필수인가?
- 동적 링킹의 필수요소인가? 혹은 인간이 보기 위한 부가정보인가?(.symtab, .strtab 처럼?)
- strip하면 사라지는가?
- Reference
필수 요소이다.
정적 링킹을 한 경우, 이미 symbol resolution이 끝났으므로 필요없으나, 동적 링킹이 필요한 경우 필수.
NO
What symbol tables stay after a strip In ELF format?
I am currently looking at the ELF format, and especially at stripped ELF executable program files. I know that, when stripped, the symbol table is removed, but some information are always needed t...
reverseengineering.stackexchange.com
중간중간 참고할 자료
물리 및 가상 메모리 영역

- 물리 메모리가 1GB인데 어떻게 4GB를 사용중이죠?
- 스왑 메모리
프로세스 메모리 구조

- 커널 영역은 어디?
- 가상 메모리의 끝부분(0xC0000000)은 커널 영역으로 사용된다
- base address 이전은?
프로세스 생명주기

의문점
- 정적 링킹이랑 동적 링킹이 구분이 잘 안됨.
- 정적 로드 후, 일부를 동적 로드하는 것이다.
- 둘은 대비되는 개념이 아니라 기본이 정적 로드, 동적 로드는 필요한 경우 발생하는 추가적인 절차
Q. 정적 링킹 → 정적 로드 되어야 하는가?
A. 이건 모든 프로그램의 필수 조건
Q. 동적 링킹을 사용한 경우는?