[CodeEngn]Advance RCE L07

Hunjison·2021년 7월 25일
0

Reverse Engineering

목록 보기
7/9

문제


Name이 CodeEngn일때 Serial은 28BF522F-A5BE61D1-XXXXXXXX 이다.

XXXXXXXX 를 구하시오

실행


위에 이름 입력, 아래에 시리얼 입력.

접근


PEiD로 패킹 조회해보니, C#으로 개발된 프로그램이었다.

  • PEiD

C#으로 개발된 프로그램은 소스코드로 디컴파일이 가능하며, 소스코드를 수정하여 다시 컴파일 하는 과정도 간단하기 때문에 어셈블리 레벨이 아니라 소스코드 레벨에서 분석을 수행할 수 있다.

분석 도구는 대표적으로 dotPeek, dnspy 등이 있는데 dnspy가 수정하여 컴파일하는 기능을 편리하게 지원하기 때문에 dnspy를 사용하였다.

풀이


1. button1_Click()


private void button1_Click(object sender, EventArgs e)
{
	string text = "";
	string text2 = "";
	string text3 = "";
	ytrewq ytrewq = new ytrewq();
	if (this.textBox1.Text.Length >= 5 && this.textBox1.Text.Length <= 27 && this.textBox2.Text.Length == 26 && this.textBox2.Text[8] == '-' && this.textBox2.Text[17] == '-')
	// textBox1(이름) 길이 5 이상. textBox2(시리얼) 길이 = 26. textBox2 형식은 8-8-8의 시리얼 형식.
  {
		for (int i = 0; i < 8; i++)
		{
			text += this.textBox2.Text[i]; // 앞 부분
		}
		uint num = Convert.ToUInt32(text, 16);
		for (int j = 9; j < 17; j++)
		{
			text2 += this.textBox2.Text[j]; // 중간 부분
		}
		uint num2 = Convert.ToUInt32(text2, 16);
		for (int k = 18; k < 26; k++)
		{
			text3 += this.textBox2.Text[k]; // 끝 부분(XXXXXXXX)
		}
		uint num3 = Convert.ToUInt32(text3, 16);
		uint num4 = ytrewq.qwerty(Form1.dfgsf(this.textBox1.Text));
		uint hashCode = (uint)this.textBox1.Text.GetHashCode(); // CodeEngn에 대한 hash.
		num3 ^= hashCode;
		this.yreee[0] = num;
		this.yreee[1] = num2;
		this.yreee[2] = num;
		this.yreee[3] = num2;
		bool flag = this.vxzzz(this.yreee, this.ewrrr, 2415796773U, num3); 
		// XXXXXXXX를 다른 함수로 가져감.
		if (flag && this.yreee[2] == hashCode && this.yreee[3] == num4) 
		// flag 제외 XXXXXXXX와 관련 없음.
		{
			MessageBox.Show("Congratulations, mate!", "Fine!", MessageBoxButtons.OK, MessageBoxIcon.Asterisk);
		}
	}
}

문제에서 시리얼의 앞, 중간 부분을 이미 주고 있기 때문에 끝 부분(XXXXXXXX)만 구하면 된다.

해당 부분은 num3에 저장되며, 이 값에 따라 flag가 결정된다.

마지막 if 문에서 flag를 제외한 2개의 bool 값은 참이다.

2. 고통의 시작


마지막 if 문에서 flag를 제외한 2개의 bool 값은 참이다.

dnspy에서 디버깅 기능을 지원한다. 위에 인용된 부분이 혹시나 거짓일까봐 디버깅을 통해 확인해보았다.

  • 확인하기 위해 코드 수정 후 재 컴파일

  • 디버깅

    • 디버깅 화면

    • dnspy 디버깅 단축키

      `F5` Run
      
      `F9` Set Breakpoint
      
      `F10` Step Over
      
      `F11` Step Into

      문제 부분(XXXXXXXX)과 연관이 없기 때문에 당연히 true가 나올줄 알았는데, false가 나왔다. 이렇게 되면 this.vxzzz()를 참으로 만든다고 해도 절대 문제를 풀 수 없다.

  • 해결

    결국 풀이를 검색해본 결과, String.GetHashCode() 함수가 32bit와 64bit에서 서로 다른 값을 반환하는데, 이 프로그램은 32 bit에서 동작하도록 구현되었고, 나는 64bit에서 이를 구동했던 것이다. 따라서 전혀 다른 Hashcode가 생성되어 flag가 둘다 false가 나왔던 것이다.

3. Windows 7 설치


이 문제를 위해 정성스레 Windows 7 iso를 다운받아 설치하자.

조금 이상하다. 분명 GetHashCode()의 결과값이 32bit와 64bit의 환경에서 다르긴 하지만, 두 개의 flag가 false가 나온다.

  • 32bit(hashCode = 0x9E1E73D5)

    flag 2개 모두 false이다..?

  • 64bit(hashcode = 0x82AD30CB)

    flag 2개 모두 false이다.

4. 마무리


flag 2,3은 포기하고!, flag 1이라도 true로 하는 조건이 무엇일까 고민해보았다.

아래 코드는 vxzzz 함수를 c++ 형식으로 바꾼 것이다.

for 문을 통하여 0부터 0x100까지 결과값이 뭐가 나오는지를 출력하도록 하였다.

#include <iostream>

bool vxzzz(unsigned int rwerqw[], unsigned int kgtsdfs[], unsigned int pgdsfa, unsigned int fsfsdf);

int main()
{
	unsigned int arr1[] = { 0x28BF522F, 0xA5BE61D1, 0x28BF522F, 0xA5BE61D1 };
	unsigned int arr2[] = { 0x408112F1, 0x1A3B0B51, 0x2957C9FD, 0x1B327D1D, 0xA38D0AF1, 0x2AD5E941 };
	unsigned int nnum = 0x8FFE2225;
	for (int i = 0; i < 0x100; i++) {
		bool flag = vxzzz(arr1, arr2, nnum, i);
		std::cout << "i : " << i << ", flag : " <<  std::boolalpha << flag <<std::endl;
	}
	std::cout << "Hello World!\n";
}

bool vxzzz(unsigned int rwerqw[], unsigned int kgtsdfs[], unsigned int pgdsfa, unsigned int fsfsdf)
{
	pgdsfa ^= fsfsdf;
	unsigned int num = pgdsfa % 57U - 1U;
	unsigned int num2 = rwerqw[0];
	unsigned int num3 = rwerqw[1];
	unsigned int num4 = num;
	unsigned int num5 = pgdsfa << (int)((char)(97U ^ num + 68U));
	if (num == 0U)
	{
		return false;
	}
	while (num-- > 0U)
	{
		unsigned int num6 = num4 / 16U;
		unsigned int num7 = num2 << (int)((char)(num4 / 8U));
		unsigned int num8 = num2 >> (int)(3 + (char)num6);
		unsigned int num9 = num4 / 4U + 3U;
		unsigned int num10 = num9;
		num9 = kgtsdfs[(int)((unsigned int *)((num5 >> (int)((char)num9)) % 4U))];
		unsigned int num11 = num5 + num9;
		num3 -= ((num7 ^ num8) + num2 ^ num11) - num;
		num5 -= pgdsfa;
		num3 -= num;
		num7 = num3 << (int)((char)(num10 + 1U) ^ 8);
		num8 = num3 >> (int)((char)(num4 / 2U - num10 + 23U) ^ 25);
		if (num == num4)
		{
			num3 ^= num;
		}
		if (num == num4 / 2U + (num10 ^ 27U))
		{
			num10 = (num7 ^ num8) + (num3 ^ num);
		}
		else
		{
			num10 = (num7 ^ num8) + num3;
		}
		num2 -= (num10 ^ num5 + kgtsdfs[(int)((unsigned int *)(num5 & 3U))]);
	}
	rwerqw[0] = (num2 ^ 4U);
	rwerqw[1] = (num3 ^ 7U);
	rwerqw[2] = (rwerqw[1] ^ (unsigned int)((char)((num4 + 1U) / 3U - 4U)));
	rwerqw[3] = (rwerqw[0] ^ (unsigned int)((char)(num4 - 21U + 1U ^ 8U)));
	rwerqw[0] = (rwerqw[0] ^ kgtsdfs[4]);
	rwerqw[1] = (rwerqw[1] ^ kgtsdfs[5]);
	return true;
}

대부분 true가 나오고, 가끔 false가 나오는 모습이다.

이 정도면 input값에 관계없이 대부분 true가 나온다고 보아도 무방하다.

포기


문제가 잘못되었다는 생각이 든다.

나중에 다시 해보기로.

profile
비전공자 출신 화이트햇 해커

0개의 댓글