【BUUCTF】[FlareOn5]Ultimate Minesweeper 1 WP

运行

运行

30*30=900 个格子里有 897 个雷,随便点一个都是雷。

分析

C# 写的,拖入 dnSpy。

img

GetKey 函数十分惹眼,下意识地都会点过去(

// UltimateMinesweeper.MainForm
// Token: 0x0600000D RID: 13 RVA: 0x000023E4 File Offset: 0x000005E4
private string GetKey(List<uint> revealedCells)
{
	revealedCells.Sort();
	Random random = new Random(Convert.ToInt32(revealedCells[0] << 20 | revealedCells[1] << 10 | revealedCells[2]));
	byte[] array = new byte[32];
	byte[] array2 = new byte[]
	{
		245,75,65,142,68,71,100,185,74,127,62,130,231,129,254,243,28,58,103,179,60,91,195,215,102,145,154,27,57,231,241,86
	};//手动压行
	random.NextBytes(array);
	uint num = 0U;
	while ((ulong)num < (ulong)((long)array2.Length))
	{
		byte[] array3 = array2;
		uint num2 = num;
		array3[(int)num2] = (array3[(int)num2] ^ array[(int)num]);
		num += 1U;
	}
	return Encoding.ASCII.GetString(array2);
}

但当时我还是更想通关扫雷于是就不管这函数了,后来重新看了这函数察觉到之前是对的,这随机函数一眼就不好爆破或瞎蒙的样子……更何况我不怎么了解 C# 各种函数细节。

顺着找扫雷图生成逻辑的思路,一通乱翻点进了这个函数:

// UltimateMinesweeper.MainForm
// Token: 0x0600000A RID: 10 RVA: 0x000022DC File Offset: 0x000004DC
private void AllocateMemory(MineField mf)
{
	for (uint num = 0U; num < MainForm.VALLOC_NODE_LIMIT; num += 1U)
	{
		for (uint num2 = 0U; num2 < MainForm.VALLOC_NODE_LIMIT; num2 += 1U)
		{
			bool flag = true;
			uint r = num + 1U;
			uint c = num2 + 1U;
			if (this.VALLOC_TYPES.Contains(this.DeriveVallocType(r, c)))
			{
				flag = false;
			}
			mf.GarbageCollect[(int)num2, (int)num] = flag;
		}
	}
}

套着“分配内存”的名义,长得却与分配地雷的逻辑十分甚至九分相似,这里面肯定有问题。

MainForm.VALLOC_NODE_LIMIT 是 30,和扫雷图的边长一样,再加上这个二重循环,大概可以推断就是在遍历扫雷图或者一个和它相关的数据块。

点进 VALLOC_TYPES 看一眼

private static uint VALLOC_TYPE_HEADER_PAGE = 4294966400U;

// Token: 0x04000008 RID: 8
private static uint VALLOC_TYPE_HEADER_POOL = 4294966657U;

// Token: 0x04000009 RID: 9
private static uint VALLOC_TYPE_HEADER_RESERVED = 4294967026U;

// Token: 0x0400000A RID: 10
private uint[] VALLOC_TYPES = new uint[]
{
    MainForm.VALLOC_TYPE_HEADER_PAGE,
    MainForm.VALLOC_TYPE_HEADER_POOL,
    MainForm.VALLOC_TYPE_HEADER_RESERVED
};

三个数刚好可以对应三个雷,简直就是在明示。然而稀奇古怪毫不相干的名字还在尽全力维护自己蹩脚的伪装……

点进 DeriveVallocType 看一眼

private uint DeriveVallocType(uint r, uint c)
{
    return ~(r * MainForm.VALLOC_NODE_LIMIT + c);
}

十分明显的文不对题,这个函数在做的很明显就是把行列坐标合并为一个整数,但是另外加了个按位取反,这也解释了为什么前面那三个数那么大。

直接做个逆运算验证一下想法:

#include <iostream>

using namespace std;

void getpos(unsigned v) {
    v = ~v;
    unsigned r = v / 30;
    unsigned c = v % 30;
    printf("%u %u\n", r, c);
}

int main() {
    getpos(4294966400);
    getpos(4294966657);
    getpos(4294967026);
    return 0;
}

得到了三组坐标:

29 25
21 8
8 29

r 是行数 c 是列数,不确定或者分不清行列那就都试一下就行,直接打开扫雷对着这几个坐标点就对了

解

结语

网上搜到了各种奇奇怪怪的作弊 WP 诸如不死挂透视挂的各种调试找的,我寻思有那么麻烦嘛……于是写了此 WP(太菜不会改不会调试 C# 导致的

posted @ 2026-01-15 00:40  BearBrine  阅读(19)  评论(0)    收藏  举报