./make all

Cracking a binary


Piracy is bad, but when you’re a broke student and you find a cracked launcher for a game you want to play, you don’t ask questions. You simply replace the launcher with the “cracked” copy and start playing. Not me—I would never do it—but I know people who have.

It’s wrong, very wrong, but at the same time, it’s fascinating how a single file you can’t even read can bypass the security put in place by a multi-million-dollar game project.

There have been several methods used to lock games down—and just as many tricks to break them open. I’ll dive into some of the techniques used in the early 2000s to crack PC games, online Flash games, and mock license servers.

Let’s start with a toy problem, pretty basic piece of code with some intentional inclusions. There are two global variables msga and msgb. Two user defined routines allow and deny. One conditional call to an external program using execvp. The idea here is to examine the executable this program creates, find out where the code lands in the executable and what the compiler adds on top of it, and then modify the binary directly and change its behaviour.

GitHub: examinebin.c

#include <stdio.h>
#include <unistd.h>

char *msga = "Allow";
char *msgb = "Deny";

void allow() {
    printf("%s\n", msga);
}

void deny() {
    printf("%s\n", msgb);
}

int main(int argc, char **argv) {
    deny();
    int runExternal = 0;
    if (runExternal) {
        char* lsargs[] = {"ls", "-l", NULL};
        execvp("ls", lsargs);
    }
}

compile the code with debug symbol and then use objdump to get the disassembled output of the code.

gcc -g examinebin.c -o examinebin
objdump -d examinebin

We are interested in Disassembly of section .text in the objdump output and looking for allow, deny and cmp and jmp instruction in main

Once the exploration is done it is time to change the behaviour of this code:

  • call allow instead of deny.
  • change runExternal flag value to non-zero.
  • change "-l" to "-a" in lsargs`

Replace deny

The hexadecimal code calling deny from objdump output

0000000000001189 <allow>:
00000000000011a3 <deny>:
00000000000011bd <main>:
    11e4:	e8 ba ff ff ff       	call   11a3 <deny>

from the call reference we know that opcode e8 takes the operand ba ff ff ff (0xffffffba) which is basically the offset from next instruction 0x11e9. So, it should point to (0x11a3)

offset = hex(0xffffffba - 0x100000000) # getting the negative value
deny_addr = hex(0x11e9 + int(offset, 16))
print(deny_addr)

To call allow instead we will have to change (0xffffffba) to something that gives (0x1189) instead.

allow_addr = hex(0x1189)
offset = hex(int(hex(int(allow_addr, 16) - 0x11e9), 16) + 0x100000000)
print(offset)
# 0xffffffa0 -> a0 ff ff ff

So all we need to do is change ba to a0 in the binary

Change runExternal

This is quite simple, all we need to do is locate the mov instruction that is putting the value in the flag.

    11e9:    c7 45 dc 00 00 00 00     movl   $0x0,-0x24(%rbp)

and change the value to any non-zero one. ref

00 00 00 00 -> 01 00 00 00

Change command flag "-l"

We basically want to change the arguments going into execvp function call. In the assembly we can see the location where the call to execvp has been made and there should be push or lea instruction before that to add the argument into the stack. Since these values are hardcoded in the binary all we need to do is get the location of -l and change it to -a.

    11f6:	48 8d 05 12 0e 00 00 	lea    0xe12(%rip),%rax        # 200f <_IO_stdin_used+0xf>
    11fd:	48 89 45 e0          	mov    %rax,-0x20(%rbp)
    1201:	48 8d 05 0a 0e 00 00 	lea    0xe0a(%rip),%rax        # 2012 <_IO_stdin_used+0x12>
    1208:	48 89 45 e8          	mov    %rax,-0x18(%rbp)
    121b:	48 8d 05 ed 0d 00 00 	lea    0xded(%rip),%rax        # 200f <_IO_stdin_used+0xf>
    1222:	48 89 c7             	mov    %rax,%rdi
    1225:	e8 66 fe ff ff       	call   1090 <execvp@plt>

lea instruction is basically calculating the effective address which is offset to the next instruction pointer. So we have three addresses, which can be calculated or seen in the objdump output as well.

print(hex(0xe12 + 0x11fd)) # 0x200f
print(hex(0xe0a + 0x1208)) # 0x2012
print(hex(0xded + 0x1222)) # 0x200f

from the xxd output we can clearly see that our strings are really there.

00002000: 0100 0200 416c 6c6f 7700 4465 6e79 006c  ....Allow.Deny.l
00002010: 7300 2d6c 0000 0000 011b 033b 4400 0000  s.-l.......;D...
00002020: 0700 0000 08f0 ffff 7800 0000 48f0 ffff  ........x...H...

Changing the fourth byte from the right 00002010: 6c -> 61 will make l->a.

Changes

Summary of the changes to modify the xxd output

xxd examinebin > examinebin.txt

Changes for allow

000011e0: 0000 0000 e8(ba) ffff ffc7 45dc 0000 0000
000011e0: 0000 0000 e8(a0) ffff ffc7 45dc 0000 0000

Changes for runExternal flag

000011e0: 0000 0000 e8ba ffff ffc7 45dc (00)00 0000
000011e0: 0000 0000 e8ba ffff ffc7 45dc (01)00 0000

Changes for l -> a

00002010: 7300 2d(6c) 0000 0000 011b 033b 5400 0000
00002010: 7300 2d(61) 0000 0000 011b 033b 5400 0000

final diff

< 000011e0: 0000 0000 e8ba ffff ffc7 45dc 0000 0000
---
> 000011e0: 0000 0000 e8a0 ffff ffc7 45dc 0100 0000

< 00002010: 7300 2d6c 0000 0000 011b 033b 4400 0000
---
> 00002010: 7300 2d61 0000 0000 011b 033b 4400 0000

Now that we have the modified text file we can create an executable using xxd

xxd -r  examinebin-mod.txt > examinebin-mod.out
chmod +x examinebin-mod.out
./examinebin-mod.out

Running the modified binary will print Allow and run the ls with -a options.

Some handy command line tools

  • file

utility that gives the file name, file type and other format related information.

  • sum

Get the checksum and number of blocks in the file. Once we do some reverese engineering this output will tell us that the new executable is not genuine.

  • ldd

Gives the list of shared objects required by the executable

  • strings

Displays all printable characters and strings in the file. Works on any file in fact not just executable

  • nm

Lists all the symbols present in the executable file address map.

  • xxd or hexdump

These are plain read-write tools to deal with binary files and not just executables. Reading part creates a text file showing hexadecimal values at each byte and if possible there is a printable version side by side. Any changes to this output text file can be fed back to the tool, which can then create a binary file.

  • objdump

using the -d option you can get the detailed version of each section and segment of your executable along with the interpreted assembly instruction.

SRE Framework

https://github.com/NationalSecurityAgency/ghidra

Debugger

https://github.com/x64dbg/x64dbg

A real binary

This was all fun and a bit too easy. When the code is this simple and written by yourself it is not too difficult to crack. For the next phase of this experiment I’ll try to see what I can do with a real game binary https://archive.org/details/Wolfenstein3d