Hollywood

Reverse Engineering

A writeup for the challenge Hollywood from CyberGon CTF 2023.

About

Share

Hack Like Hollywood

After unzipping the zip file I saw that there was an exe, so I checked to see if there were any strings showing that it was C#:

$ strings Nuke.exe | grep NET
NETG
.NETFramework,Version=v4.8
.NET Framework 4.8

This meant that I could use dnSpy to decompile the program. Looking under the main form (Secret Form, Form1), I saw that a lot of the program was obfuscated:

image

This seemed like it would evaluate to some sort of string so I decided to try reversing how the strings were decrypted. This function ended up calling another function which looked like the following:

public string wkdsyfvvjkw(string ucihwoadwjt, int ucihwoadwjt)
{
	int num = ucihwoadwjt.Length;
	char[] array = ucihwoadwjt.ToCharArray();
	while ((num -= 163199905 ^ 163199904) >= ~(-398739298 - -398739297) << 4)
	{
		array[num] = (char)((int)array[num] ^ ((int)this.vvrdrubedio[ucihwoadwjt & (~-675874878 - 675874862)] | ucihwoadwjt));
	}
	return new string(array);
}

this.vvrdrubedio seemed fairly annoying to try to reverse statically so I decided to continue with dyanmic reversing. The problem was that the author anticipated this and put in some protections. The first was a debug check which could be bypassed by deleting and recompiling, the second was that the window was set to maximize and always stay on top. To get rid of these, I set breakpoints and would change the values on runtime:

image

Once I had bypassed these I could then set breakpoints anywhere I wanted in the code base and was able to replicate the above function in python:

answers = [0xAD,0xFB,0x86,0x5D,0x29,0x49,0x2A,0xD7,0x72,0xCC,0x4A,0x31,0xA1,0x44,0x06,0x87]

def restore_string(unicode_string, extra):
    unicode_bytes = bytes(unicode_string, 'utf-16be')
    num = len(unicode_string)
    arr = []
    for x in unicode_string:
        arr.append(chr(ord(x)))

    num -= 1
    while num >= 0:
        arr[num] = chr(ord(arr[num]) ^ ((answers[extra & 15])|extra))
        num -= 1

    out = ''.join([i for i in arr])
    print(out)

Once I had reversed the strings I got a clearer view, however there were still two other functions that were used to get numbers:

public static int wkdsyfvvjkl(int ucihwoadwjt)
{
	int[] vvrdrubedij = qictcjimqnj.qautpfwfmka.vvrdrubedii.vvrdrubedij;
	return qictcjimqnj.qautpfwfmka.vvrdrubedii.vvrdrubedij[ucihwoadwjt];
}
internal static int wkdsyfvvjlp(int ucihwoadwjt)
{
	if (ucihwoadwjt <= (~233442834 ^ -233442900))
	{
		switch (ucihwoadwjt - (72107058 - 72107008))
		{
		case 0:
			return ~-518069808 ^ -518069811;
		case 1:
			break;
		case 2:
			return (-(563213124 ^ 686526847) - -307799920) ^ -148784433;
		default:
			switch (ucihwoadwjt - ((~986048383 + 301575129) ^ -684473248))
			{
			case 0:
				return (743704947 + -489808864) ^ -253896162;
			case 2:
				return -(160 << 4 >> 7);
			case 4:
				return ~(12 << 1);
			case 5:
				return 709877006 - 709877252 >> 1;
			case 8:
				return 1038821198 - 81986796 + -496404276 - 460430238;
			}
			break;
		}
	}
	else
	{
		switch (ucihwoadwjt - (((-309708691 - -3206028) ^ -306500807) >> 5))
		{
		case 0:
			return (--295828639 << 1) ^ -591657286;
		case 1:
			return (681416852 + -704626059) ^ 23209208;
		case 2:
			break;
		case 3:
			return (~(~872326184) >> 1) - 436163205;
		default:
			if (ucihwoadwjt == ~(-553073554 ^ 553073601))
			{
				return ~(--255872 >> 7) << 3 >> 7;
			}
			break;
		}
	}
	return (398952530 ^ 504527747) - -252110122 + -417126652;
}

The first function ended up just being a lookup table with the values being:

[0]    0x00000004    int
[1]    0x00000000    int
[2]    0x0000006C    int
[3]    0x00000001    int
[4]    0x0000006B    int
[5]    0x00000002    int
[6]    0x00000003    int
[7]    0x000000F3    int
[8]    0x00010000    int
[9]    0x00000005    int
[10]    0x0000027E    int
[11]    0x00000165    int
[12]    0x0000000C    int
[13]    0x00000134    int
[14]    0x0000009D    int
[15]    0x00000138    int
[16]    0x000000A1    int
[17]    0x00000144    int
[18]    0x0000012E    int
[19]    0x00000132    int
[20]    0x000000AD    int
[21]    0x000000AC    int
[22]    0x00000006    int
[23]    0x000000B0    int
[24]    0x00000007    int
[25]    0xFFFFFFFE    int
[26]    0x000001F4    int
[27]    0x00000064    int
[28]    0x00001388    int
[29]    0x000002A1    int
[30]    0x00000096    int
[31]    0x00000020    int
[32]    0x00000BB8    int
[33]    0x0000000A    int
[34]    0x000001C0    int
[35]    0x0000013E    int
[36]    0x000001C2    int
[37]    0x00000140    int
[38]    0x00000032    int
[39]    0x0000000F    int
[40]    0x0000002A    int
[41]    0x0000001F    int
[42]    0x00000023    int
[43]    0x0000000B    int
[44]    0x00000028    int
[45]    0x00000022    int
[46]    0x00000027    int
[47]    0x00000033    int
[48]    0x00000025    int
[49]    0x0000001E    int
[50]    0x0000002B    int
[51]    0x00000021    int
[52]    0x00000029    int
[53]    0x0000002E    int
[54]    0x00000034    int
[55]    0x00000036    int
[56]    0x00000009    int
[57]    0x00000039    int
[58]    0x0000003F    int
[59]    0x00000038    int
[60]    0x0000007D    int
[61]    0x0000003C    int
[62]    0x0000003D    int
[63]    0x0000000E    int
[64]    0x00000040    int
[65]    0x0000004A    int
[66]    0x00000048    int
[67]    0x00000049    int
[68]    0x0000004B    int
[69]    0x00000070    int
[70]    0x00000073    int
[71]    0x00000047    int
[72]    0x0000007E    int
[73]    0x0000004D    int
[74]    0x00000010    int
[75]    0x0000001C    int
[76]    0x00000054    int

The second was still fairly easy and just needed to be replicated in python:

def solve(x):
    if x <= (~233442834 ^ -233442900):
        y = x - (72107058 - 72107008)
        if y == 0:
            return ~-518069808 ^ -518069811
        elif y == 1:
            pass
        elif y == 2:
            return (-(563213124 ^ 686526847) - -307799920) ^ -148784433
        else:
            z = (x - ((~986048383 + 301575129) ^ -684473248))
            if z == 0:
                return (743704947 + -489808864) ^ -253896162
            elif z == 2:
                return -(160 << 4 >> 7)
            elif z == 4:
                return ~(12 << 1)
            elif z == 5:
                return 709877006 - 709877252 >> 1
            elif z == 8:
                return 1038821198 - 81986796 + -496404276 - 460430238
    else:
        y = (x - (((-309708691 - -3206028) ^ -306500807) >> 5))
        if y == 0:
            return (295828638 << 1) ^ -591657286
        elif y == 1:
            return (681416852 + -704626059) ^ 23209208
        elif y == 2:
            pass
        elif y == 3:
            return (~(~872326184) >> 1) - 436163205
        else:
            if (x == ~(-553073554 ^ 553073601)):
                return ~(255871 >> 7) << 3 >> 7
    return (398952530 ^ 504527747) - -252110122 + -417126652

Once I had replaced all of these I was able to see the important part of the code:

bool flag;
if (char.IsLetter((char)ucihwoadwjt.KeyCode))
{
	if (7 <= -44)
	{
		goto IL_239;
	}
	flag = false;
}
else
{
	flag = true;
}
char c;
if (!flag)
{
	bool flag2;
	if (((Control.ModifierKeys & (Keys)0x10000) == (Keys)0x10000) ^ Console.CapsLock)
	{
		if (114 <= 51)
		{
			goto IL_239;
		}
		flag2 = false;
	}
	else
	{
		flag2 = true;
	}
	if (!flag2)
	{
		c = char.ToUpper((char)ucihwoadwjt.KeyCode);
	}
	else
	{
		c = char.ToLower((char)ucihwoadwjt.KeyCode);
	}
}
else
{
	c = (char)ucihwoadwjt.KeyCode;
}
if ((byte)Array.IndexOf<int>(this.klkecxhvcmz, (int)c) == this.klkecxhvcmu[this.klkecxhvcmw][this.klkecxhvcmx])
{
	this.klkecxhvcmx += 0x1;
	for (;;)
	{
		int num = 0x0;
		for (;;)
		{
			switch (num ^ 0x38)
			{
			case 56:
				if (this.klkecxhvcmx == this.klkecxhvcmu[this.klkecxhvcmw].Length)
				{
					num = 1;
					continue;
				}
				return;
			case 57:
				this.show_message(string.Format("Sequence {0} correct!", this.klkecxhvcmw + 0x1));
				num = 0x3;
				continue;
			case 58:
			{
				RuntimeMethodHandle runtimeMethodHandle = methodof(Form1..ctor()).MethodHandle;
				num = 2;
				continue;
			}
			case 59:
				goto IL_138;
			}
			break;
		}
	}
	IL_138:
	for (;;)
	{
		int num2 = -25;
		for (;;)
		{
			num2 ^= 0x36;
			switch (num2 + 50)
			{
			case 0: // num2 == -50
				this.show_message("Congratulations! You have entered the correct password. Here's the flag!");
				num2 = -7;
				continue; //  go to case 1
			case 1:
				this.klkecxhvcmw = 0x0;
				num2 = -30;
				continue; // go to case 6
			case 2:
			{
				RuntimeMethodHandle runtimeMethodHandle2 = methodof(Form1.shutdown_incorrect()).MethodHandle;
				num2 = -26;
				continue;
			}
			case 3:
				this.klkecxhvcmx = 0x0;
				num2 = -28;
				continue; // go to case 4
			case 4:
				this.klkecxhvcmw += 0x1;
				num2 = -27;
				continue; // go to case 5
			case 5:
				if (this.klkecxhvcmw == this.klkecxhvcmu.Length)
				{
					num2 = -8;
					continue;
				}
				return;
			case 6:
				return;
			}
			break;
		}
	}
	return;
}

What the code above did was grab the keycode that the user entered, it then checked to see if the index of that key code in an array was equal to the position that it should have in a matrix. If the value was correct it would then increment one of the matrix indices. If all of a "sequence" was correct it would then move on to the next one. At this point all we had to do was extract the array and matrix values and then replicate the code but instead of a comparison just check which character matched up with the matrix:


indexer = [0x00000069,0x00000032,0x00000023,0x0000003A,0x00000050,0x00000037,0x0000005B,0x00000024,0x00000068,0x0000005D,0x00000031,0x00000076,0x0000000B,0x0000007E,0x0000004A,0x0000002F,0x0000003E,0x0000002B,0x0000004F,0x00000066,0x0000005C,0x00000043,0x0000006C,0x00000020,0x0000005E,0x0000000A,0x00000047,0x00000073,0x0000006B,0x00000029,0x00000045,0x0000003F,0x0000005F,0x00000078,0x00000040,0x00000079,0x0000006E,0x00000067,0x00000009,0x00000026,0x00000060,0x00000048,0x0000004E,0x00000052,0x00000059,0x00000039,0x00000041,0x00000053,0x0000000D,0x00000051,0x00000054,0x0000007B,0x0000005A,0x00000065,0x00000044,0x00000035,0x0000003C,0x00000064,0x0000004B,0x0000002C,0x0000002D,0x00000049,0x0000007C,0x00000038,0x0000004C,0x0000002E,0x0000000C,0x00000021,0x00000022,0x00000042,0x00000061,0x0000003B,0x0000003D,0x0000006F,0x00000077,0x0000002A,0x0000004D,0x00000046,0x00000036,0x00000033,0x0000006A,0x0000006D,0x00000057,0x00000030,0x00000058,0x00000055,0x0000007D,0x00000063,0x00000075,0x00000062,0x00000070,0x00000034,0x00000027,0x00000074,0x00000056,0x0000007A,0x00000025,0x00000072,0x00000071,0x00000028]

matrix = [
    [0x08,0x43,0x43,0x53,0x4A,0x20,0x51,0x53,0x24,0x4F,0x5B,0x53,0x43],
    [0x5B,0x24,0x24,0x53,0x53,0x07,0x20,0x16,0x16,0x20,0x16,0x61],
    [0x57,0x25,0x20,0x16,0x53,0x4F,0x57,0x20,0x43,0x61,0x20,0x16],
    [0x1C,0x20,0x08,0x23,0x39,0x4F,0x53,0x58,0x1C,0x4F,0x4A,0x39]
]

def check_character():
    cur_x = 0
    cur_y = 0

    s = ""

    while True:
        check = False
        pos = matrix[cur_x][cur_y]
        c = indexer[pos]
        cur_x += 1
        s += chr(c)
        print(s)
        num = 0x0

        while True:
            if num ^ 0x38 == 56:
                if cur_x == len(matrix):
                    num = 1
                else:
                    break
            elif num ^ 0x38 == 57:
                print("sequence", cur_y+1, "correct")
                # print(s)
                # s = ""
                num = 0x3
            elif num ^ 0x38 == 58:
                print("Failed")
                return
            elif num ^ 0x38 == 59:
                print("Success")
                check = True
                break

        if check:
            num2 = -25
            cur_x = 0
            cur_y += 1

check_character()

When run this program then gave us the flag:

h4ck!ng_!n_h00lyw00d_$33m_c00l_unl!k3_r34l_w0rld!

Table of Contents

Random Writeups

Copyright © 2023 LearnCyber. All rights reserved.