┌──────┐┌─────────────────────────────────────────────────────────────────────────────────────────────────────────┐┌──────┐
│┌─┐┌─┐││ idk_lol.exe _ □ x ││┌─┐┌─┐│
││┌┘└┐│││┌───────── ▄███████ ── ▄███████ ▄█ ─ █▄ ─── ▄███████ ── ▄███████ ── ▄███████ ── ▄███████ ──────────────┐│││┌┘└┐││
││└┐┌┘││││ ███ ███ ███ ███ ███ ███ ███ ███ ███ ███ ███ ███ ███ ███ ││││└┐┌┘││
││┌┘└┐││││ ███ ███ ███ █▀ ███ ███ ███ █▀ ███ ███ ███ █▀ ███ █▀ ││││┌┘└┐││
└┘└┐┌┘└┘││ ▄███▄▄▄██▀ ▄███▄▄ ███ ███ ▄███▄▄ ▄███▄▄▄██▀ ███ ▄███▄▄ ││└┘└┐┌┘└┘
┌─┐││┌─┐││ ▀▀███▀▀▀▀ ▀▀███▀▀ ███ ███ ▀▀███▀▀ ▀▀███▀▀▀▀ ▀██████████ ▀▀███▀▀ ││┌─┐││┌─┐
└┐││││┌┘││ ▀██████████ ███ █▄ ███ ███ ███ █▄ ▀██████████ ███ ███ █▄ ││└┐││││┌┘
┌┘││││└┐││ ███ ███ ███ ███ ███ ███ ███ ███ ███ ███ ▄█ ███ ███ ███ ││┌┘││││└┐
└┐││││┌┘││ ███ ███ █████████ ▀█████▀ █████████ ███ ███ ▄███████▀ █████████ ││└┐││││┌┘
┌┘└┘└┘└┐││ ███ ███ ███ ███ │┌┘└┘└┘└┐
│┌─┐┌─┐││ ▄███████ ███▄▄▄ ▄█████▄ ▄█ ███▄▄▄ ▄███████ ▄███████ ▄███████ ▄█ ███▄▄▄ ▄█████▄ ──┐┌─┐│
││┌┘└┐│││ ███ ███ ███▀▀██▄ ███ ███ ███ ███▀▀██▄ ███ ███ ███ ███ ███ ███ ███ ███▀▀██▄ ███ ███ ┌┘└┐││
││└┐┌┘│││ ███ █▀ ███ ███ ███ █▀ ███▌ ███ ███ ███ █▀ ███ █▀ ███ ███ ███▌ ███ ███ ███ █▀ │└┐┌┘││
││┌┘└┐││ ▄███▄▄ ███ ███ ▄███ ███▌ ███ ███ ▄███▄▄ ▄███▄▄ ▄███▄▄▄██▀ ███▌ ███ ███ ▄███ ││┌┘└┐││
└┘└┐┌┘└┘▀▀███▀▀ ███ ███ ▀▀███ ███▄ ███▌ ███ ███ ▀▀███▀▀ ▀▀███▀▀ ▀▀███▀▀▀▀ ███▌ ███ ███ ▀▀███ ███▄└┘└┐┌┘└┘
┌─┐││┌─┐│ ███ █▄ ███ ███ ███ ███ ███ ███ ███ ███ █▄ ███ █▄ ▀██████████ ███ ███ ███ ███ ███ ┐││┌─┐
└┐││││┌┘│ ███ ███ ███ ███ ███ ███ ███ ███ ███ ███ ███ ███ ███ ███ ███ ███ ███ ███ ███ ███ ││││┌┘
┌┘││││└┐│ █████████ ▀█ █▀ ███████▀ █▀ ▀█ █▀ █████████ █████████ ███ ███ █▀ ▀█ █▀ ███████▀ ┘││││└┐
└┐││││┌┘│ ███ ███ │└┐││││┌┘
┌┘└┘└┘└┐│ ▄███████ ▄█████▄ ▄███████ ███▄▄▄ ▄█████▄ ▄█████▄ ▀████████▄ ▄███████ ││┌┘└┘└┘└┐
│┌─┐┌─┐││ ███ ███ ███ ███ ███ ███ ███▀▀██▄ ███ ███ ███ ███ ███ ███ ███ ███ │││┌─┐┌─┐│
││┌┘└┐│││ ███ █▀ ███ ███ ███ ███ ███ ███ ███ ███ ███ ███ ███ ███ ███ █▀ ││││┌┘└┐││
││└┐┌┘││ ▄███▄▄ ███ ███ ▄███▄▄▄██▀ ███ ███ ███ ███ ███ ███ ▄███▄▄██▀ ███ ││││└┐┌┘││
││┌┘└┐││▀▀███▀▀ ███ ███ ▀▀███▀▀▀▀ ███ ███ ███ ███ ███ ███ ▀▀███▀▀██▄ ▀██████████ ││││┌┘└┐││
└┘└┐┌┘└┘│ ███ ███ ███ ▀██████████ ███ ███ ███ ███ ███ ███ ███ ██▄ ███ ││└┘└┐┌┘└┘
┌─┐││┌─┐│ ███ ███ ███ ███ ███ ███ ███ ███ ███ ███ ███ ███ ███ ▄█ ███ @0x6D6172636F││┌─┐││┌─┐
└┐││││┌┘│ ███ ────── ▀█████▀ ── ███ ─ ███ ─── ▀█ █▀ ─ ▀█████▀ ─ ▀█████▀ ▄████████▀ ─ ▄███████▀ ───────────────┘│└┐││││┌┘
┌┘││││└┐└ ███ ───────────────── ███ ─ ███ ────────────────────────────────────────────────────────────────────────┘┌┘││││└┐
└┐││││┌┘ └┐││││┌┘
┌┘└┘└┘└┐ >>Introduction<< ┌┘└┘└┘└┐
│┌─┐┌─┐│ At a high level, reverse engineering is learning how something works without knowledge of it. With │┌─┐┌─┐│
││┌┘└┐││ software, our goal is to understand the source code of a binary which we do not have the source code ││┌┘└┐││
││└┐┌┘││ to. As a question: "what does this do, and how does it do it?" Our desired outcomes come in varying ││└┐┌┘││
││┌┘└┐││ degrees, from essentially re-creating software to just understanding what certain functions are ││┌┘└┐││
└┘└┐┌┘└┘ performing. A large amount of analysis can be done by simply running and observing software, however └┘└┐┌┘└┘
┌─┐││┌─┐ this document is primarily about static analysis and examining the executable machine code. We gain ┌─┐││┌─┐
└┐││││┌┘ a detailed view of the binary because, simply put, we are looking at the exact instructions which are └┐││││┌┘
┌┘││││└┐ running on a CPU. ┌┘││││└┐
└┐││││┌┘ >Why RE/Practical reasons └┐││││┌┘
┌┘└┘└┘└┐ You might be thinking that this sounds like a tedious and daunting exercise. While not incorrect, ┌┘└┘└┘└┐
│┌─┐┌─┐│ there are a few practical reasons to do so anyways. You may have a malware sample found on a network │┌─┐┌─┐│
││┌┘└┐││ which is sending encrypted outbound internet traffic and need to figure out what the traffic was. In ││┌┘└┐││
││└┐┌┘││ this situation, dynamic analysis may not give you any insight into the payload data. Alternatively, ││└┐┌┘││
││┌┘└┐││ de-compiling may not truly recreate the source code. While useful, de-compilers contain an element of ││┌┘└┐││
└┘└┐┌┘└┘ guesswork. Therefore, you may need to reverse engineer the binary. Additionally, you will see reverse └┘└┐┌┘└┘
┌─┐││┌─┐ engineering used in context with game hacking, DRM removal, CTFs, etc. ┌─┐││┌─┐
└┐││││┌┘ >What is the goal of this document └┐││││┌┘
┌┘││││└┐ In the short term, my goal is to introduce you to reverse engineering both conceptually and ┌┘││││└┐
└┐││││┌┘ practically. Due to the abstract and esoteric nature of RE as a field, most people are entirely └┐││││┌┘
┌┘└┘└┘└┐ unaware of its existence, much less exposed to an entry point. Personally, the most difficult period ┌┘└┘└┘└┐
│┌─┐┌─┐│ I had was finding training, gaining a methodology, and learning where to start. While there are │┌─┐┌─┐│
││┌┘└┐││ fantastic resources for doing so, finding them can be tough. Reversing is an extremely deep field due ││┌┘└┐││
││└┐┌┘││ to its ability to be applied to just about anything electronic, so consider this effort to be a diving ││└┐┌┘││
││┌┘└┐││ board. Furthermore, it is a fantastic introduction to low level computing in general, where you can ││┌┘└┐││
└┘└┐┌┘└┘ leverage your knowledge for things like binary golf, exploit development, modern assembly development, └┘└┐┌┘└┘
┌─┐││┌─┐ virus development, and more. ┌─┐││┌─┐
└┐││││┌┘ In the long term, my goal is to introduce a mindset. Most of my RE experience at the time of writing └┐││││┌┘
┌┘││││└┐ has been in CTFs. Often you are presented with bizarre problems you are unprepared for, with little ┌┘││││└┐
└┐││││┌┘ insight as to proceed. In order to solve these sorts of challenges, the following methodology └┐││││┌┘
┌┘└┘└┘└┐ becomes critical: ┌┘└┘└┘└┐
│┌─┐┌─┐│ Failing attempts must be thought of iterative learning │┌─┐┌─┐│
││┌┘└┐││ "I don't understand X" must become "How do I learn more about X" ││┌┘└┐││
││└┐┌┘││ "Y is too complicated" must become "How do I break Y into smaller steps" ││└┐┌┘││
││┌┘└┐││ ││┌┘└┐││
└┘└┐┌┘└┘ >>Technical Prerequisites<< └┘└┐┌┘└┘
┌─┐││┌─┐ >Executables (aka Binaries) ┌─┐││┌─┐
└┐││││┌┘ At a high level, here is what is going on: └┐││││┌┘
┌┘││││└┐ We have instructions for humans but need to make machine code that a computer can execute. ┌┘││││└┐
└┐││││┌┘ With a compiled language, higher level code such C is parsed and compiled into machine code. └┐││││┌┘
┌┘└┘└┘└┐ >Compiling can be thought of in a few steps: ┌┘└┘└┘└┐
│┌─┐┌─┐│ A compiler takes the source code and converts it into machine language, such as an object file of │┌─┐┌─┐│
││┌┘└┐││ your source code. ││┌┘└┐││
││└┐┌┘││ A linker combines this object file with other pre-compiled object files so that your symbols ││└┐┌┘││
││┌┘└┐││ (for example: identifiers for functions in libraries, global variables, etc) can be defined. When ││┌┘└┐││
└┘└┐┌┘└┘ writing code you will often use functions you did not actually define within your source code. └┘└┐┌┘└┘
┌─┐││┌─┐ The linker takes your undefined symbols and adds the correct addresses to these instructions; ┌─┐││┌─┐
└┐││││┌┘ outputting your executable file. As an example, imagine that you have some source code calling an └┐││││┌┘
┌┘││││└┐ exported DLL function. When that source code is compiled, the function call generates an external ┌┘││││└┐
└┐││││┌┘ function reference (since the DLL is not a part of your source code.) The linker then adds the └┐││││┌┘
┌┘└┘└┘└┐ information necessary for the reference to actually find this DLL code when the process is started. ┌┘└┘└┘└┐
│┌─┐┌─┐│ │┌─┐┌─┐│
││┌┘└┐││ ┌───────────────┐ ││┌┘└┐││
││└┐┌┘││ │ source_code.c│ ││└┐┌┘││
││┌┘└┐││ ┌───────────────────────────────┐ ││┌┘└┐││
└┘└┐┌┘└┘ │#include <stdio.h> │ └┘└┐┌┘└┘
┌─┐││┌─┐ │int main() │ ┌─┐││┌─┐
└┐││││┌┘ │{ │ └┐││││┌┘
┌┘││││└┐ │ printf( "hello world\n" ); │ ┌┘││││└┐
└┐││││┌┘ │ return 0; │ └┐││││┌┘
┌┘└┘└┘└┐ │} │ ┌┘└┘└┘└┐
│┌─┐┌─┐│ └───────────────────────────────┘ │┌─┐┌─┐│
││┌┘└┐││ │ ││┌┘└┐││
││└┐┌┘││ ┌────────┴──────┐ ││└┐┌┘││
││┌┘└┐││ │ Compiler│ ││┌┘└┐││
└┘└┐┌┘└┘ └───────────────┘ └┘└┐┌┘└┘
┌─┐││┌─┐ │ ┌─┐││┌─┐
└┐││││┌┘ ┌────────┴──────┐ └┐││││┌┘
┌┘││││└┐ │compiled_code.o│ ┌┘││││└┐
└┐││││┌┘ ┌───────────────────────────────┐ └┐││││┌┘
┌┘└┘└┘└┐ │.file source_code.c │ ┌┘└┘└┘└┐
│┌─┐┌─┐│ │.LC0: │ │┌─┐┌─┐│
││┌┘└┐││ │ .string "hello world" │ ││┌┘└┐││
││└┐┌┘││ │main: │ ││└┐┌┘││
││┌┘└┐││ │ LEA ECX, [ESP+4] │ ││┌┘└┐││
└┘└┐┌┘└┘ │ AND ESP, -16 │ └┘└┐┌┘└┘
┌─┐││┌─┐ │ PUSH DWORD PTR [ECX-4] │ ┌─┐││┌─┐
└┐││││┌┘ │ PUSH EBP │ └┐││││┌┘
┌┘││││└┐ │ MOV EBP, ESP │ ┌┘││││└┐
└┐││││┌┘ │ PUSH ECX │ └┐││││┌┘
┌┘└┘└┘└┐ │ SUB ESP, 4 │ ┌┘└┘└┘└┐
│┌─┐┌─┐│ │ SUB ESP, 12 │ │┌─┐┌─┐│
││┌┘└┐││ │ PUSH OFFSET FLAT:.LC0 │ ││┌┘└┐││
││└┐┌┘││ │ CALL PUTS │ ││└┐┌┘││
││┌┘└┐││ │ ADD ESP, 16 │ ││┌┘└┐││
└┘└┐┌┘└┘ │ MOV EAX, LC0 │ └┘└┐┌┘└┘
┌─┐││┌─┐ │ MOV ECX, DWORD PTR [EBP-4] │ ┌─┐││┌─┐
└┐││││┌┘ │ LEAVE │ └┐││││┌┘
┌┘││││└┐ │ LEA ESP, [ECX-4] │ ┌┘││││└┐
└┐││││┌┘ │ RET │ └┐││││┌┘
┌┘└┘└┘└┐ └───────────────────────────────┘ ┌┘└┘└┘└┐
│┌─┐┌─┐│ │ │┌─┐┌─┐│
││┌┘└┐││ ┌────────┴──────┐ ┌───────────────┐ ││┌┘└┐││
││└┐┌┘││ │ Linker├───│ External DLLs│ ││└┐┌┘││
││┌┘└┐││ └───────────────┘ └───────────────┘ ││┌┘└┐││
└┘└┐┌┘└┘ │ └┘└┐┌┘└┘
┌─┐││┌─┐ ┌────────┴──────┐ ┌─┐││┌─┐
└┐││││┌┘ │ executable.exe│ └┐││││┌┘
┌┘││││└┐ ┌────────────────────────────────┐ ┌┘││││└┐
└┐││││┌┘ │4d 5a 90 00 03 00 00 00-04 00 00│ └┐││││┌┘
┌┘└┘└┘└┐ │00 ff ff 00 00 b8 00 00 00 00 00│ ┌┘└┘└┘└┐
│┌─┐┌─┐│ │00 00 00-40 00 00 00 00 00 00 00│ │┌─┐┌─┐│
││┌┘└┐││ │... │ ││┌┘└┐││
││└┐┌┘││ └────────────────────────────────┘ ││└┐┌┘││
││┌┘└┐││ ││┌┘└┐││
└┘└┐┌┘└┘ The compiled executable looks like gibberish to us compared to the previous source code. However, └┘└┐┌┘└┘
┌─┐││┌─┐ this won't stop us from our goals. ┌─┐││┌─┐
└┐││││┌┘ While we may describe the executable as "binary," typically we represent it in a shorter format, └┐││││┌┘
┌┘││││└┐ hex. An example of hex: B8 05 00 00 00. ┌┘││││└┐
└┐││││┌┘ Assembly is just translating this directly to a more human readable format. "B8 05 00 00 00" directly └┐││││┌┘
┌┘└┘└┘└┐ corresponds to "MOV EAX, 05" (move the value 05 into register EAX). B8 directly corresponds to the ┌┘└┘└┘└┐
│┌─┐┌─┐│ physical circuits for this instruction on the processor. This performs a MOV operation that loads a │┌─┐┌─┐│
││┌┘└┐││ value into the physical 32-bit memory register on the processor that we are referring to as EAX. The ││┌┘└┐││
││└┐┌┘││ latter half of the hex instruction for B8 is "05 00 00 00". There are 32 bits allocated for this ││└┐┌┘││
││┌┘└┐││ value, but we only need a little of that to represent our value, hence the zeroes. ││┌┘└┐││
└┘└┐┌┘└┘ Also note, the 05 is stored on the far left vs the far right. This is due to something referred to └┘└┐┌┘└┘
┌─┐││┌─┐ as "endianness," which you will want to learn more about later, but don't worry about that right now. ┌─┐││┌─┐
└┐││││┌┘ Just know that if the byte on the far left would represent the least significant bit it would be └┐││││┌┘
┌┘││││└┐ referred to as little-endian, and if the byte on the far left represented the most significant bit, ┌┘││││└┐
└┐││││┌┘ it would be referred to as big-endian. Were we to represent this as a big-endian value, the └┐││││┌┘
┌┘└┘└┘└┐ instruction in hex would be "00 00 00 05." PE (.exe) files will always be little-endian, but know ┌┘└┘└┘└┐
│┌─┐┌─┐│ that other formats such as ELF may not be. │┌─┐┌─┐│
││┌┘└┐││ ││┌┘└┐││
││└┐┌┘││ >What happens when a program is run? ││└┐┌┘││
││┌┘└┐││ 1. The program is assigned RAM, and the code/data it manipulates are loaded into RAM from storage. ││┌┘└┐││
└┘└┐┌┘└┘ 2. An instruction is read from RAM by the CPU control unit. └┘└┐┌┘└┘
┌─┐││┌─┐ 3. The instruction is executed by the CPU ALU using inputs from user or registers. ┌─┐││┌─┐
└┐││││┌┘ 4. The output is stored in registers or sent to a device. └┐││││┌┘
┌┘││││└┐ Repeat these steps until the program is done. ┌┘││││└┐
└┐││││┌┘ └┐││││┌┘
┌┘└┘└┘└┐ ┌─────────────┐ ┌────────────────┐ ┌┘└┘└┘└┐
│┌─┐┌─┐│ ┌─┤ Control Unit│───┤Arith Logic Unit│─┐ │┌─┐┌─┐│
││┌┘└┐││ │ └─────────────┘ └────────────────┘ │ ││┌┘└┐││
││└┐┌┘││ ┌────────────────────┐ │ │ ┌─────────────────────┐ ││└┐┌┘││
││┌┘└┐││ │Inst fetch from RAM │ │ ┌──────────────────────────────────┐ │ │Result stored to RAM │ ││┌┘└┐││
└┘└┐┌┘└┘ └────────────────────┘ │ │RAM Instruction0│ Result0│ │ └─────────────────────┘ └┘└┐┌┘└┘
┌─┐││┌─┐ └─│ Instruction1│ Result1├─┘ ┌─┐││┌─┐
└┐││││┌┘ │ Instruction2│ Result2│ └┐││││┌┘
┌┘││││└┐ │ ...│ ...│ ┌┘││││└┐
└┐││││┌┘ └──────────────────────────────────┘ └┐││││┌┘
┌┘└┘└┘└┐ ┌┘└┘└┘└┐
│┌─┐┌─┐│ >x86 assembly overview (32 bit) │┌─┐┌─┐│
││┌┘└┐││ Instructions in the Intel syntax follow the format of: ││┌┘└┐││
││└┐┌┘││ instruction dest_operand, source_operand ;eg MOV EAX, 0x05 moves the value five into EAX ││└┐┌┘││
││┌┘└┐││ If you see differently, you might be looking at AT&T syntax, which follow the format of: ││┌┘└┐││
└┘└┐┌┘└┘ instruction source_operand, dest_operand ;eg MOV 0x05, EAX moves the value five into EAX └┘└┐┌┘└┘
┌─┐││┌─┐ There are many, many x86 instructions but don't panic. Most of the time you will be seeing common ┌─┐││┌─┐
└┐││││┌┘ instructions. For learning x86 in general, there are a few different strategies. Reading assembly, └┐││││┌┘
┌┘││││└┐ writing assembly, writing higher level code and reading the compiled results, trying different ┌┘││││└┐
└┐││││┌┘ compilers, that sort of thing. Searches like "x86 mov" will be your friend as you try to learn how └┐││││┌┘
┌┘└┘└┘└┐ each instruction behaves. Generally we see our instructions falling into certain categories for ┌┘└┘└┘└┐
│┌─┐┌─┐│ movement of data, logic, and control flow. │┌─┐┌─┐│
││┌┘└┐││ A few common instructions: ││┌┘└┐││
││└┐┌┘││ Movement of data ││└┐┌┘││
││┌┘└┐││ MOV ││┌┘└┐││
└┘└┐┌┘└┘ LEA └┘└┐┌┘└┘
┌─┐││┌─┐ Math/logic ┌─┐││┌─┐
└┐││││┌┘ ADD └┐││││┌┘
┌┘││││└┐ SUB ┌┘││││└┐
└┐││││┌┘ OR └┐││││┌┘
┌┘└┘└┘└┐ XOR ┌┘└┘└┘└┐
│┌─┐┌─┐│ AND │┌─┐┌─┐│
││┌┘└┐││ Control flow ││┌┘└┐││
││└┐┌┘││ CMP ││└┐┌┘││
││┌┘└┐││ JMP ││┌┘└┐││
└┘└┐┌┘└┘ PUSH └┘└┐┌┘└┘
┌─┐││┌─┐ POP ┌─┐││┌─┐
└┐││││┌┘ CALL └┐││││┌┘
┌┘││││└┐ RET ┌┘││││└┐
└┐││││┌┘ └┐││││┌┘
┌┘└┘└┘└┐ >General Registers ┌┘└┘└┘└┐
│┌─┐┌─┐│ Think of registers as variables within x86. This is not an exhaustive list of every register │┌─┐┌─┐│
││┌┘└┐││ which can be used, however the vast majority of the time you will be seeing these registers. ││┌┘└┐││
││└┐┌┘││ ││└┐┌┘││
││┌┘└┐││ ┌───────────────┬───────┬───────┐ ││┌┘└┐││
└┘└┐┌┘└┘ │ 16 bits│ 8 bits│ 8 bits│ └┘└┐┌┘└┘
┌─┐││┌─┐ ┌───────────────┬───────┬───────┐ ──┐ ┌─┐││┌─┐
└┐││││┌┘ EAX │ AX │ AH │ AL │ │ └┐││││┌┘
┌┘││││└┐ ├───────────────┼───────┼───────┤ D │ ┌┘││││└┐
└┐││││┌┘ EBX │ BX │ BH │ BL │ A │ └┐││││┌┘
┌┘└┘└┘└┐ ├───────────────┼───────┼───────┤ T │ ┌┘└┘└┘└┐
│┌─┐┌─┐│ ECX │ CX │ CH │ CL │ A │ │┌─┐┌─┐│
││┌┘└┐││ ├───────────────┼───────┼───────┤ │ ││┌┘└┐││
││└┐┌┘││ EDX │ DX │ DH │ DL │ │ ││└┐┌┘││
││┌┘└┐││ ├───────────────┴───────┴───────┤ ─┤ ││┌┘└┐││
└┘└┐┌┘└┘ ESI │ │ I │ └┘└┐┌┘└┘
┌─┐││┌─┐ ├───────────────────────────────┤ N │ ┌─┐││┌─┐
└┐││││┌┘ EDI │ │ D │ └┐││││┌┘
┌┘││││└┐ ├───────────────────────────────┤ / │ ┌┘││││└┐
└┐││││┌┘ ESP │ │ P │ └┐││││┌┘
┌┘└┘└┘└┐ ├───────────────────────────────┤ N │ ┌┘└┘└┘└┐
│┌─┐┌─┐│ EBP │ │ T │ │┌─┐┌─┐│
││┌┘└┐││ └───────────────────────────────┘ ──┘ ││┌┘└┐││
││└┐┌┘││ │ 32 Bits│ ││└┐┌┘││
││┌┘└┐││ └───────────────────────────────┘ ││┌┘└┐││
└┘└┐┌┘└┘ └┘└┐┌┘└┘
┌─┐││┌─┐ Data Registers ┌─┐││┌─┐
└┐││││┌┘ EAX: Accumulator, often for math and return values. └┐││││┌┘
┌┘││││└┐ EBX: Base, often holds function params and indexed addresses. ┌┘││││└┐
└┐││││┌┘ ECX: Count, for loop counts (programmers: this is "i") and function params. └┐││││┌┘
┌┘└┘└┘└┐ EDX: Data, general use and function params. ┌┘└┘└┘└┐
│┌─┐┌─┐│ Internally labeled boxes are subregisters which can be used. │┌─┐┌─┐│
││┌┘└┐││ Index Registers ││┌┘└┐││
││└┐┌┘││ ESI: Source Index, often stores constants or used as a pointer. ││└┐┌┘││
││┌┘└┐││ EDI: Destination Index, often a destination for data or used as a pointer. ││┌┘└┐││
└┘└┐┌┘└┘ Pointers └┘└┐┌┘└┘
┌─┐││┌─┐ Points to a memory location, generally expressed in hex. Example: 0x40154b. ┌─┐││┌─┐
└┐││││┌┘ ESP: Stack pointer, stores a pointer to top of stack. └┐││││┌┘
┌┘││││└┐ EBP: Base pointer, stores a pointer to base of current stack frame. ┌┘││││└┐
└┐││││┌┘ Flags └┐││││┌┘
┌┘└┘└┘└┐ Certain instructions change flags, which are represented as simple 1 or 0 on a flag register. ┌┘└┘└┘└┐
│┌─┐┌─┐│ For example, if we perform an "add" between two numbers, we may have a few outcomes that we need │┌─┐┌─┐│
││┌┘└┐││ to be aware of. Let's say we compare two values to see if they are equal with CMP EDX, EAX. The ││┌┘└┐││
││└┐┌┘││ "zero flag" will either be set to 1 if they are equal, or 0 if they are not. An instruction ││└┐┌┘││
││┌┘└┐││ like JZ 0x40154b that jumps to memory location 0x40154b if the comparison equaled to zero would ││┌┘└┐││
└┘└┐┌┘└┘ check this flag. Think of these as boolean variables. └┘└┐┌┘└┘
┌─┐││┌─┐ ┌─┐││┌─┐
└┐││││┌┘ ┌───────────────────────────────────────────────────────────────────────────┐ └┐││││┌┘
┌┘││││└┐ │ CMP EAX, EDX ; Changes zero flag (ZF) to 1 if EAX and EDX not equal │ ┌┘││││└┐
└┐││││┌┘ │ JNE 0x40154b ; If ZF set to 0, jump to 0x40154b │ └┐││││┌┘
┌┘└┘└┘└┐ └───────────────────────────────────────────────────────────────────────────┘ ┌┘└┘└┘└┐
│┌─┐┌─┐│ │┌─┐┌─┐│
││┌┘└┐││ There are a handful of flags to learn, but a few to get you started are: ││┌┘└┐││
││└┐┌┘││ ZF, "zero flag," set if result is 0. ││└┐┌┘││
││┌┘└┐││ SF, "sign flag," set if a result is negative. ││┌┘└┐││
└┘└┐┌┘└┘ OF, "overflow flag," set if a result was larger than a register can hold. Let us say AL and DL └┘└┐┌┘└┘
┌─┐││┌─┐ are set to 0xFF. ADD AL, DL would return a 9 bit character and therefore overflow the 8 bit ┌─┐││┌─┐
└┐││││┌┘ register. └┐││││┌┘
┌┘││││└┐ Addressing modes ┌┘││││└┐
└┐││││┌┘ We have a few ways to address things. └┐││││┌┘
┌┘└┘└┘└┐ Register addressing, aka using the direct register references. Example: EAX. ┌┘└┘└┘└┐
│┌─┐┌─┐│ Immediate addressing, where we give an immediate value. Example: 0x10 │┌─┐┌─┐│
││┌┘└┐││ Memory addressing, where we use a memory address. ││┌┘└┐││
││└┐┌┘││ Direct memory addressing is when we reference the effective address of a memory location ││└┐┌┘││
││┌┘└┐││ directly, eg "JMP 0x401438" jumps directly to memory address 0x401438. ││┌┘└┐││
└┘└┐┌┘└┘ Indirect memory addressing, eg "JMP [EAX]" jumps directly to the memory address stored └┘└┐┌┘└┘
┌─┐││┌─┐ in EAX. ┌─┐││┌─┐
└┐││││┌┘ Addressing examples: └┐││││┌┘
┌┘││││└┐ Register to register (eg, MOV EDX, EAX copies data in EAX to EDX). ┌┘││││└┐
└┐││││┌┘ Register to memory (eg, MOV [EDX], EAX copies data in EAX to memory address in EDX). └┐││││┌┘
┌┘└┘└┘└┐ Memory to register (eg, MOV EDX, [EAX] copies the data at memory address in EAX to EDX). ┌┘└┘└┘└┐
│┌─┐┌─┐│ Immediate to register (eg, MOV EDX, 0x05 copies hex value 5 to EDX). │┌─┐┌─┐│
││┌┘└┐││ Immediate to memory (eg, MOV [EDX], 0x05 copies hex value 5 to memory address in EDX). ││┌┘└┐││
││└┐┌┘││ Note there is no "to immediate." This is because you can store things at a memory location, or ││└┐┌┘││
││┌┘└┐││ within a register, but you cannot store things at decimal value "10" for example. Spoken as a ││┌┘└┐││
└┘└┐┌┘└┘ analogy, you can store apples in a bucket, you can store apples at a street address for a bucket, └┘└┐┌┘└┘
┌─┐││┌─┐ but you can't store apples in an apple. ┌─┐││┌─┐
└┐││││┌┘ └┐││││┌┘
┌┘││││└┐ The Call Stack ┌┘││││└┐
└┐││││┌┘ The stack is a last in, first out data structure. Think of this like a deck of cards. If we start └┐││││┌┘
┌┘└┘└┘└┐ stacking the cards, the card we draw will always be at the top of the stack. Whatever item we ┌┘└┘└┘└┐
│┌─┐┌─┐│ placed onto the stack last will be the next to be drawn. │┌─┐┌─┐│
││┌┘└┐││ Every function has a stack frame which allows each subroutine to behave in an independent ││┌┘└┐││
││└┐┌┘││ fashion. As a simple example, one common use of the stack would be function calls. We push our ││└┐┌┘││
││┌┘└┐││ parameters on the stack in reverse order; last in, first out, remember? ││┌┘└┐││
└┘└┐┌┘└┘ └┘└┐┌┘└┘
┌─┐││┌─┐ ┌─────────────────────────────────────────────────────────────────────────┐ ┌─┐││┌─┐
└┐││││┌┘ │ PUSH 0x22 ; Arg3 │ └┐││││┌┘
┌┘││││└┐ │ PUSH 0x4058b4 ; Arg2 │ ┌┘││││└┐
└┐││││┌┘ │ PUSH EAX ; Arg1 │ └┐││││┌┘
┌┘└┘└┘└┐ │ CALL DWORD [OpenServiceW] ; AKA OpenServiceW(EAX, 0x4058b4, 0x22) │ ┌┘└┘└┘└┐
│┌─┐┌─┐│ │ MOV ESI, EAX ; Stores result upon return │ │┌─┐┌─┐│
││┌┘└┐││ └─────────────────────────────────────────────────────────────────────────┘ ││┌┘└┐││
││└┐┌┘││ ││└┐┌┘││
││┌┘└┐││ Stack frames are generally going to have 4 different parts: Parameters, a return address (to ││┌┘└┐││
└┘└┐┌┘└┘ wherever your function was called from), your saved EBP, and your local variables (any variables └┘└┐┌┘└┘
┌─┐││┌─┐ you use within the function to manipulate data.) ┌─┐││┌─┐
└┐││││┌┘ Our ESP and EBP pointers mentioned before are crucial to stack operations. └┐││││┌┘
┌┘││││└┐ EBP always points to the base of the stack frame. ┌┘││││└┐
└┐││││┌┘ ESP always points to top of stack. └┐││││┌┘
┌┘└┘└┘└┐ ESP increases as values are popped off of the stack. ┌┘└┘└┘└┐
│┌─┐┌─┐│ ESP decreases as values are added to the stack. │┌─┐┌─┐│
││┌┘└┐││ Simple Overview: ││┌┘└┐││
││└┐┌┘││ 1. When a function is called, the current EBP is pushed onto the stack. ││└┐┌┘││
││┌┘└┐││ 2. EBP is then assigned the value of ESP; EBP is now the base pointer to access vars and params. ││┌┘└┐││
└┘└┐┌┘└┘ 3. As function runs, ESP will change as needed, EBP remains static. └┘└┐┌┘└┘
┌─┐││┌─┐ 4. As function terminates, ESP is set to EBP. ┌─┐││┌─┐
└┐││││┌┘ Stack pointer is now set to top of stack to equal the base pointer. This frees memory └┐││││┌┘
┌┘││││└┐ allocated onthe stack for local vars. ┌┘││││└┐
└┐││││┌┘ 5. EBP is now popped from the stack (remember, first in last out,) and EBP can be used again as └┐││││┌┘
┌┘└┘└┘└┐ base pointer. ┌┘└┘└┘└┐
│┌─┐┌─┐│ 6. In simpler terms, stack frames contain the EBP value of the functions caller (step 1.) │┌─┐┌─┐│
││┌┘└┐││ Now we can use EBP register as this function's base pointer. ││┌┘└┐││
││└┐┌┘││ ││└┐┌┘││
││┌┘└┐││ How it works graphically and more in depth: ││┌┘└┐││
└┘└┐┌┘└┘ ┌───────────────┐ └┘└┐┌┘└┘
┌─┐││┌─┐ │ source_code.c│ ┌─┐││┌─┐
└┐││││┌┘ ┌───────────────────────────────┐ └┐││││┌┘
┌┘││││└┐ │#include <stdio.h> │ ┌┘││││└┐
└┐││││┌┘ │ │ └┐││││┌┘
┌┘└┘└┘└┐ │int subtract( int a, int b){ │ ┌┘└┘└┘└┐
│┌─┐┌─┐│ │ int c; │ │┌─┐┌─┐│
││┌┘└┐││ │ c = a - b; │ ││┌┘└┐││
││└┐┌┘││ │ return c; │ ││└┐┌┘││
││┌┘└┐││ │} │ ││┌┘└┐││
└┘└┐┌┘└┘ │ │ └┘└┐┌┘└┘
┌─┐││┌─┐ │int main(){ │ ┌─┐││┌─┐
└┐││││┌┘ │ int a = 10; │ └┐││││┌┘
┌┘││││└┐ │ int b = 5; │ ┌┘││││└┐
└┐││││┌┘ │ subtract( a, b); //my start │ └┐││││┌┘
┌┘└┘└┘└┐ │ return 0; │ ┌┘└┘└┘└┐
│┌─┐┌─┐│ │} │ │┌─┐┌─┐│
││┌┘└┐││ └───────────────────────────────┘ ││┌┘└┐││
││└┐┌┘││ Let's use some simple code to show a function we might see in assembly. All we are going to show ││└┐┌┘││
││┌┘└┐││ is the subtract function being called and eventually returning a value. ││┌┘└┐││
└┘└┐┌┘└┘ └┘└┐┌┘└┘
┌─┐││┌─┐ ┌───────────────┐ ┌──────────┐ ┌─┐││┌─┐
└┐││││┌┘ │ assembly│ 1 First Steps mem│call stack│ └┐││││┌┘
┌┘││││└┐ ┌─────────────────────────────────────────────────────────────────────────┐ ┌──────────┐ ┌┘││││└┐
└┐││││┌┘ │ #function call │ 32│ 0x05│ └┐││││┌┘
┌┘└┘└┘└┐ │ PUSH 0x05 ; int B │ ├──────────┤ ┌┘└┘└┘└┐
│┌─┐┌─┐│ │ PUSH 0x10 ; int A │ 28│ 0x10│ │┌─┐┌─┐│
││┌┘└┐││ │ CALL subtract ; Pushes ret adr, jmps to function adr │ ├──────────┤ ││┌┘└┐││
││└┐┌┘││ │ ... │ 24│ ret adr│ ││└┐┌┘││
││┌┘└┐││ │ │ ├──────────┤ ││┌┘└┐││
└┘└┐┌┘└┘ │ #just after function call, set our EBP │ 20│ EBP├─ ESP └┘└┐┌┘└┘
┌─┐││┌─┐ │ PUSH EBP ; Set EBP │ ├──────────┤ ┌─┐││┌─┐
└┐││││┌┘ │ MOV EBP, ESP ; Move ESP to EBP │ 16│ │ └┐││││┌┘
┌┘││││└┐ │ ... │ ├──────────┤ ┌┘││││└┐
└┐││││┌┘ To set the scene in our view of assembly, lets talk about the stack on the right. I like to └┐││││┌┘
┌┘└┘└┘└┐ envision my stacks as top down, from high memory to low memory. This is also nice because the ┌┘└┘└┘└┐
│┌─┐┌─┐│ order in which we read code, top to bottom, is also the order they are shown on the stack │┌─┐┌─┐│
││┌┘└┐││ when we visualize them this way. Therefore, if we popped the "top" value off the stack, it would ││┌┘└┐││
││└┐┌┘││ be our EBP. On the left side of the stack are some simple memory addresses to assist with our ││└┐┌┘││
││┌┘└┐││ stack pointer math. These increment by 4 as our 32 bit addresses and registers are composed of ││┌┘└┐││
└┘└┐┌┘└┘ 4 bytes. In terms of the code itself, we see that our C function call does a couple things. The └┘└┐┌┘└┘
┌─┐││┌─┐ two parameters are pushed to the stack in reverse order, and then we CALL subtract, which is a ┌─┐││┌─┐
└┐││││┌┘ symbol representing the memory address where the instructions for the "subtract" function reside. └┐││││┌┘
┌┘││││└┐ If this binary was stripped of its symbols, it would look something like "CALL sub_401099," with ┌┘││││└┐
└┐││││┌┘ 0x401099 being the memory address. Not only does CALL jump to this address however, it also └┐││││┌┘
┌┘└┘└┘└┐ pushes the return address (back to the function where this function was called) onto the stack. ┌┘└┘└┘└┐
│┌─┐┌─┐│ In simpler terms, call not only sends us to the function code, it also leaves a way back so that │┌─┐┌─┐│
││┌┘└┐││ our function can return integer variable "c". Additional hint for when you are hunting for ││┌┘└┐││
││└┐┌┘││ functions, you know a function has just been called when you see PUSH EBP followed by MOV EBP, ││└┐┌┘││
││┌┘└┐││ ESP. ││┌┘└┐││
└┘└┐┌┘└┘ └┘└┐┌┘└┘
┌─┐││┌─┐ ┌───────────────┐ ┌──────────┐ ┌─┐││┌─┐
└┐││││┌┘ │ assembly│ 2 Creating Local Variables mem│call stack│ └┐││││┌┘
┌┘││││└┐ ┌─────────────────────────────────────────────────────────────────────────┐ ┌──────────┐ ┌┘││││└┐
└┐││││┌┘ │ #function call │ 32│ 0x05│ └┐││││┌┘
┌┘└┘└┘└┐ │ PUSH 0x05 │ ├──────────┤ ┌┘└┘└┘└┐
│┌─┐┌─┐│ │ PUSH 0x10 │ 28│ 0x10│ │┌─┐┌─┐│
││┌┘└┐││ │ CALL subtract │ ├──────────┤ ││┌┘└┐││
││└┐┌┘││ │ ... │ 24│ ret adr│ ││└┐┌┘││
││┌┘└┐││ │ │ ├──────────┤ ││┌┘└┐││
└┘└┐┌┘└┘ │ #just after function call, set our EBP │ 20│ EBP│ └┘└┐┌┘└┘
┌─┐││┌─┐ │ PUSH EBP │ ├──────────┤ ┌─┐││┌─┐
└┐││││┌┘ │ MOV EBP, ESP │ 16│ EDX│ └┐││││┌┘
┌┘││││└┐ │ PUSH EDX ; Add EDX for int B │ ├──────────┤ ┌┘││││└┐
└┐││││┌┘ │ PUSH ECX ; Add ECX for int A │ 12│ ECX│ └┐││││┌┘
┌┘└┘└┘└┐ │ MOV EDX, [EBP+0xC] ; Load int B into EDX │ ├──────────┤ ┌┘└┘└┘└┐
│┌─┐┌─┐│ │ MOV ECX, [EBP+0x8] ; Load int A into ECX │ 8│ C├─ ESP │┌─┐┌─┐│
││┌┘└┐││ │ SUB ESP, 0x4 ; Extend ESP 4 bytes for int C │ ├──────────┤ ││┌┘└┐││
││└┐┌┘││ │ ... │ 4│ │ ││└┐┌┘││
││┌┘└┐││ We now have our local variables accounted for on the stack. Pushing values not only adds them to ││┌┘└┐││
└┘└┐┌┘└┘ the stack, it also advances ESP by 4 bytes each time. We have done this 2 times as well as moved └┘└┐┌┘└┘
┌─┐││┌─┐ ESP ourselves, so ESP is now at our memory location 8. Notice also how we are moving the values ┌─┐││┌─┐
└┐││││┌┘ from the parameters into our stack registers. EBP is our anchor and gives us a static reference └┐││││┌┘
┌┘││││└┐ point for calculating the memory required to access the parameters. EG, [EBP + 0xC] is the memory ┌┘││││└┐
└┐││││┌┘ address of our parameter int b. Since EBP will not move we know int b will be 12 bytes higher in └┐││││┌┘
┌┘└┘└┘└┐ memory. To show you another way we can make space, we also subtracted 4 bytes from ESP. Do note, ┌┘└┘└┘└┐
│┌─┐┌─┐│ this is only one way of expressing this in assembly. Since we typically are not in control of the │┌─┐┌─┐│
││┌┘└┐││ compiling process for whatever we are reversing, we may not know what compiler is being used, nor ││┌┘└┐││
││└┐┌┘││ how it behaves, and most compilers will probably behave differently from this intentionally ││└┐┌┘││
││┌┘└┐││ simplified example. ││┌┘└┐││
└┘└┐┌┘└┘ └┘└┐┌┘└┘
┌─┐││┌─┐ ┌───────────────┐ ┌──────────┐ ┌─┐││┌─┐
└┐││││┌┘ │ assembly│ 3 Performing the Function mem│call stack│ └┐││││┌┘
┌┘││││└┐ ┌─────────────────────────────────────────────────────────────────────────┐ ┌──────────┐ ┌┘││││└┐
└┐││││┌┘ │ #function call │ 32│ 0x05│ └┐││││┌┘
┌┘└┘└┘└┐ │ PUSH 0x05 │ ├──────────┤ ┌┘└┘└┘└┐
│┌─┐┌─┐│ │ PUSH 0x10 │ 28│ 0x10│ │┌─┐┌─┐│
││┌┘└┐││ │ CALL subtract │ ├──────────┤ ││┌┘└┐││
││└┐┌┘││ │ ... │ 24│ ret adr│ ││└┐┌┘││
││┌┘└┐││ │ │ ├──────────┤ ││┌┘└┐││
└┘└┐┌┘└┘ │ #just after function call, set our EBP │ 20│ EBP│ └┘└┐┌┘└┘
┌─┐││┌─┐ │ PUSH EBP │ ├──────────┤ ┌─┐││┌─┐
└┐││││┌┘ │ MOV EBP, ESP │ 16│ EDX│ └┐││││┌┘
┌┘││││└┐ │ PUSH EDX │ ├──────────┤ ┌┘││││└┐
└┐││││┌┘ │ PUSH ECX │ 12│ ECX│ └┐││││┌┘
┌┘└┘└┘└┐ │ MOV EDX, [EBP+0xC] │ ├──────────┤ ┌┘└┘└┘└┐
│┌─┐┌─┐│ │ MOV ECX, [EBP+0x8] │ 8│ C├─ ESP │┌─┐┌─┐│
││┌┘└┐││ │ SUB ESP, 0x4 │ ├──────────┤ ││┌┘└┐││
││└┐┌┘││ │ SUB ECX, EDX ; A - B, stores value in ECX │ 4│ │ ││└┐┌┘││
││┌┘└┐││ │ MOV [EBP-0xC], ECX ; Move ECX value to int C location │ ├──────────┤ ││┌┘└┐││
└┘└┐┌┘└┘ │ MOV EAX, [EBP-0xC] ; Move int C to EAX for return │ 0│ │ └┘└┐┌┘└┘
┌─┐││┌─┐ │ ... │ └──────────┘ ┌─┐││┌─┐
└┐││││┌┘ Here you can see our function being performed. Since we did not assign a register to C, merely a └┐││││┌┘
┌┘││││└┐ memory location, we must reference that location when we move our output. Following that, move ┌┘││││└┐
└┐││││┌┘ the contents to EAX for return to the main function which originally called subtract( a, b). Nice └┐││││┌┘
┌┘└┘└┘└┐ and simple, right? ┌┘└┘└┘└┐
│┌─┐┌─┐│ │┌─┐┌─┐│
││┌┘└┐││ ┌───────────────┐ ┌──────────┐ ││┌┘└┐││
││└┐┌┘││ │ assembly│ 4 Cleaning Up and Returning mem│call stack│ ││└┐┌┘││
││┌┘└┐││ ┌─────────────────────────────────────────────────────────────────────────┐ ┌──────────┐ ││┌┘└┐││
└┘└┐┌┘└┘ │ #function call │ 32│ 0x05│ └┘└┐┌┘└┘
┌─┐││┌─┐ │ PUSH 0x05 │ ├──────────┤ ┌─┐││┌─┐
└┐││││┌┘ │ PUSH 0x10 │ 28│ 0x10│ └┐││││┌┘
┌┘││││└┐ │ CALL subtract │ ├──────────┤ ┌┘││││└┐
└┐││││┌┘ │ ... │ 24│ ret adr├─ ESP └┐││││┌┘
┌┘└┘└┘└┐ │ │ ├──────────┤ ┌┘└┘└┘└┐
│┌─┐┌─┐│ │ #just after function call, set our EBP │ 20│ │ │┌─┐┌─┐│
││┌┘└┐││ │ PUSH EBP │ ├──────────┤ ││┌┘└┐││
││└┐┌┘││ │ MOV EBP, ESP │ 16│ │ ││└┐┌┘││
││┌┘└┐││ │ PUSH EDX │ ├──────────┤ ││┌┘└┐││
└┘└┐┌┘└┘ │ PUSH ECX │ 12│ │ └┘└┐┌┘└┘
┌─┐││┌─┐ │ MOV EDX, [EBP+0xC] │ ├──────────┤ ┌─┐││┌─┐
└┐││││┌┘ │ MOV ECX, [EBP+0x8] │ 8│ │ └┐││││┌┘
┌┘││││└┐ │ SUB ESP, 0x4 │ ├──────────┤ ┌┘││││└┐
└┐││││┌┘ │ SUB ECX, EDX │ 4│ │ └┐││││┌┘
┌┘└┘└┘└┐ │ MOV [EBP-0xC], ECX │ ├──────────┤ ┌┘└┘└┘└┐
│┌─┐┌─┐│ │ MOV EAX, [EBP-0xC] │ 0│ │ │┌─┐┌─┐│
││┌┘└┐││ │ ADD ESP, 0x4 ; Removing C │ └──────────┘ ││┌┘└┐││
││└┐┌┘││ │ POP ECX ; Removing A │ ││└┐┌┘││
││┌┘└┐││ │ POP EDX ; Removing B │ ││┌┘└┐││
└┘└┐┌┘└┘ │ LEAVE ; Pops EBP │ └┘└┐┌┘└┘
┌─┐││┌─┐ │ RET ; Ret to adr ESP is pointing to (main) │ ┌─┐││┌─┐
└┐││││┌┘ └─────────────────────────────────────────────────────────────────────────┘ └┐││││┌┘
┌┘││││└┐ So now we have our return value in EAX and wish to return to the function which called this one. ┌┘││││└┐
└┐││││┌┘ That address is still in our stack (ret adr). To remove our memory allocated for int C on the └┐││││┌┘
┌┘└┘└┘└┐ stack, we can simply add a 4 bytes to the value of ESP, moving it up a slot. We can then pop ECX ┌┘└┘└┘└┐
│┌─┐┌─┐│ and EDX, moving us an additional 8 bytes up (back on EBP). LEAVE pops EBP for us which drops ESP │┌─┐┌─┐│
││┌┘└┐││ onto the return address, and return will jump us to the return address which ESP is currently ││┌┘└┐││
││└┐┌┘││ pointing to. Do note, rather than pop the values and decrement manually we could have used LEAVE ││└┐┌┘││
││┌┘└┐││ which firstly shifts ESP to EBP before popping EBP and decrementing ESP by 4 bytes. Just like in ││┌┘└┐││
└┘└┐┌┘└┘ higher level programming, there are many roads to the same destination. Upon our return, to └┘└┐┌┘└┘
┌─┐││┌─┐ access the return value, we would simply reference EAX. ┌─┐││┌─┐
└┐││││┌┘ └┐││││┌┘
┌┘││││└┐ Part 2: Portable Executable Files ┌┘││││└┐
└┐││││┌┘ └┐││││┌┘
┌┘└┘└┘└┐ ┌┘└┘└┘└┐
└──────┘ └──────┘