├── NEW_game.SC2Map ├── Old_game.SC2Map └── README.md /NEW_game.SC2Map: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CTF-Archives/2023-StarCTF-DeadGame/main/NEW_game.SC2Map -------------------------------------------------------------------------------- /Old_game.SC2Map: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CTF-Archives/2023-StarCTF-DeadGame/main/Old_game.SC2Map -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # *CTF Dead Game 题解 2 | 3 | > 星际玩家最后的倔强x By 国服第一探姬 (探姬#51276) 4 | > 5 | > 现在已经成亚服难民了x 探姬#31410 6 | 7 | 感觉本题作者预期的解应该是通关,不过通过银河编辑器解题也不算非预期,毕竟不难,难的是如今找到如今还在打星际的CTFer,并且他一定意义上熟悉银河编辑器。因为SC2兵种繁多,一个技能也会绑定很多演算体 触发器 脚本,因此让新手直接上手似乎不太可能,不过我还是打算讲讲怎么用银河编辑器直接算,说实话这很有意思ww(好吧其实是曾经有过一些优质mod和图产出却没拿到一血的mapmaker最后的尊严QaQ! 8 | 9 | 题目的地图附件放在Release中,需要可自取,地图版权归暴雪娱乐所有。 10 | 11 | ## 题面 12 | 13 | ![image-20230729221044577](https://nssctf.wdf.ink//img/WDTJ/202307292210607.png) 14 | 15 | ## 通关法 16 | 17 | 还是需要银河编辑器,打开后点绿色的测试 18 | 19 | ![image-20230730013508878](https://nssctf.wdf.ink//img/WDTJ/202307300135020.png) 20 | 21 | 虽然题目有防作弊检测,但是也就检测了泽叔的生命和护盾,以及玩家单位是否只有泽叔一个。 22 | 23 | 所以用作弊码就好了。 24 | 25 | 通关推荐使用这三个,追求速度可以开技能无冷却,老玩家开个无敌就够了,开图大可不必 www 26 | 27 | ``` 28 | god #上帝模式 29 | hanshotfirst # 技能快速冷却 30 | TookTheRedPill #开图 消除战争迷雾 31 | ``` 32 | 33 | 然后跟着关卡走就行了。 34 | 35 | 36 | 37 | ## 银河编辑器解法 38 | 39 | > 40 | >写这个方法是因为 做题的时候突然会想起自己之前做图和做mod然后拿自己做的图或者mod的后门调戏群友的快乐时光,不过遗憾的是后来大家都不打星际了,再加上现在国服无了,人也越来越少了(悲 41 | 42 | 任何一张星际争霸2游戏地图比较核心的地方就是触发器,而让每个地图作者头疼的也是触发器x 43 | 44 | 我们边讲边介绍x 问题不大 45 | 46 | Map文件我们直接用银河编辑器打开即可。 47 | 48 | ![image-20230729220557647](https://nssctf.wdf.ink//img/WDTJ/202307292205963.png) 49 | 50 | > 图圈常说 买银河编辑器送星际争霸2 就得益于银河编辑器基于的GE是一种强大的游戏引擎 你甚至能用他播放视频( 51 | 52 | 很乱,很正常,要素比较多,不过不必担心,你只需要认识几个我们题解需要用到的几个部件: 53 | 54 | ![image-20230729220846357](https://nssctf.wdf.ink//img/WDTJ/202307292208377.png) 55 | 56 | 分别对应 **触发器** **单位数据** **文本信息** 57 | 58 | 和大部分MISC题目一样,从文本入手是一个不错的解决方法,所以我们可以先尝试在Text选项中搜索flag或者ctf字样: 59 | 60 | ![image-20230729221553275](https://nssctf.wdf.ink//img/WDTJ/202307292215336.png) 61 | 62 | 其实大概就知道要干什么了,找三块碎片(其实就是碰瓷末日预言关卡泽叔去三个萨尔纳加神殿收集预言碎片x) 才能获得flag。 63 | 64 | 但其实这里直接只看flag相关的文本有个坏处——容易被误导x 65 | 66 | 除了最后一块碎片可以人眼看出以外,其实其他碎片的外部指向都不可用,都只能依靠游戏内部要素拿到。 67 | ![image-20230729222245116](https://nssctf.wdf.ink//img/WDTJ/202307292222179.png) 68 | 69 | > 最后一块碎片 81!zZ@Rd —— 也就是Blizzard(暴雪)的CTF式写法ww 70 | 71 | 所以第一块和第二块碎片,显然是要依靠游戏内部运算的,所以这个时候,触发器就要登场了。 72 | 73 | 你可以根据text结果精确定位——很明显Flag有三个值,分别是: 74 | 75 | - flag_part1 76 | - flag_part2 77 | - flag_part3 78 | 79 | 当然,也可以直接用Find 找flag 全部Fuzz一遍: 80 | 81 | ![image-20230729223551083](https://nssctf.wdf.ink//img/WDTJ/202307292235156.png) 82 | 83 | 可以看到在通关后的任务结束界面会显示解析到的flag——所以通关游戏也是不错的选择。 84 | 85 | 不过都到这了,我们不妨看看每个flag的part都是怎么计算的。 86 | 87 | - flag_part1 88 | 89 | 第一部分的表达式其实有点劝退的,主要是长——人有时候遇到长的东西,会下意识回避,不过不妨静下心来,慢慢看x 90 | 91 | ![image-20230729223939301](https://nssctf.wdf.ink//img/WDTJ/202307292239360.png) 92 | 93 | ``` 94 | Variable -Set flag_part1 = (String({((Zeratul [162.87, 28.62] Maximum Life (Current))+(Zeratul [162.87, 28.62] Maximum Shields (Current)))*(Zeratul [162.87, 28.62] Maximum Shields (Current))*(Zeratul [162.87, 28.62] Shields Regeneration Rate (Current))+((Zeratul [162.87, 28.62] Maximum Life (Current))+(Zeratul [162.87, 28.62] Maximum Shields (Current)))*(Zeratul [162.87, 28.62] Shields Regeneration Rate (Current))+((Zeratul [162.87, 28.62] Movement Speed Max (Current))+(Zeratul [162.87, 28.62] Life Regeneration Rate (Current)))*(Zeratul [162.87, 28.62] Shields Regeneration Rate (Current))*10+2*(Zeratul [162.87, 28.62] Shields Regeneration Rate (Current))+(Zeratul [162.87, 28.62] Movement Speed Max (Current))+(Zeratul [162.87, 28.62] Life Regeneration Rate (Current))})) 95 | ``` 96 | 97 | 其实把数据填入后就是小学加减乘除,而问题就在于,又臭又长的表达式让人走不到这一步,上面涉及到一些单位的数据 如生命值 护盾值,这个时候我们就要去 **单位数据** Data里面去寻找了。 98 | 99 | 我们先整理一下,我们需要什么数据: 100 | 101 | | 数据名称 | 数量 | 代换 | 102 | | ----------------------------------------------------------- | ---- | -------------- | 103 | | Zeratul [162.87, 28.62] Maximum Life (Current) | 2 | **记作** **A** | 104 | | Zeratul [162.87, 28.62] Maximum Shields (Current) | 3 | **记作** **B** | 105 | | Zeratul [162.87, 28.62] Shields Regeneration Rate (Current) | 4 | **记作** **C** | 106 | | Zeratul [162.87, 28.62] Movement Speed Max (Current) | 2 | **记作** **D** | 107 | | *Zeratul [162.87, 28.62] Life Regeneration Rate (Current)* | 2 | **记作** **E** | 108 | 109 | 可以看到全部都是泽拉图这个单位的数据 包括 `生命值 ` `护盾值` `护盾恢复速度` `最大移动速度` `生命回复速度` 110 | 111 | 得到简化表达式: 112 | 113 | ``` 114 | Variable -Set flag_part1 = (String({((A)+(B))*(B)*(C)+((A)+(B))*(C)+((D)+(E))*(C)*10+2*(C)+(D)+(E)})) 115 | ``` 116 | 117 | 要算的值也很简单: 118 | 119 | ``` 120 | ((A)+(B))*(B)*(C)+((A)+(B))*(C)+((D)+(E))*(C)*10+2*(C)+(D)+(E) 121 | ``` 122 | 123 | 接下来只需要去Data里面找泽叔的数据: 124 | 125 | 使用搜索功能即可: 126 | 127 | ![image-20230729230014444](https://nssctf.wdf.ink//img/WDTJ/202307292300509.png) 128 | 129 | 要注意的是,星际2中不同场景的单位,单位名字虽然相同,但是背后的ID不是相同的 会附带单位的一些要素 所以要完全对应触发器的单位 一个字不差,这里触发器使用的是 Zeratul 的数据。 130 | 131 | | 数据名称 | 值 | 132 | | :---------------------------------------------------------- | ---- | 133 | | Zeratul [162.87, 28.62] Maximum Life (Current) | 300 | 134 | | Zeratul [162.87, 28.62] Maximum Shields (Current) | 100 | 135 | | Zeratul [162.87, 28.62] Shields Regeneration Rate (Current) | 2 | 136 | | Zeratul [162.87, 28.62] Movement Speed Max (Current) | 3 | 137 | | Zeratul [162.87, 28.62] Life Regeneration Rate (Current) | 0 | 138 | 139 | ``` 140 | ((300)+(100))*(100)*(2)+((300)+(100))*(2)+((3)+(0))*(2)*10+2*(2)+(3)+(0) = 80867 141 | ``` 142 | 143 | 看吧,我就说小学加减乘除啦~ 144 | 145 | 所以 flag_part1 的值我们就得到了 80867 146 | 147 | 注意: 148 | 149 | > 因为作者没有使用行为升级的方式去更改默认数据,所以静态解析直接算值是可行的。 150 | > 151 | > 不过严格意义来讲 因为触发器使用的 (Current) 值 所以这样计算其实是不严谨的,因为我随时可以在游戏中动态的去调整生命值。 152 | > 153 | > 以及这里出现了一个小插曲 我把 Zeratul [162.87, 28.62] Movement Speed Max (Current)的值看成2了(所以还对着虚空找了老久的bug。 154 | > 但在截图中也可以看出 Zeratul [162.87, 28.62] Movement Speed Max (Current) 的值就是 3,而后续也没有任何行为的影响。 155 | > 也就是说,开局的表达式((A)+(B))*(B)*(C)+((A)+(B))*(C)+((D)+(E))*(C)*10+2*(C)+(D)+(E) 带入后是 156 | > ((300)+(100))*(100)*(2)+((300)+(100))*(2)+((3)+(0))*(2)*10+2*(2)+(3)+(0) = 80867 157 | > 没有任何问题 158 | > 而在测试中 Zeratul [162.87, 28.62] Life Regeneration Rate (Current) 的表现形式(即单位生命的回复)确实是 0 159 | > 所以编辑器静态环境完全能够做出,对数据错误表示抱歉。 160 | 161 | 162 | 163 | - flag_part2 164 | 165 | 同样我们用查找功能去找这个变量的触发器: 166 | 167 | ![image-20230730012539629](https://nssctf.wdf.ink//img/WDTJ/202307300125670.png) 168 | 169 | 可以看到flag2的值有如下变化: 170 | 171 | ``` 172 | Variable -Set flag_part2 = {flag_part2"K"} 173 | Variable -Set flag_part2 = {flag_part2(String((Zeratul count for player ijqvaelrggonccpy, counting Queued Or Better)))} Variable -Set flag_part2 = {flag_part2(String({(Zeratul [162.87, 28.62] Maximum Life (Current))/300}))} 174 | Variable -Set flag_part2 = {flag_part2(String({(Zeratul [162.87, 28.62] Maximum Shields (Current))/100}))} 175 | Variable -Set flag_part2 = {flag_part2(String({((Zeratul [162.87, 28.62] Maximum Life (Current))+(Zeratul [162.87, 28.62] Maximum Shields (Current)))/80}))} 176 | ``` 177 | 178 | 而每一个变化对应一个独立的触发器,所以我在前面有说到,记得注意通关顺序节奏,得跟着任务目标走,不然flag就错了x 179 | 180 | 结合我们之前拿到的数据,对应的值不难求出,唯一特殊的: 181 | 182 | `{flag_part2"K"}`给flag_2添加字符K,这里是当追猎喊出 " For Shakuras! " (为了夏古拉斯)的时候触发,不过作者很鸡贼用爆蚊把追猎爆了,所以这波属于是棺材板呐喊了! 183 | 184 | `String((Zeratul count for player ijqvaelrggonccpy, counting Queued Or Better))`应该是玩家泽叔的个数,在遇到巢虫领主时触发,为1。 185 | 186 | 这两稍不注意 1和K就互换了 我当时卡在这卡了老久,因为通关节奏太快了()导致flag一直不对nnd 187 | 188 | 然后后面就是计算了, 189 | 190 | - `(Zeratul [162.87, 28.62] Maximum Life (Current))/300})) = 1` 191 | - `(Zeratul [162.87, 28.62] Maximum Shields (Current))/100})) = 1` 192 | - `((Zeratul [162.87, 28.62] Maximum Life (Current))+(Zeratul [162.87, 28.62] Maximum Shields (Current)))/80 = 400 / 80 =5` 193 | 194 | 所以第二部分到没问题( 195 | 196 | flag_part2 的值为 K_1115 197 | 198 | - flag_part3 199 | 200 | 其实在看Text能看出个大概,如果你是忠诚的暴黑,那么这个字符就是送分的玻璃渣。 201 | 202 | ![image-20230730022107614](https://nssctf.wdf.ink//img/WDTJ/202307300221638.png) 203 | 204 | 当然如果你不是,我们依旧可以通过触发器去算() 205 | 206 | ![image-20230730014927487](https://nssctf.wdf.ink//img/WDTJ/202307300149535.png) 207 | 208 | 提取出来内容如下: 209 | 210 | ``` 211 | Variable -Set flag_part3 = {flag_part3(Substring(table, {(Xel'Naga Shrine [166.56, 111.52] Life (Current))%71}, {(Xel'Naga Shrine [166.56, 111.52] Life (Current))%71}))} 212 | 213 | Variable -Set flag_part3 = {flag_part3(Substring(table, {(Food of a Thousand Feasts [179.44, 9.73] Life (Current))+1}, {(Food of a Thousand Feasts [179.44, 9.73] Life (Current))+1}))} 214 | 215 | Variable -Set flag_part3 = {flag_part3(Substring(table, {(Super Warp Gate [181.00, 10.00] Life (Current))/10+13}, {(Super Warp Gate [181.00, 10.00] Life (Current))/10+13}))} 216 | 217 | Variable -Set flag_part3 = {flag_part3(Substring(table, {((Zeratul [162.87, 28.62] Movement Speed Max (Current))+3)*((Zeratul [162.87, 28.62] Movement Speed Max (Current))+3)}, {((Zeratul [162.87, 28.62] Movement Speed Max (Current))+3)*((Zeratul [162.87, 28.62] Movement Speed Max (Current))+3)}))} 218 | 219 | Variable -Set flag_part3 = {flag_part3(Substring(table, {(Void Seeker [69.01, 133.48] Life (Current))%69}, {(Void Seeker [69.01, 133.48] Life (Current))%69}))} 220 | 221 | Variable -Set flag_part3 = {flag_part3(Substring(table, {(Zeratul [162.87, 28.62] Maximum Life (Current))/2-66}, {(Zeratul [162.87, 28.62] Maximum Life (Current))/2-66}))} 222 | 223 | Variable -Set flag_part3 = {flag_part3(Substring(table, {(Zeratul [162.87, 28.62] Maximum Shields (Current))/2+4}, {(Zeratul [162.87, 28.62] Maximum Shields (Current))/2+4}))} 224 | 225 | Variable -Set flag_part3 = {flag_part3(Substring(table, {(Square root((Beacon (Protoss Large) [22.82, 96.07] Life (Current))))+9}, {(Square root((Beacon (Protoss Large) [22.82, 96.07] Life (Current))))+9}))} 226 | 227 | ``` 228 | 229 | 看着很吓人,其实确实很吓人( 230 | 231 | 利用 **Substring** 函数 直接从 table里面取值,所以看着表达式长 但前后都一样 232 | 233 | 以及最后的 **Square root** 平方根函数 234 | 235 | 记得我们的table么: 236 | 237 | ![image-20230730015312923](https://nssctf.wdf.ink//img/WDTJ/202307300153948.png) 238 | 239 | ``` 240 | 0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ!"#$%&'()*+,-./:;<=>?@[\]^_`{|}~ 241 | ``` 242 | 243 | ```json 244 | { 245 | "1": "0", "2": "1", "3": "2", "4": "3", "5": "4", "6": "5", "7": "6", "8": "7", "9": "8", "10": "9", "11": "a", "12": "b", "13": "c", "14": "d", "15": "e", "16": "f", "17": "g", "18": "h", "19": "i", "20": "j", "21": "k", "22": "l", "23": "m", "24": "n", "25": "o", "26": "p", "27": "q", "28": "r", "29": "s", "30": "t", "31": "u", "32": "v", "33": "w", "34": "x", "35": "y", "36": "z", "37": "A", "38": "B", "39": "C", "40": "D", "41": "E", "42": "F", "43": "G", "44": "H", "45": "I", "46": "J", "47": "K", "48": "L", "49": "M", "50": "N", "51": "O", "52": "P", "53": "Q", "54": "R", "55": "S", "56": "T", "57": "U", "58": "V", "59": "W", "60": "X", "61": "Y", "62": "Z", "63": "!", "64": "\ ", "65": "#", "66": "$", "67": "%", "68": "&", "69": "'", "70": "(", "71": ")", "72": "*", "73": "+", "74": ",", "75": "-", "76": ".", "77": "/", "78": ":", "79": ";", "80": "<", "81": "=", "82": ">", "83": "?", "84": "@", "85": "[", "86": "\\", "87": "]", "88": "^", "89": "_", "90": "`", "91": "{", "92": "|", "93": "}", "94": "~" 246 | } 247 | 248 | ``` 249 | 250 | 然后我们再去数据里找萨尔那加神殿(Xel'Naga Shrine)等缺失的数据: 251 | 252 | | 数据名称 | 值 | 253 | | ------------------------------------------------------- | ---- | 254 | | Xel'Naga Shrine [166.56, 111.52] Life (Current) | 1500 | 255 | | Food of a Thousand Feasts [179.44, 9.73] Life (Current) | 1 | 256 | | Super Warp Gate [181.00, 10.00] Life (Current) | 500 | 257 | | Zeratul [162.87, 28.62] Movement Speed Max (Current) | 3 | 258 | | Void Seeker [69.01, 133.48] Life (Current) | 200 | 259 | | Zeratul [162.87, 28.62] Maximum Life (Current) | 300 | 260 | | Zeratul [162.87, 28.62] Maximum Shields (Current) | 100 | 261 | | Beacon (Protoss Large) | 25 | 262 | 263 | 所以转换出来: 264 | 265 | ``` 266 | (Substring(table, {(Xel'Naga Shrine [166.56, 111.52] Life (Current))%71}, {(Xel'Naga Shrine [166.56, 111.52] Life (Current))%71})) = 9 ——"8" 267 | 268 | (Substring(table, {(Food of a Thousand Feasts [179.44, 9.73] Life (Current))+1}, {(Food of a Thousand Feasts [179.44, 9.73] Life (Current))+1})) = 2 ——"1" 269 | 270 | (Substring(table, {(Super Warp Gate [181.00, 10.00] Life (Current))/10+13}, {(Super Warp Gate [181.00, 10.00] Life (Current))/10+13})) = 63 ——"!" 271 | 272 | (Substring(table, {((Zeratul [162.87, 28.62] Movement Speed Max (Current))+3)*((Zeratul [162.87, 28.62] Movement Speed Max (Current))+3)}, {((Zeratul [162.87, 28.62] Movement Speed Max (Current))+3)*((Zeratul [162.87, 28.62] Movement Speed Max (Current))+3)})) = 36 ——"z" 273 | 274 | (Substring(table, {(Void Seeker [69.01, 133.48] Life (Current))%69}, {(Void Seeker [69.01, 133.48] Life (Current))%69})) = 62 ——"Z" 275 | 276 | (Substring(table, {(Zeratul [162.87, 28.62] Maximum Life (Current))/2-66}, {(Zeratul [162.87, 28.62] Maximum Life (Current))/2-66})) = 84 ——"@" 277 | 278 | (Substring(table, {(Zeratul [162.87, 28.62] Maximum Shields (Current))/2+4}, {(Zeratul [162.87, 28.62] Maximum Shields (Current))/2+4})) = 54 ——"R" 279 | 280 | (Substring(table, {(Square root((Beacon (Protoss Large) [22.82, 96.07] Life (Current))))+9}, {(Square root((Beacon (Protoss Large) [22.82, 96.07] Life (Current))))+9})) = (5+9) ——"d" 281 | ``` 282 | 283 | 得到第三部分的flag `81!zZ@Rd` 284 | 285 | 最后拼接即可:**\*CTF{80867_K1115_81!zZ@Rd}** 286 | 287 | ![image-20230730022208313](https://nssctf.wdf.ink//img/WDTJ/202307300222344.png) 288 | 289 | ## 彩蛋 290 | 291 | 为什么换附件x 292 | 293 | 在第一次放题的时候,对最后一个字母的计算 出题人用的是 294 | 295 | ``` 296 | Variable -Set flag_part3 = {flag_part3(Substring(table, {(Square root((Beacon (Protoss Large) [22.82, 96.07] Life (Current))))+9}, {(Square root((Baneling (Burrowed) [30.75, 26.80] Life (Current))))+9}))} 297 | ``` 298 | 299 | emm 信标生命到毒爆虫,看样子还是对25气矿恋恋不忘呢x?? 300 | 301 | 不过,最后生成的字符是8,emm 神奇的编辑器 () 302 | 303 | ![image-20230730024057353](https://nssctf.wdf.ink//img/WDTJ/202307300240389.png) 304 | 305 | ![img](https://nssctf.wdf.ink//img/WDTJ/202307301219390.jpeg) 306 | --------------------------------------------------------------------------------