BJD hamburger competition

BJDCTF2020]BJD hamburger competition

首先发现是一个由unity引擎编写的游戏
一般遇到由unity编写的程序的逆向题,主要关注的是Assembly-CSarp.dll,其核心代码都在这个 dll 文件中。
这里我们使用ILSpy打开attachment\BJD hamburger competition_Data\Managed\Assembly-CSarp.dll,使用dnspy也行

代码如下

public class ButtonSpawnFruit : MonoBehaviour
{
	public GameObject toSpawn;

	public int spawnCount = 1;

	public AudioSource[] audioSources;

	public string result = "";

	public static string Md5(string str)
	{
		byte[] bytes = Encoding.UTF8.GetBytes(str);
		byte[] arg_1D_0 = MD5.Create().ComputeHash(bytes);
		StringBuilder stringBuilder = new StringBuilder();
		byte[] array = arg_1D_0;
		for (int i = 0; i < array.Length; i++)
		{
			byte b = array[i];
			stringBuilder.Append(b.ToString("X2"));
		}
		return stringBuilder.ToString().Substring(0, 20);
	}

	public static string Sha1(string str)
	{
		byte[] bytes = Encoding.UTF8.GetBytes(str);
		byte[] arg_1D_0 = SHA1.Create().ComputeHash(bytes);
		StringBuilder stringBuilder = new StringBuilder();
		byte[] array = arg_1D_0;
		for (int i = 0; i < array.Length; i++)
		{
			byte b = array[i];
			stringBuilder.Append(b.ToString("X2"));
		}
		return stringBuilder.ToString();
	}

	public void Spawn()
	{
		FruitSpawner component = GameObject.FindWithTag("GameController").GetComponent<FruitSpawner>();
		if (component)
		{
			if (this.audioSources.Length != 0)
			{
				this.audioSources[UnityEngine.Random.Range(0, this.audioSources.Length)].Play();
			}
			component.Spawn(this.toSpawn);
			string name = this.toSpawn.name;
			if (name == "汉堡底" && Init.spawnCount == 0)
			{
				Init.secret += 997;
			}
			else if (name == "鸭屁股")
			{
				Init.secret -= 127;
			}
			else if (name == "胡罗贝")
			{
				Init.secret *= 3;
			}
			else if (name == "臭豆腐")
			{
				Init.secret ^= 18;
			}
			else if (name == "俘虏")
			{
				Init.secret += 29;
			}
			else if (name == "白拆")
			{
				Init.secret -= 47;
			}
			else if (name == "美汁汁")
			{
				Init.secret *= 5;
			}
			else if (name == "柠檬")
			{
				Init.secret ^= 87;
			}
			else if (name == "汉堡顶" && Init.spawnCount == 5)
			{
				Init.secret ^= 127;
				string str = Init.secret.ToString();
				if (ButtonSpawnFruit.Sha1(str) == "DD01903921EA24941C26A48F2CEC24E0BB0E8CC7")
				{
					this.result = "BJDCTF{" + ButtonSpawnFruit.Md5(str) + "}";
					Debug.Log(this.result);
				}
			}
			Init.spawnCount++;
			Debug.Log(Init.secret);
			Debug.Log(Init.spawnCount);
		}
	}
}

可以注意到选取原料构成的str的Sha1需要等于"DD01903921EA24941C26A48F2CEC24E0BB0E8CC7"

DD01903921EA24941C26A48F2CEC24E0BB0E8CC7解密后为1001
然后经过函数Md5

public static string Md5(string str)
	{
		byte[] bytes = Encoding.UTF8.GetBytes(str);
		byte[] arg_1D_0 = MD5.Create().ComputeHash(bytes);   //MD5加密
		StringBuilder stringBuilder = new StringBuilder();
		byte[] array = arg_1D_0;
		for (int i = 0; i < array.Length; i++)
		{
			byte b = array[i];
			stringBuilder.Append(b.ToString("X2"));   //变大写
		}
		return stringBuilder.ToString().Substring(0, 20);  //取前20
	}

可以注意到对MD5后的数值做了变换
最后得到flag

关键知识点

ILSpy

StringBuilder类

append(String str)/append(Char c):字符串连接
toString():返回一个与构建起或缓冲器内容相同的字符串
appendcodePoint(int cp):追加一个代码点,并将其转换为一个或两个代码单元并返回this
setCharAt(int i, char c):将第 i 个代码单元设置为c
insert(int offset, String str)/insert(int offset, Char c):在指定位置之前插入字符(串)
delete(int startIndex,int endIndex):删除起始位置(含)到结尾位置(不含)之间的字符串

[WUSTCTF2020]level3

拿到的是一个未加壳的64位elf文件
使用IDA打开,main反汇编代码

int __cdecl main(int argc, const char **argv, const char **envp)
{
  const char *v3; // rax
  char v5; // [rsp+Fh] [rbp-41h]
  char v6[56]; // [rsp+10h] [rbp-40h] BYREF
  unsigned __int64 v7; // [rsp+48h] [rbp-8h]

  v7 = __readfsqword(0x28u);
  printf("Try my base64 program?.....\n>");
  __isoc99_scanf("%20s", v6);
  v5 = time(0LL);
  srand(v5);
  if ( (rand() & 1) != 0 )
  {
    v3 = (const char *)base64_encode(v6);
    puts(v3);
    puts("Is there something wrong?");
  }
  else
  {
    puts("Sorry I think it's not prepared yet....");
    puts("And I get a strange string from my program which is different from the standard base64:");
    puts("d2G0ZjLwHjS7DmOzZAY0X2lzX3CoZV9zdNOydO9vZl9yZXZlcnGlfD==");
    puts("What's wrong??");
  }
  return 0;
}

可以看到特殊的字符串d2G0ZjLwHjS7DmOzZAY0X2lzX3CoZV9zdNOydO9vZl9yZXZlcnGlfD==,放入base64解密,发现不行
点入base64_encode函数

char *__fastcall base64_encode(char *a1)
{
  int v1; // eax
  int v2; // eax
  int v4; // [rsp+1Ch] [rbp-54h]
  int v5; // [rsp+20h] [rbp-50h]
  int v6; // [rsp+24h] [rbp-4Ch]
  int v7; // [rsp+28h] [rbp-48h]
  int v8; // [rsp+2Ch] [rbp-44h]
  char src[56]; // [rsp+30h] [rbp-40h] BYREF
  unsigned __int64 v10; // [rsp+68h] [rbp-8h]

  v10 = __readfsqword(0x28u);
  v1 = strlen(a1);
  v8 = v1 % 3;
  v7 = v1 / 3;
  memset(src, 0, 0x30uLL);
  v6 = 0;
  v4 = 0;
  v5 = 0;
  while ( v4 < v7 )
  {
    src[v6] = base64_table[a1[v5] >> 2];
    src[v6 + 1] = base64_table[(16 * (a1[v5] & 3)) | (a1[v5 + 1] >> 4)];
    src[v6 + 2] = base64_table[(4 * (a1[v5 + 1] & 0xF)) | (a1[v5 + 2] >> 6)];
    v2 = v6 + 3;
    v6 += 4;
    src[v2] = base64_table[a1[v5 + 2] & 0x3F];
    v5 += 3;
    ++v4;
  }
  if ( v8 == 1 )
  {
    src[v6] = base64_table[a1[v5] >> 2];
    src[v6 + 1] = base64_table[16 * (a1[v5] & 3)];
    strcat(src, "==");
  }
  else if ( v8 == 2 )
  {
    src[v6] = base64_table[a1[v5] >> 2];
    src[v6 + 1] = base64_table[(16 * (a1[v5] & 3)) | (a1[v5 + 1] >> 4)];
    src[v6 + 2] = base64_table[4 * (a1[v5 + 1] & 0xF)];
    src[v6 + 3] = 61;
  }
  strcpy(a1, src);
  return a1;
}

点入base64_table

.data:00000000006020A0 base64_table    db 'A'                  ; DATA XREF: base64_encode+B9↑r
.data:00000000006020A0                                         ; base64_encode+109↑r ...
.data:00000000006020A1 aBcdefghijklmno db 'BCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/',0

点中base64_table使用ctrl+x,发现引用,点入

__int64 O_OLookAtYou()
{
  __int64 result; // rax
  char v1; // [rsp+1h] [rbp-5h]
  int i; // [rsp+2h] [rbp-4h]

  for ( i = 0; i <= 9; ++i )
  {
    v1 = base64_table[i];
    base64_table[i] = base64_table[19 - i];
    result = 19 - i;
    base64_table[result] = v1;
  }
  return result;
}

发现对base64表做了变换
在Base64中的可打印字符包括字母A-Z、a-z、数字0-9 ,这样共有62个字符,另外Base64的2个字符是:“+/”
base64加密的具体过程:https://www.cnblogs.com/chengmo/archive/2014/05/18/3735917.html
编写代码:对加密后的字符串进行替换,然后使用base64解密

import base64

crypto='d2G0ZjLwHjS7DmOzZAY0X2lzX3CoZV9zdNOydO9vZl9yZXZlcnGlfD=='
list_invert=list('ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/')
string1=''
for i in range(10):
    temp=list_invert[i]
    list_invert[i]=list_invert[19-i]
    list_invert[19-i]=temp
string1="".join(list_invert)
print(string1)
table='ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/'
table = str.maketrans(string1, table)
print (base64.b64decode(crypto.translate(table)))

得到结果:

TSRQPONMLKJIHGFEDCBAUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/
b'wctf2020{Base64_is_the_start_of_reverse}'
最后flag为:
flag

posted @ 2021-09-25 22:25  超级想睡觉  阅读(95)  评论(0)    收藏  举报