Realistically, when people do "reverse engineering", they usually figure-out what the code is doing and they start-over and write a new program that does he same thing. (Or, they steal the source code or simply copy the hex code.)
You do have to know what it runs on... C/C++ is portable (with some limitations) so you can compile a C/C++ program to run on an Arduino, PC, or Mac, but the machine language hex code only runs on the machine it's intended for. In other words, the hex code is meaningless unless you know what the machine language is.
You can open any file in a [u]Hex Editor[/u] and the Hex Editor will show the ASCII character for any values that can be converted to text (including values that don't represent text). So, if you find a readable string you can read it, or you can change it with the hex editor.
There is a one-to-one relationship between assembly language and machine language (the hex file). That is, every assembly language instruction has a corresponding machine language instruction so it's fairly trivial to [u]disassemble[/u] the machine language and convert it to assembly language.
There are no variables, labels, or comments in machine code, so variables & labels will be simply replaced by memory addresses and there will be no comments in the disassembled code. And, I don't know if there's a disassembler for the AVR or if you'd have to write it yourself or do it by hand.
There are also [u]decompilers[/u] for converting machine code to C or C++. But, I have no idea if you can find one for the AVR/Arduino.
There is NOT a one-to-one relationship between C/C++ and machine language. That means you can't get back the exact-original C/C++ program, but you can get one that functions the same when re-complied. An analogy would be if you gave a homework assignment to write a program that does some particular thing, every student's program would be slightly different although they all do the same thing.
There's more than one way to make a loop and the decompiler might choose a different method from what the original programmer used. Again since there are no variable names in machine code, You might get variable names like "Variable1" and "Variable2" instead of original-meaningful names like "RedLED" or "StartTime". And of course, no comments.
You could decompile to C, Java, or BASIC without knowing the original language used to write the program.
You might have to convert the Intel hex file to "raw" hex before using any of these tools. (You'd have to check the specs/instructions for the decompiler or disassembler.) I opened it with a hex editor, and because of the Intel formatting I'm not seeing the "real" hex values.... The hex file looks nothing like a "typical" exe file, etc.