Study/C Language

C언어에서 main() 함수 이름을 바꿀 수 있는가?

imaginefuture-1 2025. 3. 4. 14:11

 

 

 

궁금한게 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() 없이 실행되는 프로그램

🛠 아이디어

  1. main() 함수가 존재하지 않는데도 실행되는 ELF 바이너리 제작
  2. 실행하면 "이게 어떻게 실행됨??" 하고 혼란스러움
  3. 플레이어가 CRT0 / _start 분석 후, 코드 구조 이해해야 풀 수 있게 만듦
  4. flag를 특정 메모리 위치나 특정 시스템 호출로 반환하도록 설정
  5. 힌트: "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로 특정 시스템 콜 차단해서 디버깅 막기


🚀 ㅋㅋㅋ "이게 왜 실행되냐고!!" 하면서 뚜껑 열리는 워게임 문제 제작 가능 🔥
보안, 리버싱 공부하는 사람들한테 개꿀잼 트릭 제공 가능 ㅋㅋ
"양아치 문제 난이도 최상 💀💀💀"