Stop Before Stops - Reversing Writeup

energy·2026년 1월 27일

문제 정보

  • 플랫폼: Dreamhack

  • 카테고리: Reversing

  • 난이도: 중급


1. 문제 분석 시작

1.1 main 함수 확인

IDA로 바이너리를 열고 main 함수를 디컴파일했다.

int __fastcall main(int argc, const char **argv, const char **envp)
{
  if ( argc == 2 )
  {
    if ( strlen(argv[1]) == 24 &&
         std::operator==(&vlkjbkldsajfksdkfl2, argv[1]) )
    {
      // 플래그 출력
      std::cout << "DH{" << lkjasvhkjsldhkl << "}" << std::endl;
      return 0;
    }
  }
  // ... 에러 처리
}

1.2 성공 조건 파악

main 함수에서 핵심 조건을 찾았다:
┌────────────────────────────────┬───────────────────────────┐
│ 조건 │ 설명 │
├────────────────────────────────┼───────────────────────────┤
│ argc == 2 │ 인자 1개 필요 │
├────────────────────────────────┼───────────────────────────┤
│ strlen(argv[1]) == 24 │ 입력값 길이 24자 │
├────────────────────────────────┼───────────────────────────┤
│ argv[1] == vlkjbkldsajfksdkfl2 │ 특정 문자열과 일치해야 함 │
└────────────────────────────────┴───────────────────────────┘
목표: vlkjbkldsajfksdkfl2 변수의 값을 알아내야 한다

2. 정적 분석 - 변수 역추적

2.1 분석 방법론

긴 main 함수를 분석할 때는 역추적(Backtracking) 방법을 사용한다

성공 조건 발견

비교 대상 변수 확인 (vlkjbkldsajfksdkfl2)

변수 초기화 위치 추적

초기화 함수 분석

원본 데이터 복호화

2.2 변수 초기화 추적

IDA에서 vlkjbkldsajfksdkfl2의 크로스 레퍼런스(Xrefs)를 확인했다.

사용처:

  • main 함수 - 비교용
  • __static_initialization_and_destruction_0 함수 - 초기화!

2.3 초기화 함수 분석

  // __static_initialization_and_destruction_0 함수
  sldkhjlhvfiuh(&vlkjbkldsajfksdkfl2);

sldkhjlhvfiuh 함수가 vlkjbkldsajfksdkfl2에 값을 채워넣는다.

포인트: & (주소 전달)로 함수에 변수 주소를 넘기면, 함수 내부에서 해당 변수를 직접 수정할 수 있다.

2.4 sldkhjlhvfiuh 함수 분석

  __int64 sldkhjlhvfiuh(__int64 a1)
  {
      std::string::basic_string(a1, &salkdhvkhlklhkjfdhkjd::segment, ...);
      return a1;
  }

결론: vlkjbkldsajfksdkfl2 = salkdhvkhlklhkjfdhkjd::segment

2.5 segment 생성 추적

다시 초기화 함수에서:

  v8 = Getslifdhiuil3();
  std::string_view::basic_string_view(&salkdhvkhlklhkjfdhkjd::segment, v8);

결론: segment는 Getslifdhiuil3() 함수의 반환값!


3. XOR 복호화 분석

3.1 Getslifdhiuil3 함수 분석

  _BYTE *Getslifdhiuil3(void)
  {
      if ( isEncrypted )
      {
          for ( i = 0; i <= 0x17; ++i )   // 24번 반복
              data[i] ^= 87 - i;          // XOR 복호화
          isEncrypted = 0;
      }
      return data;
  }

암호화 로직: data[i] ^= (87 - i)

3.2 암호화된 데이터 위치 찾기

디스어셈블리에서 data 주소 확인:

  lea rdx, Getslifdhiuil3::data   ; 0xd1a0

3.3 데이터 읽기 (IDA Python)

  import idc

  addr = 0xd1a0
  data = []
  for i in range(24):
      data.append(idc.get_wide_byte(addr + i))

  print(data)

  [0x15, 0x35, 0x07, 0x11, 0x2b, 0x63, 0x05, 0x05,
  0x2a, 0x71, 0x20, 0x0e, 0x76, 0x23, 0x73, 0x74,
  0x14, 0x3e, 0x1a, 0x35, 0x33, 0x08, 0x16, 0x74]

3.4 XOR 복호화 수행

  import idc

  addr = 0xd1a0
  result = ""

  for i in range(24):
      byte = idc.get_wide_byte(addr + i)
      result += chr(byte ^ (87 - i))

  print(result)
  BcREx1TUe?mB=i:<Sx_qpJW4

  복호화 결과: BcREx1TUe?mB=i:<Sx_qpJW4

4. 동적 분석 - 디버깅 검증

4.1 디버깅 환경 설정

  • IDA Remote Linux Debugger 사용
  • WSL에서 linux_server64 실행

4.2 비교 함수에 브레이크포인트

  .text:0000555555557DA8 call    std::operator==
  .text:0000555555557DAD test    al, al     ← 브레이크포인트

4.3 std::string 구조 이해

디버깅 중 RAX 레지스터의 주소로 이동하면 std::string 객체가 보인다:

std::string 객체 구조 (24바이트):
├─ [+0x00] 문자열 포인터 ← 실제 문자열은 여기를 따라가야 함
├─ [+0x08] 길이
└─ [+0x10] 용량

메모리 덤프 예시:
0x555555561460:
10 3F 57 55 55 55 00 00 ← 포인터 (0x555555573F10)
18 00 00 00 00 00 00 00 ← 길이 (0x18 = 24)

포인터 주소(0x555555573F10)로 이동하면 실제 문자열을 확인할 수 있다.


5. 플래그 획득

5.1 프로그램 실행

./stop_before_stops 'BcREx1TUe?mB=i:<Sx_qpJW4'

주의: 문자열에 ?, <, : 같은 특수문자가 있으므로 작은따옴표로 감싸야 한다!

┌────────┬────────────────────┐
│ 따옴표 │ 특수문자 처리 │
├────────┼────────────────────┤
│ "..." │ 일부 해석됨 │
├────────┼────────────────────┤
│ '...' │ 전부 그대로 전달 ✓ │
└────────┴────────────────────┘

5.2 플래그 출력

Congratulations! The flag is:
DH{**}


6. 분석 요약

전체 추적 흐름

main 함수

성공 조건: argv[1] == vlkjbkldsajfksdkfl2

vlkjbkldsajfksdkfl2 초기화 추적

sldkhjlhvfiuh() → salkdhvkhlklhkjfdhkjd::segment 복사

Getslifdhiuil3() → XOR 복호화 후 반환

암호화된 data (0xd1a0) 읽기

data[i] ^ (87 - i) 복호화

"BcREx1TUe?mB=i:<Sx_qpJW4" 획득

프로그램 실행 → 플래그!

배운 점

  1. 역추적 분석: 성공 조건에서 시작해서 변수의 생성 위치까지 추적

  2. XOR 복호화: 정적 분석으로 암호화 로직 파악 후 직접 복호화

  3. std::string 구조: C++ 문자열은 포인터를 한번 더 따라가야 실제 데이터 확인 가능

  4. IDA Python 활용: idc.get_wide_byte()로 메모리 데이터 추출

  5. 쉘 특수문자: 작은따옴표로 특수문자 포함 문자열 전달


    사용 도구

  • IDA Pro 9.0
  • IDA Python
  • IDA Remote Linux Debugger
  • WSL (Windows Subsystem for Linux)

0개의 댓글