#include <stdio.h>
#include <stdint.h>
#include <stdlib.h>
int main() {
uint8_t v1 = 0;
int v2 = 0;
char v3[31];
uint16_t v4[10]={0,};
while (v4[v1] < UINT16_MAX && v1 < 10) {
v1++;
printf("v4[%d]: %p\n", v1, &v4[v1]);
v2 += v1;
if (v2 > 10000) {
FILE *fp = fopen("/flag.txt", "r");
fgets(v3, 31, fp);
fclose(fp);
fp = fopen("/home/stupid_gcc/flag.txt", "w");
fwrite(v3, 31, 1, fp);
fclose(fp);
return 0;
}
}
return 0;
}
위 코드는 C로 작성된 프로그램이며, 특정 조건을 충족할 때 /flag.txt 파일의 내용을 /home/stupid_gcc/flag.txt로 복사하는 역할을 합니다. 아래는 코드의 주요 부분과 그 작동 방식을 설명합니다.
코드 분석
1. 변수 선언
uint8_t v1 = 0;
int v2 = 0;
char v3[31];
uint16_t v4[10] = {0,};
- v1: 0부터 시작하는 8비트 정수.
- v2: 누적 값을 저장하는 32비트 정수.
- v3: flag.txt 파일의 내용을 저장할 31바이트 크기의 문자 배열.
- v4: 10개의 16비트 정수 배열이며 초기값은 모두 0.
2. 반복문
while (v4[v1] < UINT16_MAX && v1 < 10) {
v1++;
printf("v4[%d]: %p\n", v1, &v4[v1]);
v2 += v1;
if (v2 > 10000) {
// 파일 읽기 및 쓰기 수행
}
}
- 조건: v4[v1]이 UINT16_MAX(65535) 미만이고 v1이 10 미만일 때 반복.
- 작동:
- v1을 증가시키고 v4[v1]의 주소를 출력.
- v2에 v1 값을 누적.
- v2가 10000보다 커지면 파일 작업 수행 후 종료.
3. 파일 작업
FILE *fp = fopen("/flag.txt", "r");
fgets(v3, 31, fp);
fclose(fp);
fp = fopen("/home/stupid_gcc/flag.txt", "w");
fwrite(v3, 31, 1, fp);
fclose(fp);
- /flag.txt에서 내용을 읽어 v3에 저장.
- /home/stupid_gcc/flag.txt로 복사.
실행 조건
- v2 > 10000인 경우에만 파일 작업이 실행됩니다.
- 이는 v1의 값이 점진적으로 증가하면서 v2에 누적되므로 충분히 반복한 후 조건을 충족할 수 있습니다.
예상 결과
- 실행 도중 v4 배열의 주소와 v1의 값을 출력.
- 특정 시점에서 /flag.txt의 내용을 /home/stupid_gcc/flag.txt로 복사.
실행 방법
- 코드 컴파일:
gcc -o flag_program program.c
- 프로그램 실행:
./flag_program
- 출력 파일 확인:
cat /home/stupid_gcc/flag.txt
Note: 프로그램 실행 전에 /flag.txt 파일이 존재해야 하며, /home/stupid_gcc/ 경로에 쓰기 권한이 있어야 합니다.
1. gcc 최적화 옵션
-O1 : O 옵션과 같은 단계의 옵션. 최소한의 스레드 분기 동작 횟수를 줄이고, 호출된 각 함수 반환 시 스택에 인수를 모아 두었다 동시에 꺼낸다.
-O2 : O1 단계의 최적화와 함께 프로세서가 다른 명령어의 결과나 캐시 메모리 또는 메모리의 데이터를 기다리는 동안 컴파일러가 다른 명령어를 실행하도록 한다. 컴파일 시간이 더 오래 걸리지만, 수정된 코드는 더 최적화되어 실행이 빨라진다.
-O3 : -O2 단계의 모든 최적화와 루프 해체, 그 밖의 프로세서 전용 특징을 포함
https://velog.io/@titfhs/%EB%93%9C%EB%A6%BC%ED%95%B5-Stupid-GCC
아하, 그러면 문제의 핵심은 컴파일 최적화 옵션 -O3를 사용했을 때 코드가 의도한 대로 동작하고 플래그를 출력한다는 점이었네요! 👍
왜 -O3가 정답이었을까?
gcc의 최적화 옵션 중 -O3는 최대 수준의 최적화를 수행하며, 다음과 같은 작업을 포함합니다:
- 루프 언롤링(loop unrolling): 반복문을 최적화하여 실행 속도를 높임.
- 인라인 함수화(inline functions): 작은 함수 호출을 직접 코드에 삽입하여 오버헤드를 줄임.
- 미사용 코드 제거(dead code elimination): 실행되지 않는 코드를 제거하여 깔끔한 바이너리 생성.
- CPU 명령어 최적화: 특정 CPU에서 더 빠르게 실행되는 명령어로 변환.
문제에서 -O3의 역할
-O3 옵션이 없으면 반복문(while 등)과 조건문 최적화가 제대로 이루어지지 않아 v4 값이 업데이트되지 않거나 루프 조건이 충족되지 않았을 가능성이 높습니다. 결과적으로, if (v2 > 10000) 조건문이 실행되지 않아 /flag.txt 파일을 읽는 로직이 동작하지 않았던 것 같습니다.
반면 -O3를 사용하면:
- 루프와 조건문이 최적화되어 v4[v1]와 v2가 빠르게 업데이트.
- 최적화 덕분에 v2 > 10000 조건이 충족되어 /flag.txt 읽기 및 쓰기 로직이 실행.
실행 명령어
gcc -O3 -o a.out source_code.c
./a.out
./a.out을 실행하면 /home/stupid_gcc/flag.txt에 플래그가 생성되거나 바로 출력되었을 것입니다.
배운 점
- 컴파일러 최적화 옵션이 코드 실행 결과에 얼마나 큰 영향을 미칠 수 있는지 알 수 있음.
- 최적화 옵션을 사용해야 코드가 제대로 동작하도록 설계된 문제를 만났을 때, 이를 인지하고 테스트해보는 것이 중요.
잘 해결하셨네요! 🎉
'Dreamhack > Dreamhack Wargame (Challenge)' 카테고리의 다른 글
[126] IT 비전공자 [dreamhack]Find The Lost Flag문제 풀기 (0) | 2025.01.14 |
---|---|
[125] IT 비전공자 [dreamhack]set-int문제 풀기 (0) | 2025.01.13 |
[123] IT 비전공자 [dreamhack]chinese what?문제 풀기 (0) | 2025.01.11 |
[122] IT 비전공자 [dreamhack]Stop before stops!문제 풀기 (0) | 2025.01.10 |
[121] IT 비전공자 [dreamhack]ICM2022문제 풀기 (0) | 2025.01.09 |