CodeEngn Write Up/Advance

CodeEngn Advance RCE L11 Write Up

서원근양학계정 2022. 8. 19. 14:53

Serial이 94E7DB1B 일때 Name은 무엇인가
해당 Serial에 대한 정답이 여러개 나오는 문제이며 Contact로 보내주시면 인증키를 보내드리겠습니다
해당 Serial에 대해서 'Serial accepted' 메시지가 나와야 합니다.

https://ch.codeengn.com/

 

CodeEngn.com [코드엔진]

코드엔진은 국내 리버스엔지니어링 정보공유를 위해 2007년 부터 리버스엔지니어링 컨퍼런스 및 세미나, 워크숍을 현업 실무자들과 함께 운영하고 있는 비영리 커뮤니티입니다.

ch.codeengn.com

 

먼저 문제 파일을 실행시켜보겠습니다.

 

그리고 아무 값이나 넣고 실행시키면 틀린 시리얼이라고 합니다.

 

ExeinfoPE를 이용해서 정적 분석을 진행해보니 특별한 것은 찾을 수 없었습니다.

 

x32DBG를 이용해서 분석해보겠습니다.

문자열 검색을 통해서 실패와 성공이 나뉘는 부분을 쉽게 찾을 수 있었습니다.

 

실패와 성공이 나뉘는 부분에서 호출하는 함수가 하나밖에 없기 때문에 저 함수가 상당히 중요하다는 것을 알 수 있습니다.

이 함수를 분석해보겠습니다.

 

이 함수는 어떠한 긴 문자열에서

 

값을 하나씩 가져옵니다.

 

그리고 이 값에 따라서 특정한 작업을 수행한 다음 0x00401164로 돌아와서 이 과정을 다시 수행합니다.

이 프로그램이 작동하는 방식을 알아냈습니다.

이제 값에 따라서 수행하는 특정한 작업들을 하나하나씩 분석하면 됩니다.

 

하지만 분석하면서 문제가 발생했습니다.

값에 따라서 특정한 작업을 수행한다고 했는데 이 특정한 작업이 너무 많아서 모두 분석하는 것은 매우 힘듭니다.

또한 문자열도 길어서 반복하는 횟수가 많기 때문에 하나하나 분석하면서 알맞은 Name값을 구하는 것은 불가능에 가깝습니다.

리버싱 도구인 IDA의 코드 구조를 그래프로 나타내주는 기능을 사용했을 때의 모습입니다.

정말 복잡해보입니다.

 

그래서 저는 고민했습니다. 어떻게 해야 이 문제를 풀 수 있을지를.

 

먼저 저는 성공과 실패를 가르는 부분을 분석했습니다.

 

0x402439에는 저희가 입력한 Name값으로부터 만들어진 것으로 추정되는 값이, 0x40233A에는 저희가 Serial값이 들어있습니다.

그래서 저는 덤프 창에는 0x402439를 띄워놓고 0x401164에 BP를 걸고 F9를 한 번씩 눌러가면서 덤프의 값이 어떻게 변하는지 지켜보기로 했습니다.

 

먼저 0x38과 0x64를 더합니다. 그리고 더한 값인 0x9C를 6D와 곱합니다. 곱한 값인 0x426C와 이름의 첫번째 글자의 아스키코드 값인 0x31을 곱합니다. 곱한 값인 0xCB6AC를 0x3B10에 더해줍니다. 그러면 0xCF1BC가 됩니다.

이 과정을 이름의 길이만큼 반복합니다.

그렇게 반복을 마치면 0x34A448이 됩니다.

 

하지만 이것이 시리얼이 되는 것은 아닙니다.

조금 더 실행시키다 보면 0xF가 생기는 것을 볼 수 있습니다.

 

그리고 조금 더 실행시키면 값이 0x3과 0x3826B로 기존의 값인 0x34A448에서 크게 바뀌게 됩니다.

 

이 값들은 기존의 값을 0xF로 나눈 나머지와 몫입니다.

0x3826B * 0xF + 0x3 = 0x34A448

이 다음에는 나머지와 몫의 자리를 바꾸고 나머지인 0x3에 0x30을 더해서 0x33을 만듭니다.

 

0x33을 문자로 바꾼 3이 Serial의 첫 글자가 되고 0x402439에 저장됩니다.

그리고 몫에 0x2를 곱합니다.

 

이 과정을 몫이 0이 될 때까지 반복합니다. 

Name에 1234를 넣었을 때는 30D1B932가 Serial과 비교되는 값이 됩니다.

 

이제 값을 만드는 방법은 알았으니 0x94E7DB1B이 나오게 하는 Name값을 찾으면 됩니다.

하지만 제가 하나하나씩 직접 하기에는 힘이 많이 드므로 코드를 작성해보았습니다.

#define _CRT_SECURE_NO_WARNINGS

#include <limits.h>
#include <stdbool.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

int main()
{
	int a;
	int b = 0x426C;
	char aa[81];
	char aaa[9] = { "94E7DB1B" };

	int i, j;

	for (i = 0x0; i <= 0xFFFF; i++)
	{
		memset(aa, 0, sizeof(aa));
		j = 0;

		a = b * i + 0x3B10;

		while (a != 0)
		{
			aa[j] = a % 0xF + 0x30;
			a = a / 0xF * 2;
			j++;
		}

		if (strncmp(aa, aaa, 8) == 0)
			break;
	}

	printf("%s\n%X", aa, i);

	return 0;
}

 

65536가지의 경우의 수를 계산하고 맞는 값인지 확인하는 코드입니다.

하지만 무려 65536가지 경우의 수를 대입해보았음에도 올바른 값을 찾을 수 없었습니다.

그래서 저는 값을 계산하면서 실수를 했다고 생각해서 몇 가지 실험을 했지만 그 때는 결과가 잘 나왔습니다.

그래서 저는 다시 x32DBG를 켜서 값을 계산하는 과정을 다시 침착하게 지켜보았습니다.

그리고 저는 특정한 상황에서 나머지에 0x30을 더하고 0x8을 추가로 더하는 것을 확인했습니다.

 

 

그리고 저는 0x30을 더한 값이 0x3A보다 크거나 같을 때라는 사실을 알아냈습니다.

이 때 0x8을 추가로 더하는 이유는 아스키코드 표에서 0x3A부터 0x40까지는 특수기호가 포함되어 있어서 8을 더해서 특수기호가 나오지 않게 하려는 것 같습니다.

어쨌든 이 부분을 적용해서 코드를 수정하고 다시 코드를 작동시켜보았습니다.

#define _CRT_SECURE_NO_WARNINGS

#include <limits.h>
#include <stdbool.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

int main()
{
	int a;
	int b = 0x426C;
	char aa[81];
	char aaa[9] = { "94E7DB1B" };

	int i, j;

	for (i = 0x0; i <= 0xFFFF; i++)
	{
		memset(aa, 0, sizeof(aa));
		j = 0;

		a = b * i + 0x3B10;

		while (a != 0)
		{
			aa[j] = a % 0xF + 0x30;
			if (aa[j] >= 0x3A)
				aa[j] += 0x8;
			a = a / 0xF * 2;
			j++;
		}

		if (strncmp(aa, aaa, 8) == 0)
			break;
	}

	printf("%s\n%X", aa, i);

	return 0;
}

 

 

첫 번째 줄에 94E7DB1B이 잘 출력된 것을 볼 수 있습니다.

두 번째 줄에 32B라는 16진수 숫자가 있는데 이 숫자가 뜻하는 것은 Name값의 아스키코드 합이 32B인 경우에 올바른 Serial값이 만들어진다는 것입니다.

따라서 Name값은 여러개가 될 수 있습니다.

저는 Name값으로 AAAAAAAAAAA`를 넣어보았습니다.

 

Flag: 아스키코드 값의 합이 0x32B인 모든 문자열

AAAAAAAAAAA`

'CodeEngn Write Up > Advance' 카테고리의 다른 글

CodeEngn Advance RCE L09 Write Up  (7) 2022.06.01
CodeEngn Advance RCE L08 Write Up  (3) 2022.05.29
CodeEngn Advance RCE L07 Write Up  (8) 2022.05.28
CodeEngn Advance RCE L06 Write Up  (2) 2022.05.19
CodeEngn Advance RCE L05 Write Up  (2) 2022.05.19