Reverse Engineering

Reversing C++ programs

Consider a simple C++ class

class Rectangle{
   int width;
   int height;
}

When the program is compiled, the concept of class is lost and the variables width and height are loaded into memory whenever an object of the class is created. In the decompiled view, if a variable appears out of nowhere, it might be a member of some class. To fix this, create a new struct in IDA.

struct rect{
   int width;
   int height;
}

After creating the struct, change the types of suspicious variables. In decompiled view, the first argument of any member function of a class would be a pointer to an object of that class. Before creating the struct, reset the pointer type of the first argument.

Floating Point Arithmetic

Parallel computing

A type of computation in which many calculations or processes are carried out simultaneously. Large problems can be divided into smaller ones, which can then be solved at the same time.

SIMD(Single instruction, multiple data)

An organization that includes many processing units under the supervision of a common control unit. All processors receive the same instruction from the control unit but operate on different items of data.

SSE (SIMD Single-Precision Floating-Point Instructions ) Instructions

SSE instructions are an extension of the SIMD execution model introduced with the MMX technology. SSE instructions are divided into four groups:

  • SIMD single precision floating point instructions that operate on XMM registers.
  • MXSCR state management instructions
  • 64-bit SIMD integer instructions that operate on MMX registers
  • Instructions that provide cache control

XMM Registers

XMM registers are a completely separate register set, introduced with SSE. They are 128 bit wide, with instructions that can treat them as arrays of 64,32 (integer and floating point ), 16 or 8 bit(integer only values). 8 of them are present in 32 bit mode, and 16 of them are present in 64 bit mode. Virtually, all floating point math is done in SSE(and thus XMM registers), in 64 bit mode

YMM Registers

YMM and ZMM registers were introduced respectively with the AVX and AVX-512 instruction sets, and they expand the XMM registers. The whole 256 bit register is referred to as YMMx (x varies from 0 to 15).

ZMM Registers

AVX-512 expands the registers above to 512 bit, the whole register is ZMMx, the lower 256 bits are YMMx, and the lower 128 bits are XMMx.

Let’s write a simple C program

#include<stdio.h>

float var_one = 15.4;
float function1(){
   float x = 10.4;
   float y = 10.5;
   float  sum = x+y;
   return sum;
}

double function2(){
  double x = 11.1;
  double y = 10.2;
  double answer =x+y;
  return answer;
}
int main(){
        var_one += 1;
        function1();
        function2();
  return 0;
}

Let’s see the disassembly of function1 and function2

function1:

   0x0000000000001129 <+0>:     endbr64
   0x000000000000112d <+4>:     push   rbp
   0x000000000000112e <+5>:     mov    rbp,rsp
   0x0000000000001131 <+8>:     movss  xmm0,DWORD PTR [rip+0xecf]        # 0x2008
   0x0000000000001139 <+16>:    movss  DWORD PTR [rbp-0xc],xmm0
   0x000000000000113e <+21>:    movss  xmm0,DWORD PTR [rip+0xec6]        # 0x200c
   0x0000000000001146 <+29>:    movss  DWORD PTR [rbp-0x8],xmm0
   0x000000000000114b <+34>:    movss  xmm0,DWORD PTR [rbp-0xc]
   0x0000000000001150 <+39>:    addss  xmm0,DWORD PTR [rbp-0x8]
   0x0000000000001155 <+44>:    movss  DWORD PTR [rbp-0x4],xmm0
   0x000000000000115a <+49>:    movss  xmm0,DWORD PTR [rbp-0x4]
   0x000000000000115f <+54>:    pop    rbp
   0x0000000000001160 <+55>:    ret

function2:
   0x0000000000001161 <+0>:     endbr64
   0x0000000000001165 <+4>:     push   rbp
   0x0000000000001166 <+5>:     mov    rbp,rsp
   0x0000000000001169 <+8>:     movsd  xmm0,QWORD PTR [rip+0xe9f]        # 0x2010
   0x0000000000001171 <+16>:    movsd  QWORD PTR [rbp-0x18],xmm0
   0x0000000000001176 <+21>:    movsd  xmm0,QWORD PTR [rip+0xe9a]        # 0x2018
   0x000000000000117e <+29>:    movsd  QWORD PTR [rbp-0x10],xmm0
   0x0000000000001183 <+34>:    movsd  xmm0,QWORD PTR [rbp-0x18]
   0x0000000000001188 <+39>:    addsd  xmm0,QWORD PTR [rbp-0x10]
   0x000000000000118d <+44>:    movsd  QWORD PTR [rbp-0x8],xmm0
   0x0000000000001192 <+49>:    movsd  xmm0,QWORD PTR [rbp-0x8]
   0x0000000000001197 <+54>:    pop    rbp
   0x0000000000001198 <+55>:    ret

movss

movss xmm1, xmm2

Moves a scalar single-precision floating-point value from the source operand (second operand) to the destination operand (first operand).

movsd

movsd xmm1, xmm2

move scalar double-precision floating-point value between XMM registers and memory.

addsd

add scalar double-precision floating-point values

pxor

PXOR xmm1, xmm2/m128

Performs a bitwise xor of the source operand (an xmm2 register or a 128-bit memory location) and the destination operand (xmm1), and stores the result in xmm1. Its C++ equivalent is:

__m128i _mm_xor_si128 ( __m128i a, __m128i b)

_mm_xor_si128(__m128i a,__m128i b)

Computes the bitwise XOR of 128 bits (representing integer data) in a and b.