Practical Malware Analysis - Chapter 4 Lab Write-up

10 minute read

PMALab

Chapter 4. A Crash Course in x86 Disassembly

There are no questions in this chapter, but instead covers the following:

Levels of abstraction

  • Hardware
  • Microcode (firmware)
  • Machine Code
  • Low-level languages
  • High-level languages
  • Interpreted languages

Main Memory

  • Data (DS - Data Segment
  • Code (CS - Code Segment)
  • Heap (dynamic memory)
  • Stack (SS - Stack Segment)
  • Extra (ES - Extra Segment)

Instructions

  • made of a mneumonic and 0 or more operands
Mnemonic Destination Operand Source Operand
mov ecx 0x42

Opcodes and Endianness

  • x86 uses little-endian format
  • network data uses big-endian format

Operands

  • Immediate operands (fixed values e.g. 0x42)
  • Register operands (e.g. ecx)
  • Memory address operands (e.g. eax)

Registers

x86 processors:

  • General registers used by the CPU at execution
  • Segment registers used to track sections of memory
  • Status flags used to make decisions
  • Instruction pointers used for process flow

x86 EAX Register Breakdown:

  • EAX contains the value 0xA9DC81F5 (32bits)
  • AX contains 81F5 (16bits)
  • AH contains 81 (8 bits)
  • AL contains F5 (8 bits)

EFLAGS Register:

  • ZF: Zero flag, set to 1 when the output of an operation is equal to zero.
  • CF: Carry flag, set to 1 when the output of an operation is too large or too small for the destination operand.
  • SF: Sign flag, set to 1 when the output of an operation is negative, or when the most significant bit is set after an arithmetic operation.
  • TF: Trap flag, when set to one the x86 processor will only execute 1 instruction at a time.

EIP Instruction Pointer:

  • Contains memory address of next instruction to be executed

Note: The following sections have been added to support PMA material and understanding of 8-bit / 16-bit / 32-bit / 64-bit general-purpose registers and other common registers.

8-bit Register Usage

Register Common Name Purpose
AH Accumulator ‘High’ Register Used for arithmetic, operational, or result temporary storage.
AL Accumulator ‘Low’ Register Used for arithmetic, operational, or result temporary storage.
BH Base ‘High’ Register Used for pointing to a base (index) address within the data segment.
BL Base ‘Low’ Register Used for pointing to a base (index) address within the data segment.
CH Counter ‘High’ Register Used for operations that require looping, rotation/shifting, or string operations.
CL Counter ‘Low’ Register Used for operations that require looping, rotation/shifting, or string operations.
DH Data ‘High’ Register Used for Input/Output pointers and operations, similarly to AH.
DL Data ‘Low’ Register Used for Input/Output pointers and operations, similarly to AL.

16-bit Register Usage

Register Common Name Purpose
AX Accumulator Register Used for arithmetic, operational, or result temporary storage.
BX Base Register Used for pointing to a base (index) address within the data segment.
CX Counter Register Used for operations that require looping, rotation/shifting, or string operations.
DX Data Register Used for Input/Output pointers and operations, similarly to EAX.
SI Source Index Register Used for storing pointer to a location in Data Segment Register, or source of string or stream operations.
DI Destination Index Register Used for storing pointer to a location in Extra Segment Register, or destination of string or stream operations.
IP Instruction Pointer Register Used for keeping track of instructions within the Code Segment. Stores offset address of next instruction.
SP Stack Pointer Register Used for accessing values on the program Stack Segment. Stores offset of value on top of stack.
BP Base Pointer Register Used for accessing the base of the Stack Segment. Usually used in conjunction with an offset to get location of variables on stack.

32-bit Register Usage

Register Common Name Purpose
EAX Accumulator Register Used for arithmetic, operational, or result temporary storage.
EBX Base Register Used for pointing to a base (index) address within the data segment.
ECX Counter Register Used for operations that require looping, rotation/shifting, or string operations.
EDX Data Register Used for Input/Output pointers and operations, similarly to EAX.
ESI Source Index Register Used for storing pointer to a location in Data Segment Register, or source of string or stream operations.
EDI Destination Index Register Used for storing pointer to a location in Extra Segment Register, or destination of string or stream operations.
EIP Instruction Pointer Register Used for keeping track of instructions within the Code Segment. Stores offset address of next instruction.
ESP Stack Pointer Register Used for accessing values on the program Stack Segment. Stores offset of value on top of stack.
EBP Base Pointer Register Used for accessing the base of the Stack Segment. Usually used in conjunction with an offset to get location of variables on stack.

64-bit Register Usage

Register Common Name Purpose
RAX Accumulator Register Used for arithmetic, operational, or result temporary storage.
RBX Base Register Used for pointing to a base (index) address within the data segment.
RCX Counter Register Used for operations that require looping, rotation/shifting, or string operations.
RDX Data Register Used for Input/Output pointers and operations, similarly to EAX.
RSI Source Index Register Used for storing pointer to a location in Data Segment Register, or source of string or stream operations.
RDI Destination Index Register Used for storing pointer to a location in Extra Segment Register, or destination of string or stream operations.
RIP Instruction Pointer Register Used for keeping track of instructions within the Code Segment. Stores offset address of next instruction.
RSP Stack Pointer Register Used for accessing values on the program Stack Segment. Stores offset of value on top of stack.
RBP Base Pointer Register Used for accessing the base of the Stack Segment. Usually used in conjunction with an offset to get location of variables on stack.

Notes:

  • In 64-bit mode R8 to R15 and XMM8 to XMM15 are new QWORD registers available.
  • 32-bit DWORD registers can still be accessed with 32-bit variants of the above extra registers available through R8D to R15D.
  • 16-bit WORD registers can still be accessed with 32-bit variants of the above extra registers available through R8W to R15W.
  • 8-bit BYTE registers can still be accessed with 8-bit variants of the above extra registers available through R8L to R15L.

Simple Instructions

  • Mov (move data around, e.g. into registers or RAM)
    • mov destination, source
    • Operands surrounded by [] are memory references to data
    • mov eax, ebx (copies ebx into eax)
    • mov eax, [0x4037C4] (copies 4 bytes at memory location 0x4037C4 into eax)
    • mov eax, [ebx+esi4] (copies 4 bytes at memory location of the result from the equation ebx+esi4 into the EAX register) -lea (load effective address, similar to mov)
    • put memory address into a location
    • lea eax, [ebx+8]
    • lea eax, [ebx+8] = mov eax, ebx+8 (except the mov command is invalid)
    • useful when calculating values

Arithmetic:

  • sub (subtract value from destination)
    • sub destination, value
  • add (add value to destination)
    • add destination, value
  • inc (increase register by 1)
    • inc register
  • dec (decrease register by 1)
    • dec register
  • mul (multiply EAX by a value and store result in EDX:EAX) - imul is signed version
    • mul value
  • div (divides EDX:EAX by a value and store result in EAX with remainder in EDX) - idiv is signed version
    • div value

x86 Logical Operators:

  • OR
  • AND
  • XOR
  • shr (shift bits right)
    • shr destination count
  • shl (shift bits left)
    • shl destination count
  • ror
    • Similar to sh except bits are rotated to the other end instead of falling off
  • rol
  • nop
    • 0x90, does nothing and goes to next instruction.

Shifting often used instead of multiplication as its faster and doesn’t require registers.

Generally functions containing only xor, or, shl, shr, ror, or rol randomly is a compression or encryption/encoding function.

The Stack

  • Memory for functions, local variables and flow control is stored in stack.
  • Last in First Out (LIFO) Structure.
  • ESP = Stack Pointer (Changes as items are pushed on and off)
  • EBP = Base Pointer (Stays constant within function)
  • Stack Instructions.
    • Push
    • Pop
    • Call
    • Leave
    • Enter
    • Ret

Function Calls:

  • Code to perform a specific task.
  • Stack is used the same throughout any given binary.
  • Most common convention is cdecl.
    • Arguments pushed to stack.
    • Call memory_location used to store current instruction onto stack and run function.
    • Function prologue creates space on stack for local variables, and EBP.
    • Function executes.
    • Function epilogue restores stack and EBP, ESP frees local variables. Leave instruction can be used for this.
    • Function calls the ret instruction which pops return address off stack and onto EIP.
    • Stack is adjusted to remove arguments.

Stack Layout:

  • Stack grows upward.
  • Higher memory addresses used first.
  • A new call = A new stack frame.
  • x86 provides instructions for popping and pushing.
    • pusha (Pushes 16-bit registers on stack in this order: AX, CX, DX, BX, SP, BP, SI, DI)
    • pushad (Pushes 32-bit registers on the stack in this order: EAX, ECX, EDX, EBX, ESP, EBP, ESI, EDI)
    • popa
    • popad
  • pusha/pushad are typically seen in shellcode and often indicate hand coded shellcode/assembly.

Conditionals

  • test (identical to AND, except operands are not modified)
  • cmp (identical to SUB, except operands are not modified)
    • ZF and CF may be modified as a result of cmp.

Branching

  • Code executed depending on flow of the program.
  • Most popular way is with jump instructions.
  • jmp
    • unconditional jump
  • More than 30 types of conditional jumps exist, but only subset commonly seen.
    • jz loc (Jump if ZF. Jump to location if ZF = 1)
    • jnz loc (Jump if Not ZF. Jump to location if ZF = 0)
    • je loc (Jump if Equal. Generally after cmp, jumps if destination operand = source operand)
    • jne loc (Jump if Not Equal. Generally after cmp, jumps if destination operand != source operand)
    • jg loc (Jump if Greater. Signed jump after cmp, jumps if destination operation > source operand)
    • jge loc (Jump if Greater or Equal. Signed jump after cmp, jumps if destination operand >= source operand)
    • ja loc (Jump if A. Unsigned jump, same as jg)
    • jae loc (Jump if A or Equal. Unsigned jump, same as jge)
    • jl loc (Jump if Less. Signed jump after cmp, jumps if destination operand is < source operand)
    • jle loc (Jump if Less or Equal. Signed jump after a cmp, jumps if destination operand is <= source operand)
    • jb loc (Jump if B. Unsigned jump, same as jl)
    • jbe loc (Jump if B or Equal. Unsigned jump, same as jle)
    • jo loc (Jump if OF. Jumps if previous instruction set the Overflow Flag)
    • js loc (Jump if SF. Jumps if the Sign Flag is set)
    • jecxz loc (Jump if not ECX. Jumps if ECX = 0)

Rep Instructions

  • Used for manipulating data buffers.
  • Usually array of bytes (b) or single (w) or double (d) words.
  • Most common instructions:
    • movs[b-w-d]
    • cmps[b-w-d]
    • stos[b-w-d]
    • scas[b-w-d]
  • ESI/EDI used in these operations.
  • ESI = Source, EDI = Destination, ECX = Counting variable.
  • rep will continue until ECX = 0.
  • repe/repz/repne/repnz will continue until ECX = 0 or ZF = 1 or 0.
Instruction Description
rep Repeat until ECX = 0
repe, repz Repeat until ECX = 0 or ZF = 0
repne, repnz Repeat until ECX = 0 or ZF = 1

movsb is used to move bytes from 1 location to the next. Rep movsb is equivalent of memcopy in C. Movsb takes byte at ESI and stores it at EDI and increments/decrements ESI and EDI registers based on DF (Direction Flag). DF @ 0 = incremented, else decremented.

cmpsb is used to compare 2 sequences of bytes. It subtracts value at EDI from ESI and updates the flags. Cmpsb takes byte at ESI, compares to value at EDI and increments ESI/EDI registers by 1. Bundling this with rep is equivalent to memcmp in C.

scasb is used to search for 1 value in a sequence of bytes as defined within AL register. Compares ESI to AL. If found ESI stores location of those bytes.

stosb is used to store values in EDI specified location. Same as scasb, but byte not searched instead it is placed into EDI location. When bundled with rep, this is equivalent to memset in C.

Instruction Description
repe cmpsb Compare 2 buffers. EDI and ESI must contain buffer locations, and ECX = buffer length. Will continue until ECX = 0 or buffers aren’t equal.
rep stosb Initialize all bytes of a buffer to a value. EDI = buffer location, and AL = Initialization Value. Often seen with xor eax, eax.
rep movsb Copy a buffer of bytes. ESI = Source, EDI = Destination, ECX = Length to copy. Byte copy with continue until ECX = 0.
repne scasb Search a data buffer for a byte. EDI = Address of buffer. AL = byte looking for. ECX = Buffer length. Comparison will continue until ECX = 0 or byte is found.

C Main Methods and Offsets

Malware is often written in C. A C program often has 2 arguments for the main method with argc and argv.

argc is an integer which contains number of arguments on command line. argv is a pointer to an array that contains command line arguments.

There’s also a couple of useful images at this point which show a simple program in C and what the main method looks like in Assembly, in addition to useful further reading.

This concludes chapter 4, proceed to the next chapter.