55번째 문제 가보자고!
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;
}
sub_140001000 조건을 확인해보자
__int64 __fastcall sub_140001000(__int64 a1)
{
int i; // [rsp+0h] [rbp-18h]
for ( i = 0; (unsigned __int64)i < 0x15; ++i )
{
if ( (unsigned __int8)(-5 * *(_BYTE *)(a1 + i)) != byte_140003000[i] )
return 0LL;
}
return 1LL;
}
byte_14003000의 값을 확인하면
.data:0000000140003000 byte_140003000 db 0ACh, 0F3h, 0Ch, 25h, 0A3h, 10h, 0B7h, 25h, 16h, 0C6h
.data:0000000140003000 ; DATA XREF: sub_140001000+40↑o
.data:000000014000300A db 0B7h, 0BCh, 7, 25h, 2, 0D5h, 0C6h, 11h, 7, 0C5h, 0Ch dup(0)
이렇다
문제의 핵심은 if 안의 조건문이다.
if ( (unsigned __int8)(-5 * *(_BYTE *)(a1 + i)) != byte_140003000[i] )
내용을 요약하면 다음과 같다.
(-5) * 입력한 문자 = 140003000에 저장된 문자
해당 식을 for 문이 끝날 때까지 만족한다면 Correct를 출력한다.
여기서 -5는 정수이므로 4바이트 문자는 1바이트이다.
4바이트와 1바이트를 계산하면 1바이트 높은 주소부분을 0으로 채우고 4바이트로 만든 다음에 계산한다.
다음으로 unsigned __int8으로 형변환이 되므로 앞에 3바이트를 날려버린다.
그리고 140003000에 저장된 문자와 비교한다.
출처 https://lemon-soju.tistory.com/20
movsxd rax, [rsp+18h+var_18]
mov rcx, [rsp+18h+arg_0]
movzx eax, byte ptr [rcx+rax]
imul eax, 0FBh
and eax, 0FFh
movsxd rcx, [rsp+18h+var_18]
lea rdx, byte_140003000
movzx ecx, byte ptr [rdx+rcx]
cmp eax, ecx
jz short loc_140001053
위 코드는 조건문의 어셈블리 코드 부분이다. 주목해야할 점은 (-5)가 0FBh로 계산되고 있다는 점이다.
movzx eax, byte ptr [rcx+rax] //입력한 문자를 eax에 저장한다.
imul eax, 0FBh //입력한 문자에 0xFB(=-5)를 곱한다.
and eax, 0FFh //높은 주소 3바이트를 날리고 1바이트로 만든다.
출처 ㅣ https://lemon-soju.tistory.com/20
이 문제는 C언어나 C++에서 다루는 코드와 관련된 내용이네. 요약하자면, 입력된 문자를 특정 방식으로 변환해서, 특정 메모리 위치에 저장된 값과 비교하는 과정인데, 이를 좀 더 쉽게 설명할게.
### 코드 내용 설명:
```c
if ( (unsigned __int8)(-5 * *(_BYTE *)(a1 + i)) != byte_140003000[i] )
```
이 코드는 반복문 안에서 `if` 문을 사용해 어떤 조건을 확인하고 있어. 여기서 중요한 부분을 하나씩 설명할게.
1. **`*(_BYTE *)(a1 + i)`**:
- 이 부분은 **포인터 연산**이야. `a1`이라는 주소값에 `i`라는 인덱스를 더해 그 위치에 있는 **문자**를 가져오는 거지.
- `(_BYTE *)`는 1바이트(8비트) 크기의 데이터를 가리키는 포인터 형식을 의미해. 즉, `a1 + i` 주소에서 **1바이트** 문자를 읽어오는 거야.
2. **`-5 * *(_BYTE *)(a1 + i)`**:
- 여기서 가져온 문자에 `-5`를 곱하고 있어. 이 말은, 입력한 문자에 `-5`를 곱한 결과를 계산한다는 뜻이야.
3. **`unsigned __int8`으로 형 변환**:
- 이제 이 값을 `unsigned __int8`으로 형 변환해.
- `unsigned __int8`은 1바이트짜리 부호 없는 정수 타입을 의미하는데, **1바이트는 8비트로 이루어져 있어**.
- 형 변환 과정에서, 입력값의 상위 3바이트(32비트 중에서 상위 24비트)는 모두 **잘리고** 하위 **1바이트만 남겨진다**는 뜻이야.
- 즉, 값이 크더라도 **1바이트만 유지**되는 거야.
4. **`byte_140003000[i]`**:
- 이 부분은 `byte_140003000`이라는 **배열의 i번째 값**을 의미해. 아마도 이 배열은 정답이 담겨 있는 **비교 대상** 값이겠지.
### 쉽게 풀어보자면:
1. **입력 문자**를 하나씩 가져와 (`*(_BYTE *)(a1 + i)` 부분) 그 문자를 `-5`로 곱해.
2. 곱한 결과를 **1바이트 크기의 부호 없는 값으로 변환**해서 남겨.
- 이 과정에서 계산된 결과의 상위 바이트들은 사라지고, 하위 8비트만 남아.
3. 그 값을 **미리 저장된 값(`byte_140003000[i]`)과 비교**해.
- 만약 같으면, 그 문자 변환은 올바르게 된 거고, 같지 않으면 틀린 거야.
### 예시로 설명:
예를 들어, 입력 문자 'A'가 있다고 치자. 'A'의 아스키 값은 **65**야.
1. 'A'에 **-5를 곱하면**: `-5 * 65 = -325`가 돼.
2. 이 값은 4바이트 정수로 표현될 수 있지만, **1바이트로 변환되면** 하위 8비트만 남아:
- `-325`는 이진수로 `11111111111111111111111011001011`이야. 여기서 **하위 8비트는 `11001011`** (즉, `-75`)가 돼.
3. 이 값을 미리 저장된 `byte_140003000[i]` 값과 비교해. 값이 같다면 올바르게 변환된 거고, 틀리면 다음 반복으로 넘어가면서 계속 확인하는 과정이야.
### 결론:
입력된 문자를 변환해서 특정 값과 비교하는 코드인데, 이 과정에서 4바이트 정수를 1바이트로 바꿔서 처리하고 있어. 그리고 그 변환된 값이 미리 정해진 값과 같으면 "맞다", 아니면 "틀리다"를 판단하는 조건이야.
입력한 문자열과 미리 정해진 값을 맞추기 위해 **브루트포스** 방식으로 값을 찾는다는 말은, 가능한 모든 경우의 수를 다 시도해 본다는 뜻이야. 질문에서 언급된 "올림수가 생기는 경우의 수가 256가지"라는 부분을 이해하려면, 먼저 1바이트 크기의 숫자(즉, 8비트)와 관련된 수를 알아야 해.
### 1. **1바이트(8비트)의 범위**
컴퓨터에서 1바이트는 **256가지**의 값을 가질 수 있어. 그 이유는 1바이트는 8비트로 구성되어 있고, 각 비트는 0 또는 1의 값을 가질 수 있기 때문에 \( 2^8 = 256 \) 가지의 조합이 가능하다는 의미야.
1바이트의 숫자 범위는 **부호가 없는 경우**는 0에서 255까지고, **부호 있는 경우**는 -128에서 127까지야.
### 2. **브루트포스와 256가지의 경우**
이제 문제에서 나오는 `0xFB`와의 곱셈과 관련된 조건을 설명해볼게.
우리가 입력한 문자가 있을 때, 그 문자는 **1바이트 크기**야. 이 문자가 `-5`로 곱해져서 특정한 값과 비교되고 있어. 곱셈 연산의 결과는 **정수 오버플로(overflow)**로 인해 상위 비트가 잘려 나가게 돼.
즉, 곱셈 결과의 하위 1바이트만 남기 때문에, 우리는 **256가지 경우의 수** 중 하나만이 올 수 있다는 의미가 돼.
### 3. **곱셈의 올림수와 경우의 수**
`0xFB`는 1바이트 값으로 **251**을 의미해. 이 값을 특정 값과 곱할 때 발생하는 경우를 살펴보자:
- `251`을 어떤 값과 곱했을 때, 그 결과의 하위 1바이트 값만 남기고 상위 비트는 버려진다.
- 예를 들어, 어떤 문자 값(예를 들어, 'A'는 65)이 있다고 할 때, `251`과 곱했을 때 발생하는 경우의 수를 모두 고려하면, 결과는 0부터 255 사이의 값으로만 나와야 해.
그러므로 1바이트 문자가 가질 수 있는 값(0~255) 모두를 시도해봐야 하므로 **256가지의 경우의 수**가 생기는 거야.
### 결론:
1바이트로 표현되는 입력값의 모든 가능한 경우의 수가 **256가지**라는 것은, 곱셈 연산에서 하위 1바이트만 남기고 상위 비트를 잘라낸 결과가 256가지 중 하나로 나올 수 있다는 뜻이야.
이제 exploit코드를 작성하러가보자
https://lemon-soju.tistory.com/20
[Dreamhack] rev-basic-8
문제에서 주어진 파일을 IDA Pro 7.5 디컴파일러로 디컴파일한 결과 아래와 같은 코드가 출력된다. int __cdecl main(int argc, const char **argv, const char **envp) { char v4[256]; // [rsp+20h] [rbp-118h] BYREF memset(v4, 0, siz
lemon-soju.tistory.com
https://saedal-a.tistory.com/17
[Dreamhack.io] rev-basic-8
dreamhack.io의 Wargame rev-basic-8 문제이다. https://dreamhack.io/wargame/challenges/22/ rev-basic-8 Reversing Basic Challenge #8 이 문제는 사용자에게 문자열 입력을 받아 정해진 방법으로 입력값을 검증하여 correct 또는
saedal-a.tistory.com
https://velog.io/@helenason/dreamhack-wargame-rev-basic-8
[드림핵] 워게임 rev-basic-8 풀이
드림핵 워게임 리버싱 rev-basic-8 문제풀이
velog.io
#include<stdio.h>
#include<stdlib.h>
int main(void)
{
char buf[]={0xAC, 0xF3, 0x0C, 0x25, 0xA3, 0x10, 0xB7, 0x25, 0x16, 0xC6, 0xB7, 0xBC, 0x07, 0x25, 0x02, 0xD5, 0xC6, 0x11, 0x07, 0xC5,0x00 };
int a;
for(int j=0;j<21;j++)
{
for(int i=0;i<256;i++)
{
a=buf[j]+i*256;
if(a%0xfb==0)
{
printf("%c",a/0xfb);
}
}
}
이 코드를 분석해 보면, `buf` 배열에 있는 값들을 하나씩 순회하면서, 특정 조건을 만족하는 값을 찾고, 그 값을 문자로 출력하는 작업을 수행하는 구조로 되어 있습니다.
### 코드 분석
1. **buf 배열**:
- 배열 `buf`는 21개의 고정된 1바이트 정수로 이루어져 있으며, 각각의 값은 `0xAC`, `0xF3` 등 16진수로 표현된 값들입니다.
- 이 배열은 각 문자의 인코딩된 값을 저장하고 있다고 볼 수 있습니다.
2. **이중 for문**:
- 바깥쪽 `for(int j=0; j<21; j++)` 루프는 `buf` 배열의 각 원소를 순차적으로 탐색합니다. 즉, 21개의 값을 하나씩 처리합니다.
- 안쪽 `for(int i=0; i<256; i++)` 루프는 각 배열 원소에 대해, `i` 값을 0부터 255까지 대입하면서 다양한 조건을 시험합니다. 이 `i` 값은 각 문자의 1바이트 가능한 값을 전부 시도하는 역할을 합니다.
3. **곱셈과 나눗셈 조건**:
```c
a = buf[j] + i * 256;
if(a % 0xfb == 0)
{
printf("%c", a / 0xfb);
}
```
- 이 부분에서, `a`는 `buf[j]`에 `i * 256`을 더한 값입니다. 즉, `buf[j]`는 1바이트 크기이므로, 여기에 2바이트에 해당하는 값을 더해주고 있습니다. `i * 256`은 한 비트가 올라간 상태를 시뮬레이션하는 과정입니다.
- `a % 0xFB == 0` 조건은 `a`가 `0xFB` (즉, 251)로 나누어 떨어질 때만 특정 작업을 하겠다는 뜻입니다. 이 작업은 입력된 값을 `0xFB`로 나누어 떨어질 때만 결과를 처리한다는 의미입니다.
- `a / 0xFB`는 해당 값을 문자로 변환하여 출력하는 역할을 합니다.
### 요약
1. **목적**: 이 프로그램은 `buf` 배열의 각 값을 256배수로 확장하면서, 그 값이 `251 (0xFB)`로 나누어 떨어지는 경우를 찾아 그 몫을 문자로 출력합니다.
2. **핵심 로직**: 각 `buf[j]` 값을 `i * 256`과 더한 값이 `0xFB`로 나누어 떨어지는 경우, 그 몫을 ASCII 문자로 변환하여 출력합니다. 이 과정은 일종의 역코딩 또는 해킹 문제에서 자주 사용되는 방식으로, 특정 암호화된 문자열에서 원래의 값을 복원하려는 시도를 의미합니다.
이 코드는 기본적으로 어떤 인코딩된 데이터를 역산해서 올바른 문자 값을 찾아내려는 작업을 하고 있는 것으로 보입니다.
'Dreamhack > Dreamhack Wargame (Challenge)' 카테고리의 다른 글
[57] IT 비전공자 [dreamhack]oneshot문제 풀기 (0) | 2024.11.05 |
---|---|
[56] IT 비전공자 [dreamhack]error based sql injection문제 풀기 (0) | 2024.11.04 |
[54] IT 비전공자 [dreamhack]basic_exploitation_003문제 풀기 (0) | 2024.11.02 |
[53] IT 비전공자 [dreamhack]out_of_bound문제 풀기 (0) | 2024.11.01 |
[52] IT 비전공자 [dreamhack]simple_sqli_chatgpt문제 풀기 (0) | 2024.10.31 |