Grip (rev)

its...its...Grippy

This binary has an initialisation routine which involves printing :3 followed by calling exit(). We can bypass the exit() call and allow the program to continue running by patching the call to exit() with some other instruction such as a jmp. This allows the program to continue executing the main function.

The decompiled main code looks as follows:

undefined8 main(void)

{
  undefined8 *puVar1;
  long in_FS_OFFSET;
  ulong local_d0;
  undefined8 local_b8;
  undefined8 local_b0;
  undefined8 local_a8;
  undefined8 local_a0;
  undefined8 local_98;
  undefined8 local_90;
  undefined8 local_88;
  undefined8 local_80;
  undefined8 local_78;
  undefined8 local_70;
  undefined8 local_68;
  undefined8 local_60;
  undefined8 local_58;
  undefined8 local_50;
  undefined8 local_48;
  undefined5 local_40;
  undefined3 uStack_3b;
  undefined5 uStack_38;
  undefined8 local_33;
  long local_20;
  
  local_20 = *(long *)(in_FS_OFFSET + 0x28);
  local_b8 = 0xb21e71ba177bbaa7;
  local_b0 = 0xf2f2dad7f679ba96;
  local_a8 = 0xba32c30ab77bbaf2;
  local_a0 = 0xcbd3d5c3d1dbd14a;
  local_98 = 0xc9c4c481d848bac3;
  local_90 = 0xba22b77bba84c0ef;
  local_88 = 0xc0efc94aba2aa77b;
  local_80 = 0xef48bad483dbd384;
  local_78 = 0xbacdc9c284de81d2;
  local_70 = 0x3516a77bba2eb77b;
  local_68 = 0xea19f2f2f2f23eb7;
  local_60 = 0x22f7b644fd3eb779;
  local_58 = 0x3eb779307bb00271;
  local_50 = 0xf33eb77122f7a67a;
  local_48 = 0xba621084e93e8f71;
  local_40 = 0xba960ab779;
  uStack_3b = 0xd7f6d9;
  uStack_38 = 0x86f2f2f2da;
  local_33 = 0x313bf2f2f2f21af7;
  for (local_d0 = 0; local_d0 < 0x8d; local_d0 = local_d0 + 1) {
    *(byte *)((long)&local_b8 + local_d0) = *(byte *)((long)&local_b8 + local_d0) ^ 0xf2;
  }
  puVar1 = (undefined8 *)mmap((void *)0x0,0x8d,6,0x22,-1,0);
  *puVar1 = local_b8;
  puVar1[1] = local_b0;
  puVar1[2] = local_a8;
  puVar1[3] = local_a0;
  puVar1[4] = local_98;
  puVar1[5] = local_90;
  puVar1[6] = local_88;
  puVar1[7] = local_80;
  puVar1[8] = local_78;
  puVar1[9] = local_70;
  puVar1[10] = local_68;
  puVar1[0xb] = local_60;
  puVar1[0xc] = local_58;
  puVar1[0xd] = local_50;
  puVar1[0xe] = local_48;
  puVar1[0xf] = CONCAT35(uStack_3b,local_40);
  *(ulong *)((long)puVar1 + 0x7d) = CONCAT53(uStack_38,uStack_3b);
  *(undefined8 *)((long)puVar1 + 0x85) = local_33;
  (*(code *)puVar1)();
  puts(":3");
  if (local_20 != *(long *)(in_FS_OFFSET + 0x28)) {
                    /* WARNING: Subroutine does not return */
    __stack_chk_fail();
  }
  return 0;
}

Essentially, the local variables represent the bytecode to be executed. The bytecode first undergoes an XOR decryption operation, then the bytecode is loaded into the memory at the address puVar1 and executed.

I then set a breakpoint at the line where the bytecode is executed to see what the bytecode does. The bytecode basically writes characters into the stack then slowly decrypts them, giving us the flag: akasec{sh1tty_p4ck3d_b1n4ry}

Last updated