Dreamhack/Dreamhack Wargame (Challenge)

[51] IT 비전공자 [dreamhack]rev-basic-6문제 풀기

imaginefuture-1 2024. 10. 30. 09:23

51번째 문제는 rev-basic!! 반가운 친구~!

 

 

 

 

correct 찾아서

 

correct전 chall6.7ff7605f1000
들어가면 이렇게 나와있다. 좋아 ida로 가자 ㅋㅋ

 

 

 

int __fastcall main(int argc, const char **argv, const char **envp)
{
  char v4[256]; // [rsp+20h] [rbp-118h] BYREF

  memset(v4, 0, sizeof(v4));
  sub_1400011B0("Input : ", argv, envp);
  sub_140001210("%256s", v4);
  if ( (unsigned int)sub_140001000(v4) )
    puts("Correct");
  else
    puts("Wrong");
  return 0;
}

 

편안 해졌죠?

 

 

 

 

이 `main` 함수를 분석해 보겠습니다. 이 함수에는 몇몇 함수 호출과 버퍼(`v4`)가 포함되어 있습니다. 각 줄의 세부 내용과 기능은 다음과 같습니다.



### 코드 분석



```c

 

int __fastcall main(int argc, const char **argv, const char **envp)

 

{

 

    char v4[256]; // [rsp+20h] [rbp-118h] BYREF

 

```



- 이것은 `argc`, `argv`, `envp`라는 일반적인 인수를 갖는 `main` 함수입니다.

 

- `v4`는 256바이트의 버퍼로, `[rsp+20h]`(스택 포인터로부터의 오프셋)에 있습니다.

 

- `BYREF`는 메모리 위치에 대한 참조임을 나타냅니다.



```c

 

    memset(v4, 0, sizeof(v4));

 

```



- 이 줄은 `v4` 버퍼의 256바이트를 각각 `0`으로 설정하여 버퍼를 0으로 설정합니다.



```c

 

    sub_1400011B0("Input : ", argv, envp);

 

```



- 이것은 `"Input : "`, `argv`, `envp`라는 인수를 사용하여 `sub_1400011B0` 함수를 호출합니다.

 

- `sub_1400011B0`의 내부 구조를 모르더라도 `"Input : "` 문자열을 기반으로 출력이나 로깅을 수행할 가능성이 있다고 가정할 수 있습니다.



```c

 

    sub_140001210("%256s", v4);

 

```



- 이것은 `sub_140001210` 함수를 호출하여 형식 지정자 `"%256s"`와 `v4` 버퍼를 인수로 전달합니다.

 

- `"%256s"`는 이 함수가 최대 256자 길이의 문자열 입력을 `v4`로 읽어들인다고 제안합니다.

 

- 이 사용법을 기반으로 볼 때 `sub_140001210`은 `scanf` 또는 유사한 입력 함수에 대한 사용자 정의 구현이나 래퍼일 가능성이 높습니다.



```c

 

    if ( (unsigned int)sub_140001000(v4) )

 

        puts("Correct");

 

    else

 

        puts("Wrong");

 

```



- 이 코드는 `v4`를 인수로 사용하여 `sub_140001000` 함수를 호출하고 반환 값은 `unsigned int`로 캐스팅됩니다.

 

- `sub_140001000(v4)`가 0이 아닌 값을 반환하면 `"Correct"`를 인쇄합니다.

 

- 그렇지 않으면 `"Wrong"`를 인쇄합니다.



```c

 

    return 0;

 

}

 

```



- 함수는 일반적으로 프로그램이 성공적으로 종료되었음을 나타내는 `return 0;`로 끝납니다.



### 요약



이 함수는:

 

1. 256바이트 버퍼 `v4`를 0으로 초기화합니다.

 

2. 사용자에게 입력을 요구합니다(아마도 `sub_1400011B0`를 호출하여).

 

3. 표준 입력에서 최대 256자를 `v4`로 읽습니다.

 

4. `v4`를 `sub_140001000`에 전달합니다. 이는 입력을 검증하는 것으로 추정됩니다.

 

5. `sub_140001000`의 반환 값에 따라 검증이 성공하면 `"Correct"`를 인쇄하고 그렇지 않으면 `"Wrong"`을 인쇄합니다.



### 주요 관찰 사항



- **버퍼 크기 제한**: `%256s`를 사용하면 버퍼 오버플로가 방지됩니다.입력 크기를 256자로 제한하기 때문입니다.

 

- **`sub_140001000`의 기능**: 이 기능은 특정 조건(예: 올바른 비밀번호 또는 형식)에 대해 `v4`를 확인할 가능성이 높습니다.


  

`sub_140001000`을 분석하거나 볼 수 있다면 "올바른" 또는 "틀린" 응답을 결정하는 정확한 조건을 명확히 하여 `v4`가 어떻게 검증되는지 이해하는 데 도움이 될 것입니다.

 

 


 

`sub_140001000` 분석하러가자

 

 

__int64 __fastcall sub_140001000(__int64 a1)
{
  int i; // [rsp+0h] [rbp-18h]

  for ( i = 0; (unsigned __int64)i < 0x12; ++i )
  {
    if ( byte_140003020[*(unsigned __int8 *)(a1 + i)] != byte_140003000[i] )
      return 0LL;
  }
  return 1LL;
}

 

 

입력 버퍼에 대한 유효성 검사를 수행하는 함수 `sub_140001000`을 분석해 보겠습니다. 다음은 줄별 분석입니다.



### 코드 분석



```c

 

__int64 __fastcall sub_140001000(__int64 a1)

 

{

 

    int i; // [rsp+0h] [rbp-18h]

 

```



- `sub_140001000`은 `__int64` 유형의 단일 인수 `a1`을 취하는 함수입니다. 이 인수는 문자 버퍼의 주소(예: `main`의 `v4`)를 나타낼 가능성이 높습니다.

 

- `i`는 루프 카운터로 사용되는 정수 변수입니다.



```c

 

    for ( i = 0; (unsigned __int64)i < 0x12; ++i )

 

    {

 

        if ( byte_140003020[*(unsigned __int8 *)(a1 + i)] != byte_140003000[i] )

 

            return 0LL;

 

    }

 

    return 1LL;

 

}

 

```



- **루프 구조**:

 

  - 이 루프는 `i = 0`에서 `i < 0x12`(10진수로 18)까지 반복합니다. 즉, 18번 반복합니다.

 

  - 각 반복에서 `byte_140003020` 및 `byte_140003000`의 요소를 사용하여 조건을 확인합니다.



- **조건 검사**:

 

  - `*(unsigned __int8 *)(a1 + i)`는 입력 버퍼 `a1`의 `i`번째 바이트를 검색합니다.

 

  - `byte_140003020[*(unsigned __int8 *)(a1 + i)]`는 이 바이트를 인덱스로 사용하여 `byte_140003020`을 인덱싱하고 `byte_140003020` 배열에서 해당 값을 검색합니다.

 

  - 이 검색된 값은 `byte_140003000[i]`와 비교됩니다.

 

  - **같지 않으면** 함수는 즉시 `0`을 반환하여 실패를 나타냅니다.



- **반환 값**:

 

  - 루프가 불일치를 찾지 않고 완료되면 함수는 `1`을 반환하여 성공을 나타냅니다.



### 함수 동작 요약



- 이 함수는 `byte_140003020` 및 `byte_140003000`의 값을 기반으로 입력의 처음 18바이트(`a1`이 가리킴)가 특정 조건을 충족하는지 확인합니다.

 

- 입력의 각 바이트에 대해:

 

  - 해당 바이트를 `byte_140003020`의 인덱스로 사용합니다.

 

  - 결과 값을 `byte_140003000`의 해당 값과 비교합니다.

 

- 이 조건을 충족하는 바이트가 없으면 함수는 `0`을 반환합니다(불일치를 나타냄).

 

- 18바이트가 모두 조건을 충족하면 `1`을 반환합니다(일치함을 나타냄).



### 실제 해석



본질적으로 `sub_140001000`은 특정 패턴에 대해 18자 입력을 검증하는 것 같습니다.

 

- `byte_140003020`은 **매핑 테이블**(예: 입력 문자의 변환)을 나타낼 수 있습니다.

 

- `byte_140003000`에는 변환된 입력이 일치해야 하는 **대상 패턴**이 포함되어 있습니다.



검사를 통과하려면:

 

1. `a1`의 처음 18바이트에 있는 각 문자는 `byte_140003020`에 따라 올바르게 변환되어야 합니다.

 

2. 변환된 결과는 `byte_140003000`의 시퀀스와 일치해야 합니다.



이 함수는 제공된 입력이 특정 기준을 충족하는지 확인하는 암호 또는 입력 검증기로 사용될 가능성이 높습니다.

 


byte_140003020` 및 `byte_140003000` 값들이다

 

함수 sub_140001000의 if절을 확인해보면
byte_140003020[a1[i]] == byte_140003000[i]
를 만족해야함을 확인할 수 있습니다.
byte_140003020[j] == byte_140003000[i]
먼저 이를 만족하는 j를 찾고 a1[i]의 값을 j로 설정해줍니다.

출처ㅣ https://velog.io/@helenason/dreamhack-wargame-rev-basic-6



140003000 과 140003020 을 각각 str00[18] , str20[128]로 나타내겠다.
그리고 우리가 알아낼 값들을 저장할 올바른 입력값을 str33[19] 라고 하겠다.
 
다시, 조건문을 살펴보면 
다음과 같이 표현할 수 있다.
str20[ str33 [ i ] ]  !=  str00[ i ]

따라서 위 조건문은 
입력값을 저장할 때, 숫자로 저장하는데,
그 숫자를 str20 의 인덱스로 집어넣어서, 
str20의 해당 인덱스에 위치한 숫자가
str00의 i번째의 숫자와 일치하는지 확인하는 것이다.

출처ㅣ https://aawjej.tistory.com/193
.data:0000000140003000 byte_140003000  db 0, 4Dh, 51h, 50h, 0EFh, 0FBh, 0C3h, 0CFh, 92h, 45h
.data:0000000140003000                                         ; DATA XREF: sub_140001000+40↑o
.data:000000014000300A                 db 4Dh, 0CFh, 0F5h, 4, 40h, 50h, 43h, 63h, 0Eh dup(0)
.data:0000000140003020 byte_140003020  db 63h, 7Ch, 77h, 7Bh, 0F2h, 6Bh, 6Fh, 0C5h, 30h, 1, 67h
.data:0000000140003020                                         ; DATA XREF: sub_140001000+31↑o
.data:000000014000302B                 db 2Bh, 0FEh, 0D7h, 0ABh, 76h, 0CAh, 82h, 0C9h, 7Dh, 0FAh
.data:0000000140003035                 db 59h, 47h, 0F0h, 0ADh, 0D4h, 0A2h, 0AFh, 9Ch, 0A4h, 72h
.data:000000014000303F                 db 0C0h, 0B7h, 0FDh, 93h, 26h, 36h, 3Fh, 0F7h, 0CCh, 34h
.data:0000000140003049                 db 0A5h, 0E5h, 0F1h, 71h, 0D8h, 31h, 15h, 4, 0C7h, 23h
.data:0000000140003053                 db 0C3h, 18h, 96h, 5, 9Ah, 7, 12h, 80h, 0E2h, 0EBh, 27h
.data:000000014000305E                 db 0B2h, 75h, 9, 83h, 2Ch, 1Ah, 1Bh, 6Eh, 5Ah, 0A0h, 52h
.data:0000000140003069                 db 3Bh, 0D6h, 0B3h, 29h, 0E3h, 2Fh, 84h, 53h, 0D1h, 0
.data:0000000140003073                 db 0EDh, 20h, 0FCh, 0B1h, 5Bh, 6Ah, 0CBh, 0BEh, 39h, 4Ah
.data:000000014000307D                 db 4Ch, 58h, 0CFh, 0D0h, 0EFh, 0AAh, 0FBh, 43h, 4Dh, 33h
.data:0000000140003087                 db 85h, 45h, 0F9h, 2, 7Fh, 50h, 3Ch, 9Fh, 0A8h, 51h, 0A3h
.data:0000000140003092                 db 40h, 8Fh, 92h, 9Dh, 38h, 0F5h, 0BCh, 0B6h, 0DAh, 21h
.data:000000014000309C                 db 10h, 0FFh, 0F3h, 0D2h, 0CDh, 0Ch, 13h, 0ECh, 5Fh, 97h
.data:00000001400030A6                 db 44h, 17h, 0C4h, 0A7h, 7Eh, 3Dh, 64h, 5Dh, 19h, 73h
.data:00000001400030B0                 db 60h, 81h, 4Fh, 0DCh, 22h, 2Ah, 90h, 88h, 46h, 0EEh
.data:00000001400030BA                 db 0B8h, 14h, 0DEh, 5Eh, 0Bh, 0DBh, 0E0h, 32h, 3Ah, 0Ah
.data:00000001400030C4                 db 49h, 6, 24h, 5Ch, 0C2h, 0D3h, 0ACh, 62h, 91h, 95h, 0E4h
.data:00000001400030CF                 db 79h, 0E7h, 0C8h, 37h, 6Dh, 8Dh, 0D5h, 4Eh, 0A9h, 6Ch
.data:00000001400030D9                 db 56h, 0F4h, 0EAh, 65h, 7Ah, 0AEh, 8, 0BAh, 78h, 25h
.data:00000001400030E3                 db 2Eh, 1Ch, 0A6h, 0B4h, 0C6h, 0E8h, 0DDh, 74h, 1Fh, 4Bh
.data:00000001400030ED                 db 0BDh, 8Bh, 8Ah, 70h, 3Eh, 0B5h, 66h, 48h, 3, 0F6h, 0Eh
.data:00000001400030F8                 db 61h, 35h, 57h, 0B9h, 86h, 0C1h, 1Dh, 9Eh, 0E1h, 0F8h
.data:0000000140003102                 db 98h, 11h, 69h, 0D9h, 8Eh, 94h, 9Bh, 1Eh, 87h, 0E9h
.data:000000014000310C                 db 0CEh, 55h, 28h, 0DFh, 8Ch, 0A1h, 89h, 0Dh, 0BFh, 0E6h
.data:0000000140003116                 db 42h, 68h, 41h, 99h, 2Dh, 0Fh, 0B0h, 54h, 0BBh, 16h

 

짱많다


소스코드를 작성해보자

 

#include <stdio.h>
int main() {

    char str20[128] = { // byte_140003020

    0x63, 0x7C, 0x77, 0x7B, 0xF2, 0x6B, 0x6F, 0xC5,  0x30, 0x01, 0x67, 0x2B, 0xFE, 0xD7, 0xAB, 0x76,
            0xCA ,0x82 ,0xC9 ,0x7D ,0xFA ,0x59 ,0x47 ,0xF0, 0xAD, 0xD4, 0xA2, 0xAF, 0x9C, 0xA4, 0x72, 0xC0,
            0xB7, 0xFD, 0x93, 0x26, 0x36, 0x3F, 0xF7, 0xCC, 0x34, 0xA5, 0xE5, 0xF1, 0x71, 0xD8, 0x31, 0x15,
            0x04, 0xC7, 0x23, 0xC3, 0x18, 0x96, 0x05, 0x9A, 0x07, 0x12, 0x80, 0xE2, 0xEB, 0x27, 0xB2, 0x75,
            0x09, 0x83, 0x2C, 0x1A, 0x1B, 0x6E, 0x5A, 0xA0, 0x52, 0x3B, 0xD6, 0xB3, 0x29, 0xE3, 0x2F, 0x84,
            0x53, 0xD1, 0x00, 0xED, 0x20, 0xFC, 0xB1, 0x5B, 0x6A, 0xCB, 0xBE, 0x39, 0x4A, 0x4C, 0x58, 0xCF,
            0xD0, 0xEF, 0xAA, 0xFB, 0x43, 0x4D, 0x33, 0x85, 0x45, 0xF9, 0x02, 0x7F, 0x50, 0x3C, 0x9F, 0xA8,
            0x51, 0xA3, 0x40, 0x8F, 0x92, 0x9D, 0x38, 0xF5, 0xBC, 0xB6, 0xDA, 0x21, 0x10, 0xFF, 0xF3, 0xD2
    };

    char str00[18] = { // byte_14003000
        0x00, 0x4D, 0x51, 0x50, 0xEF,
        0xFB, 0xC3, 0xCF,  0x92, 0x45,
        0x4D, 0xCF, 0xF5, 0x04, 0x40,
        0x50, 0x43, 0x63
    };


    char  str33[19] = { 0 }; //input
   
    int i;
    int j;
    for (i = 0; i < 18; i++) {
        for (j = 0; j < 128; j++) {

            if (str20[j] == str00[i]) {
                //140003020의 사용자의 입력 인텍스 값과 140003000의 값이 일치?

                str33[i] = j;
                //일치하면 str30에 j(14003040의 인덱스 넘버)의 값을 저장

            }
        }

    }

    int t;
    for (t = 0; t < 19; t++) {
        printf("%c", str33[t]);
    }
    printf("\n");
    return 0;
}

참고

https://aawjej.tistory.com/193

 

[ dreamhack ] - [ reversing | rev-basic-6]

https://dreamhack.io/wargame/challenges/20/ rev-basic-6 Reversing Basic Challenge #6 이 문제는 사용자에게 문자열 입력을 받아 정해진 방법으로 입력값을 검증하여 correct 또는 wrong을 출력하는 프로그램이 주어집니

aawjej.tistory.com

 

짜자잔

 

 

캬하ㅏ아아~