WeChall - Towers of Hanoi (Java, Cracking)

Challenge

Java applet 实现汉诺塔,10 个盘子从左柱移到右柱,限 60 秒。无法在现代浏览器直接运行(Java applet 已被弃用),需要逆向 jar 文件离线算出答案。

Solution

Step 0: 获取 JAR

JAR 文件在挑战页面的 <applet> 标签中通过 archive="hanoi2.jar" 引用,与页面同目录:

1
$ wget https://www.wechall.net/challenge/Z/hanoi/hanoi2.jar

Step 1: 逆向分析

1
2
$ jar xf hanoi2.jar
$ javap -c -p Tower.class Tower\$TowerPanel.class

反编译关键发现:

  • 每次合法拖拽后,mouseUp() 构造 last_tower + new_tower(如 "a" + "b" = "ab"),调用 Tower.query(move)
  • query()/challenge/Z/hanoi/?query=<move> 发 HTTP 请求,读取服务器返回的 2 字符响应,追加到 solution 字符串
  • 所有 10 盘移到右柱(win=true)时,对 solution 做 SHA-512,导航到 ?solution=<hash>
  • 60 秒超时(javax.swing.Timer),移动超过 1023 步自动重置

Step 2: 获取 6 种 query 的返回值

服务器对每种合法移动返回固定 2 字符令牌:

Move Response
ab we
ac ch
ba lr
bc al
ca ul
cb z!

这些值可通过实际运行 applet 抓包或直接 curl 逐个获取:

1
2
$ curl -s 'https://www.wechall.net/challenge/Z/hanoi/?query=ab'
we

Step 3: 生成最优序列

10 盘汉诺塔最优解需要 2^10 − 1 = 1023 步。经典的递归解法:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
import hashlib

MOVE_RESP = {
'ab': 'we', 'ac': 'ch', 'ba': 'lr',
'bc': 'al', 'ca': 'ul', 'cb': 'z!',
}
TOWER = ['a', 'b', 'c']

def hanoi_moves(n, src, dst, aux):
if n == 0: return
yield from hanoi_moves(n - 1, src, aux, dst)
yield (src, dst)
yield from hanoi_moves(n - 1, aux, dst, src)

moves = list(hanoi_moves(10, 0, 2, 1))
solution = ''.join(
MOVE_RESP[TOWER[s] + TOWER[d]] for s, d in moves
)
sha = hashlib.sha512(solution.encode()).hexdigest()
print(sha)

输出的 SHA-512 即为答案。

Step 4: 提交

1
$ curl "https://www.wechall.net/challenge/Z/hanoi/index.php?solution=<sha512>"

服务器返回 "Correct!" 即通过。

注意: 该 hash 是确定性的(固定响应表 + 最优路径 = 固定 result),所以可以直接用提交的方式跳过 applet。如服务器已有提交记录,会提示 "Your answer is correct but you have already solved this challenge."

feb3a1f6e5e259f381f42a4e72aceaea204403fab7eec9a2d3d0bcff076a647be88f6f6caeeb1b6295aabba9807f1a2260b466f9f0512498fb50300703eb2552