Introduction

“If” statements in C programming are used to execute a block of statements if a certain condition is true. They allow programmers to control the execution of code and allow user inputs to direct the actions of the program, which adds flexibility to programs. “If” statements are used in security situations which require login credentials. If the credentials match the database for that user, the user is granted access. If the credentials don’t match, the user is rejected. 

When it comes to reverse-engineering, “if” statements are very commonly seen when analyzing binaries. The majority of malware programs make use of “if” statements to make decisions based on a condition. For instance, a malware author may use an “if” statement to stop executing the binary if a debugger is attached to it.

Being able to spot “if” statements in assembly is a must-have skill for a reverse engineer. In this article, we will discuss how “if” statements can be spotted when reversing a binary.

Identifying “if” statements

“If” statements are written in syntax that is either simple or nested. 

Simple -if statements

The Figure 1 code snippet shows the use of an “if” statement in C programming.

#include <stdio.h> 

void main()

{

int a = 30;

int b = 20;

if (a > b){

printf(“a is greater than b\n”);

}

else{

printf(“b is greater than a\n”);

}

}

Figure 1

 

When the code in Figure 1 is compiled and the binary is opened using a debugger (OllyDbg in this case), Figure 2 results.

PUSH EBP

MOV EBP,ESP

AND ESP,FFFFFFF0

SUB ESP,20

CALL if.004015F0

MOV DWORD PTR SS:[ESP+1C],1E

MOV DWORD PTR SS:[ESP+18],14         ; |

MOV EAX,DWORD PTR SS:[ESP+1C]        ; |

CMP EAX,DWORD PTR SS:[ESP+18]        ; |

JLE SHORT if.00401546                ; |

MOV DWORD PTR SS:[ESP],if.00404000   ; |ASCII “a is greater than b”

CALL <JMP.&msvcrt.puts>              ; \puts

JMP SHORT if.00401552

MOV DWORD PTR SS:[ESP],if.00404014   ; |ASCII “b is greater than a”

CALL <JMP.&msvcrt.puts>              ; \puts

NOP

LEAVE

RETN

Figure 2

 

The conditional jump JLE appears in Figure 2. It’s present to decide whether to take the branch or not. This conditional jump is preceded by a CMP instruction. The condition depends on the outcome of the CMP instruction.

The hex value 1E is pushed onto the stack, and then the hex value 14 is pushed onto the stack using the following instructions in Figure 3. 

 

MOV DWORD PTR SS:[ESP+1C],1E

MOV DWORD PTR SS:[ESP+18],14

Figure 3

 

The hex values 1E and 14 translate to decimal 30 and 20 respectively. These two values are pushed onto the stack and referenced by stack addresses because they are used with local variables. 

The hex value 1E is moved to the EAX register using the instruction in Figure 4. The value stored in the EAX register will be used as the first argument in CMP instruction.

 

MOV EAX,DWORD PTR SS:[ESP+1C] 

Figure 4 

 

The Figure 5 instruction is executed next, to compare the two values. 

 

CMP EAX,DWORD PTR SS:[ESP+18] 

Figure 5

 

The EAX holds the value 30 and [ESP+18] refers to decimal value 20. The Figure 5 instruction compares the first source operand (value stored in EAX) with the second source operand (value referenced by the address ESP+18) and sets the status flags in the EFLAGS register according to the results. Figure 6 is the status of all the flags after this instruction is executed.

Figure 6

 

Figure 7 is the next instruction, which checks the status of some of the flags and makes a decision. 

 

JLE SHORT if.00401546 

Figure 7

 

The JLE instruction usually checks for the flags O, S and Z. None of these flags are set; thus, the conditional jump will not be taken, and the next instruction will be executed. 

Figure 8 is an overview of the instructions shown so far.

Figure 8

 

“If” statements usually have conditional jumps, but not all conditional jumps are “if” statements.

 

Nested “if”

In addition to simple “if” statements which translate to conditional jumps when exploring disassembly, there are nested “if” statements. Figure 9 show a nested “if” statement where an “if” condition is inserted inside an “if” condition. 

#include <stdio.h>

void main()

{

int a = 30;

int b = 20;

if (a > b){

if(a==30){

printf(“a is greater than b and a equals to 30\n”);

}

}

else{

printf(“b is greater than a\n”);

}

}

Figure 9

 

Figure 10 shows disassembly when the Figure 9 code is compiled and the binary is opened using a debugger (OllyDbg in this case).

PUSH EBP

MOV EBP,ESP

AND ESP,FFFFFFF0

SUB ESP,20

CALL nested-i.004015F0

MOV DWORD PTR SS:[ESP+1C],1E             ; |

MOV DWORD PTR SS:[ESP+18],14             ; |

MOV EAX,DWORD PTR SS:[ESP+1C]            ; |

CMP EAX,DWORD PTR SS:[ESP+18]            ; |

JLE SHORT nested-i.0040154D              ; |

CMP DWORD PTR SS:[ESP+1C],1E             ; |

JNZ SHORT nested-i.00401559              ; |

MOV DWORD PTR SS:[ESP],nested-i.00404000 ; |ASCII “a is greater than b and a equals to 30”

CALL <JMP.&msvcrt.puts>                  ; \puts

JMP SHORT nested-i.00401559

MOV DWORD PTR SS:[ESP],nested-i.00404027 ; |ASCII “b is greater than a”

CALL <JMP.&msvcrt.puts>                  ; \puts

NOP

LEAVE

RETN

Figure 10

 

In Figure 10, there are two conditional jumps due to the additional “if” condition added inside the existing “if” statement. A conditional jump (JLE) is executed first but the jump is not taken in Figure 10. 

Inside the “if” block, there is another conditional jump using JNZ. This instruction is preceded by a CMP instruction. The CMP instruction is comparing the hex value 1E (decimal 30) with the value referenced with [ESP+18]. The hex value 18 translates to decimal 24. Figure 11 shows the stack before the CMP instruction is executed.

Figure 11

 

Both operands are holding the same values. Thus, the CMP instruction results in setting the zero flag, as shown in Figure 12.

Figure 12

 

Figure 13 is the instruction with the conditional jump after the CMP instruction is executed.

 

JNZ SHORT nested-i.00401559   

Figure 13

 

JNZ takes a jump if the zero flag is not set. As the zero flag is set, the jump will not be taken. The next instruction will be executed, printing, “a is greater than b and a equals to 30.”

Conclusion

Programs are written to run sequentially, from function call to function call, the same way every time. “If” statements offer more options. They allow programs to branch out and make conditional jumps beyond the sequence. 

“If” statements create a flow of conditional validation that are used to create security programs which can control user authentication. 

 

Sources

  1. x86 instruction set reference, c9x.me
  2. Assembly – Conditions, Tutorialspoint
  3. Michael Sikorski and Andrew Honig, “Practical Malware Analysis: The Hands-On Guide to Dissecting Malicious Software,” No Starch Press, February 2012

Be Safe

Section Guide

Srinivas

View more articles from Srinivas

As you grow in your cybersecurity career, Infosec Skills is the platform to ensure your skills are scaled to outsmart the latest cyber threats.

Section Guide

Srinivas

View more articles from Srinivas