GREM · Domain 2
Reversing Malicious Code
About 25% of the exam
Registers to know
- Accumulator
- arithmetic and function return value
- Counter
- loop counts and repeat operations
- Source and destination
- string and memory copy operations
- Stack pointer
- top of the current stack
- Base pointer
- anchor for the stack frame
- Instruction pointer
- address of the next instruction
- Segment register
- reaches the thread environment block
Instructions that matter
- Move
- copies a value, source unchanged
- Load effective address
- computes an address, not contents
- Exclusive or with self
- fast way to zero a register
- Compare and jump
- sets flags, then branches
- Conditional move
- branchless choice, harder to follow
- Repeat string store
- fills a memory region quickly
- Leave and return
- tears down the frame, returns
Learn what an instruction does to the flags, because the branch after it carries the logic
Calling conventions
- Caller cleanup passes arguments on the stack
- Callee cleanup is common in system libraries
- Register conventions pass the first arguments quickly
- Sixty four bit code favors registers first
- Object methods carry a hidden instance pointer
- Return values arrive in the accumulator
Stack frames
- Push arguments
- Call pushes return address
- Prologue saves base pointer
- Locals allocated
- Epilogue restores
- Return
- Positive offsets reach the arguments
- Negative offsets reach the locals
- Frame pointer omission complicates reading
- Saved return address is the exploit target
Recognizing constructs
Control flow
- Compare then conditional jump is a branch
- Backward jump to a compare is a loop
- Jump table implements a wide switch
- Repeated small blocks suggest inlining
Data handling
- Byte loop with exclusive or decrypts
- Rotate and add patterns hash names
- Sign extension precedes signed division
- Zero extension moves a byte into a word
Object patterns
- Table pointer as the first field
- Indirect call through a table entry
- Instance pointer passed in a register
- Constructor and destructor pairs
A loop that transforms every byte with a constant is almost always the string decryptor
Static tooling workflow
- Let auto analysis run before renaming
- Rename functions as meaning emerges
- Comment the decrypted strings inline
- Use the pseudocode view for shape
- Cross references reveal who calls what
- Export embedded objects for separate analysis
Indirect and hidden calls
- Call through a register hides the target
- Loader walks the module list manually
- Function names hashed instead of stored
- Import table rebuilt at runtime
- Direct system calls skip the usual library
- Trampolines and thunks add indirection
When names are hashed, find the hashing routine and precompute the table rather than guessing
Debugging basics
- Software breakpoint
- instruction replaced, easy to detect
- Hardware breakpoint
- processor registers, limited in number
- Memory breakpoint
- fires on access to a page
- Step into
- follow the call inside
- Step over
- run the call to completion
- Run to return
- finish the current function
- Patch and continue
- force a branch you want
Anti-debugging tricks
- Process block flags checked directly
- Heap flags set only under a debugger
- Timing gaps reveal single stepping
- Deliberate exception with a custom handler
- Callbacks running before the entry point
- Watchdog thread checking the main thread
- Breakpoint bytes detected by self checksum
Defeating the checks
Environment
- Remove obvious virtualization strings
- Give the machine realistic resources
- Populate documents and browsing history
- Simulate user movement during the run
Debugger side
- Hide the debugger from common checks
- Prefer hardware breakpoints where detection scans memory
- Patch the comparison instead of the value
- Suspend or neutralize the watchdog thread
Discipline
- Change one thing at a time
- Record every patch you applied
- Confirm behavior on a clean snapshot
- Keep the original sample untouched
Managed and scripted samples
- Intermediate language decompiles almost fully
- Obfuscators rename symbols, tools can restore
- Attach a debugger after compilation happens
- Compiled scripts unpack to readable source
- Interpreted loaders hide the real payload
- Language runtimes complicate packer heuristics
Working from memory
- Dump the process once the code decrypts
- Rebuild the import table after dumping
- Fix the header before reloading the dump
- Compare memory image against the disk file
- Scan memory with signature rules
- Recover configuration structures from the heap
Rapid recall
- Original entry point
- where the real code begins
- Thunk
- small jump wrapper to a function
- Vtable
- array of method pointers
- Prologue
- instructions that build the frame
- Cross reference
- every place a symbol is used
- Pseudocode
- decompiler approximation of the source
- Patch
- modified bytes that change behavior
Know the order
- Map the entry point
- Find the decryption routine
- Recover strings and names
- Follow the command handler
- Reach the payload logic
- Document the capability
Reverse toward a question, not toward the last instruction; most samples never need full coverage
Reference strip: reversing essentials
Registers and frames
- Accumulator, counter, index registers
- Stack and base pointers
- Arguments at positive offsets
- Locals at negative offsets
Pattern recognition
- Compare and branch pairs
- Loop with byte transformation
- Jump tables for switches
- Indirect calls through tables
Dynamic resolution
- Module list walking
- Hashed function names
- Runtime import rebuilding
- Direct system call stubs
Anti-debugging
- Process block and heap flags
- Timing and exception tricks
- Callbacks before the entry point
- Self checksum over the code
Analyst moves
- Rename and comment as you go
- Patch checks rather than fight them
- Dump and rebuild after decryption
- Verify findings on a clean snapshot
Quick exam traps
- Trap: Load effective address always reads memory
- Trap: An indirect call target can be resolved statically
- Trap: Software breakpoints are invisible to the sample
- Trap: Managed binaries cannot be meaningfully obfuscated
- Trap: The disassembly listing shows every instruction that will execute
- Trap: A memory dump runs correctly without import reconstruction
- Trap: Full coverage of the binary is required before reporting
cybercertprep.com · original revision sheet written from the public body of knowledge