Alok Menghrajani

Previously: security engineer at Square, co-author of HackLang, put the 's' in https at Facebook. Maker of CTFs.

Home | Contact me | Github | Twitter | Facebook

This post is split in multiple parts. Make sure you first read part 1 and part 2.

Understanding how the assembly code works requires reverse engineering it. The first step is to look at the program's control flow. In some cases, the control flow becomes very complex to decipher (e.g. when dynamic dispatches or signals come into play). To understand the control flow, start by tagging instructions which jump (either conditionally or unconditionally).

Thankfully we aren't dealing with anything too complicated. The code is fairly short and hasn't been purposefully obfuscated. Without yet decoding individual instructions, we can tell:

  • We have an infinite loop that starts at 000B and runs until 004C, 0054 or 005A.
  • We have a subroutine that starts at 005D and ends at 007E. This subroutine gets called 4x per iteration of the above infinite loop.
  • The infinite loop contains 3 conditionals. The subroutine has 1.

Knowing this information, we draft the equivalent code in a higher level language:

main() {
  ... some initialization ...
  while (1) {
    ...
    if (some_condition) {
      ...
    }
    ...
    do_something()
    ...
    do_something()
    ...
    do_something()
    ...
    do_something()
    ...
    if (some_condition) {
      continue;
    }
    ...
    if (some_condition) {
      continue;
    }
    ...
  }
}

do_something() {
  if (some_condition) {
    ...
    return;
  }
  ...
  return;
}

The next post will use this structure and convert each individual instruction.