Step 66. 실행 파일의 구조 — 아이콘 속의 설계도
Level 1 — 프로그래밍과 컴퓨터 내부 | 난이도 ★★★☆☆ | 예상 소요 시간 3시간
전제: Step 56~65를 마쳤다. C 프로그램을 gcc로 컴파일할 수 있고, 컴파일 과정과 어셈블리 출력을 한 번쯤 봤다.
- 준비물: 리눅스 터미널(WSL 또는 우분투)과 gcc. 오늘 쓰는 readelf, nm, objdump, strings, xxd, strip은 gcc와 함께 딸려 오는 도구들이라 별도 설치가 필요 없습니다.
- 주의: 오늘 실습은 100% 안전합니다. 우리가 직접 만든 실행 파일을 "읽기만" 합니다. 시스템 파일이나 남의 프로그램은 건드리지 않습니다.
우리가 만든 실행 파일은 겉보기엔 그저 아이콘 하나입니다. 그런데 안을 열어 보면 정돈된 구조가 있습니다. 앞부분에 설명서, 그 뒤에 코드 구역, 데이터 구역이 차례로 붙은 모양새입니다. 이 구조를 배우는 이유는 하나입니다 — 앞으로 만날 온갖 분석 도구(리버싱 도구, 디버거)는 전부 이 구조를 파싱해서 보여 줍니다. 구조를 모르면 도구의 출력이 외계어이고, 알면 지도가 됩니다. 오늘은 명령 여섯 개로 우리가 만든 실행 파일을 직접 헤집니다. 우리가 만든 파일이라 무엇이 나와도 두렵지 않습니다.
1. 학습 목표
이 챕터를 끝내면 다음을 할 수 있습니다:
- 실행 파일이 "설명서(헤더) + 용도별 구역(섹션)"으로 이루어져 있음을 설명한다
- readelf로 ELF 헤더와 섹션 목록을 읽는다
- .text, .data, .bss 세 섹션의 역할을 말한다
- strings와 nm으로 실행 파일의 단서를 뽑는다
- strip이 무엇을 떼어 내는지, 왜 그것이 분석을 어렵게 하는지 설명한다
2. 배경 지식 — 오늘의 도구와 개념
오늘의 도구 한눈에 보기
| 구분 | 내용 |
|---|---|
| 언어·환경 | C 언어 + 리눅스 터미널 (WSL 우분투, gcc 13.3.0에서 실측) |
| 오늘의 명령어 | file(파일 신분 확인), xxd(바이트 그대로 보기), readelf -h/-S(헤더/섹션 읽기), nm(심볼 목록), objdump -d/-h(기계어/섹션 지도), strings(문자열 추출), strip(심볼 제거) |
| 필요한 개념 | ELF, PE, 헤더, 섹션(.text/.data/.bss), 심볼, 매직 넘버 |
| 오늘의 산출물 | 실행 파일 헤집기 리포트 — 우리가 만든 파일의 신분증·구역 지도·이름표를 정리한 문서 |
2-1. ELF와 PE — 두 세계의 파일 형식
리눅스의 실행 파일 형식은 ELF(Executable and Linkable Format), 윈도우의 것은 PE(Portable Executable)입니다. 이름은 다르지만 하는 일은 같습니다. "이 파일이 무엇이고, 어디에 코드가 있고, 어디서 시작하는가"를 담는 약속된 구조입니다. 우리는 리눅스에서 공부하니 ELF가 주인공이고, PE는 개념 비교로만 다룹니다.
2-2. 헤더 — 파일의 신분증
파일 맨 앞에는 헤더(header)가 있습니다. "나는 ELF다", "64비트용이다", "x86-64 CPU용이다", "실행은 이 주소부터 시작해라(진입점, entry point)" 같은 정보가 약속된 자리에 적혀 있습니다. 운영체제는 이 신분증을 보고 실행을 준비합니다.
2-3. 섹션 — 용도별 구역
헤더 뒤에는 용도별 구역인 섹션(section)들이 붙습니다. 대표적인 세 개만 기억하세요.
- .text: 기계어 코드가 사는 구역. 이름은 text지만 코드입니다. 역사적으로 내려오는 이름입니다.
- .data: 초깃값이 있는 전역 변수들이 사는 구역.
- .bss: 초깃값이 없는(0으로 시작하는) 전역 변수들의 자리 표시. "자리만 잡아 둬"라는 예약 구역이라 실제 내용은 파일에 없습니다.
Step 60의 메모리 지도(코드/데이터 구역)가 파일 안에도 그대로 새겨져 있는 셈입니다. 파일의 섹션은 실행될 때 메모리 구역으로 실려 올라갑니다.
2-4. 심볼 — 이름표들
함수와 변수의 이름표(main, add 같은)를 심볼(symbol)이라고 합니다. 심볼이 있으면 분석 도구가 "이 코드 덩어리는 main입니다"라고 이름으로 보여 줄 수 있습니다. strip이라는 도구로 이 이름표들을 떼어 낼 수 있는데, 그러면 분석이 어려워집니다. 세상에 공개되는 프로그램 대부분이 strip된 상태이고, 그것이 리버싱의 난이도를 올리는 첫 번째 장치입니다.
3. 따라 하기
3-1. 실험용 파일 준비
오늘의 실험 대상을 직접 만듭니다. 초깃값 있는 전역 변수, 없는 전역 변수, 정적 변수, 함수를 골고루 넣은 것이 포인트입니다.
입력 (reg.c)
#include <stdio.h>
int g_init = 42; /* 초깃값 있는 전역 변수 → .data 후보 */
int g_zero; /* 초깃값 없는 전역 변수 → .bss 후보 */
static int s_init = 7; /* 정적 변수 → .data 후보 */
int add(int a, int b) { return a + b; }
int main(void) {
int z = add(g_init, s_init);
printf("z = %d\n", z);
printf("g_zero = %d\n", g_zero);
return 0;
}
입력
gcc -g -O0 reg.c -o reg
./reg
출력 (2026-09-09 실측):
z = 49
g_zero = 0
읽는 법: -g는 심볼과 디버그 정보를 살려 두라는 옵션이고, -O0은 "최적화하지 마라"라는 뜻이라 우리가 쓴 코드 그대로의 구조가 유지됩니다. g_zero는 한 번도 값을 준 적이 없는데 0이 찍힙니다 — 초깃값 없는 전역 변수는 0으로 시작한다는 약속의 확인입니다.
3-2. file과 매직 넘버 — 파일의 진짜 신분
입력
file reg
xxd reg | head -2
출력 (2026-09-09 실측):
reg: ELF 64-bit LSB pie executable, x86-64, version 1 (SYSV), dynamically linked, interpreter /lib64/ld-linux-x86-64.so.2, ..., with debug_info, not stripped
00000000: 7f45 4c46 0201 0100 0000 0000 0000 0000 .ELF............
00000010: 0300 3e00 0100 0000 6010 0000 0000 0000 ..>.....`.......
읽는 법: file은 파일의 진짜 형식을 알려 줍니다. "ELF 64비트, x86-64용, 심볼이 안 떼어진(not stripped) 상태"라고 답했습니다. xxd는 파일을 16진수로 그대로 보여 주는 바이트 수준의 현미경입니다. 첫 네 바이트 7f 45 4c 46이 보이죠. 45 4c 46은 ASCII로 "ELF"입니다 — 모든 ELF 파일이 이 인사로 시작하는데, 이런 첫 바이트들을 매직 넘버(magic number)라고 합니다. JPG는 ff d8로, PNG는 89 50 4e 47로, ZIP(과 docx, xlsx)은 50 4b로 시작합니다.
왜: 파일의 진짜 신분은 확장자가 아니라 이 첫 바이트들입니다. 아래 예측 실험이 그 증명입니다.
예측:
cp reg fake.jpg로 이름을 바꾼 뒤file fake.jpg를 하면 file은 뭐라고 답할까요? 확장자를 믿을까요, 내용을 볼까요? (2026-09-09 실측 답:fake.jpg: ELF 64-bit LSB pie executable, x86-64, ...— 확장자가 아니라 매직 넘버를 봅니다.) 이름만 .jpg로 바꾼 파일을 가려내는 것이 매직 넘버 검사의 쓸모입니다.
3-3. readelf -h — 신분증 읽기
입력
readelf -h reg
출력 (2026-09-09 실측, 주요 항목):
ELF Header:
Magic: 7f 45 4c 46 02 01 01 00 00 00 00 00 00 00 00 00
Class: ELF64
Data: 2's complement, little endian
Type: DYN (Position-Independent Executable file)
Machine: Advanced Micro Devices X86-64
Entry point address: 0x1060
읽는 법: 다섯 줄이면 신분 파악이 끝납니다. Magic은 3-2에서 본 그 인사말이고, Class는 64비트, Machine은 x86-64 CPU용, Entry point(진입점)는 실행이 시작되는 주소입니다. Type의 DYN은 "어느 주소에 실려도 실행되는(ASLR 친화적인) 형식"이라는 뜻으로, 요즘 gcc의 기본 산출물입니다.
예측: 부품 파일(.o)에 readelf -h를 하면 Type이 뭐라고 나올까요?
gcc -c reg.c -o reg_part.o로 부품을 만들어 확인해 보세요. (2026-09-09 실측 답:REL (Relocatable file)— 아직 주소가 확정되지 않은 "재배치 가능한 부품"입니다. DYN인 완성품과 다른 신분입니다.)
3-4. readelf -S — 구역 지도 펼치기
입력
readelf -S reg | grep -E "Nr|text|\.data|\.bss"
출력 (2026-09-09 실측):
[Nr] Name Type Address Offset
[16] .text PROGBITS 0000000000001060 00001060
[25] .data PROGBITS 0000000000004000 00003000
[26] .bss NOBITS 0000000000004018 00003018
읽는 법: 세 구역이 보입니다. .text가 0x1060에서 시작합니다 — 3-3의 진입점과 같은 번지입니다. 실행은 코드 구역에서 시작된다는 연결을 눈으로 확인한 것입니다. .bss는 Type이 NOBITS인데, "파일 안에 실제 내용은 없고 자리만 있다"는 뜻입니다. 0으로 시작할 변수들이라 파일에 낭비할 필요가 없는 것입니다.
왜: "파일의 구역 = 실행될 때 메모리의 구역"이라는 대응이 보이면, 실행 파일을 보는 눈이 지도 읽기로 바뀝니다. 참고로 grep을 안 쓰면 이 파일에는 섹션이 37개(2026-09-09 실측, readelf -h의 Number of section headers)나 나옵니다. 우리가 만든 것 외에 부가 정보 구역이 많으니, 찾을 것을 정하고 거르는 것이 기본기입니다.
3-5. nm — 이름표 목록과 변수의 소속 확인
입력
nm reg | grep -E " g_init| g_zero| s_init| add$| main$"
출력 (2026-09-09 실측):
0000000000001149 T add
0000000000004010 D g_init
000000000000401c B g_zero
0000000000001161 T main
0000000000004014 d s_init
읽는 법: nm은 심볼(이름표) 목록을 주소와 함께 보여 줍니다. 주소 뒤의 알파벳이 소속 구역입니다. T는 .text(코드), D는 .data, B는 .bss입니다. 우리 예측대로 g_init과 s_init은 D(0x4010, 0x4014 — .data 구역 0x4000 안), g_zero는 B(0x401c — .bss 구역 0x4018 안)에 배치됐습니다. 코드 속 변수들이 약속된 구역으로 정확히 들어간 것을 번호로 확인한 것입니다.
3-6. objdump -d와 -h — 같은 지도, 다른 도구
nm이 알려 준 add의 주소 0x1149가 진짜인지, 기계어를 직접 열어 확인해 봅시다.
입력
objdump -d reg | sed -n "/<add>:/,/ret/p"
출력 (2026-09-09 실측):
0000000000001149 <add>:
1149: f3 0f 1e fa endbr64
114d: 55 push %rbp
...
115d: 01 d0 add %edx,%eax
115f: 5d pop %rbp
1160: c3 ret
읽는 법: objdump -d는 .text 구역의 기계어를 어셈블리로 되돌려 보여 줍니다(디스어셈블리). 시작 주소가 정확히 nm가 말한 1149입니다. 도구 둘이 서로 같은 지도를 보고 있다는 증거입니다. 01 d0이라는 두 바이트가 add %edx,%eax — 우리가 쓴 a + b의 기계어입니다.
입력
objdump -h reg | grep -E "Idx|text|\.data|\.bss"
출력 (2026-09-09 실측):
Idx Name Size VMA LMA File off Algn
15 .text 00000161 0000000000001060 0000000000001060 00001060 2**4
24 .data 00000018 0000000000004000 0000000000004000 00003000 2**3
25 .bss 00000008 0000000000004018 0000000000004018 00003018 2**2
읽는 법: readelf -S와 같은 구역들이 조금 다른 서식으로 나옵니다. .text 크기는 0x161바이트, VMA(메모리에 실릴 주소)는 0x1060으로 readelf와 일치합니다. 도구는 달라도 지도는 하나입니다. 한 도구가 막혔을 때 다른 도구로 교차 확인하는 것이 분석가의 습관입니다.
3-7. strings — 파일 속 문장 뒤지기
입력
strings reg | grep -E "z =|g_zero"
strings reg | grep -i "gcc"
출력 (2026-09-09 실측):
z = %d
g_zero = %d
g_zero
g_zero
GCC: (Ubuntu 13.3.0-6ubuntu2~24.04.1) 13.3.0
읽는 법: strings는 파일에서 "사람이 읽을 수 있는 문자열"만 골라 보여 줍니다. 우리가 printf에 적어 둔 "z = %d"가 그대로 들어 있습니다. 의외의 수확은 두 번째입니다 — 어떤 컴파일러로 만들었는지까지 파일에 새겨져 있습니다. 우리가 넣지 않은 정보인데 -g 옵션의 디버그 정보에 포함된 것입니다.
왜: 이 사실은 중요합니다. 실행 파일에는 문자열이 날것으로 남습니다. 비밀번호나 서버 주소를 코드에 박아 두면 strings 한 방에 보인다는 뜻입니다. CTF와 악성코드 분석에서 strings가 "일단 돌려 보는" 첫 명령인 이유입니다.
3-8. strip — 이름표 떼기와 그 후
입력
cp reg reg_stripped
strip reg_stripped
nm reg_stripped
strings reg_stripped | grep "z ="
ls -l reg reg_stripped
./reg_stripped
출력 (2026-09-09 실측):
nm: reg_stripped: no symbols
z = %d
-rwxr-xr-x 1 root root 17512 ... reg
-rwxr-xr-x 1 root root 14480 ... reg_stripped
z = 49
g_zero = 0
읽는 법: 이름표를 떼었더니 nm는 "심볼이 없다(no symbols)"고 답합니다. 그런데 문자열 "z = %d"는 살아 있고(심볼과 문자열은 다른 물건입니다), 실행도 똑같이 됩니다. 크기는 17512바이트에서 14480바이트로 줄었습니다. 이름표는 CPU가 아니라 사람과 도구를 위한 것이었으니까, 떼어도 실행에는 지장이 없는 것입니다.
왜: "코드는 남기고 이름만 떼는" 이 장치가 왜 분석을 어렵게 하는지 체감하는 것이 목표입니다. objdump로 열어도 코드는 그대로지만 "이 함수는 main입니다"라는 이름표가 사라지므로, 분석가는 기계어를 읽으며 "이쯤이 main이겠군"을 추론해야 합니다. 세상의 프로그램 대부분이 이 상태입니다.
4. 미션과 연습문제
미션 — 실행 파일 헤집기 리포트
- 직접 만든 C 프로그램(초깃값 있는 전역 변수, 없는 전역 변수, 함수 둘 이상 포함)을
-g -O0으로 컴파일합니다 - readelf -h로 신분증 다섯 줄(Magic, Class, Type, Machine, Entry)을 옮겨 적고 각각 뜻을 답니다
- readelf -S로 .text, .data, .bss를 찾아 주소를 기록하고, 자신의 전역 변수가 어느 섹션에 갔는지 nm과 대조합니다
- strings로 찾은 흥미로운 문자열 다섯 개를 기록합니다. "내가 넣지 않았는데 보이는 것"이 있으면 왜 보이는지 추론합니다
- 복사본을 strip하고, nm·objdump·strings 세 도구의 출력이 어떻게 달라지는지 대조표를 만듭니다
- 리포트 말미에 답합니다: ".text, .data, .bss의 역할을 각각 한 문장으로."
연습문제
문제 1. readelf -S 출력에서 .bss의 Type만 NOBITS로 다릅니다. PROGBITS와 NOBITS의 차이를 "파일에 내용이 있는가"의 관점으로 설명해 보세요.
문제 2. 어떤 실행 파일에 nm을 쳤더니 no symbols가 나왔습니다. 이것이 고장인가요? 이 한 줄의 정보가 분석가에게 주는 단서는 무엇인가요?
문제 3. cp reg fake.jpg 후 file fake.jpg가 "ELF executable"이라고 답하는 이유를 매직 넘버 관점에서 설명해 보세요.
문제 4. strip한 파일에서도 strings로 "z = %d"가 보이는데, nm에서는 main이 안 보입니다. 심볼과 문자열의 차이를 이 결과로 설명해 보세요.
5. 모범 답안과 완료 기준
미션 모범 답안
리포트의 뼈대 예시입니다 (수치는 2026-09-09 실측 reg 기준 — 여러분의 파일은 주소가 다를 수 있습니다):
[신분증]
Magic: 7f 45 4c 46 ... → 모든 ELF 파일의 인사말(\x7f + "ELF")
Class: ELF64 → 64비트용
Type: DYN → 어느 주소에 실려도 되는 ASLR 친화 형식
Machine: X86-64 → x86-64 CPU용
Entry: 0x1060 → 실행 시작 주소, .text 구역의 시작과 같음
[구역 지도]
.text 0x1060 / .data 0x4000 / .bss 0x4018
nm 대조: g_init 0x4010(D)와 s_init 0x4014(d)는 .data 안, g_zero 0x401c(B)는 .bss 안
[strings 수확]
"z = %d", "g_zero = %d" (내가 적은 것), /lib64/ld-linux-x86-64.so.2 (실행에 필요한
라이브러리 경로), "GCC: (Ubuntu 13.3.0...) 13.3.0" (컴파일러 신분 — -g 디버그 정보)
[strip 대조표]
nm objdump -d strings
원본 main/add <main>: 이름 있음 z = %d 보임
strip본 no symbols 이름표 사라짐 z = %d 그대로
[한 문장씩]
.text: 기계어 코드가 사는 구역. .data: 초깃값 있는 전역 변수의 구역.
.bss: 0으로 시작하는 전역 변수의 자리 표시(파일에 내용 없음).
검증하는 법: ① nm 출력에서 내 전역 변수들의 알파벳(D/B)이 예측과 일치하는가. ② strip 후에도 프로그램이 똑같이 실행되는가. ③ strings 수확에 "내가 안 넣은 문자열"이 하나 이상 포함됐는가. 셋이 전부 ‘예’이면 완성입니다.
연습문제 해답
문제 1 해답. PROGBITS는 파일 안에 실제 내용(바이트)이 들어 있는 섹션이고, NOBITS는 파일에는 자리(크기와 주소)만 적혀 있고 내용은 없는 섹션입니다. .bss는 어차피 0으로 채워질 변수들이라 파일에 0을 기록해 둘 필요가 없어서 — 실행 시 운영체제가 메모리에 자리를 마련하며 0으로 채웁니다.
문제 2 해답. 고장이 아니라 정보입니다. strip됐거나 처음부터 심볼 없이 빌드된 파일이라는 뜻입니다. 분석가에게는 "이름표 없이 기계어만 읽어야 하는 파일이구나"라는 전략 수립의 첫 단서가 됩니다 (2026-09-09 실측 메시지: nm: reg_stripped: no symbols).
문제 3 해답. file 명령은 확장자가 아니라 파일 내용의 첫 바이트(매직 넘버)를 보고 형식을 판별하기 때문입니다. reg의 첫 네 바이트 7f 45 4c 46은 이름을 바꿔도 그대로이므로, file은 "ELF"라고 정확히 짚어 냅니다 (2026-09-09 실측). 확장자는 사람을 위한 라벨이고, 매직 넘버가 진짜 신분증입니다.
문제 4 해답. 심볼은 "이 주소의 코드/데이터는 이런 이름이다"라는 이름표로, strip이 제거하는 대상입니다. 문자열은 프로그램이 출력할 내용으로 .rodata 같은 데이터 섹션의 실제 내용이라, 이름표를 떼어도 남습니다. 코드 실행에 필요한 것(기계어, 문자열)은 남고 사람을 위한 것(이름표)만 사라지는 것이 strip입니다.
완료 기준 체크리스트
- [ ] 실행 파일이 헤더와 섹션으로 구성됨을 설명할 수 있다
- [ ] readelf -h의 주요 항목(Magic, Class, Type, Machine, Entry)을 읽을 수 있다
- [ ] .text, .data, .bss의 역할을 말할 수 있다
- [ ] nm의 알파벳(T/D/B)으로 심볼의 소속 구역을 알아낼 수 있다
- [ ] strings와 strip의 관계(문자열은 남고 이름표는 사라짐)를 설명할 수 있다
- [ ] file이 확장자가 아닌 매직 넘버를 본다는 것을 실험으로 확인했다
- [ ] 미션: 실행 파일 헤집기 리포트를 완성했다
6. 흔한 실수와 해결
벽 1. 출력이 너무 길다
증상: readelf -S를 쳤더니 수십 개의 섹션이 쏟아집니다 (실측에서도 섹션이 37개였습니다).
원인: 실행 파일에는 우리가 만든 것 외에 부가 정보 구역이 많습니다.
해결: grep으로 거릅니다. readelf -S reg | grep -E "text|data|bss". 출력을 다 읽으려 하지 말고, 찾을 것을 정하고 거르는 것이 터미널 작업의 기본기입니다.
벽 2. nm에서 no symbols가 나온다
증상 (2026-09-09 실측):
nm: reg_stripped: no symbols
원인: 두 경우입니다. strip된 파일이거나, 처음부터 심볼 없이 빌드된 파일.
해결: 고장이 아니라 정보입니다. "이 파일은 이름표가 떼어진 상태구나"가 분석 전략을 정하는 첫 단서입니다. 3-8에서 직접 그 상태를 만들어 봤으니, 이제 이 메시지는 당황이 아니라 단서입니다.
벽 3. 주소가 실행할 때와 다르다
증상: readelf의 주소(0x1060 같은 작은 숫자)와 실행 중에 찍히는 주소(0x55… 같은 큰 숫자)가 다릅니다.
원인: Type이 DYN(위치 독립 실행 형식)이면 실행 때마다 기준점이 움직입니다(ASLR — Step 70에서 직접 관측합니다). 파일 속 주소는 "기준점으로부터의 거리"로 해석됩니다.
해결: 지금은 "같은 파일 안의 상대적 배치"를 보는 데 집중하세요. 주소의 절댓값보다 섹션들의 순서와 관계가 본질입니다.
벽 4. 윈도우 .exe에 readelf를 쓴다
증상: .exe 파일을 가져다 readelf를 쳤더니 안 읽힙니다.
원인: 윈도우는 ELF가 아니라 PE 형식입니다.
해결: 개념은 같습니다(헤더 + 섹션 + 심볼). 도구만 다릅니다. 나중에 윈도우 분석을 할 때 PE용 도구를 만나게 되지만, 오늘 배운 "신분증 + 구역 + 이름표"라는 세 층의 사고방식은 그대로 씁니다.
벽 5. grep 패턴이 텍스트도 덩그러니 가져온다
증상: grep text를 하면 .text 말고 .note.gnu.property 같은 엉뚱한 줄도 섞여 나옵니다.
원인: grep은 글자가 포함된 줄을 전부 가져오기 때문입니다.
해결: 정규식을 조금 더 정확히 씁니다. grep -E "Nr|text|\.data|\.bss"처럼요. \.는 "진짜 점 하나"라는 뜻의 이스케이프입니다. 처음엔 줄이 좀 섞여 나와도 괜찮습니다 — 눈으로 한 번 더 거르면 됩니다.
7. 정리
오늘의 개념
| 개념 | 한 줄 설명 |
|---|---|
| ELF / PE | 리눅스 / 윈도우의 실행 파일 형식 — "헤더 + 섹션" 구조는 같다 |
| 헤더 | 파일 맨 앞의 신분증 (형식, 비트 수, CPU, 진입점) |
| .text / .data / .bss | 기계어 코드 / 초깃값 있는 전역 변수 / 0 시작 변수의 자리 표시 |
| 심볼 | 함수·변수의 이름표 — 있으면 분석이 쉽고, strip으로 뗄 수 있다 |
| 매직 넘버 | 파일 첫 바이트들의 형식 인사말 — ELF는 7f 45 4c 46 |
| 진입점(entry point) | 실행이 시작되는 주소 — .text 구역 안에 있다 |
오늘의 명령어
| 명령어 | 하는 일 |
|---|---|
file 파일 |
매직 넘버로 파일의 진짜 형식 확인 |
xxd 파일 | head |
파일을 16진수로 그대로 보기 |
readelf -h 파일 |
ELF 헤더(신분증) 읽기 |
readelf -S 파일 |
섹션(구역) 목록 보기 |
nm 파일 |
심볼(이름표)과 주소 목록 |
objdump -d 파일 |
기계어를 어셈블리로 되돌려 보기 |
objdump -h 파일 |
섹션 지도를 readelf와 다른 서식으로 |
strings 파일 |
파일 속 읽을 수 있는 문자열 추출 |
strip 파일 |
심볼(이름표) 떼어 내기 |
명령어보다 중요한 감각
낯선 파일 하나를 받았을 때 오늘 배운 도구들로 접근하는 순서를 정리해 둡니다. (가) file로 신분 확인 — 진짜 형식이 무엇인가. (나) 매직 넘버 확인 — 확장자와 일치하는가. (다) strings로 속마음 훑기 — 보이는 문자열이 무엇인가. (라) readelf로 구조 보기. (마) 그다음에야 무거운 분석 도구. 이 순서의 미덕은 "가벼운 것부터"입니다. 십 초짜리 확인으로 걸러지는 것을 열 시간짜리 분석으로 하지 않겠다는 것. 수사는 언제나 싼 질문부터입니다.
보안과의 연결: 오늘의 가장 실용적인 교훈은 strings입니다. 실행 파일에는 문자열이 날것으로 남고 — 우리가 안 넣은 컴파일러 버전까지요 (2026-09-09 실측) — 그래서 개발자는 코드에 비밀을 박아 두지 않고, 분석가는 strings로 속마음부터 훑습니다. 그리고 파일 구조가 약속이라는 점도 기억하세요. 윈도우에서 만든 PDF를 리눅스에서 열 수 있는 것은 구조가 공개된 약속이기 때문이고, 약속을 아는 사람은 파일을 직접 만들 수도 이상한 파일을 감별할 수도 있습니다. 앞으로 패킷을 배울 때도 "헤더 + 구역"이라는 오늘의 사고방식이 그대로 쓰입니다. 구조를 읽는 눈은 하나 익히면 열 곳에 쓰입니다. 분석 연습은 내가 만든 파일과 분석이 허락된 파일에서만입니다.
전부 체크되면 Step 66 완료입니다. 사이드바의 체크박스를 눌러 진도를 저장하세요.