Pewpewboat was one of the most fun challenges so far! The executable provided is a 64-bit Linux executable (the reason for 64-bit will be apparant later), which is the game Battleship. You enter coordinates in a prompt which consist of a letter for the Y-coordinate and a number for the X-coordinate. If you sink all the ships in one level, you win! However, use too many shots and you will run out of ammo and the game ends.
1 2 3 4 5 6 7 8 A |_|_|_|_|_|_|_|_| B |_|_|_|_|_|_|_|_| C |_|_|_|_|_|_|_|_| D |_|_|_|_|_|_|_|_| E |_|_|_|_|_|_|_|_| F |_|_|_|_|_|_|_|_| G |_|_|_|_|_|_|_|_| H |_|_|_|_|_|_|_|_| Rank: Seaman Recruit Welcome to pewpewboat! We just loaded a pew pew map, start shootin'! Enter a coordinate:
Playing along, after solving the first level, there is another obstacle:
1 2 3 4 5 6 7 8 A |_|_|_|_|_|_|_|_| B |_|_|_|X|X|X|X|_| C |_|_|_|X|_|_|_|_| D |_|_|_|X|_|_|_|_| E |_|_|_|X|X|X|X|_| F |_|_|_|X|_|_|_|_| G |_|_|_|X|_|_|_|_| H |_|_|_|_|_|_|_|_| Rank: Seaman Recruit Nice shot! Hit! You sunk all the ships!! NotMd5Hash("OHHG") >
The game wants us to hash a value and input it in a prompt. Also, the hits on the map of the first level is in the shape of the character F, which is obviously significant!
Time to start reversing
There are a few interesting points in the binary:
0x40398f- out-of-ammo check
0x403eda- max moves check
0x403b4e- check if the player finished the map
0x403be0- the call to NotMD5Hash()
0x403757- memcmp() call after the ‘NotMD5Hash’ prompt
I tried patching the binary at first to show the position of the boats on the map and to skip the NotMD5Hash() prompt; but for some reason in the later levels the program started outputting crap and crashing. Not sure if my patching was not done well or there is some sort of protection in place; I quickly abandoned this approach.
What I did instead is use gdb with two breakpoints. The first one at
0x403757, after the prompt for NotMD5Hash(). The prompt is easy to pass; simply set $rdi to $rsi or vice-versa so the parameters are equal, and you skip the hash check.
The second one I placed at
0x403b4e, which is the check to see if the level is finished. By printing $rax with the debugger, you can get the bitmask with valid hits in the level. I did this for every level and wrote a little python script to get the moves required to beat each level (b is the bitmask obtained from $rax):
for x in range(0, 63): if b & (1 << x): print("%c%d" % ((int)(ord('a') + (x / 8)), (x % 8) + 1))
I simply pasted the output of the python script into the game to easely beat each level.
End of the game
When all the levels are finished, the following message appears:
Aye!PEWYouPEWfoundPEWsomePEWlettersPEWdidPEWya?PEWToPEWfindPEWwhatPEWyou'rePEWlookingPEWfor,PEWyou'llPEWwantPEWtoPEWre-orderPEWthem:PEW9,PEW1,PEW2,PEW7,PEW3,PEW5,PEW6,PEW5,PEW8,PEW0,PEW2,PEW3,PEW5,PEW6,PEW1,PEW4.PEWNextPEWyouPEWletPEW13PEWROTPEWinPEWthePEWsea!PEWTHEPEWFINALPEWSECRETPEWCANPEWBEPEWFOUNDPEWWITHPEWONLYPEWTHEPEWUPPERPEWCASE. Thanks for playing!
The letters we obtained are
FHGUZREJVO. After re-ordering, that turns to
OHGJURERVFGUREHZ. ROT13 de-coding that turns it into
Re-running the game and entering
BUTWHEREISTHERUM into the coordinate prompt (‘in the sea’) gives us the flag!