벌써 10월도 끝나간다..시간이 정말 빠르다는걸 느낀다. 오늘도 후회없이 살아보자!
디컴파일러해보자!
주어진 C/C++ 코드의 main 함수는 사용자의 입력을 받고, 그 입력을 특정 함수로 검증하여 "Correct" 또는 "Wrong"을 출력하는 구조입니다. 각 부분을 자세히 분석하겠습니다.
코드 분석
1. 함수 서명
- int: 함수가 정수를 반환함을 나타냅니다. 일반적으로 프로그램의 종료 상태를 나타냅니다.
- __fastcall: 호출 규약으로, 일부 인자를 레지스터를 통해 전달하여 성능을 향상시킵니다.
- argc: 명령줄 인자의 개수입니다.
- argv: 각 인자를 포함하는 문자열 배열입니다.
- envp: 환경 변수를 포함하는 문자열 배열입니다.
2. 변수 선언
- char v4[256];: 크기가 256인 문자 배열을 선언하여 사용자 입력을 저장할 공간을 마련합니다. 이 배열은 스택에 할당됩니다.
3. 메모리 초기화
- memset 함수를 사용하여 v4 배열의 모든 요소를 0으로 초기화합니다. 이는 문자열을 사용할 때의 안전성을 높이기 위한 것입니다.
4. 입력 요청
- 이 함수는 사용자에게 "Input : " 메시지를 출력하고 입력을 요청하는 것으로 추측됩니다. argv와 envp를 사용할 수 있지만, 이 함수의 구체적인 구현은 주어진 코드에서 알 수 없습니다.
5. 입력 받기
- 이 함수는 사용자 입력을 v4 배열에 저장합니다. "%256s" 형식 지정자는 최대 256자의 문자열을 입력받도록 설정되어 있습니다. 이는 입력을 안전하게 제한하는 것으로 보입니다.
6. 입력 검증
- sub_140001000(v4) 함수는 v4에 저장된 문자열을 검증하는 함수입니다. 이 함수의 반환 값이 0이 아니면 조건이 참으로 판단됩니다.
- 조건이 참일 경우 "Correct"를 출력하고, 거짓일 경우 "Wrong"을 출력합니다.
7. 종료
- 프로그램을 정상적으로 종료합니다.
전체적인 기능
- 목적: 사용자로부터 문자열 입력을 받아, 그 입력이 특정 조건을 충족하는지 검증하는 프로그램입니다.
- 입력 처리:
- 사용자의 입력을 안전하게 받아오기 위해 버퍼를 초기화하고 최대 길이를 제한하고 있습니다.
- 검증 로직:
- 입력된 문자열이 sub_140001000 함수에 의해 검사됩니다. 이 함수가 특정 조건을 확인하여 입력의 유효성을 판단합니다.
결론
이 main 함수는 사용자 입력을 받아서 이를 검증하는 구조를 가지고 있습니다. 함수 sub_140001000의 구현에 따라 입력된 문자열의 유효성이 결정되며, 이를 기반으로 "Correct" 또는 "Wrong" 메시지를 출력하게 됩니다. 이 코드는 입력 검증의 기본적인 패턴을 보여주며, 사용자 입력에 대한 안전성을 높이기 위한 조치를 취하고 있습니다.
주어진 C/C++ 코드의 sub_140001000 함수는 특정 조건을 검사하여 입력 값의 유효성을 판단하는 역할을 합니다. 아래에서 각 부분을 자세히 분석하겠습니다.
함수 서명
- __int64: 함수가 64비트 정수를 반환함을 나타냅니다.
- __fastcall: 호출 규약을 나타내며, 일부 매개변수를 레지스터를 통해 전달하여 성능을 향상시킵니다.
- a1: 64비트 정수형 매개변수로, 검사할 데이터의 주소를 나타냅니다.
변수 선언
- i: 반복문에서 사용할 인덱스 변수입니다.
반복문
- 이 반복문은 0부터 28(0x1C는 28의 16진수 표현)까지의 정수를 반복합니다. 총 28회 반복하게 됩니다.
조건문
- 이 부분은 다음과 같은 작업을 수행합니다:
- *(unsigned __int8 *)(a1 + i): a1 주소에서 i 바이트 위치의 값을 가져옵니다. 이는 a1의 각 바이트를 검사하는 과정입니다.
- 16 * *(_BYTE *)(a1 + i): 현재 바이트 값을 16배로 곱합니다.
- ((int)*(unsigned __int8 *)(a1 + i) >> 4): 현재 바이트 값을 4비트 오른쪽으로 시프트하여 상위 4비트를 가져옵니다.
- |: 두 값을 비트wise OR 연산을 수행합니다.
- 결과적으로 이 조건문은 계산된 값이 byte_140003000[i]와 같지 않으면 0LL을 반환합니다. 즉, 조건이 만족하지 않을 경우 함수는 종료됩니다.
성공 조건
- 반복문이 끝까지 실행되고, 모든 비교가 성공적으로 통과하면 1LL을 반환합니다. 이는 입력값이 올바르다는 것을 나타냅니다.
전체적인 기능
이 함수의 전체적인 기능을 정리하면 다음과 같습니다:
- 목적: a1로 주어진 메모리 주소에 저장된 데이터를 검증하는 것입니다.
- 검증 방법:
- a1에서 28바이트의 값을 읽고, 각 값에 대해 특정 계산을 수행한 후, byte_140003000 배열의 값과 비교합니다.
- 만약 계산된 값이 byte_140003000의 해당 인덱스와 다르면 함수는 0을 반환하여 실패를 나타냅니다.
- 결과:
- 모든 값이 일치하면 1을 반환하고, 하나라도 다르면 0을 반환합니다.
결론
이 함수는 특정 입력값이 기대하는 형식이나 내용을 충족하는지 검사하는 데 사용됩니다. byte_140003000 배열이 미리 정의된 기준 값을 나타내며, 입력된 데이터의 유효성을 판단하여 성공 여부를 결정하는 역할을 합니다. 이를 통해 주어진 입력 값의 무결성을 확인할 수 있습니다.
rev-basic-03이 xor연산이었다면 이건 두값 비교 및 or연산이다!
https://hyunhe2e.tistory.com/m/16
https://velog.io/@helenason/dreamhack-wargame-rev-basic-4
입력값을 'a'로 가정해보자
(16 * *(_BYTE *)(a1 + i)) | ((int)*(unsigned __int8 *)(a1 + i) >> 4)) != byte_140003000[i] )
(16*a1[i]) | (a1[i]>>4) !=byte_1400300[i]
순서는
1. 16*a1[i]
2. a1[i]>>4
3. (16*a1[i]) | (a1[i]>>4)
16(10)*16진수
0xA *16(10) = 0xA0
0xB *16(10) = 0xB0
.
.
.
하나의 16진수에다가 십진수 16을 곱하면 그 결과는 16진수의 맨 오른쪽에 0을 하나 더 붙이는거랑 같다.
a1[i]>>4
0xA>>4
0000 1010>>4
0000 0101 (쉬프트 한번)
0000 0010 (쉬프트 2번)
0000 0001 (쉬프트 3번)
0000 0000 (쉬프트 4번)
0xA>>4는 0x0
(16*a1[i]) | (a1[i]>>4)
입력값이 0xA라고 모두 가정하고 모두 정리하면
0xA0 | 0x0 != byte_140003000[i]
1010 0000 | 0000 0000 != byte_140003000[i]
1010 0000 != byte_140003000[i]
즉, 1010 0000 과 byte_140003000[i] 를 비교하는 것이다.
그런데, 1010 0000 은 처음 입력했던 0xA 를 2진수에서 4비트끼리 앞뒤로 뒤집은 값이다.
-->
0xA = 0000 1010 (2)
0xA0 = 1010 0000 (2)
라는 것이다.
따라서 byte_140003000[i] 에 저장되어 있는 문자들을
거꾸로 다시 앞뒤로 4비트씩 뒤집으면
Correct를 출력할 수 있는 입력값을 알 수 있다.
출처ㅣ https://aawjej.tistory.com/191
=> 상위 4비트와 하위 4비트를 바꾼 결과 값을 해당 연산을 통해 새로 생성하고 있다.
.data:0000000140003000 byte_140003000 db 24h, 27h, 13h, 2 dup(0C6h), 13h, 16h, 0E6h, 47h, 0F5h
.data:0000000140003000 ; DATA XREF: sub_140001000+50↑o
.data:000000014000300A db 26h, 96h, 47h, 0F5h, 46h, 27h, 13h, 2 dup(26h), 0C6h
.data:0000000140003014 db 56h, 0F5h, 2 dup(0C3h), 0F5h, 2 dup(0E3h), 5 dup(0)
byte_140003000[i] 에 저장되어 있는 값들
24h, 27h, 13h, C6h, C6h,
13h, 16h, E6h, 47h, F5h,
26h, 96h, 47h, F5h, 46h,
27h, 13h, 26h, 26h, C6h,
56h, F5h, C3h, C3h, F5h,
E3h, E3h
로 27개 이다.
#include <stdio.h>
int main() {
int byte_140003000[27] =
{
0x24, 0x27, 0x13, 0xC6, 0xC6,
0x13, 0x16, 0xE6, 0x47,0xF5,
0x26, 0x96, 0x47, 0xF5, 0x46,
0x27, 0x13, 0x26, 0x26, 0xC6,
0x56, 0xF5, 0xC3, 0xC3, 0xF5,
0xE3,0xE3 };
for (int i = 0; i < 27; i++) {
printf("%c", (16 * byte_140003000[i]) & 0xF0 | (byte_140003000[i] >> 4));
}
return 0;
}
코드 출처 https://aawjej.tistory.com/191
각 부분 설명
- 헤더 파일 포함:
- 표준 입출력 함수를 사용하기 위해 stdio.h 헤더 파일을 포함합니다.
-
c코드 복사#include <stdio.h>
- 메인 함수:
- 프로그램의 진입점인 메인 함수입니다.
-
c코드 복사int main() {
- 정수 배열 선언 및 초기화:
- byte_140003000 배열을 선언하고 27개의 16진수 값을 초기화합니다. 각 값은 0x00에서 0xFF 범위의 정수입니다.
-
c코드 복사int byte_140003000[27] = { 0x24, 0x27, 0x13, 0xC6, 0xC6, 0x13, 0x16, 0xE6, 0x47, 0xF5, 0x26, 0x96, 0x47, 0xF5, 0x46, 0x27, 0x13, 0x26, 0x26, 0xC6, 0x56, 0xF5, 0xC3, 0xC3, 0xF5, 0xE3, 0xE3 };
- 문자 출력 루프:
- 배열의 각 요소에 대해 다음 작업을 수행합니다:
- 변환:
- (16 * byte_140003000[i]) & 0xF0: 배열의 각 요소를 16배한 값에서 상위 4비트를 추출합니다.
- (byte_140003000[i] >> 4): 각 요소의 하위 4비트를 추출합니다.
- 두 값을 OR 연산(|)하여 결합합니다.
- 출력: printf("%c", ...)를 통해 변환된 값을 문자로 출력합니다.
- 변환:
- 배열의 각 요소에 대해 다음 작업을 수행합니다:
-
c코드 복사for (int i = 0; i < 27; i++) { printf("%c", (16 * byte_140003000[i]) & 0xF0 | (byte_140003000[i] >> 4)); }
- 종료:
- 프로그램을 정상적으로 종료합니다.
-
c코드 복사return 0;
변환 과정 예시
예를 들어, byte_140003000[0]의 값이 0x24(36)일 때, 변환 과정은 다음과 같습니다:
- 16 * 0x24 = 0x240 (576)
- 0x240 & 0xF0 = 0x240 & 0xF0 = 0x40 (64)
- 0x24 >> 4 = 0x2 (2)
- 0x40 | 0x2 = 0x42 (66), ASCII로는 B
이와 같은 방식으로 배열의 각 값이 변환되어 출력됩니다.
결론
이 코드는 주어진 정수 배열의 값을 특정 방식으로 변환한 후, 이를 ASCII 문자로 출력하는 기능을 수행합니다. 변환 방식은 각 값의 상위 비트와 하위 비트를 조합하여 새로운 문자를 생성하는 과정을 포함하고 있습니다. 이를 통해 배열의 각 요소가 어떻게 변환되는지를 확인할 수 있습니다.
또다른 분의 코드다
#include<stdio.h>
int main() {
int input[28] = { 0x24, 0x27, 0x13, 0xC6, 0xC6, 0x13, 0x16, 0xE6, 0x47, 0xF5, 0x26, 0x96, 0x47, 0xF5, 0x46, 0x27,
0x13, 0x26, 0x26, 0xC6, 0x56, 0xF5, 0xC3, 0xC3, 0xF5, 0xE3, 0xE3, 0x00 };
int flag[28] = { 0 };
for (int i = 0; i < 28; i++) {
flag[i] = 16 * input[i] | input[i] >> 4;
}
printf("flag is ");
for (int i = 0; i < 28; i++)
printf("%c", flag[i]);
return 0;
}
출처
https://hyunhe2e.tistory.com/m/16
주어진 C 코드 조각은 배열의 값을 변환하여 플래그를 생성하고, 이 플래그를 문자로 출력하는 프로그램입니다. 코드를 단계별로 분석하겠습니다.
### 각 부분 설명
1. **헤더 파일 포함**:
```c
#include <stdio.h>
```
- 표준 입출력 함수를 사용하기 위해 `stdio.h` 헤더 파일을 포함합니다.
2. **메인 함수**:
```c
int main() {
```
- 프로그램의 진입점인 메인 함수입니다.
3. **입력 배열 선언 및 초기화**:
```c
int input[28] = {
0x24, 0x27, 0x13, 0xC6, 0xC6, 0x13, 0x16, 0xE6, 0x47, 0xF5,
0x26, 0x96, 0x47, 0xF5, 0x46, 0x27, 0x13, 0x26, 0x26, 0xC6,
0x56, 0xF5, 0xC3, 0xC3, 0xF5, 0xE3, 0xE3, 0x00
};
```
- `input`이라는 정수 배열을 선언하고, 28개의 16진수 값으로 초기화합니다. 마지막 값 `0x00`은 문자열 종료를 나타냅니다.
4. **플래그 배열 선언**:
```c
int flag[28] = { 0 };
```
- `flag` 배열을 선언하고, 모든 요소를 `0`으로 초기화합니다. 이 배열은 변환된 값을 저장하는 데 사용됩니다.
5. **변환 루프**:
```c
for (int i = 0; i < 28; i++) {
flag[i] = 16 * input[i] | input[i] >> 4;
}
```
- `input` 배열의 각 요소에 대해 다음 작업을 수행합니다:
- `16 * input[i]`: 각 요소를 16배로 증가시킵니다.
- `input[i] >> 4`: 각 요소를 4비트 오른쪽으로 시프트하여 상위 4비트를 가져옵니다.
- 두 값을 비트wise OR(`|`) 연산하여 결합하고, 그 결과를 `flag[i]`에 저장합니다.
6. **결과 출력**:
```c
printf("flag is ");
for (int i = 0; i < 28; i++)
printf("%c", flag[i]);
```
- "flag is "라는 문자열을 출력한 후, `flag` 배열의 각 요소를 문자로 출력합니다. 이 부분에서 변환된 값이 ASCII 코드로 해석되어 출력됩니다.
7. **종료**:
```c
return 0;
```
- 프로그램을 정상적으로 종료합니다.
### 변환 과정 예시
`input` 배열의 첫 번째 값 `0x24`(36)를 변환하는 과정을 살펴보면:
1. `16 * 0x24` = `0x240` (576)
2. `0x24 >> 4` = `0x2` (2)
3. `0x240 | 0x2` = `0x242` (578)
`0x242`는 ASCII 코드로는 `B`에 해당합니다.
이러한 방식으로 배열의 모든 요소가 변환되어 출력됩니다.
### 결론
이 코드는 주어진 정수 배열의 값을 변환한 후, 이를 ASCII 문자로 출력하는 기능을 수행합니다. 변환 방식은 각 값의 상위 비트와 하위 비트를 조합하여 새로운 문자를 생성하는 과정을 포함하고 있습니다. 이를 통해 배열의 각 요소가 어떻게 변환되는지를 확인할 수 있습니다.
'Dreamhack > Dreamhack Wargame (Challenge)' 카테고리의 다른 글
[48] IT 비전공자 [dreamhack]proxy-1문제 풀기 (4) | 2024.10.27 |
---|---|
[47] IT 비전공자 [dreamhack]simple-ssti문제 풀기 (1) | 2024.10.26 |
[45] IT 비전공자 [dreamhack]Return Address Overwrite문제 풀기 (2) | 2024.10.24 |
[44] IT 비전공자 [dreamhack]rev-basic-2 문제 풀기 (4) | 2024.10.23 |
[43] IT 비전공자 [dreamhack]basic_exploitation_001 문제 풀기 (4) | 2024.10.22 |