본문 바로가기
리버싱/CodeEngn 문제해결

Basic L17

by 멍청이 개발자 2026. 9. 19.
반응형

문제는 다음과 같다.

그림0. 문제화면

지금까지의 문제와는 다르게 키를 가지고 Name을 유추해야 한다.

 

DIE로 봤을때 패킹은 되어있지 않았다.

그림1. 프로그램 화면

문제 힌트로 Name은 한 자리 라고 했으므로 알파벳 하나만 넣고 키를 넣은후 Check it을 눌러봤다.

그림2. 더 많은 문자를 넣으라고 한다.

하지만 보다시피 문자를 1개만 넣었더니 더 길게 쓰라고 한다.

 

디버거에서 이 문자열을 검색해봤다.

그림3. Please...으로 검색

해당 위치로 이동한다.

그림4. 비교구문

eax값을 3과 비교후 넘어오는걸로 봐서 3자 이상을 넣도록 설정되어있다.

해당 구문을 1로 바꾸고 패치한다.

 

패치한 파일을 디버거에 올리고 아까처럼 a하나만 입력후 실행해봤다.

그림5. 암호화 코드

한 줄씩 실행하다 보면 45b850함수를 호출한 직후에 코드가 생겨나는것을 확인 할 수 있다,

 

이 함수를 분석해보자.

그림6. 첫번째 로직

45b850함수를 실행하다 보면 처음으로 마주치는 암호화 로직이다.

 

위 부분을 C++코드로 작성해봤다.

더보기

코드 내용

#include <iostream>

int logic1(char* input) 
{
	int cnt = strnlen_s(input, 100);

	int result = 0;
	int dummy = 0;
	for (int i = 0; i < cnt; ++i) {
		result = input[i];
		result += dummy;
		result = result * 0x772;
		dummy = result;
		dummy *= result;
		result += dummy;
		result = result * 0x474;
		result += result;
		dummy = result;
	}

	return result;
}

int main()
{
	char input[100];
	std::cin.getline(input, 100);
	
	int r1 = logic1(input);
	std::cout << std::hex << r1 << std::endl;

	return 0;
}

그림7. 첫번째 로직 결과

 

이 함수의 실행 결과는 esi와 edx에 저장된다. 0x66ee46f0

 

그림8. 두번째 로직

위 코드는 두번째 로직이다.

이 코드에서는 esi는 건들지 않았다.

그래서 첫번째 로직에서의 결과는 esi, 두번째 로직의 결과는 ebp-10주소가 가리키는 값에 위치한다.

코드를 이어서 추가해봤다.

더보기

코드 내용

int logic2(char* input)
{
	int cnt = strnlen_s(input, 100);

	int result = 0;
	int dummy = 0;
	for (int i = cnt-1; i > -1; --i) {
		dummy = input[i];
		dummy += 0x11;
		dummy -= 0x5;
		dummy *= 0x92;
		dummy += dummy;
		dummy *= 0x819;
		result += dummy;
	}

	return result;
}

 

그림9. 두번째 로직 결과

현재까지 ESI는 0x66ee46f0, ebp-10에는 0x03eec434가 저장되어있다.

 

이어서 진행을 하다보면 갑자기 아래와 같이 ebp-8에 알수없는 긴 문자가 생성된다.

그림10. 세번째 로직

45b54c함수 호출 이후 나왔다.

우선은 ebp-8에 세번째 로직의 결과가 저장되어있다는 사실만 인지하고 이어서 진행했다.

esi = 0x66ee46f0 //첫번째
ebp-10 = 0x03eec434 //두번째
ebp-8 = "2A13918845DE8306F4CE135E4BB32CDD" //세번째

 

이어서 진행하다 보면 네번째 로직이 보인다.

그림11. 네번째 로직

우선 분석전부터 알 수 있는것은 esi는 마찬가지로 건들지 않았다.

 

C++로 이어서 작성해봤다.

더보기

코드 내용

int logic4(char* input)
{
	int cnt = strnlen_s(input, 100);

	int result = 0; // eax
	int dummy = 0; // edi
	for (int i = cnt-1; i > -1; --i) {
		dummy += input[i];
		dummy += 0x929;
		dummy += 0x767;
		dummy += result;
		dummy *= 0x8392;
		result = dummy;
        // 한 자리 Name에서 사실상 이 부분은 의미 없음
		result -= 0x33;
		result *= dummy;
		result += dummy;
	}

	return dummy; // edi가 결과값임
}

그림12. 네번째 로직 결과

네번째 로직의 결과로는 0x8b4fc72가 edi에 저장되었다.

 

eax가 아닌 edi를 결과값으로 생각한이유는 다음과 같다.

네번째 로직 코드 종료후의 내용을 보면 이상한점이 있다.

그림13. 네번째 로직 종료후 모습

eax값을 다른 값으로 덮어 씌운다. 어디에 저장한적도 없다. 그래서 연산에 사용되었던 edi 레지스터의 값이 결과라고 생각되었다.

esi = 0x66ee46f0 //첫번째
ebp-10 = 0x03eec434 //두번째
ebp-8 = "2A13918845DE8306F4CE135E4BB32CDD" //세번째
edi = 0x08b4fc72 // 네번째

 

다음으로 다섯번째 로직이다.

그림14. 다섯번째 로직

위 코드는 ebx에 결과를 저장하고 있다.

 

코드 흐름을 C++로 작성해서 실행해봤다.

더보기

코드 내용

int logic5(char* input)
{
	int cnt = strnlen_s(input, 100);

	int result = 0; //ebx
	int dummy = 0; //edx
	for (int i = 0; i < cnt; ++i) {
		dummy = input[i];
		result += dummy;
		result += result;
		dummy = result;
		dummy *= result;
		result *= dummy;
		result ^= 0x10;
		result |= 0x44;
		dummy = result * 0x373;
		dummy += 0x443;
		result = dummy;
		dummy = input[i];
		result += dummy;
		result *= result;
	}

	return result;
}

그림15. 다섯번째 로직 결과

ebx에 0x79e23040을 저장한다.

esi = 0x66ee46f0 //첫번째
ebp-10 = 0x03eec434 //두번째
ebp-8 = "2A13918845DE8306F4CE135E4BB32CDD" //세번째
edi = 0x08b4fc72 // 네번째
ebx = 0x79e23040 // 다섯번째

 

이제 이어서 진행을 해보면 드디어 위에 저장된 다섯가지 값들을 참조해서 코드를 만드는 부분을 마주하게 된다.

그림16. 첫번째 결과 이용

esi에 저장되어있던 결과를 eax에 옮겨서 4086c8함수를 실행하면 16진수 값이 문자열로 저장된다.

이어서 404d0c함수를 실행하면 ebp-1C주소에 문자열의 앞 4자리만 저장된다. 

0x66ee46f0 기준으로 66EE이다.

그림17. 두번째 결과 이용

마찬가지로 두번째 결과의 저장 주소인 ebp-10에 접근하여 문자열로 변환후 앞 네자리만 가져온다.

0x03eec434 기준으로 앞에 0은 무시하여 3EEC가 ebp-24에 저장된다.

그림18. 세번째 결과 이용

세번째 결과의 저장 주소인 ebp-8에 접근하여 앞8자리를 가져와 ebp-2C에 저장한다.

"2A13918845DE8306F4CE135E4BB32CDD"값 기준으로 2A139188이 저장된다.

그림19. 네번째 결과 이용

네번째 결과의 저장주소인 edi에 접근하여 문자열로 변환후 앞 네자리만 가져온다.

0x08b4fc72기준으로 8B4F가 ebp-30에 저장된다.

그림20. 다섯번째 결과 이용

마지막으로 다섯번째 결과의 저장주소인 ebx에 접근하여 문자열로 변환후 앞 네자리만 가져온다.

0x79e23040기준으로 79E2가 ebp-38에 저장된다.

그림21. 결과 종합

마지막으로 404BA8함수를 호출하여 결과 순서대로 '-'로 이어서 붙인다.

66EE-3EEC-2A139188-8B4F-79E2가 나온다.

 

자, 이렇게 세번째 결과를 제외하고 암호화 과정을 알아봤다.

(세번째 로직은 너무 복잡하기도 하고 세번째 로직을 분석하지 않고도 이미 나머지 로직들을 통해서 답을 충분히 찾아낼 수 있기에 따로 분석하지 않았다.)

 

여기까지 분석을 해봤을때 바로 답을 찾아낼 수 있다.

문제에서 주어진 키를 가지고 분석을 해보자.

BEDA-2F56-BC4F4368-8A71-870B 이 값에서 우리가 알 수 있는것은 첫번째 결과의 앞 네자리가 BEDA로 시작한다는 것이다.

이 외에도 두번째, 세번째, 네번째, 다섯번째도 같은 방식으로 유추할 수 있다. 

우린 단순히 브루트포스를 이용하여 알파벳부터 숫자까지 첫번째 결과 로직에 대입하여 BEDA로 시작하는 결과가 나오는 값을 확인하면 된다.

#include <iostream>

// 이전 함수를 문자 하나만 받도록 임시적으로 수정함
int logic1(char input) 
{
	int cnt = 1;

	int result = 0;
	int dummy = 0;
	for (int i = 0; i < cnt; ++i) {
		result = input;
		result += dummy;
		result = result * 0x772;
		dummy = result;
		dummy *= result;
		result += dummy;
		result = result * 0x474;
		result += result;
		dummy = result;
	}

	return result;
}

int main()
{
	char alphaInteger[63] = "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ";

	for (int i = 0; i < 63; ++i) {
		int r1 = logic1(alphaInteger[i]);
		std::cout << alphaInteger[i] << " : " << std::hex << r1 << std::endl;
	}

	return 0;
}

그림22. F를 보자

브루트포스를 돌리니 대문자 F에서 bedaca60이 보인다.

그림23. 성공

Name은 F가 맞았다.

 

F의 MD5해쉬 값이 정답이므로 800618943025315f869e4e1f09471012가 정답이다.

 

반응형

'리버싱 > CodeEngn 문제해결' 카테고리의 다른 글

Basic L16  (0) 2026.09.19
Basic L15  (0) 2026.09.18
Basic L14  (0) 2026.09.18
Basic L13  (0) 2026.09.18
Basic L12  (0) 2026.09.18