How to Quickly Solve Disassembly Problems
For DCU’s Secure Programming course, the disassembly problems have a certain pattern. Using a fixed approach to solving them can help achieve quick results.
Prerequisite Skills
- Familiarity with assembly commands Assembly Instructions
- Understanding
%
and$
for registers and immediate values $ and % Registers and Immediate Values - Knowledge of direct and indirect addressing Direct and Indirect Addressing
- Familiarity with one example C Code to Assembly Example
Approach to Solving
- Identify the number of parameters
- Identify the number of local variables
- Recognize the loop body
- Analyze remaining code snippets
- Identify the return value
Identify the Number of Parameters
The position of ebp
is the saved frame pointer, and ebp+4
is the return address. Since the problems typically assume all parameters are of type int
or int*
, ebp+8
, ebp+c
, and ebp+10
correspond to the first, second, and third parameters, respectively.
By quickly scanning the code for occurrences of 0x__(%ebp)
and identifying the largest offset, the number of parameters can be determined as (offset - 4) // 4
.
For example:
1 | push %ebp <foo+0> |
Here, 0x10(%ebp)
exists, so the parameter count is (16 - 4) / 4 = 3
.
We can construct the framework of the code as:
1 | int foo(int a, int b, int c) { |
a
, b
, and c
correspond to ebp+8
, ebp+c
, and ebp+10
, respectively. Note that parameters are pushed onto the stack in reverse order, so the closer to ebp
, the earlier the parameter appears in the list.
For now, assume all are int
types. Adjust later if inconsistencies are found.
Identify the Number of Local Variables
The number of local variables is determined by the third line of the code: sub $0x4, %esp
. The amount subtracted corresponds to the length of the allocated local variables.
In this example, sub $0x4, %esp
indicates 4 bytes, so there is one local variable. Assume it is an int
and name it i
.
The code expands to:
1 | int foo(int a, int b, int c) { |
Recognize the Loop Body
Loops are typically while
or for
loops. To identify:
Judgment Entry:
- Look for a comparison instruction (e.g.,
cmp
) followed by a jump instruction (e.g.,jge
orjle
). - These indicate the start of a condition check.
- Look for a comparison instruction (e.g.,
Loop Body:
- Unconditional
jmp
instructions signify loops. The jump target is the beginning of the condition check.
- Unconditional
Condition:
- The judgment condition combines the comparison and preceding instructions into a complete condition.
Example:
1 | push %ebp <foo+0> |
Judgment Entry:
The combination of cmp
and jge
indicates a judgment entry.
Loop:
The jmp
command jumps to <foo+12>
, signifying the loop condition check.
Condition:
mov -0x4(%ebp), %eax
: Assigns the value ofi
toeax
.cmp 0x10(%ebp), %eax
: Compareseax
(value ofi
) withc
.
This calculates i - c
and checks the condition with jge
. In assembly, conditions are reversed compared to C: jge
skips the loop if the condition is met. Thus, the C condition is i - c < 0
.
The code updates to:
1 | int foo(int a, int b, int c) { |
Analyze Remaining Code Snippets
Before the Loop:
1 | mov 0x8(%ebp), %eax <foo+6> |
These lines assign the value of a
to i
:
1 | i = a; |
Inside the Loop:
1 | mov 0xc(%ebp), %eax <foo+20> |
mov 0xc(%ebp), %eax
andincl (%eax)
increment the value at the address stored inb
:
1 | (*b)++; |
lea -0x4(%ebp), %eax
andincl (%eax)
incrementi
:
1 | i++; |
The updated code becomes:
1 | int foo(int a, int *b, int c) { |
Identify the Return Value
In x86 calling conventions, return values are stored in the eax
register.
1 | mov $0x0, %eax |
This indicates the function returns 0
:
1 | return 0; |
Final Code
1 | int foo(int a, int *b, int c) { |