흰싸라기 2022. 5. 3. 21:15

rev-basic-9: https://dreamhack.io/wargame/challenges/23/

 

rev-basic-9

Reversing Basic Challenge #9 이 문제는 사용자에게 문자열 입력을 받아 정해진 방법으로 입력값을 검증하여 correct 또는 wrong을 출력하는 프로그램이 주어집니다. 해당 바이너리를 분석하여 correct를 출

dreamhack.io

 

eax : strlen

  • 주로 함수 return 값을 eax에 저장하기 때문

cdq : 나누기 연산시 사용

  •  eax 부호비트를 edx까지 확장 -> edx는 0으로 설정됨
  • 나누기 연산 시 몫을 eax에 나머지를 edx에 넣기 위해 사용됨

test : and 연산 수행, 결과값은 저장하지 않음

jz : 왼쪽 인자 값이 0이면 점프

-> eax가 0이 되어야함 -> 7로 나누었을 때 몫과 나머지가 같아야함 -> 문자열 길이는 8의 배수-1

 

: (strlen+1) % 8 = 7 이면 loc_7FF7403C1030로 점프

 

loc_7FF7403C1030

[rsp+38h+var_18]을 0으로 초기화하고 이동

int i = 0; 으로 예측 가능

 

loc_7FF7403C1045

jge: 왼쪽 인자 값이 오른쪽 값보다 크거나 같으면 점프

i >= strlen([rsp+38h+var_14])+1 이면 loc_7FF7403C106B로 점프(왼쪽)

-> i < strlen+1 인 동안 반복

 

점프하지 않은 경우는(오른쪽), 

rax와 rcx를 주소 str+i로 세팅하고 함수 sub_7FF7403C10A0 부르고 

loc_7FF7403C103A로 점프

 

loc_7FF7403C106B

memcmp(buf1, buf2, 0x19) 호출

buf2는 { 0x7E, 0x7D, 0x9A, 0x8B, 0x25, 0x2D, 0xD5, 0x3D, 0x03, 0x2B, 0x38, 0x98, 0x27, 0x9F, 0x4F, 0xBC, 0x2A, 0x79, 0x00, 0x7D, 0xC4, 0x2A, 0x4F, 0x58, 0x00 }

buf1이 연산으로 구해지는 듯함, 따라서 buf1이 buf2처럼 만들어져야함!

 

문자열 길이를 추측해보면, 우선 strlen은 8의 배수- 1이어야함.

따라서 25(0x19)가 아닌 23로 추측되고 마지막은 '0x00'(문자열 마지막)

나머지 하나는 연산하며 추가되는 걸로 추측해볼 수 있다.

(실제로, bp를 걸고 실행했을 때 문자열 길이가 23인 경우에만 bp가 실행되고 24, 25는 그냥 종료되었다.) 

 

loc_7FF7403C103A

i를 8 증가시킨다.

아무래도 아까 call한 함수에서 8개의 문자로 연산을 진행하는듯

F7로 해당 함수를 들어가 살펴보아야할 필요가 있어보임

 

sub_7FF7403C10A0

여기서는 I_am_KEY 문자열과 0x10번의 반복문이 있다는 것을 확인 가능함

밑으로 내려가면, 

 

또 반복문이 있는 것을 확인

따라서 여기서는 중첩반복문을 사용했음을 알 수 있다.

 

그리고 오른쪽 아래의 박스에서 연산을 진행함을 알 수 있다. 

연산이 복잡해서 실제 코드로 작성해 정리하며 분석했다.

정리한 코드 아래와 같다. 

#include <iostream>
#include <cmath>
#include <string>
using namespace std;

void ROR(int& data, int c) {
	for (int i = 0; i < c; i++) {
		if (data % 2 == 0) {
			data /= 2;
		}
		else {
			data /= 2;
			data += pow(2, 7);
		}
	}
}

int main() {
	int array[256];
	cout << "array : "; 
	for (int i = 0; i < 256; i++) { // 입력
		int k; cin >> hex >> k;
		array[i] = k;
	}


	int Buf1[25] = { 0, };
	for (int i = 0; i < 23; i++) { // 입력
		char a;
		cin >> a;
		Buf1[i] = (int)a;
	}

	for (int i = 0; i < 23+1; i += 8) {
		string iam = "I_am_KEY";
		for (int j = 0; j < 0x10; j++) {
			for (int k = 0; k < 8; k++) {
				
				int var_48 = Buf1[i + k];

				var_48 ^= (int)iam[k];

				int arr = array[var_48];
				int eax = arr + Buf1[i + ((k + 1) % 8)];
				eax %= 256;
				
				ROR(eax, 5);

				Buf1[i + ((k + 1) % 8)] = eax;
			}
			cout << endl;
		}
	}
}
void ROR(int& data, int c) ROR연산을 수행하는 함수 
int array[256] byte_7FF741E54020에 저장된 값
(코드 실행시 입력하여 사용)
int Buf1[25] 사용자가 입력하는 값
for(int k=0;k<8;k++){} 어셈블리어로 되어있는 연산을 이해하기 쉽도록 분석 후 소스코드로 작성

: 명령어를 해석하고 헷갈리는 부분은 값을 넣고 실행하며 진행과정을 보면서 해석할 수 있었다!

 

이제 연산을 반대로 진행하여 원하는 답을 구하면 문제를 해결할 수 있다. 

답을 구하는 코드는 아래와 같다. 

#include <iostream>
#include <string>
using namespace std;

void ROL(int& data, int c) {
	for (int i = 0; i < c; i++) {
		if (data >= 128) {
			data %= 128;
			data = data * 2 + 1;

		}
		else {
			data *= 2;
		}
	}
}

int main() {
	int array[256];
	cout << "array : ";
	for (int i = 0; i < 256; i++) {
		int k; cin >> hex >> k;
		array[i] = k;
	}

	int Buf[25] = { 0x7E, 0x7D, 0x9A, 0x8B, 0x25, 0x2D, 0xD5, 0x3D,
					0x03, 0x2B, 0x38, 0x98, 0x27, 0x9F, 0x4F, 0xBC,
					0x2A, 0x79, 0x00, 0x7D, 0xC4, 0x2A, 0x4F, 0x58, 0x00 };
	int str[25] = { 0, };
	string iam = "I_am_KEY";

	for (int i = 0; i < 24; i += 8) { // 연산 순서에 맞게 변경
		for (int j = 0; j < 8; j++) {
			if (j == 0) str[i + j] = Buf[i + j];
			else str[i + j] = Buf[i + 8 - j];
		}
	}

	for (int i = 0; i < 23 + 1; i += 8) {
		for (int j = 0; j < 0x10; j++) {
			for (int k = 0; k < 8; k++) {
				int c_var = str[i + k];
				int m_var = str[i + (k + 1) % 8];

				ROL(c_var, 5); // ROR(eax, 5); ok

				m_var ^= (int)iam[7 - k]; // var_48 ^= (int)iam[k];
				int arr = array[m_var]; // int arr = array[var_48];
				str[i + k] = c_var - arr; // int eax = arr + Buf1[i + ((k + 1) % 8)];

				
				if (str[i + k] < 0)	str[i + k] += 256; //eax %= 256

			}
		}
	}

	for (int i = 0; i < 24; i += 8) { // 다시 되돌리기
		for (int j = 0; j < 8; j++) {
			if (j == 0)Buf[i + j] = str[i + j];
			else Buf[i + j] = str[i + 8 - j];
		}
	}

	for (int i = 0; i < 24; i++) {
		cout << char(Buf[i]);

	}

	return 0;
}
void ROL(int& data, int c) ROL연산을 수행하는 함수 
int array[256] byte_7FF741E54020에 저장된 값
(코드 실행시 입력하여 사용)
int Buf[25] memcmp에서 연산 결과와 비교하는 값 
-> 이 값을 반대로 연산해서 답을 얻을 수 있다.
int str[25] 연산을 반대로 진행하기 위해 순서를 바꾼 배열 
-> 값을 저장한 순서가 Buf에서는 8 1 2 3 4 5 6 7 이기 때문에 8 7 6 5 4 3 2 1이 되도록 수정함
for(int k=0;k<8;k++){} 반대로 연산을 진행 
cout<< char(Buf[i]) 연산하기 편하도록 순서를 변경했으므로 다시 원래대로 순서를 변경해서 Buf를 출력해 답 확인

위 코드에 array값을 입력하면 답을 얻을 수 있다.

 

<답>