🔐
땡칠로그/🔐시스템 보안 정리 - ELF & 링킹

시스템 보안 정리 - ELF & 링킹

태그
수업
CS
정리
완료 일시
Apr 11, 2024
  • 프로그램 로드
    • 섹션은 어떻게 실제 메모리에 매핑되는가?
  • 페이징, 페이지드 세그멘테이션

ELF

Executable and Linkable Format
실행 가능하거나, 링크할 수 있는 파일들의 사전 정의된 구조(Format)
초록색 영역이 ELF 파일들
초록색 영역이 ELF 파일들
ex) .o, .so, .out(실행파일)

ELF 구조

프로그램은 메모리에 올라가기 전, 디스크에 있는 바이너리를 말함.

(참고) 섹션 vs 세그먼트

notion image
프로그램의 영역 구분 - 섹션 → 잘게 쪼개져있음
프로세스의 영역 구분 - 세그먼트 → 4개(큰 단위)
프로그램의 영역 구분 - 섹션 → 잘게 쪼개져있음 프로세스의 영역 구분 - 세그먼트 → 4개(큰 단위)
링킹하는 관점이냐?, 실행하는 관점이냐? 에 따라 이름이 다름.
→ 따라서 ‘readelf 를 한다’ → 단위는 ‘섹션’ → readelf -S

프로세스 ELF 구조 (Segments)

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

정의하기

정적 링킹 & 로딩이 무엇인가?

정적 링킹

= 심볼테이블 병합 및 재구성
링킹 시 참조 대상을 모두 resolve(symbol resolution) 후, 바이너리에 탑재.
notion image

정적 로딩

별거없고, 실행 시 메모리에 올림(로드)

동적 로딩 & 동적 링킹이 무엇인가?

정적 링킹 시 참조 대상의 링크 정보만을 탑재,
load-time에 필요한 라이브러리를 메모리에 로드 및 재배치,
실제 함수 호출시 다이나믹 링커가 라이브러리의 함수로 링크한다.
💡

링크 및 로드 과정

정적 링킹 →

  • (symbol resolution 과정을 통해) 바이너리에 함수 및 그 주소가 내장된다. (링킹)
    • 결과적으로 .text 부분에 해당 함수 주소로 바로 점프하도록 박혀있어 함수 호출에 별도 symbol resolution이 필요없다.
    • ex) 정적 링킹을 한 경우, 다른 목적 파일 의 함수 기계어는 .text에, 정적 변수는 .data/.bss에 이미 내장되어있다.

정적 로딩 →

  • 로드 시 메모리에 그대로 적재한다. (일부 section 제외)
    • notion image

동적 로딩 →

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

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

동적 링킹(lazy-binding)

  • 두번째 호출 부터는 바인딩이 필요 없음
💡
정적 링킹을 명시한 경우 동적 링킹이 일어나지 않는다(gcc -static)
이미 symbol resolution이 종료되었기 때문이다.
→ libc.so 등 외부 라이브러리도 이미 정적 링킹되었다. (자동임)

동적 링킹 과정(Detailed)

위의 내용 중 동적 로딩은 완료되었다고 보고, 그 이후 동적 링킹이 실제로 어떻게 수행되는지 살펴본다.

동적 링킹이 완료되지 않은 경우(첫 실행)

notion image
💡
6/12 다시 간단 정리
함수 호출하면 먼저 .plt의 해당 함수 영역으로 감
해당 영역은 .got.plt를 참조해 점프하도록 함
그러나 첫 호출시 .got.plt에는 함수@.plt 아래의 [ reloc offset 푸시 + dl_runtime_resolve@.plt로 점프] 인스트럭션 부분 주소가 있음.
이걸 카운터 기준으로 정리하자면 함수@.plt에서 →(.got.plt 참조해서) 함수@.plt+6으로 갔다가 → .plt+0(dl_runtime_resolve@.plt)으로 갔다가 → (.got.plt 참조해서) 실제 라이브러리의 dl_runtime_resolve 호출함
그러면 dl_runtime_resolve는 .rel.plt 참고해서 .got.plt의 해당 함수 주소를 실제 라이브러리의 주소로 수정하겠죠?
그러면 마침내 링킹이 끝나고, dl_runtime_resolve에서 .got.plt의 바뀐 주소 참조해서 점프시킴
 
외부에서 보면 알아서 링킹하고 함수 실행까지 한 것으로 보이겠죠?
  • (0) .text 의 기계어 코드에 의해 add@.plt로 점프(call)
    • .text → .plt
      notion image
  • (1) add@.got.plt 가 가리키는 곳으로 점프
    • .text → .plt — .got.plt 참조
      notion image
      ↘️
      (1-a) add@.got.plt 참조
      notion image
      add@.got.plt을 봤더니 → add@.plt+6의 주소(0x0804845e)가 있음
      아직 symbol resolution 이전이기 때문임.
      동적 링킹이 끝난 상태라면, 라이브러리의 함수 주소가 있을것.
  • (2) add@.plt+6로 점프
    • .text → .plt — .got.plt 참조 —> .plt
      (1) → (1-a) 과정으로 프로그램 카운터는 add@.plt+6로 이동됨
  • (3) .plt+0 으로 점프
    • .text → .plt — .got.plt 참조 —> .plt → .plt
      notion image
      여기서부터 reloc 오프셋을 푸시하고,
      💡
      reloc offset?
      GOT 테이블에 할당된 주소를 라이브러리 함수 주소로 재할당
      dl_fixup() 시 .rel.plt 참조용
      notion image
      _dl_runtime_resolve()를 호출하는 .plt+0으로 점프를 실행한다.
  • (4) 0x8049ffc가 가리키는 곳(.got.plt 범위)으로 점프
    • .text → .plt — .got.plt 참조 —> .plt → .plt → .got.plt
      notion image
      점프 전에 pushl 을 하는 것은 ‘링크맵 구조체 포인터’를 스택에 삽입하는 것(아직 몰라도 됨)
      그 이후 0x8049ffc가 가리키는 곳(_dl_runtime_resolve()의 주소)으로 점프함
  • (5) _dl_runtime_resolve() 점프
  • (6) 끝났으니까 .got.plt의 바뀐 주소 참조해서 점프
    • 이렇게 하는 ASM은 ld.so:_dl_runtime_resolve()에 있음.

개쩌는 레퍼런스

Symbol Resolution — _dl_runtime_resolve()

notion image
notion image
이제 libcalc.so의 베이스에 더하면 → 함수 주소
.rel.plt를 참고해 .got.plt에 매핑해주면 됨.
 

💡
.plt, .got, .got.plt 🆚 .dynsym, .dynstr
.plt, .got, .got.plt는 함수 호출 시 항상 참고함
반면 .dynsym, .dynstr은 ld.so의 _dl_runtime_resolve() 가 symbol resolution 과정에서 참고하는 용도 (동적 링킹시에만 쓰임)
그래서 .text 영역의 코드를 호출하면 → .plt로 갔다가 → 바로 .got.plt로 점프시키고, .got.plt



의문점(주제 외)

Base Address & Entry Point

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

동적 링킹에 재배치 과정이 포함되나?

아니다. 재배치 과정은 동적 로딩의 일부.
동적 로딩(+재배치) 이후 동적 링킹이 수행된다

PIC(아직 잘모르겠음)

상대주소로 되어있음.

.dynsym, .dynstr은 필수인가?

  • 동적 링킹의 필수요소인가? 혹은 인간이 보기 위한 부가정보인가?(.symtab, .strtab 처럼?)
    • 필수 요소이다.
      정적 링킹을 한 경우, 이미 symbol resolution이 끝났으므로 필요없으나, 동적 링킹이 필요한 경우 필수.
  • strip하면 사라지는가?
    • NO

중간중간 참고할 자료

물리 및 가상 메모리 영역

notion image
  • 물리 메모리가 1GB인데 어떻게 4GB를 사용중이죠?
    • 스왑 메모리

프로세스 메모리 구조

notion image
  • 커널 영역은 어디?
    • 가상 메모리의 끝부분(0xC0000000)은 커널 영역으로 사용된다
  • base address 이전은?

프로세스 생명주기

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