EpochZero Learn
EpochZero LearnMulti-Domain Tech Learning Hub
Courses
LeaderboardAbout
Dashboard
EpochZero
EpochZero Learn
Multi-Domain Tech Learning Hub

Structured learning for Reverse Engineering, Cloud Security, Cryptography, and Web Development. Articles, videos, tests, and peer discussion.

Learn

  • Learning Path
  • All Articles
  • Video Lessons
  • Podcast
  • eBooks & PDFs
  • Question Banks
  • Cheatsheets
  • MCQ Banks

Tests & Forum

  • All Tests
  • REMA Tests
  • Cloud Tests
  • Forum
  • REMA Forum
  • Cloud Forum
  • Crypto Forum
  • Web Dev Forum

Campus

  • REMA Club
  • Full Stack Dev Club
  • Extension Activity
  • Events
  • CTF Competitions
  • Workshops
  • Industrial Visits

Platform

  • Dashboard
  • Leaderboard
  • About
  • Verify Certificate

© 2026 EpochZero Learn. Educational content for learning purposes.

Course Instructor: Ashish Revar

All articles
Educationcfgbasic-blocksobfuscation

Control Flow Analysis

Basic blocks, control flow graphs, CFG construction in IDA Pro and Ghidra, and the obfuscation techniques malware uses to corrupt disassembler output — with practical bypass approaches for each.

Ashish Revar12 May 202615 min read2 views

Sign in to mark this article as read and track your progress.

Related to this topic

Continue learning

Listen

The Malicious Process Lifecycle: From Execution to Evasion

How malware executes, establishes persistence, injects into legitimate processes, and evades detection inside modern operating systems.

Reference material

eBook
REMA eBook 2026
v1.0
Open resource
Cheatsheet
REMA Cheatsheet 2026
v1.0
Open resource
MCQ Bank
REMA MCQ Bank 2026
v1.0
Open resource
Question Bank
REMA Question Bank 2026
v1.0
Open resource

External references

Ghidra Function Graph — Official Documentation

Ghidra cheat sheet including Function Graph navigation shortcuts. Use alongside the CFG analysis workflow.

reference
CrackMes.one — CFG Analysis Practice

Practice CFG analysis on beginner CrackMes. Start with difficulty 1-2. Map the full CFG before attempting a solution.

tool
REMA eBook 2026 — Chapter 3, Control Flow Analysis

Full CFG diagrams and obfuscation technique explanations in the REMA eBook Chapter 3.

ebook
More articlesTest your knowledge

What is a Control Flow Graph?

A Control Flow Graph (CFG) represents all possible execution paths through a function. Nodes are basic blocks — straight-line sequences of instructions. Edges connect blocks through their jump targets.

The CFG is the primary visual aid for understanding program logic at the assembly level. IDA Pro (Spacebar) and Ghidra (Function Graph) both render CFGs interactively.

Basic Blocks

A basic block is a maximal sequence of instructions where:

  • Execution enters only at the first instruction
  • Execution exits only at the last instruction (via a jump, conditional branch, call, return, or program termination)
; This is ONE basic block — straight-line execution, one exit
    mov  eax, [ebp+arg_0]
    cmp  eax, 0
    jz   Block_3           ; ← exit — ends the block

Basic blocks are terminated by:

  • Conditional jumps (JZ, JNZ, JG, etc.)
  • Unconditional jumps (JMP)
  • RET
  • CALL (in some analysis frameworks)

A CALL instruction by itself does not end a basic block in most analyses — the function call returns and execution continues in the same block.

Reading a CFG in IDA Pro

In IDA Pro graph view:

Edge ColourMeaning
GreenConditional jump taken (true path)
RedConditional jump not taken (false path)
BlueUnconditional jump

Back-edges — edges pointing upward (from a lower block to a higher one) — always indicate loops.

Practical workflow:

1. Press Spacebar to enter graph view
2. Scan for back-edges (upward blue arrows) → these are loops
3. Follow green/red edges from CMP+Jcc pairs → reconstruct if/else
4. Large fan-out from one block → likely switch/jump table
5. Add comments as you decode each block's purpose
6. Rename the function when intent is clear

CFG Obfuscation

Malware authors use four techniques to corrupt CFG output:

1. Opaque Predicates

A conditional jump whose outcome is deterministic at runtime but cannot be proved by static analysis. The disassembler creates edges to both targets — one of which contains garbage bytes.

    mov  eax, 5
    imul eax, eax      ; eax = 25
    and  eax, 1        ; 25 is odd → eax = 1 always
    jz   fake_target   ; ZF=0 always → NEVER taken
    ; real code here
fake_target:
    db   0xFF, 0x15    ; garbage — never decoded

Bypass: In IDA: right-click garbage region → Undefine, then mark as data (D key). Confirm with debugger that the jump is never taken.

2. Overlapping Instructions

x86 instructions have variable length. A JMP into the middle of a multi-byte instruction decodes as a completely different instruction.

addr_00:  EB 01       ; JMP addr_02 (skip 1 byte)
addr_01:  E8          ; this byte is skipped by the jump
addr_02:  B8 01000000 ; MOV EAX, 1  (what CPU actually executes)
; Disassembler decoding linearly sees:
;   addr_01: E8 B8 01 00 00  → CALL some_address (WRONG)

Bypass: Use recursive descent disassembly (IDA/Ghidra default), or trace in debugger where CPU resolves correct instruction boundaries.

3. Indirect Jumps

JMP EAX or CALL [EBX+0x10] with runtime-computed targets defeat static CFG construction entirely — the destination is unknown without execution.

Bypass: Set a breakpoint on the indirect jump in x64dbg. When it fires, read the target register. Manually add a cross-reference in IDA with Ctrl+Alt+K (add code cross-reference).

4. Exception-Based Control Flow (SEH Abuse)

Malware installs a Structured Exception Handler, then deliberately triggers an exception. The OS dispatches execution to the handler — a hidden code path invisible to linear disassembly.

    push  handler_address   ; install SEH
    push  fs:[0]
    mov   fs:[0], esp
    xor   eax, eax
    div   eax               ; divide by zero → triggers exception
    ; disassembler thinks this continues linearly
    ; CPU actually goes to handler_address

Bypass: In x64dbg: View → SEH Chain to find all handler addresses. Set breakpoints on handlers before running. When exception fires, debugger pauses at the hidden code path.

Practical CFG Analysis Workflow

1. Open function in IDA/Ghidra graph view
2. Identify all basic blocks (count nodes)
3. Trace all back-edges → map loops
4. Trace all conditional edges → map if/else/switch
5. Identify any indirect jumps → note for dynamic confirmation
6. Check for opaque predicates → verify with debugger
7. Annotate each block with its purpose in plain English
8. Rename function to reflect discovered behaviour

Annotating and renaming is not optional — it is the primary deliverable of code analysis. Comments and renamed functions become the analysis report.