Malware analysis

Understanding if statements in C

Srinivas
November 5, 2019 by
Srinivas

“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 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 bn");

}

else{

printf("b is greater than an");

}

}

When the code is compiled and the binary is opened using a debugger (OllyDbg in this case), below 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

The conditional jump JLE appears above. 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. 

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

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 below. The value stored in the EAX register will be used as the first argument in CMP instruction.

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

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

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

The EAX holds the value 30 and [ESP+18] refers to decimal value 20. The above 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. Below is the status of all the flags after this instruction is executed.

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

JLE SHORT if.00401546 

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. 

Below is an overview of the instructions shown so far.

 

“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. Below shows 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 30n");

}

}

else{

printf("b is greater than an");

}

}

Below shows disassembly when the above 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

In the code above, 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. The figure below shows the stack before the CMP instruction is executed.

 

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

 

Below is the instruction with the conditional jump after the CMP instruction is executed.

JNZ SHORT nested-i.00401559   

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
Srinivas
Srinivas

Srinivas is an Information Security professional with 4 years of industry experience in Web, Mobile and Infrastructure Penetration Testing. He is currently a security researcher at Infosec Institute Inc. He holds Offensive Security Certified Professional(OSCP) Certification. He blogs atwww.androidpentesting.com. Email: srini0x00@gmail.com