1625-5 王子昂 总结《2018年2月10日》 【连续第498天总结】
A. Harekaze CTF(re200)-15Puzzle
B.
Solve 15-puzzle 1000 times!
运行看一下,挺有意思的15Puzzle游戏
随手玩了一下,十分钟才过关拿了一分,这要一千分……嗯……
查壳查壳,发现是C#,于是dnSpy走起
目标很显然就是调用FlagGenerator类的ShowFlag函数,进去看一下
public static void ShowFlag(Random rnd)
{
for (int i = 0; i < FlagGenerator.flag.Length; i++)
{
byte[] array = FlagGenerator.flag;
int num = i;
array[num] ^= (byte)rnd.Next(256);
}
MessageBox.Show(string.Format("Congratulations! The flag is {0}", Encoding.ASCII.GetString(FlagGenerator.flag)));
}
发现是跟随机数异或来的,那就有点神奇了
要不这里就是个幌子,要不说明随机数种子有问题
于是找rnd的生成,查看交叉引用
发现在load函数中生成
this.rnd = new Random((from x in typeof(Form1).GetMethods(BindingFlags.DeclaredOnly | BindingFlags.Instance | BindingFlags.NonPublic)
select CRC32.Compute(x.GetMethodBody().GetILAsByteArray())).Aggregate((int x, int y) => x ^ y));
这么复杂,看傻了
不过大致从几个关键词可以猜出来是什么意思,GetMethods是取该程序的方法,CRC32当然就是对方法的IL字节码进行哈希了
所以整个函数就是自校验,通过自身代码来生成一个种子
这意味着如果Patch就会使得代码被改变,进而影响种子和flag的生成
然而注意rnd的读取除了flag还有另外一个函数ShufflePannels
这个函数查看一下交叉引用可以发现它是在生成新的关卡,也要读取若干随机数。而这里必然是要Patch的:动态调试只能修改变量的值,而这个函数合法状态下必须执行1000次(使得随机数序列恰当)
那么真正的解法就很明显了:首先调试截获rnd类的seed,然后将其Patch上去过自校验,再将过关校验处的函数Patch,使其执行1000次,最后执行ShowFlag
在这里下断,用源程序运行截获种子
然后将这里的代码改为
this.rnd = new Random(-24110677);
再Patch校验处
编译后运行即可得到flag
C. 明日计划
py-服务器