Reading decompiled (reverse-engineered) code is not as insanely hard as it sounds. You can usually find functions, and then it's a matter of finding _what_ a function does.
If you can somehow attach a debugger or get breakpoints, it's even easier.
In some cases, oddly, the intent of a function can actually become clearer when the logic gets stripped of all the bad naming protocols and names for the moving pieces have to be reconstructed from only its actions and contexts.
In a perfect world, this shouldn't be true and the content embedded within those symbols in the source code should be an easy lever towards relatively perfect understanding of both intent and implementation; however, software is a relatively young discipline and this is actually a difficult linguistic problem.
If you can somehow attach a debugger or get breakpoints, it's even easier.