This is a great guide. The “data is king” message is hard to overstate.

Some other bits and pieces not mentioned that could be helpful for beginners, from my own personal experience, that I hope show up in later parts:

• Know the calling convention[0] of the platform you are working on! If you don’t know where arguments come from or how values are returned from functions, you are not even going to be able to get started.

• In addition to working backwards from known system calls, embedded strings can also be a great place to start. They frequently contain filenames, error or debug messages, well-known magic values (PNG chunk identifiers, FourCC codes, file extensions, etc.), and other junk that can be used to identify potentially interesting functions. If you get really lucky and the binary uses some string-based message sending you will gain an enormous advantage this way.

• While I agree with the author that starting from `main` is mostly futile, it can still often be helpful to do a little bit there depending upon the kind of the binary you are reverse engineering. For example, GUI apps will generally have some initialisation code, then an event loop, then some teardown code. Identifying these sections is useful when you start looking at deeper functions to know if you are hitting a function that is called by init (more useful) or teardown (less useful) to make smarter choices on where to spend effort. The event loop will also use documented OS messages and types, so you can use those to get an idea of which system events map to which functions and what data they receive from the system, which again helps give useful context for deeper functions later on.

• Depending on the compiler or optimiser that was used, code within TUs may simply be emitted in sequence, so if you are disassembling some code and you can determine that a few functions next to each other are all member functions for the same C++ class, you can assume with high confidence that adjacent functions in other places also all operate on the same kind of object and create associations between groups of functions that way.

• Similar to the last point, looking for vtables in the data section of the binary can help to quickly learn about the size and fields of objects. If you see a sequence of function offsets, whatever function points to the start of that sequence is probably a constructor (and will likely use the offset like `mov [rax+0], offset <list of functions>`). The `malloc` call in the constructor or the constructor’s caller will tell the object size, and then reviewing all the vtable functions will let you fill out the object’s structure pretty easily.

• Notwithstanding “data is king”, knowing what code implementations of common data types look like can be very helpful. Once you know what it looks like to access a hash map, or traverse a linked list, or grow a vec, or get the length of a C-string, when you encounter that pattern, you immediately know what kind of object you’re working with.

• Understanding some of the optimisations that compilers use is also very helpful since they can look very weird, though this is more of a rote memorisation/exposure thing. Tools like Assembly x86 Emulator[1] can be super helpful when encountering confusing stuff since you can just paste in there, step through, and mess with the data/registers without needing to try to run a debugger on the original code.

• For whatever reason, the first few months I started reverse engineering I could not do it without using graph view. After that time, something shifted in my brain and now I can no longer work effectively without using a straight disassembly view most of the time. Pick what works for you, and just like an IDE, make sure to take time to configure your decompiler/disassembler with the visibility options that feel like they give you the strongest orientation.

The basic approach and difficulty curve for reverse engineering a binary is essentially the same as solving a jigsaw puzzle. Finding the corner pieces is simple, finding the edge pieces is harder, and finding the first pieces to connect to the edges is the hardest part. As you start filling in the picture, though, it becomes exponentially easier, so just keep at it.

[0] https://en.wikipedia.org/wiki/Calling_convention

[1] https://carlosrafaelgn.com.br/Asm86/