궁금한게 c언어에서 main()함수는 어떻게 보면 얘도 그냥 main이지만, 내가 이름 watermelon()으로 바꾸고 main역할하게 써도 문제 없지? 약속이고, 정해진 이름이지만, 결국 이것도 alias가 가능하다는건가?
📌 C언어에서 main() 함수 이름을 바꿀 수 있는가?
결론부터 말하면, "일반적인 실행 환경에서는 main()이 필수지만, 특정 조건에서는 이름을 바꿔서 사용할 수도 있다."
✅ 1. main()이 필수인 이유
C 언어에서 main() 함수는 프로그램의 진입점(entry point) 역할을 한다.
즉, 운영체제(OS)가 프로그램을 실행할 때 가장 먼저 호출하는 함수가 main()이다.
📌 즉, main()은 C 표준에서 약속된 함수이므로 필수!
int main() {
printf("Hello, World!\n");
return 0;
}
이걸 watermelon() 으로 바꾼다면?
int watermelon() {
printf("Hello, World!\n");
return 0;
}
❌ 컴파일은 되지만 실행은 안 됨
💡 이유: OS는 main()을 찾아서 실행하는데, watermelon()이라는 함수는 알 수 없음.
✅ 2. main() 없이 실행하는 방법들
하지만 몇 가지 방법을 사용하면 main() 없이도 실행 가능함.
🔹 (1) #define을 이용한 aliasing
#define main watermelon
int main() {
printf("Hello, World!\n");
return 0;
}
✅ 이렇게 하면 main()이 watermelon()으로 대체됨
✅ 결국 실행은 main()으로 동작하지만, 코드상에서는 watermelon()처럼 보임
🔹 (2) 링커 옵션 사용 (GCC -e 옵션)
💡 컴파일러에 -e 옵션을 주면 실행 시작점을 바꿀 수 있음.
int watermelon() {
printf("Hello, World!\n");
return 0;
}
✅ 컴파일 시:
gcc -o test test.c -e watermelon
✅ 실행 가능!
💡 OS가 실행할 엔트리포인트를 watermelon()으로 지정하기 때문에 동작함.
🔹 (3) crt0 (C 런타임 부트스트랩 코드) 수정
💡 C 프로그램이 실행될 때 main()을 호출하는 역할을 하는 것은 C 런타임 초기화 코드(crt0.o) 이다.
이걸 수정하면 아예 main() 없이 실행 가능하지만, 실무에서는 거의 안 씀.
.global _start
_start:
call watermelon
movl $0, %ebx
movl $1, %eax
int $0x80
💡 리눅스에서 실행 파일을 직접 만들 때 활용 가능.
✅ 결론: main()을 변경할 수 있는가?
방법 가능 여부 설명
main()을 watermelon()으로 변경 | ❌ | OS가 실행할 수 없음 |
#define main watermelon 사용 | ✅ | 코드에서는 watermelon(), 실행은 main() |
GCC -e watermelon 옵션 사용 | ✅ | 엔트리포인트를 watermelon()으로 변경 |
crt0 커스텀 부트스트랩 코드 작성 | ✅ | 완전한 로우레벨 조작 가능 |
🚀 즉, 표준 C에서는 main()을 필수로 사용해야 하지만, OS 및 링커 설정을 건드리면 변경 가능!
ㅋㅋ이거 재밌는 사실이네 나름. 나중에 wargame 만들때, c언어 crt0수정해야서 main()함수 없이 실행가능하게 만들어서 flag 보이게 하면 웃길뜻
ㅋㅋㅋ 그거 진짜 개꿀잼 워게임 문제 될 듯 😆
main() 없이 실행되는 바이너리를 보면 당황할 거 100%!
✅ 💡 워게임 아이디어: main() 없이 실행되는 프로그램
🛠 아이디어
- main() 함수가 존재하지 않는데도 실행되는 ELF 바이너리 제작
- 실행하면 "이게 어떻게 실행됨??" 하고 혼란스러움
- 플레이어가 CRT0 / _start 분석 후, 코드 구조 이해해야 풀 수 있게 만듦
- flag를 특정 메모리 위치나 특정 시스템 호출로 반환하도록 설정
- 힌트: "C 언어의 진입점(entry point)은 main()이 아니다"
🔥 📝 워게임 문제 예제 코드 (crt0.o 활용)
.section .text
.global _start
_start:
mov $1, %rax # syscall: sys_write
mov $1, %rdi # stdout (file descriptor 1)
lea message(%rip), %rsi # message 주소 로드
mov $13, %rdx # 문자열 길이
syscall # 커널 호출 (write)
mov $60, %rax # syscall: sys_exit
xor %rdi, %rdi # exit(0)
syscall # 종료
.section .data
message:
.ascii "FLAG{NoMain!}\n"
➡ 이걸 ELF 바이너리로 만들면 main() 없이 실행됨
➡ 실행하면 FLAG{NoMain!} 출력되는데, 코드 분석하면 main()이 없어서 당황
🔥 📌 해결책 (플레이어 관점)
1. strings로 쉽게 풀리는 걸 방지하기 위해 난독화
- 문자열 XOR 인코딩 후 sys_write 호출 전에 디코딩하도록 변경
- 실행하면 그냥 "이게 왜 실행되지?"만 보이게 함
2. 리버싱해야 FLAG 찾을 수 있게 설계
- readelf -h ./binary 로 보면 Entry point: 0x401000 같은 주소 나옴
- objdump -d ./binary 로 _start를 분석해야 함
- main()을 못 찾으니까 _start부터 분석해야 하는 문제
✅ 💡 보너스: gcc로 main() 없이 컴파일하기
__attribute__((noreturn)) void _start() {
write(1, "FLAG{NoMain!}\n", 14);
_exit(0);
}
컴파일:
gcc -nostartfiles -o no_main no_main.c
➡ main() 없이 _start() 가 실행됨!
➡ "이게 어떻게 돌아가냐??" 하는 문제로 만들기 완벽 ㅋㅋㅋ
🎯 💡 워게임 문제 난이도 조절
난이도 방법
쉬움 | main() 없이 실행되도록 -nostartfiles 옵션 사용 |
중간 | crt0.o 수정해서 _start로 직접 실행 |
어려움 | syscall 직접 사용 + 난독화 |
🚀 "와 main() 없이도 실행되네??" 하면서 CS 개념도 배우고, 리버싱도 연습할 수 있는 문제 가능!"
🔥 워게임 만들 때 넣으면 개꿀잼될 듯!
ㅋㅋㅋ문제 제목은 where is main? 혹은 main is not main. ㅋㅋㅋㅋㅋ재밌네 이거.
이런식의 워게임도 제작이 되는구나?!
ㅋㅋㅋㅋ "Where is main?" or "Main is not main." 개웃기다 🤣
이거 진짜 신박한 워게임 문제 될 듯!
✅ 💡 워게임 문제 제작의 핵심
✔ 일반적인 main() 함수가 존재하지 않는 ELF 바이너리
✔ 플레이어가 당연히 main()을 분석하려고 Ghidra, IDA, objdump 돌려봄
✔ "어? main()이 없는데 실행되네?" → 혼란스러움 ㅋㅋ
✔ 결국 CRT0 (_start) 분석해야 풀리는 문제
🔥 📌 워게임 문제 제작 예제
🛠 1. 기본적인 main() 없는 문제
.section .text
.global _start
_start:
mov $1, %rax # syscall: sys_write
mov $1, %rdi # stdout (file descriptor 1)
lea message(%rip), %rsi # message 주소 로드
mov $15, %rdx # 문자열 길이
syscall # 커널 호출 (write)
mov $60, %rax # syscall: sys_exit
xor %rdi, %rdi # exit(0)
syscall # 종료
.section .data
message:
.ascii "FLAG{WhereIsMain}\n"
➡ main() 없이 _start가 실행됨.
➡ objdump -d 하면 _start를 분석해야 함.
➡ readelf -h 보면 Entry Point가 _start로 설정되어 있음.
🔥 📌 난이도 조절
난이도 방법
쉬움 | main() 없이 _start에서 바로 printf 호출 |
중간 | syscall 직접 사용 + 문자열 XOR 인코딩 (리버싱 필요) |
어려움 | _start에서 여러 개의 난독화된 로직 포함 (ROP 개념도 활용 가능) |
🛠 2. 난독화 추가
.section .text
.global _start
_start:
mov $1, %rax
mov $1, %rdi
lea encrypted_message(%rip), %rsi
mov $15, %rdx
syscall
mov $60, %rax
xor %rdi, %rdi
syscall
.section .data
encrypted_message:
.byte 0x26, 0x36, 0x28, 0x38, 0x2A, 0x32, 0x3F, 0x3E, 0x30, 0x3B, 0x33, 0x27, 0x3A, 0x31, 0x34
➡ 실행하면 이상한 문자 출력됨 (XOR 인코딩된 메시지)
➡ 플레이어가 디버깅해서 XOR 키 찾고 디코딩해야 FLAG{MainIsGone} 보임
🎯 💡 워게임 문제 해결 과정 (플레이어 입장)
1️⃣ strings → 아무것도 안 나옴
2️⃣ objdump -d → main()이 없어서 당황
3️⃣ readelf -h → Entry Point가 _start로 설정된 거 확인
4️⃣ _start 어셈 분석 → sys_write 호출 확인
5️⃣ encrypted_message 발견 → XOR 복호화 시도
6️⃣ "FLAG{MainIsGone}" 발견 → 문제 해결! 🎉
✅ 🚀 워게임 제작자로서 이런 문제 만드는 게 재밌는 이유
✔ 플레이어가 평소 당연하게 생각했던 개념을 뒤흔듦
✔ 기본적인 리버싱 & ELF 구조 분석 실력을 요구함
✔ 난독화, XOR 인코딩, _start 엔트리포인트 같은 요소 추가하면 고급 문제로 발전 가능
✔ “아 main()이 없어도 프로그램이 실행될 수 있구나” 하는 깨달음 제공
🔥 "Where is main?" 시리즈, 이거 무조건 재미보장 ㅋㅋㅋ"
➡ 나중에 티오리 워게임 제작자로 일하면 이런 문제 하나 넣어도 개꿀잼될 듯! 🚀
결국 c언어든 뭐든, 프로그램이잖아 이것도? ㅋㅋㅋㅋ환경설정 파일이 무조건 있는데, 그거 건들면 머리 아프지..
ㅇㅇ 맞아 ㅋㅋ "C언어든 뭐든, 결국 프로그램 = 실행 환경이 있다"
➡ 운영체제(OS) + 링커 + 실행 환경을 이해하면, 우리가 당연하게 쓰는 것들도 사실 변경 가능!
🔥 프로그램 실행 원리 다시 정리해보면?
📌 1. 결국 모든 프로그램은 OS 위에서 실행됨
✔ 우리가 main() 쓰면, OS가 알아서 실행해주는 것 같지만
✔ 사실 CRT0 (crt0.o = C Runtime Startup)에서 main()을 찾아 호출하는 것뿐!
✔ 실행 환경만 수정하면, main() 없어도 프로그램 실행 가능!
➡ main()이 필수가 아니라 **OS가 정해준 "약속"**일 뿐
➡ 그 약속을 깨버리면 예상 못한 결과가 나옴 (워게임 만들 때 꿀팁)
📌 2. 환경설정 건드리면 게임 끝!
✔ 실행 파일이 돌아가기 위해서는 **"필수적인 설정 파일"**이 있음
✔ 이걸 OS, 컴파일러, 링커, 실행 환경 등에서 변경 가능 ✔ 이걸 역이용하면 개꿀잼 워게임 문제 만들 수 있음
📌 예제 - 실행 환경을 바꿔버리는 방법들
방법 설명 응용 가능
main()을 _start()로 변경 | 실행 환경을 수정하면 main() 없이 실행 가능 | 워게임 문제 제작 |
ELF Entry Point 변경 | main 대신 다른 함수 실행되도록 설정 가능 | 바이너리 난독화 |
LD_PRELOAD 사용 | 실행 파일을 건드리지 않고 동작 변경 가능 | 보안 우회, 리버싱 |
환경 변수 조작 | 실행 파일이 참조하는 설정을 바꿀 수 있음 | 취약점 공격 |
➡ "실행 환경만 조작하면, 프로그램이 완전히 다른 방식으로 동작할 수 있다"
📌 3. 실제 해킹 & 보안에서 이걸 어떻게 쓰냐?
✔ 취약점 공격 (Exploit)
- 실행 환경 조작해서 악성 코드 실행 (예: 환경변수 조작, LD_PRELOAD)
- main() 없이도 실행되게 바이너리 수정 → 실행 흐름을 바꿔버림
✔ 워게임 문제 제작
- "당연한 실행 흐름"을 깨트려서 플레이어 당황시키기
- main()이 없어도 돌아가도록 만들거나, 특정 조건에서만 실행되게 설정
- 실행 환경을 조작해야 flag를 얻을 수 있게 설계
✔ 보안 연구 & 분석
- 악성코드 분석할 때, 실행 환경을 바꿔서 샌드박스 우회 탐지
- 프로그램이 의존하는 설정 파일이나 환경 변수를 추적해서 공격 벡터 찾기
🔥 결론
✅ 우리가 "당연하다"고 생각하는 것들은 사실 설정값일 뿐!
✅ 실행 환경을 바꾸면, 프로그램이 완전히 다른 방식으로 동작할 수도 있음!
✅ 이걸 응용하면 해킹도 가능하고, 개꿀잼 워게임 문제도 만들 수 있음!
➡ ㅋㅋㅋ 이걸 알고 나면, "C언어 main()이 사실 필수가 아니다" 같은 깨달음이 오는 거지 🤣
➡ "실행 환경을 조작하면 프로그램이 어떻게든 돌아간다" = 핵심 🏆
양아치 문제긴하네..ㅋㅋㅋㅋㅋㅋㅋㅋ
ㅋㅋㅋㅋㅋㅋㅋ 개양아치 문제임 🤣
이거 진짜 리버싱하는 사람들 멘탈 터뜨리는 유형임 ㅋㅋㅋㅋ
🔥 main() 없는 양아치 워게임 문제 특징
✔ "당연히 main()이 있겠지~" → 근데 없음 ㅋㅋ
✔ strings 돌려도 flag 안 나옴 → XOR 난독화까지 해버리면 멘붕
✔ readelf -h 보면서 Entry Point 확인해야 함 → 보통 _start로 돼 있음
✔ objdump -d 로 _start 분석해야 함 → 여기서 복호화 루틴 숨겨놓으면 핵빡침
✔ LD_PRELOAD로 힌트 줄 수도 있음 → 설정 조작이 필요하다는 개념 학습
🔥 이 문제를 풀기 위해 요구되는 스킬셋
요구 스킬 설명 난이도
main() 없이 프로그램이 실행될 수 있다는 개념 | crt0.o & _start 구조 이해 | ⭐⭐⭐ |
readelf -h / objdump -d 사용법 | 실행 파일 엔트리포인트 분석 | ⭐⭐ |
환경 변수 조작 (LD_PRELOAD, LD_LIBRARY_PATH) | 실행 환경을 바꿔야 해결 가능 | ⭐⭐⭐ |
바이너리 난독화 (XOR, ROP) | 디코딩 & 실행 흐름 분석 | ⭐⭐⭐⭐ |
🔥 더 양아치스럽게 만들고 싶다면?
✔ flag를 직접 출력하지 않고, 특정 조건에서만 나오게 하기
✔ 실행할 때마다 출력값이 랜덤으로 바뀌는 듯한 착각 유도 (time() 활용)
✔ LD_PRELOAD 써야 printf() 동작해서 flag가 나오게 설정
✔ seccomp로 특정 시스템 콜 차단해서 디버깅 막기
🚀 ㅋㅋㅋ "이게 왜 실행되냐고!!" 하면서 뚜껑 열리는 워게임 문제 제작 가능 🔥
➡ 보안, 리버싱 공부하는 사람들한테 개꿀잼 트릭 제공 가능 ㅋㅋ
➡ "양아치 문제 난이도 최상 💀💀💀"
'Study > C Language' 카테고리의 다른 글
char *name vs char name[] (0) | 2025.03.08 |
---|---|
int arr[3] = {1};가 {1, 0, 0}으로 인식되는 이유 (0) | 2025.03.08 |
씹어먹는 C언어 혼자 실습 (3) switch문 (0) | 2024.10.17 |
씹어먹는 C언어 혼자 실습 (2) (0) | 2024.10.16 |
씹어먹는 C언어 혼자 실습 (1) (0) | 2024.10.16 |