This is the English translation of my original writeup.
In PlaidCTF 2021, I solved the challenge
The Watness III, then I spent my time on another challenge
dr. Unfortunately, the regular expression algorithm in
dr is too complex to understand, thus I failed to solve it.
There was a similar challenge in PlaidCTF 2020,
The Watness II, I solved that last year when I was a member of A*0*E, which was a reverse challenge of HyperCard game on the m68k platform. I thought that this challenge has the same game logic with as previous one, so I spend some time finding the color logic used in
The Watness II. But the result shows that this challenge is different from that one.
The length of the array
loopback is 250, and the first 200 bytes come from the array
p, which is the data read from the shader code. The last 50 bytes is the array
loopback[200:204], and mouse event into
loopback is the completion flag, and
loopback[130:148] is the decryption key.
loopback by changing the color of the pixels, which is actually done by setting
The shader code writes data to the array
loopback by function
W, and reads data by
M. Once we find this trick, we can get the logic of the game.
CP in function
main is used for recording the current level. There are three levels, and if all three levels are cleared, the program will set the completion flag at
loopback. The function
main selects different render functions by
The return value
CS has two members,
AB indicates whether we have cleared the current level.
Cs.AB is not zero, then we have cleared this level, and the decryption key at
loopback[130:148] is updated, and the variable
CP, which stores the current level, is incremented by 1. By looking into the function
Cl of the first level, we can find that
AB comes from another function
It is obvious that the check logic is in
By adding one line of code,
loopback array. We can find that the macro
M reads the moves of the player. If you have learned about OpenGL, you will notice that
texture2D is a function that returns a pixel from a texture. This function
Bj maps every edge of our path to a pixel in the texture
introImage, and then checks if that pixel’s alpha value is smaller than 255. After checking the picture by Python library PIL, we can find that some pixels’ alpha value is 254. We can use deep-first search to find a possible way.
introImage is this picture：
The function structures of the other two levels are similar to the first level. The checker functions are
BZ. The checker of the second level only checks the points we pass and do not check the edges, so we can easily find a possible way. The third checker function is a bit interesting:
Bg are the vectors of the current edge and previous edge on our path, respectively.
Bh is the outer product of these two vectors. If
Bg are parallel, the z component of the result is zero. Whether the z component is positive or negative depends on the angle between
Bg. Then we can figure out the logic: there is no restriction on walking straight, but when we turn clockwise (or counterclockwise), our position must be one of the positions in the array
Bb). We can find the right way from the final goal step by step.
The author of this challenge displays his sophisticated programming skill in computer graphics. The first two levels can be cleared by playing the game and we can get the decryption keys of these two levels. But the map in the third level is placed upside down to make it harder to play. I have to study the key generation algorithm in the function
AO, and calculate the last decryption key directly.
许可协议 CC BY-NC-ND 4.0