Dreamhack/Dreamhack Wargame (Challenge)

[78] IT 비전공자 [dreamhack]Simple Crack Me문제 풀기

imaginefuture-1 2024. 11. 26. 08:04

리버러러러러싱

 

 

기드라 이용 방법인데 ida를 사용했다 헤헤

 

프로그램 파일을 받아 디컴파일러하면 이런 소스코드가 뜬다

 

// positive sp value has been detected, the output may be wrong!
void __fastcall __noreturn start(__int64 a1, __int64 a2, int a3)
{
  __int64 v3; // rax
  int v4; // esi
  __int64 v5; // [rsp-8h] [rbp-8h] BYREF
  _UNKNOWN *retaddr; // [rsp+0h] [rbp+0h] BYREF

  v4 = v5;
  v5 = v3;
  sub_402E10((unsigned int)sub_401AD5, v4, (unsigned int)&retaddr, 0, 0, a3, (__int64)&v5);
  __halt();
}

 

 

이 코드는 C 언어로 작성된 함수로, 특정 작업을 수행한 후 프로그램을 중단하는 역할을 합니다. 주요 부분을 분석하면 다음과 같은 흐름과 특징을 이해할 수 있습니다.


1. 함수의 시그니처

void __fastcall __noreturn start(__int64 a1, __int64 a2, int a3)
  • __fastcall:
    • 함수가 인자를 레지스터를 통해 전달받는 호출 규약을 의미합니다. 보통 첫 번째와 두 번째 정수형 인자(a1, a2)는 RCX와 RDX 레지스터를 사용합니다. 세 번째 인자(a3)는 R8에 전달됩니다.
  • __noreturn:
    • 함수가 종료되지 않고 프로그램을 멈추거나 종료될 것임을 의미합니다. (__halt() 때문에 해당 속성이 붙습니다.)

2. 변수 선언

__int64 v3;              // rax 레지스터에서 가져오는 64비트 값 (초기화되지 않음)
int v4;                  // esi 레지스터에서 사용되는 32비트 값
__int64 v5;              // 64비트 크기의 스택 변수, 포인터로도 사용 가능
_UNKNOWN *retaddr;       // 반환 주소를 저장하는 포인터
  • v3와 v5:
    • v3는 초기화되지 않으며, 이후 v5에 대입됩니다. 이는 실행 중 예기치 않은 동작을 일으킬 수 있습니다.
  • v4 = v5;:
    • v5를 v4에 복사합니다. 하지만 v5도 초기화되지 않았으므로, 결과적으로 v4 또한 예측할 수 없는 값을 가질 가능성이 큽니다.

3. sub_402E10 호출

sub_402E10((unsigned int)sub_401AD5, v4, (unsigned int)&retaddr, 0, 0, a3, (__int64)&v5);

인자 설명:

  1. (unsigned int)sub_401AD5:
    • 함수 포인터를 전달합니다. sub_401AD5는 이 함수 내부에서 호출될 가능성이 있습니다.
  2. v4:
    • 초기화되지 않은 값을 전달합니다.
  3. (unsigned int)&retaddr:
    • retaddr의 주소를 전달합니다. 일반적으로 스택의 반환 주소를 다룰 때 사용됩니다.
  4. 0, 0:
    • 상수 값.
  5. a3:
    • 호출 시 전달받은 세 번째 인자.
  6. (__int64)&v5:
    • v5의 주소를 전달합니다.

sub_402E10 역할 추정:

  • 분석 대상:
    • 주어진 인자들(sub_401AD5, v4, a3, v5의 주소)을 조합하여 특정 조건을 평가하거나 작업을 수행합니다.
  • sub_401AD5:
    • 함수 포인터이므로, sub_402E10에서 호출되어 추가적인 작업이 진행될 가능성이 높습니다.

4. __halt()

__halt();
  • 프로그램 실행을 멈추는 명령입니다. 이후에는 어떤 작업도 실행되지 않습니다.
  • 주로 디버깅 또는 종료 조건에서 사용됩니다.

5. 문제점

  • 초기화되지 않은 변수 사용:
    • v3와 v5는 초기화되지 않은 값을 가지므로, v4와 v5의 주소를 사용하는 sub_402E10 호출 시 비정상 동작이 발생할 가능성이 큽니다.
  • 스택 오프셋 경고:
    • // positive sp value has been detected, the output may be wrong! 경고는 스택 포인터의 값이 비정상적으로 설정되어 있다는 것을 의미합니다.
    • 스택 프레임 초기화 또는 함수 호출 과정에서 잘못된 메모리 참조가 일어날 가능성을 나타냅니다.

6. 코드 흐름 요약

  1. start 함수는 호출된 세 인자(a1, a2, a3)를 받고 실행됩니다.
  2. 초기화되지 않은 변수 v3, v5를 조작하여 v4를 설정합니다.
  3. sub_402E10 함수를 호출하여 주요 작업을 수행합니다.
  4. 작업이 끝난 후 __halt()를 호출하여 프로그램을 멈춥니다.

7. 개선 및 확인해야 할 사항

  • v3와 v5 초기화:
    • 변수에 명시적인 값을 설정해야 실행 중 예측 불가능한 동작을 방지할 수 있습니다.
  • sub_402E10와 sub_401AD5 분석:
    • 해당 함수들이 어떤 역할을 수행하며, 어떤 조건에서 올바르게 작동하는지 확인해야 합니다.
  • 스택 포인터 정렬 확인:
    • 스택 오프셋 문제가 발생하지 않도록 스택 프레임의 설정을 명확히 해야 합니다.

위 사항을 바탕으로 디버깅 또는 추가 분석을 진행해야 코드의 정확한 동작을 이해할 수 있습니다.

 


 

 

syb_401ad5값을 클릭해본다

 

__int64 __fastcall sub_401AD5(__int64 a1, __int64 a2, int a3, int a4, int a5, int a6)
{
  __int64 result; // rax
  int v7; // [rsp+4h] [rbp-Ch] BYREF
  unsigned __int64 v8; // [rsp+8h] [rbp-8h]

  v8 = __readfsqword(0x28u);
  v7 = 0;
  sub_40BB20((unsigned int)&unk_4B6004, (unsigned int)&v7, a3, a4, a5, a6);
  if ( v7 == 322376503 )
  {
    sub_41A400("Correct!");
    result = 0LL;
  }
  else
  {
    sub_40B990((__int64)"%x is wrong x(\n", v7);
    result = 1LL;
  }
  if ( v8 != __readfsqword(0x28u) )
    sub_45A420();
  return result;
}

 

 

주어진 함수 sub_401AD5의 역할과 로직을 분석한 결과, 이 함수는 특정 조건을 만족했을 때 **"Correct!"**라는 메시지를 출력하며 성공을 나타내고, 그렇지 않을 경우 실패를 나타냅니다. 이를 통해 v7 값을 조작하여 322376503이라는 특정 값을 만들 필요가 있습니다. 주요 부분을 분석해 보겠습니다.


1. 함수의 시그니처

__int64 __fastcall sub_401AD5(__int64 a1, __int64 a2, int a3, int a4, int a5, int a6)
  • __fastcall:
    • 인자를 레지스터를 통해 전달합니다.
    • a1, a2는 64비트 정수형으로, 각각 RCX와 RDX 레지스터를 통해 전달됩니다.
    • a3, a4, a5, a6는 정수형으로, 나머지 레지스터를 통해 전달됩니다.
  • 반환값은 64비트 정수형(__int64)이며, 함수의 결과로 성공(0) 또는 실패(1)를 나타냅니다.

2. 코드 분석

(1) 로컬 변수 초기화

int v7; // [rsp+4h] [rbp-Ch] BYREF
unsigned __int64 v8; // [rsp+8h] [rbp-8h]

v8 = __readfsqword(0x28u);
v7 = 0;
  • v8:
    • __readfsqword(0x28u)는 스레드 정보 블록(TIB)의 특정 위치 값을 읽어옵니다. 이는 스택 보호를 위한 stack cookie로, 함수 종료 시 스택 변조 여부를 확인하는 데 사용됩니다.
  • v7:
    • 초기값은 0입니다. 이후 sub_40BB20 함수 호출에서 수정될 가능성이 있습니다.

(2) sub_40BB20 호출

sub_40BB20((unsigned int)&unk_4B6004, (unsigned int)&v7, a3, a4, a5, a6);
  • 목적:
    • sub_40BB20 함수는 v7 값을 설정하는 주요 역할을 합니다.
    • 입력 인자:
      • &unk_4B6004: 외부 데이터 또는 설정 값을 가리키는 포인터.
      • &v7: 출력값이 저장될 변수.
      • a3, a4, a5, a6: 전달받은 인자.
  • 결과:
    • v7 값을 수정하여, 이후 조건문에서 평가에 사용됩니다.

(3) 조건 평가

if (v7 == 322376503)
{
  sub_41A400("Correct!");
  result = 0LL;
}
else
{
  sub_40B990((__int64)"%x is wrong x(\n", v7);
  result = 1LL;
}
  • v7 값이 322376503(0x1337BAB7)과 같으면 "Correct!"를 출력하고 성공(0)을 반환합니다.
  • 그렇지 않으면 "%x is wrong x(\n" 포맷 문자열과 함께 v7 값을 출력하며 실패(1)를 반환합니다.

(4) 스택 보호 검증

if (v8 != __readfsqword(0x28u))
    sub_45A420();
  • 함수 종료 시 v8 값을 재확인합니다.
  • 스택 변조가 발생한 경우, 보호 루틴(sub_45A420)이 실행됩니다.

3. sub_40BB20의 역할

sub_40BB20 함수는 v7 값을 수정하는 핵심 함수입니다. 이 함수 내부를 분석하여 v7 값을 322376503으로 설정하기 위한 조건을 확인해야 합니다.

  • sub_40BB20는 (unsigned int)&unk_4B6004, (unsigned int)&v7를 포함한 여러 인자를 전달받습니다.
  • 목표:
    • a3, a4, a5, a6 값 조합을 통해 v7 값을 322376503으로 설정하는 조건을 찾습니다.

4. 결론: "Correct!"를 출력하기 위한 조건

  • sub_401AD5 함수가 "Correct!"를 출력하려면 다음이 필요합니다:
    • sub_40BB20 호출 이후 v7 값이 정확히 322376503(0x1337BAB7)이어야 합니다.
    • 이를 위해:
      1. a3, a4, a5, a6의 적절한 조합을 찾습니다.
      2. &unk_4B6004와 관련된 외부 데이터를 확인하여 필요한 값을 설정합니다.
    • 디버깅 도구를 사용해 sub_40BB20 내부에서 v7 값을 계산하는 방식을 분석합니다.
  • 스택 보호:
    • 실행 중 스택 변조가 발생하지 않도록 해야 합니다. 이를 위해 함수의 인자와 로컬 변수 크기를 정확히 맞춰야 합니다.

디버깅 또는 sub_40BB20 분석을 통해 "Correct!" 출력 조건을 정확히 확인할 수 있습니다. 필요하면 이를 도와줄 수 있는 추가 분석을 요청하세요!

 


 

 

correct 출력할려면 322376503이 필요하다. 

 

DH{322376503}