LabyREnth 2017: Binary 4

Hint: Send me this identifier together with your $$$$ to decrypt your file: da91e949f4c2e814f811fbadb3c195b8

Binary4 is a Mach-‍O binary, nothing IDA Pro can’t handle.

You will notice it has some obfuscated names, like wshwfknafsknfadj, pojklfasd, etc.

Looking at the main() function, IDA Pro’s decompiler produces the following pseudo-‍C code:

memset(&v17, 0, 0x400uLL);
v17 = 46;
LODWORD(v3) = wshwfknafsknfadj(&v17, 0LL);
v15 = v3;
LODWORD(v4) = bmasdfiukjwe(&v17);
v14 = v4;
if ( 999 != pojklfasd() )
{
  LODWORD(v9) = *(_BYTE *)(v15 + 1);
  LODWORD(v10) = *(_BYTE *)(v15 + 2);
  LODWORD(v11) = *(_BYTE *)(v15 + 3);
  LODWORD(v12) = *(_BYTE *)(v15 + 4);
  LODWORD(v13) = *(_BYTE *)(v15 + 5);
  __snprintf_chk(&v16, 0xDuLL, 0, 0xDuLL, "%02x%02x%02x%02x%02x%02x", *(_BYTE *)v15, v9, v10, v11, v12, v13);
  uygjhbjk(v14, &v16);
  LODWORD(v5) = weknfsdik(v15);
  LODWORD(v6) = oohbfhwebabje(v5, 20LL);
  printf("Send me this identifier together with your $$$$ to derypt your file: \n%s\n", v6);
}

Looks straight forward enough, so let’s start tracing what each of the function does. This binary can be solved simply by static analysis without actually having to run it on a Mac.

There are a few function calls here, let’s talk about the parts on top first, wshwfknafsknfadj and bmasdfiukjwe. Followed by the decision function pojklfasd which should return 999 for it to continue into the block.

Inside the if block, we have a snprintf call, followed by a call to uygjhbjk, then to weknfsdik and oohbfhwebabje before finally showing the message.

Top Half

We explore wshwfknafsknfadj function, which calls 2 other functions: erkhyhaksdfljkej and hirnihfjiwk.

v2 = malloc(6);
v5 = 0;
v5 = erkhyhaksdfljkej(&v4);
if ( !v5 )
{
  v5 = hirnihfjiwk(v4, v3, 6);
  if ( !v5 )
  {
    for ( i = 0; i < 6; ++i )
      v2[i] = v3[i];
  }
}
IOObjectRelease(v4);
return v2;

I would list the decompiled output for those functions, because they are pretty boilerplate code copied from some Apple API guide.

The function erkhyhaksdfljkej looks for the primary Ethernet interface, by specifying an IOPropertyMatch for IOPrimaryInterface to the IOServiceGetMatchingServices function.

If it succeeds (i.e. v5 == KERN_SUCCESS), it calls hirnihfjiwk with the iterator returned via v4. This function will then retrieve the MAC address of the matching Ethernet device into the buffer v3.

A for loop then copies the buffer yet again into a malloced buffer v2, which is returned to main().

pojklfasd

Referring back to the main function, this function must return a value != 999 for it to proceed. The pojklfasd does a simple check for the presence of a virtual machine by checking the system’s devices via ioreg:

FILE *f = popen(
      "ioreg -l | 
		grep -e Manufacturer -e 'Vendor Name' | 
		grep -e 'Parallels' -e VMware -e Fusion -e 'Virtual Box'",
      "r");
if ( f )
{
  if ( fgets(buf, BUFSIZE, f) )
  {
    ret = 999;
    v10 = 1;
  }
  else if ( pclose(f) )
  {
    v2 = printf("Command not found or exited with error status\n");
    ret = -1;
    v10 = 1;
    v7 = v2;
  }
  else
  {
    ret = 0;
    v10 = 1;
  }
}

In the case where grep finds a matching device, it spits out the matching lines on stdout, which means fgets() won’t be zero and ret = 999. When nothing matches and grep exits, that block will not be entered. Instead, grep exits with a non-zero status
returned via pclose() and ret becomes -1.

Bottom Half

Moving our attention back to main, a call is made to snprintf to create a lowercase hex string (%02x...) out of the MAC address:

LODWORD(v9) = *(_BYTE *)(v15 + 1);
LODWORD(v10) = *(_BYTE *)(v15 + 2);
LODWORD(v11) = *(_BYTE *)(v15 + 3);
LODWORD(v12) = *(_BYTE *)(v15 + 4);
LODWORD(v13) = *(_BYTE *)(v15 + 5);
__snprintf_chk(&v16, 0xDuLL, 0, 0xDuLL, "%02x%02x%02x%02x%02x%02x", *(_BYTE *)v15, v9, v10, v11, v12, v13);

While the decompiled output shows assignment to what looks like local variables v9 thru v13, these are actually spill-over variable arguments passed via the stack to snprintf. You can tell because the variable v9 starts at SP + 0:

__int64 v9; // [sp+0h] [bp-4A0h]@0
__int64 v10; // [sp+8h] [bp-498h]@0
__int64 v11; // [sp+10h] [bp-490h]@0
__int64 v12; // [sp+18h] [bp-488h]@0
__int64 v13; // [sp+20h] [bp-480h]@0

It’s odd that IDA Pro didn’t elide these assignments.