CodeEngn Reversing Advance_Level07


실행 하면 뜨는 창이다. 이름 부분에 글자가 이미디폴트로 써 있다. 아래 란에는 시리얼을 적는다. Check 버튼을 눌러도 반응이 없다. 별다른 오류 메시지는 띄우지 않는 것 같다.

PEiD로 조회해보니 C# 으로 구현되었음을 알 수 있다. 올리 디버거에서 실행되지 않는다. .NET Reflector를 이용하여 디컴파일 할 수 있다.

다음은 main 함수이다. Form1이라는 인스턴스를 생성한다.

Form1 생성자에서는 InitializeComponent 함수를 호출한다.

이것저것 초기화한다. Check 버튼을 눌렀을 시 호출되는 핸들러를 보면 시리얼 알 수 있을 것 같다. 이벤트 핸들러로 등록 된 button1_Click 함수를 보았다.

이것저것 연산한 뒤 적절한 시리얼을 입력했을 때 성공 메시지를 띄운다. 코드를 자세히 보자.
31 라인을 보면 이와 같은 조건문이 있다. 이는 가장 먼저 시리얼을 체크하는 루틴이다. 시리얼의 형식에 대하여 체크한다.
if ((((this.textBox1.Text.Length >= 5) && (this.textBox1.Text.Length <= 0x1b)) && ((this.textBox2.Text.Length == 0x1a) && (this.textBox2.Text[8] == '-'))) && (this.textBox2.Text[0x11] == '-'))- id와 비슷한 것을 입력하는 testbox1의 입력값 길이는 0x5 ~ 0x1b 사이여야 한다.
- 시리얼의 길이는 0x1a여야 한다.
- 시리얼의 9번째 글자와 18번째 글자는 ’-’ 이다.
문제에서는 ID가 CodeEngn일 때의 번호를 알아내라 하였으므로 ID는 CodeEngn으로 한다.
시리얼로 여러 연산을 한 후, 아래 체크 루틴에서 성공 메시지를 띄운다. 성공 여부를 판단하는 루틴은 다음과 같다. 각 조건을 분석할 필요가 있다.
if ((this.vxzzz(this.yreee, this.ewrrr, 0x8ffe2225, fsfsdf) && (this.yreee[2] == hashCode)) && (this.yreee[3] == num5))
{
MessageBox.Show("Congratulations, mate!", "Fine!", MessageBoxButtons.OK, MessageBoxIcon.Asterisk);
}vxzzz 함수의 내용은 다음과 같다.
private bool vxzzz(uint[] rwerqw, uint[] kgtsdfs, uint pgdsfa, uint fsfsdf)
{
pgdsfa ^= fsfsdf;
uint num = (pgdsfa % 0x39) - 1;
uint num10 = rwerqw[0];
uint num11 = rwerqw[1];
uint num6 = num;
uint num2 = pgdsfa << ((byte) (0x61 ^ (num + 0x44)));
if (num != 0)
{
while (num-- > 0)
{
uint num5 = num6 / 0x10;
uint num3 = num10 << ((byte) (num6 / 8));
uint num4 = num10 >> (3 + ((byte) num5));
uint num7 = (num6 / 4) + 3;
uint num9 = num7;
num7 = kgtsdfs[(int) ((IntPtr) ((num2 >> ((byte) num7)) % 4))];
uint num8 = num2 + num7;
num11 -= (((num3 ^ num4) + num10) ^ num8) - num;
num2 -= pgdsfa;
num11 -= num;
num3 = num11 << (((byte) (num9 + 1)) ^ 8);
num4 = num11 >> (((byte) (((num6 / 2) - num9) + 0x17)) ^ 0x19);
if (num == num6)
{
num11 ^= num;
}
if (num == ((num6 / 2) + (num9 ^ 0x1b)))
{
num9 = (num3 ^ num4) + (num11 ^ num);
}
else
{
num9 = (num3 ^ num4) + num11;
}
num10 -= num9 ^ (num2 + kgtsdfs[(int) ((IntPtr) (num2 & 3))]);
}
rwerqw[0] = num10 ^ 4;
rwerqw[1] = num11 ^ 7;
rwerqw[2] = rwerqw[1] ^ ((byte) (((num6 + 1) / 3) - 4));
rwerqw[3] = rwerqw[0] ^ ((byte) (((num6 - 0x15) + 1) ^ 8));
rwerqw[0] ^= kgtsdfs[4];
rwerqw[1] ^= kgtsdfs[5];
return true;
}
return false;
}인자로 받은 rwerqw는 yreee 배열로, 이는 함수 내에서 값이 변경된다. 또한 이후 조건 체크 루틴에서 바뀐 값이 적절한지 체크한다.
인자 pgdsfa는 0x8ffe2225로 고정되며, kgtsdfs 배열 값도 고정된다. 인자 fsfsdf는 구해야 하는 시리얼 마지막 8글자에 영향을 받는다.
브루트포스 방식으로 조건에 맞는 시리얼 마지막 8글자를 알아냈다. 그 코드는 다음과 같다.
private bool vxzzz(uint[] rwerqw, uint[] kgtsdfs, uint pgdsfa, uint temp)
{
uint pgdsfa_ori = 0x8ffe2225;
for (uint fsfsdf = 1; fsfsdf != 0; fsfsdf++)
{
pgdsfa = pgdsfa_ori;
pgdsfa ^= fsfsdf;
uint num = (pgdsfa % 0x39) - 1;
uint num10 = rwerqw[0];
uint num11 = rwerqw[1];
uint num6 = num;
uint num2 = pgdsfa << ((byte)(0x61 ^ (num + 0x44)));
if (num != 0)
{
if (num == 0xFFFFFFFF)
continue;
while (num-- > 0)
{
uint num5 = num6 / 0x10;
uint num3 = num10 << ((byte)(num6 / 8));
uint num4 = num10 >> (3 + ((byte)num5));
uint num7 = (num6 / 4) + 3;
uint num9 = num7;
num7 = kgtsdfs[(int)((IntPtr)((num2 >> ((byte)num7)) % 4))];
uint num8 = num2 + num7;
num11 -= (((num3 ^ num4) + num10) ^ num8) - num;
num2 -= pgdsfa;
num11 -= num;
num3 = num11 << (((byte)(num9 + 1)) ^ 8);
num4 = num11 >> (((byte)(((num6 / 2) - num9) + 0x17)) ^ 0x19);
if (num == num6)
{
num11 ^= num;
}
if (num == ((num6 / 2) + (num9 ^ 0x1b)))
{
num9 = (num3 ^ num4) + (num11 ^ num);
}
else
{
num9 = (num3 ^ num4) + num11;
}
num10 -= num9 ^ (num2 + kgtsdfs[(int)((IntPtr)(num2 & 3))]);
}
if (fsfsdf % 10000 == 0)
{
System.IO.File.AppendAllText("result.txt", fsfsdf.ToString() + "\r\n", Encoding.Default);
}
if ( ((num11 ^ 7) ^ ((byte)(((num6 + 1) / 3) - 4))) != hash_glo)
continue;
if ( ( (num10 ^ 4) ^ ((byte)(((num6 - 0x15) + 1) ^ 8)) ) != num5_glo)
continue;
rwerqw[0] = num10 ^ 4;
rwerqw[1] = num11 ^ 7;
rwerqw[2] = rwerqw[1] ^ ((byte)(((num6 + 1) / 3) - 4));
rwerqw[3] = rwerqw[0] ^ ((byte)(((num6 - 0x15) + 1) ^ 8));
rwerqw[0] ^= kgtsdfs[4];
rwerqw[1] ^= kgtsdfs[5];
MessageBox.Show((fsfsdf^hash_glo).ToString("X"), "Fine!", MessageBoxButtons.OK, MessageBoxIcon.Asterisk);
return true;
}
}
return false;
}
Colored by Color Scripterhash 값과 num5 값은 클래스 멤버 변수로 빼서 조건 비교에 용이하게 하였다. num이 0xFFFFFFFF가 될 경우 너무 오래 걸려 뺐다. 단 주의할 점이 있다. 7번 문제를 실행하는 환경이 64비트 OS일 경우, 해시의 결과가 달라진다. 주어진 시리얼 앞자리들이 맞기 위해서는 32비트 환경에서 실행해야 한다.

위 코드로 나온 결과이다.

flag는 11E051D1이다.
tags: writeup, reversing, pe file, windows, c sharp, dotnet, bruteforce