DRM the Big Blue way

A few months ago, I downloaded an evaluation copy of IBM’s XLC compiler to try it out on FFmpeg. The trial licence has now expired, so what better way to spend a few minutes than by cracking it?

The installation script, as expected, copied a number of files into a directory under /opt. More unusually, it also created a small shared library, libxlc101e.so.1, and placed it in /usr/lib. No other files from the installation package were modified, so this must be where the licence is hiding. Without further ado, we proceed to take it apart.

We begin by looking at the symbol table using readelf -s:

Symbol table '.symtab' contains 44 entries:
   Num:    Value  Size Type    Bind   Vis      Ndx Name
     0: 00000000     0 NOTYPE  LOCAL  DEFAULT  UND
     1: 000000b4     0 SECTION LOCAL  DEFAULT    1
     2: 0000017c     0 SECTION LOCAL  DEFAULT    2
     3: 0000036c     0 SECTION LOCAL  DEFAULT    3
     4: 0000055c     0 SECTION LOCAL  DEFAULT    4
     5: 00000580     0 SECTION LOCAL  DEFAULT    5
     6: 000005c4     0 SECTION LOCAL  DEFAULT    6
     7: 00010a3c     0 SECTION LOCAL  DEFAULT    7
     8: 00010a50     0 SECTION LOCAL  DEFAULT    8
     9: 00010ac8     0 SECTION LOCAL  DEFAULT    9
    10: 00010ad8     0 SECTION LOCAL  DEFAULT   10
    11: 00000000     0 SECTION LOCAL  DEFAULT   11
    12: 00000000     0 SECTION LOCAL  DEFAULT   12
    13: 00000000     0 SECTION LOCAL  DEFAULT   13
    14: 00000000     0 SECTION LOCAL  DEFAULT   14
    15: 00000000     0 FILE    LOCAL  DEFAULT  ABS xleval.c
    16: 00010a50     0 OBJECT  LOCAL  HIDDEN   ABS _DYNAMIC
    17: 00010acc     0 OBJECT  LOCAL  HIDDEN   ABS _GLOBAL_OFFSET_TABLE_
    18: 000005f0    24 OBJECT  GLOBAL DEFAULT    6 xlc_extended_eval_lic_dir
    19: 00000660    22 OBJECT  GLOBAL DEFAULT    6 libxlfextendeval_name
    20: 00000738    42 OBJECT  GLOBAL DEFAULT    6 stm_compiler_name
    21: 00000640    16 OBJECT  GLOBAL DEFAULT    6 libupclicense_name
    22: 00000764    32 OBJECT  GLOBAL DEFAULT    6 xlf_compiler_name
    23: 00000580    68 FUNC    GLOBAL DEFAULT    5 _xlgetevalbeta
    24: 00000678    22 OBJECT  GLOBAL DEFAULT    6 libxlcextendeval_name
    25: 00000608    24 OBJECT  GLOBAL DEFAULT    6 xlf_extended_eval_lic_dir
    26: 000006d8    17 OBJECT  GLOBAL DEFAULT    6 xlcmp_name
    27: 000006c0    12 OBJECT  GLOBAL DEFAULT    6 xlc_package_name
    28: 00000650    16 OBJECT  GLOBAL DEFAULT    6 libstmlicense_name
    29: 000005e0    16 OBJECT  GLOBAL DEFAULT    6 xlf_extend_eval_env_var
    30: 000005d0    16 OBJECT  GLOBAL DEFAULT    6 xlc_extend_eval_env_var
    31: 00000620    16 OBJECT  GLOBAL DEFAULT    6 libxlflicense_name
    32: 00010cd0     0 NOTYPE  GLOBAL DEFAULT  ABS __bss_start
    33: 00010a3c    20 OBJECT  GLOBAL DEFAULT    7 _xlevalbeta
    34: 000005c4    10 OBJECT  GLOBAL DEFAULT    6 liblicense_dir
    35: 00000690    22 OBJECT  GLOBAL DEFAULT    6 libupcextendeval_name
    36: 000006ec    30 OBJECT  GLOBAL DEFAULT    6 xlc_compiler_name
    37: 00000630    16 OBJECT  GLOBAL DEFAULT    6 libxlclicense_name
    38: 00010cd0     0 NOTYPE  GLOBAL DEFAULT  ABS _edata
    39: 00010cd0     0 NOTYPE  GLOBAL DEFAULT  ABS _end
    40: 000006cc    12 OBJECT  GLOBAL DEFAULT    6 xlf_package_name
    41: 000006a8    22 OBJECT  GLOBAL DEFAULT    6 libstmextendeval_name
    42: 00010ad8   504 OBJECT  GLOBAL DEFAULT   10 versionString
    43: 0000070c    42 OBJECT  GLOBAL DEFAULT    6 upc_compiler_name

Notice the lone function at position 23, _xlgetevalbeta, which we proceed to disassemble:

00000580 <_xlgetevalbeta>:
 580:   94 21 ff f0     stwu    r1,-16(r1)
 584:   93 c1 00 08     stw     r30,8(r1)
 588:   93 e1 00 0c     stw     r31,12(r1)
 58c:   7c 3f 0b 78     mr      r31,r1
 590:   7d 88 02 a6     mflr    r12
 594:   42 9f 00 05     bcl-    20,4*cr7+so,598 <_xlgetevalbeta+0x18>
 598:   7f c8 02 a6     mflr    r30
 59c:   3f de 00 01     addis   r30,r30,1
 5a0:   3b de 05 34     addi    r30,r30,1332
 5a4:   7d 88 03 a6     mtlr    r12
 5a8:   80 1e ff fc     lwz     r0,-4(r30)
 5ac:   7c 03 03 78     mr      r3,r0
 5b0:   81 61 00 00     lwz     r11,0(r1)
 5b4:   83 cb ff f8     lwz     r30,-8(r11)
 5b8:   83 eb ff fc     lwz     r31,-4(r11)
 5bc:   7d 61 5b 78     mr      r1,r11
 5c0:   4e 80 00 20     blr

This is fairly standard, unoptimised code. After saving a few registers on the stack, it computes the address of the global offset table: 0x598 + 0x10000 + 1332 = 0x10acc, matching _GLOBAL_OFFSET_TABLE_ from the symbol table. Next, a value is loaded from the GOT, forming the return value of the function after the stack has been restored.

To find out what this return value really is, we look at the relocation table (by means of readelf -r):

Relocation section '.rela.dyn' at offset 0x55c contains 3 entries:
 Offset     Info    Type            Sym.Value  Sym. Name + Addend
00010a40  00000016 R_PPC_RELATIVE                               00000784
00010a44  00000016 R_PPC_RELATIVE                               0000097c
00010ac8  00001414 R_PPC_GLOB_DAT    00010a3c   _xlevalbeta + 0

The third entry is the one we are looking for: its offset matches the location read by the code at 0x5a8. This means the _xlgetevalbeta function is returning a pointer to _xlevalbeta, which makes some kind of sense.

Another quick look at the symbol table tells us _xlevalbeta lives at address 0x10a3c and is 20 bytes in size. The section header (provided by readelf -S) helps us find the corresponding location in the file:

Section Headers:
  [Nr] Name              Type            Addr     Off    Size   ES Flg
  [ 0]                   NULL            00000000 000000 000000 00
  [ 1] .hash             HASH            000000b4 0000b4 0000c8 04   A
  [ 2] .dynsym           DYNSYM          0000017c 00017c 0001f0 10   A
  [ 3] .dynstr           STRTAB          0000036c 00036c 0001ee 00   A
  [ 4] .rela.dyn         RELA            0000055c 00055c 000024 0c   A
  [ 5] .text             PROGBITS        00000580 000580 000044 00  AX
  [ 6] .rodata           PROGBITS        000005c4 0005c4 000475 00   A
  [ 7] .data.rel.ro      PROGBITS        00010a3c 000a3c 000014 00  WA
  [ 8] .dynamic          DYNAMIC         00010a50 000a50 000078 08  WA
  [ 9] .got              PROGBITS        00010ac8 000ac8 000010 04  WA
  [10] .data             PROGBITS        00010ad8 000ad8 0001f8 00  WA
  [11] .comment          PROGBITS        00000000 000cd0 000028 00
  [12] .shstrtab         STRTAB          00000000 000cf8 000073 00
  [13] .symtab           SYMTAB          00000000 000fc4 0002c0 10
  [14] .strtab           STRTAB          00000000 001284 000206 00
Key to Flags:
  W (write), A (alloc), X (execute), M (merge), S (strings)
  I (info), L (link order), G (group), x (unknown)
  O (extra OS processing required) o (OS specific), p (processor specific)

The address we are looking for is at the start of the .data.rel.ro section, which can be found at offset 0xa3c in the file. It is time for the hexdump tool:

00000a30  69 62 69 74 65 64 2e 00  00 00 00 00 00 00 00 01
00000a40  00 00 00 00 00 00 00 00  00 00 24 05 4a 0c 65 c8

The last four bytes here, 4a 0c 65 c8, are interesting. Taken as a 32-bit big endian value, they are exactly equal to the modification time of the file, or in other words, the time the compiler was installed. This cannot be a coincidence, so using a hex editor, we replace this with the current time, 4a 80 74 31.

Lo and behold, the compiler is working again.

One hopes the engineers at IBM developing the compiler are not the same ones thinking this copy protection method was a good idea. Then again, perhaps they are; it failed miserably at compiling FFmpeg.

Bookmark the permalink.

2 Responses to DRM the Big Blue way

  1. ritchan says:

    Wow – that seemed relatively easy. I always thought cracking software was hard.

  2. Grumblings I’ve heard from “people that know” say the XL C is a pretty good compiler and sticks to the standards. Generally, you need to do some light patching and usually need to use some extra compiler flags since many projects use nonstandard GNU stuff.

    This may help a bit http://www.perzl.org/aix/index.php?n=Main.Instructions, also look through his patches to see common things.

Leave a Reply

Your email address will not be published. Required fields are marked *