Roar CTF 2019 坦克大战 Writeup
下载压缩包 解压看到
可知该游戏由Unity3D编写,由于没有相关逆向经验,上网查阅得知游戏代码部分都在Assembly-CSharp.dll文件中,通过dnspy可以打开此类型的dll.
直接查看跟题目相关的函数,在MapManager下有名为WinGame的函数.源码如下
// MapManager
// Token: 0x06000029 RID: 41 RVA: 0x00003050 File Offset: 0x00001250
public static void WinGame()
{
if (!MapManager.winGame && (MapManager.nDestroyNum == 4 || MapManager.nDestroyNum == 5))
{
string text = "clearlove9";
for (int i = 0; i < 21; i++)
{
for (int j = 0; j < 17; j++)
{
text += MapManager.MapState[i, j].ToString();
}
}
string a = MapManager.Sha1(text);
if (a == "3F649F708AAFA7A0A94138DC3022F6EA611E8D01")
{
FlagText._instance.gameObject.SetActive(true);
FlagText.str = "RoarCTF{wm-" + MapManager.Md5(text) + "}";
MapManager.winGame = true;
}
}
}
具体就是对数组进行字符化累加后,判断Hash值是否与给定值相等.
其中nDestroyNum和MapManager.MapState是关键所在.
先看MapManager.MapState(在MapManager.init()中定义.
// MapManager
// Token: 0x06000028 RID: 40 RVA: 0x000027AC File Offset: 0x000009AC
public static void Init()
{
MapManager.MapState = new int[,]
{
{
8,
8,
8,
8,
8,
8,
8,
8,
8,
8,
8,
8,
8,
8,
8,
8,
8
},
{
8,
8,
4,
5,
8,
1,
1,
1,
1,
1,
1,
8,
8,
8,
8,
4,
8
},
{
8,
2,
8,
1,
8,
8,
5,
1,
8,
8,
8,
1,
8,
1,
8,
4,
8
},
{
8,
5,
8,
2,
8,
8,
8,
8,
1,
8,
8,
4,
8,
1,
1,
5,
8
},
{
8,
8,
8,
8,
2,
4,
8,
1,
1,
8,
8,
1,
8,
5,
1,
5,
8
},
{
8,
8,
8,
8,
5,
8,
8,
1,
5,
1,
8,
8,
8,
1,
8,
8,
8
},
{
8,
8,
8,
1,
8,
8,
8,
8,
8,
8,
8,
8,
1,
8,
1,
5,
8
},
{
8,
1,
8,
8,
1,
8,
8,
1,
1,
4,
8,
8,
8,
8,
8,
1,
8
},
{
8,
4,
1,
8,
8,
5,
1,
8,
8,
8,
8,
8,
4,
2,
8,
8,
8
},
{
1,
1,
8,
5,
8,
2,
8,
5,
1,
4,
8,
8,
8,
1,
5,
1,
8
},
{
0,
1,
4,
8,
8,
8,
8,
8,
8,
8,
8,
8,
8,
8,
8,
8,
8
},
{
1,
1,
8,
1,
8,
8,
2,
1,
8,
8,
5,
2,
1,
8,
8,
8,
8
},
{
8,
8,
8,
8,
4,
8,
8,
2,
1,
1,
8,
2,
1,
8,
1,
8,
8
},
{
8,
1,
1,
8,
8,
4,
4,
1,
8,
4,
2,
4,
8,
4,
8,
8,
8
},
{
8,
4,
8,
8,
1,
2,
8,
8,
8,
8,
1,
8,
8,
1,
8,
1,
8
},
{
8,
1,
1,
5,
8,
8,
8,
8,
8,
8,
8,
8,
1,
8,
8,
8,
8
},
{
8,
8,
1,
1,
5,
2,
8,
8,
8,
8,
8,
8,
8,
8,
2,
8,
8
},
{
8,
8,
4,
8,
1,
8,
2,
8,
1,
5,
8,
8,
4,
8,
8,
8,
8
},
{
8,
8,
2,
8,
1,
8,
8,
1,
8,
8,
1,
8,
2,
2,
5,
8,
8
},
{
8,
2,
1,
8,
8,
8,
8,
2,
8,
4,
5,
8,
1,
1,
2,
5,
8
},
{
8,
8,
8,
8,
8,
8,
8,
8,
8,
8,
8,
8,
8,
8,
8,
8,
8
}
};
}
查看游戏地图.
对游戏地图分析.大致可以知道关键部分
0 对应 自己的基地.
1 对应 墙体
8 对应 空白处
9 对应 毁坏后的基地
再看nDestroyNum在dll中的引用
// Bullect
// Token: 0x0600000A RID: 10 RVA: 0x00002898 File Offset: 0x00000A98
private void OnTriggerEnter2D(Collider2D collision)
{
int num = (int)collision.gameObject.transform.position.x;
int num2 = (int)collision.gameObject.transform.position.y;
string tag = collision.tag;
if (tag != null)
{
if (!(tag == "Tank"))
{
if (!(tag == "Heart"))
{
if (!(tag == "Enemy"))
{
if (!(tag == "Wall"))
{
if (tag == "Barrier")
{
if (this.isPlayerBullect)
{
collision.SendMessage("PlayAudio");
}
UnityEngine.Object.Destroy(base.gameObject);
}
}
else
{
MapManager.MapState[num + 10, num2 + 8] = 8;
MapManager.nDestroyNum++;
UnityEngine.Object.Destroy(collision.gameObject);
UnityEngine.Object.Destroy(base.gameObject);
}
}
else if (this.isPlayerBullect)
{
collision.SendMessage("Die");
UnityEngine.Object.Destroy(base.gameObject);
}
}
else
{
MapManager.MapState[num + 10, num2 + 8] = 9;
MapManager.nDestroyNum++;
collision.SendMessage("Die");
UnityEngine.Object.Destroy(base.gameObject);
}
}
else if (!this.isPlayerBullect)
{
collision.SendMessage("Die");
UnityEngine.Object.Destroy(base.gameObject);
}
}
}
分析代码,num和num2是方块在地图里的坐标(有负数),但经过"num+10,num2+8"调整后即为MapState数组中的下标.每当击中墙体,将墙体变成空白(MapManager.MapState[num + 10, num2 + 8] = 8;),再增加nDestroyNum的值,同理,击中基地时,将基地变成毁坏后的基地(MapManager.MapState[num + 10, num2 + 8] = 9;)再增加nDestroyNum的值.
那么大致思路就出来了: 累计击中4次或5次墙体或基地,进行Hash判断是否与给定Hash相等.游戏内一一尝试击中不太现实.我们可以直接在WinGame函数做点动作.更改后的WinGame函数如下
// MapManager
// Token: 0x06000029 RID: 41 RVA: 0x00003044 File Offset: 0x00001244
public static void WinGame()
{
int num = 65;
for (int i = 0; i < num - 2; i++)
{
for (int j = i + 1; j < num - 1; j++)
{
for (int k = j + 1; k < num; k++)
{
if (!MapManager.winGame)
{
MapManager.Init();
MapManager.MapState[10, 0] = 9; //10,0是基地在MapState中的下标
int num2 = -1;
string text = "clearlove9";
for (int l = 0; l < 21; l++)
{
for (int m = 0; m < 17; m++)
{
if (MapManager.MapState[l, m] == 1)
{
num2++;
if (num2 == i || num2 == j || num2 == k)
{
MapManager.MapState[l, m] = 8;
}
}
text += MapManager.MapState[l, m].ToString();
}
}
if (MapManager.Sha1(text) == "3F649F708AAFA7A0A94138DC3022F6EA611E8D01")
{
FlagText._instance.gameObject.SetActive(true);
FlagText.str = "RoarCTF{wm-" + MapManager.Md5(text) + "}";
StreamWriter streamWriter = new StreamWriter("flag.txt");
streamWriter.WriteLine(FlagText.str);
streamWriter.Close();
MapManager.winGame = true;
return;
}
}
}
}
}
}
大致就是设置标记位,将墙体标记起来并设置为8,每次循环都调用MapManager.init()以来恢复初始值.
理论上有4种情况
- 击中3次墙体 + 1次基地(也就是上述代码) (nDestroyNum == 4)
- 击中4次墙体 (nDestroyNum == 4)
- 击中4次墙体 + 1次基地 (nDestroyNum == 5)
- 击中5次墙体 (nDestroyNum == 5)
然而,就算得到Flag.str,游戏内好像没有相关函数将他显示出来(也有可能我没看见).所以调用写文件函数.
为了方便起见,在进入游戏时就调用WinGame进行判断,进入游戏后查看有无flag.txt文件就知道是否成功
// Option
// Token: 0x0600002F RID: 47 RVA: 0x00003264 File Offset: 0x00001464
private void Update()
{
if (Input.GetKeyDown(KeyCode.W))
{
this.choice = 1;
base.transform.position = this.posOne.position;
}
else if (Input.GetKeyDown(KeyCode.S))
{
this.choice = 2;
base.transform.position = this.posTwo.position;
}
if (this.choice == 1 && Input.GetKeyDown(KeyCode.Space))
{
MapManager.Init();
SceneManager.LoadScene(1);
MapManager.WinGame();
}
}
运行游戏.
查看游戏文件夹.
得到flag.
上一篇: 通过掌纹,可以看出女朋友的性格
下一篇: 上课心太累了