[强网杯 2022]GameMaster Writeup

  • 还是想刷一些题,在此简单记录一下

  • .net逆向,用dnspy打开

  • private static void Main(string[] args)
    {
    	ConfigurationManager.AppSettings.Set("microsoft:WorkflowComponentModel:DisableActivitySurrogateSelectorTypeCheck", "true");
    	FileStream fileStream = File.OpenRead("gamemessage");
    	int num = (int)fileStream.Length;
    	Program.memory = new byte[num];
    	fileStream.Position = 0L;
    	fileStream.Read(Program.memory, 0, num);
    	Console.Title = "♠ Blackjack Game" + new string(' ', 11) + "...Based on Konstantin Tarkus' code";
    	Console.BufferWidth = (Console.WindowWidth = 70);
    	Console.BufferHeight = (Console.WindowHeight = 26);
    	Console.CursorVisible = false;
    	ArrayList arrayList = new ArrayList();
    	Game game = new Game();
    	game.Player.BalanceChanged += Program.OnBalanceChanged;
    	game.LastStateChanged += Program.OnLastStateChanged;
    	game.AllowedActionsChanged += Program.OnAllowedActionsChanged;
    	game.Dealer.Hand.Changed += Program.OnHandChanged;
    	game.Player.Hand.Changed += Program.OnHandChanged;
    	game.Play(20m, 5m);
    	for (;;)
    	{
    		ConsoleKey key = Console.ReadKey(true).Key;
    		ConsoleKey consoleKey = key;
    		if (consoleKey <= ConsoleKey.Add)
    		{
    			switch (consoleKey)
    			{
    			case ConsoleKey.Enter:
    			{
    				bool flag = (game.AllowedActions & GameAction.Deal) == GameAction.Deal;
    				if (flag)
    				{
    					game.Deal();
    				}
    				else
    				{
    					game.Stand();
    				}
    				continue;
    			}
    			case (ConsoleKey)14:
    			case (ConsoleKey)15:
    			case (ConsoleKey)16:
    			case (ConsoleKey)17:
    			case (ConsoleKey)18:
    			case ConsoleKey.Pause:
    			case (ConsoleKey)20:
    			case (ConsoleKey)21:
    			case (ConsoleKey)22:
    			case (ConsoleKey)23:
    			case (ConsoleKey)24:
    			case (ConsoleKey)25:
    			case (ConsoleKey)26:
    			case (ConsoleKey)28:
    			case (ConsoleKey)29:
    			case (ConsoleKey)30:
    			case (ConsoleKey)31:
    			case ConsoleKey.PageUp:
    			case ConsoleKey.PageDown:
    			case ConsoleKey.End:
    			case ConsoleKey.Home:
    			case ConsoleKey.LeftArrow:
    			case ConsoleKey.RightArrow:
    			case ConsoleKey.Select:
    			case ConsoleKey.Print:
    			case ConsoleKey.Execute:
    			case ConsoleKey.PrintScreen:
    			case ConsoleKey.Insert:
    			case ConsoleKey.Delete:
    			case ConsoleKey.Help:
    			case (ConsoleKey)58:
    			case (ConsoleKey)59:
    			case (ConsoleKey)60:
    			case (ConsoleKey)61:
    			case (ConsoleKey)62:
    			case (ConsoleKey)63:
    			case (ConsoleKey)64:
    				continue;
    			case ConsoleKey.Escape:
    				Program.verifyCode(arrayList, game);
    				continue;
    			case ConsoleKey.Spacebar:
    			{
    				bool flag2 = (game.AllowedActions & GameAction.Deal) == GameAction.Deal;
    				if (flag2)
    				{
    					game.Deal();
    				}
    				else
    				{
    					game.Hit();
    				}
    				continue;
    			}
    			case ConsoleKey.UpArrow:
    				break;
    			case ConsoleKey.DownArrow:
    				goto IL_5A0;
    			case ConsoleKey.D0:
    				arrayList.Add('0');
    				continue;
    			case ConsoleKey.D1:
    				arrayList.Add('1');
    				continue;
    			case ConsoleKey.D2:
    				arrayList.Add('2');
    				continue;
    			case ConsoleKey.D3:
    				arrayList.Add('3');
    				continue;
    			case ConsoleKey.D4:
    				arrayList.Add('4');
    				continue;
    			case ConsoleKey.D5:
    				arrayList.Add('5');
    				continue;
    			case ConsoleKey.D6:
    				arrayList.Add('6');
    				continue;
    			case ConsoleKey.D7:
    				arrayList.Add('7');
    				continue;
    			case ConsoleKey.D8:
    				arrayList.Add('8');
    				continue;
    			case ConsoleKey.D9:
    				arrayList.Add('9');
    				continue;
    			case ConsoleKey.A:
    				arrayList.Add('A');
    				continue;
    			case ConsoleKey.B:
    				arrayList.Add('B');
    				continue;
    			case ConsoleKey.C:
    				arrayList.Add('C');
    				continue;
    			case ConsoleKey.D:
    				arrayList.Add('D');
    				continue;
    			case ConsoleKey.E:
    				arrayList.Add('E');
    				continue;
    			case ConsoleKey.F:
    				arrayList.Add('F');
    				continue;
    			case ConsoleKey.G:
    				arrayList.Add('G');
    				continue;
    			case ConsoleKey.H:
    				arrayList.Add('H');
    				continue;
    			case ConsoleKey.I:
    				arrayList.Add('I');
    				continue;
    			case ConsoleKey.J:
    				arrayList.Add('J');
    				continue;
    			case ConsoleKey.K:
    				arrayList.Add('K');
    				continue;
    			case ConsoleKey.L:
    				arrayList.Add('L');
    				continue;
    			case ConsoleKey.M:
    				arrayList.Add('M');
    				continue;
    			case ConsoleKey.N:
    				arrayList.Add('N');
    				continue;
    			case ConsoleKey.O:
    				arrayList.Add('O');
    				continue;
    			case ConsoleKey.P:
    				arrayList.Add('P');
    				continue;
    			case ConsoleKey.Q:
    				arrayList.Add('Q');
    				continue;
    			case ConsoleKey.R:
    				arrayList.Add('R');
    				continue;
    			case ConsoleKey.S:
    				arrayList.Add('S');
    				continue;
    			case ConsoleKey.T:
    				arrayList.Add('T');
    				continue;
    			case ConsoleKey.U:
    				arrayList.Add('U');
    				continue;
    			case ConsoleKey.V:
    				arrayList.Add('V');
    				continue;
    			case ConsoleKey.W:
    				arrayList.Add('W');
    				continue;
    			case ConsoleKey.X:
    				arrayList.Add('X');
    				continue;
    			case ConsoleKey.Y:
    				arrayList.Add('Y');
    				continue;
    			case ConsoleKey.Z:
    				arrayList.Add('Z');
    				continue;
    			default:
    				if (consoleKey != ConsoleKey.Add)
    				{
    					continue;
    				}
    				break;
    			}
    			game.Player.Bet += 1m;
    		}
    		else
    		{
    			if (consoleKey == ConsoleKey.Subtract)
    			{
    				goto IL_5A0;
    			}
    			if (consoleKey == ConsoleKey.Oem1)
    			{
    				arrayList.Add(":");
    			}
    		}
    		continue;
    		IL_5A0:
    		game.Player.Bet -= 1m;
    	}
    }
    
    
  • 往内存里加载了个文件,先观察一下对这个文件如何加密的

  • 找到如下函数:

  • private static void verifyCode(ArrayList arrayList, Game game)
    		{
    			string str = "";
    			for (int i = 0; i < arrayList.Count; i++)
    			{
    				str += arrayList[i].ToString()[0].ToString();
    			}
    			Program.goldFunc(arrayList, game);
    			arrayList.Clear();
    		}
    
  • 跟进:private static void goldFunc(ArrayList input, Game game)

  • 找到几处关键加密:

  • else
    									{
    										bool flag23 = Program.memcmp1(input, "MF3K", 4);
    										if (flag23)
    										{
    											try
    											{
    												game.Player.Bet -= 22m;
    												for (int i = 0; i < Program.memory.Length; i++)
    												{
    													byte[] array = Program.memory;
    													int num = i;
    													array[num] ^= 34;
    												}
    												Environment.SetEnvironmentVariable("AchivePoint1", game.Player.Balance.ToString());
    											}
    											catch
    											{
    											}
    										}
    
  • 简单异或

  • else
    												{
    													bool flag41 = Program.memcmp1(input, "EEPW", 4);
    													if (flag41)
    													{
    														try
    														{
    															game.Player.Balance += 175m;
    															byte[] key = new byte[]
    															{
    																66,
    																114,
    																97,
    																105,
    																110,
    																115,
    																116,
    																111,
    																114,
    																109,
    																105,
    																110,
    																103,
    																33,
    																33,
    																33
    															};
    															ICryptoTransform cryptoTransform = new RijndaelManaged
    															{
    																Key = key,
    																Mode = CipherMode.ECB,
    																Padding = PaddingMode.Zeros
    															}.CreateDecryptor();
    															Program.m = cryptoTransform.TransformFinalBlock(Program.memory, 0, Program.memory.Length);
    															Environment.SetEnvironmentVariable("AchivePoint2", game.Player.Balance.ToString());
    														}
    														catch
    														{
    														}
    													}
    
  • AES-ECB加密,有key可解密

  • 将gamemessage文件拖进cyberchef进行解密,

  • 找到MZ头,dump下来用dnspy分析

  • 关键函数如下:

  • using System;
    using System.Linq;
    using System.Text;
    using System.Windows.Forms;
    
    namespace T1Class
    {
    	// Token: 0x02000002 RID: 2
    	public class T1
    	{
    		// Token: 0x06000001 RID: 1 RVA: 0x00002050 File Offset: 0x00000250
    		private static void Check1(ulong x, ulong y, ulong z, byte[] KeyStream)
    		{
    			int num = -1;
    			for (int i = 0; i < 320; i++)
    			{
    				x = (((x >> 29 ^ x >> 28 ^ x >> 25 ^ x >> 23) & 1UL) | x << 1);
    				y = (((y >> 30 ^ y >> 27) & 1UL) | y << 1);
    				z = (((z >> 31 ^ z >> 30 ^ z >> 29 ^ z >> 28 ^ z >> 26 ^ z >> 24) & 1UL) | z << 1);
    				bool flag = i % 8 == 0;
    				if (flag)
    				{
    					num++;
    				}
    				KeyStream[num] = (byte)((long)((long)KeyStream[num] << 1) | (long)((ulong)((uint)((z >> 32 & 1UL & (x >> 30 & 1UL)) ^ (((z >> 32 & 1UL) ^ 1UL) & (y >> 31 & 1UL))))));
    			}
    		}
    
    		// Token: 0x06000002 RID: 2 RVA: 0x00002110 File Offset: 0x00000310
    		private static void ParseKey(ulong[] L, byte[] Key)
    		{
    			for (int i = 0; i < 3; i++)
    			{
    				for (int j = 0; j < 4; j++)
    				{
    					Key[i * 4 + j] = (byte)(L[i] >> j * 8 & 255UL);
    				}
    			}
    		}
    
    		// Token: 0x06000003 RID: 3 RVA: 0x0000215C File Offset: 0x0000035C
    		public T1()
    		{
    			try
    			{
    				string environmentVariable = Environment.GetEnvironmentVariable("AchivePoint1");
    				string environmentVariable2 = Environment.GetEnvironmentVariable("AchivePoint2");
    				string environmentVariable3 = Environment.GetEnvironmentVariable("AchivePoint3");
    				bool flag = environmentVariable == null || environmentVariable2 == null || environmentVariable3 == null;
    				if (!flag)
    				{
    					ulong num = ulong.Parse(environmentVariable);
    					ulong num2 = ulong.Parse(environmentVariable2);
    					ulong num3 = ulong.Parse(environmentVariable3);
    					ulong[] array = new ulong[3];
    					byte[] array2 = new byte[40];
    					byte[] array3 = new byte[40];
    					byte[] array4 = new byte[12];
    					byte[] first = new byte[]
    					{
    						101,
    						5,
    						80,
    						213,
    						163,
    						26,
    						59,
    						38,
    						19,
    						6,
    						173,
    						189,
    						198,
    						166,
    						140,
    						183,
    						42,
    						247,
    						223,
    						24,
    						106,
    						20,
    						145,
    						37,
    						24,
    						7,
    						22,
    						191,
    						110,
    						179,
    						227,
    						5,
    						62,
    						9,
    						13,
    						17,
    						65,
    						22,
    						37,
    						5
    					};
    					byte[] array5 = new byte[]
    					{
    						60,
    						100,
    						36,
    						86,
    						51,
    						251,
    						167,
    						108,
    						116,
    						245,
    						207,
    						223,
    						40,
    						103,
    						34,
    						62,
    						22,
    						251,
    						227
    					};
    					array[0] = num;
    					array[1] = num2;
    					array[2] = num3;
    					T1.Check1(array[0], array[1], array[2], array2);
    					bool flag2 = first.SequenceEqual(array2);
    					if (flag2)
    					{
    						T1.ParseKey(array, array4);
    						for (int i = 0; i < array5.Length; i++)
    						{
    							array5[i] ^= array4[i % array4.Length];
    						}
    						MessageBox.Show("flag{" + Encoding.Default.GetString(array5) + "}", "Congratulations!", MessageBoxButtons.OK);
    					}
    				}
    			}
    			catch (Exception)
    			{
    			}
    		}
    	}
    }
    
  • 逻辑很简单,我们首先用z3解一下array[0], array[1], array[2]这三个数据

  • 如下照抄dnspy即可写出脚本:

  • from z3 import *
    
    x,y,z=BitVecs('x y z',64)
    s=Solver()
    KeyStream=[0]*40
    array2 = [101, 5, 80, 213, 163, 26, 59, 38, 19, 6, 173, 189, 198, 166, 140, 183, 42, 247, 223, 24, 106,20, 145, 37, 24, 7, 22, 191, 110, 179, 227, 5, 62, 9, 13, 17, 65, 22, 37, 5]
    num=-1
    for i in range(320):
        x = (((x >> 29 ^ x >> 28 ^ x >> 25 ^ x >> 23) & 1) | x << 1)
        y = (((y >> 30 ^ y >> 27) & 1) | y << 1)
        z = (((z >> 31 ^ z >> 30 ^ z >> 29 ^ z >> 28 ^ z >> 26 ^ z >> 24) & 1) | z << 1)
        if i%8==0:
            if i!=0:
                s.add(KeyStream[num]==array2[num])
            num += 1
        KeyStream[num] = (KeyStream[num] << 1) | (((z >> 32 & 1 & (x >> 30 & 1)) ^ (((z >> 32 & 1) ^ 1) & (y >> 31 & 1))))
    print(s.check())
    print(s.model())
    [y = 868387187, x = 156324965, z = 3131229747]
    
  • 接着照抄dnspy:

  • def ParseKey(array,key):
        for i in range(3):
            for j in range(4):
                key[i * 4 + j] = ((array[i] >> j * 8) & 255)
        return key
    array=[156324965,868387187,3131229747]
    array4=[0]*12
    array4=ParseKey(array,array4)
    array5 =                [60,
    						100,
    						36,
    						86,
    						51,
    						251,
    						167,
    						108,
    						116,
    						245,
    						207,
    						223,
    						40,
    						103,
    						34,
    						62,
    						22,
    						251,
    						227]
    c=''
    for i in range(len(array5)):
        c+=chr(array5[i]^array4[i%12])
    print('flag{'+c+'}')
    
  • 成功得到:flag{Y0u_@re_G3meM3s7er!}

  • 该题主要考察文件加密,C#语言,以及算法“照抄”能力。。。