┌──────┐┌─────────────────────────────────────────────────────────────────────────────────────────────────────────┐┌──────┐ │┌─┐┌─┐││ 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 ┌┘││││└┐ └┐││││┌┘ └┐││││┌┘ ┌┘└┘└┘└┐ ┌┘└┘└┘└┐ └──────┘ └──────┘